TransGUI/0000755000000000000000000000000012261774331011216 5ustar rootrootTransGUI/connoptions.pas0000644000000000000000000004101612261763702014276 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit ConnOptions; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, Spin, ComCtrls, Buttons, ButtonPanel, ExtCtrls, BaseForm; const DefSpeeds = '0,10,25,50,100,250,500,750,1000,2500,5000,7000'; resourcestring sNoHost = 'No host name specified.'; sNoProxy = 'No proxy server specified.'; SDelConnection = 'Are you sure to delete connection ''%s''?'; SNewConnection = 'New connection to Transmission'; type { TConnOptionsForm } TConnOptionsForm = class(TBaseForm) btNew: TButton; btDel: TButton; btRename: TButton; Buttons: TButtonPanel; cbProxyAuth: TCheckBox; cbUseProxy: TCheckBox; cbUseSocks5: TCheckBox; cbAuth: TCheckBox; cbShowAdvanced: TCheckBox; cbAskPassword: TCheckBox; edRpcPath: TEdit; edUpSpeeds: TEdit; edHost: TEdit; cbSSL: TCheckBox; cbConnection: TComboBox; edDownSpeeds: TEdit; edProxy: TEdit; edProxyPassword: TEdit; edProxyPort: TSpinEdit; edProxyUserName: TEdit; edUserName: TEdit; edPassword: TEdit; edPaths: TMemo; gbSpeed: TGroupBox; txRpcPath: TLabel; txConName: TLabel; txConnHelp: TLabel; txDownSpeeds: TLabel; panTop: TPanel; tabProxy: TTabSheet; tabMisc: TTabSheet; txUpSpeeds: TLabel; txPaths: TLabel; tabPaths: TTabSheet; Page: TPageControl; tabConnection: TTabSheet; txProxy: TLabel; txProxyPassword: TLabel; txProxyPort: TLabel; txProxyUserName: TLabel; txUserName: TLabel; txPort: TLabel; edPort: TSpinEdit; txHost: TLabel; txPassword: TLabel; procedure btDelClick(Sender: TObject); procedure btNewClick(Sender: TObject); procedure btOKClick(Sender: TObject); procedure btRenameClick(Sender: TObject); procedure cbAskPasswordClick(Sender: TObject); procedure cbAuthClick(Sender: TObject); procedure cbConnectionSelect(Sender: TObject); procedure cbProxyAuthClick(Sender: TObject); procedure cbShowAdvancedClick(Sender: TObject); procedure cbUseProxyClick(Sender: TObject); procedure edHostChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure tabPathsShow(Sender: TObject); private FCurConn: string; FCurHost: string; edConnection: TEdit; function Validate: boolean; procedure BeginEdit; procedure EndEdit; procedure SaveConnectionsList; public ActiveConnection: string; ActiveSettingChanged: boolean; procedure LoadConnSettings(const ConnName: string); procedure SaveConnSettings(const ConnName: string); function IsConnSettingsChanged(const ConnName: string): boolean; end; implementation uses Main, synacode, utils, rpc; { TConnOptionsForm } procedure TConnOptionsForm.btOKClick(Sender: TObject); begin if not Validate then exit; EndEdit; SaveConnSettings(FCurConn); SaveConnectionsList; ModalResult:=mrOk; end; procedure TConnOptionsForm.btRenameClick(Sender: TObject); begin if edConnection.Visible then begin if Trim(edConnection.Text) = '' then exit; EndEdit; exit; end; if cbConnection.Text = '' then exit; BeginEdit; ActiveControl:=edConnection; edConnection.SelectAll; end; procedure TConnOptionsForm.cbAskPasswordClick(Sender: TObject); begin EnableControls(not cbAskPassword.Checked and cbAskPassword.Enabled, [txPassword, edPassword]); end; procedure TConnOptionsForm.cbAuthClick(Sender: TObject); begin EnableControls(cbAuth.Checked, [txUserName, edUserName, txPassword, cbAskPassword]); cbAskPasswordClick(nil); end; procedure TConnOptionsForm.cbConnectionSelect(Sender: TObject); var i: integer; s: string; begin if edConnection.Visible then exit; i:=cbConnection.ItemIndex; if i >= 0 then s:=cbConnection.Items[i] else s:=''; if (FCurConn <> s) and (FCurConn <> '') then begin if not Validate then begin cbConnection.ItemIndex:=cbConnection.Items.IndexOf(FCurConn); exit; end; SaveConnSettings(FCurConn); end; if s <> '' then LoadConnSettings(s); end; procedure TConnOptionsForm.cbProxyAuthClick(Sender: TObject); begin EnableControls(cbProxyAuth.Checked and cbProxyAuth.Enabled, [txProxyUserName, edProxyUserName, txProxyPassword, edProxyPassword]); end; procedure TConnOptionsForm.cbShowAdvancedClick(Sender: TObject); begin txRpcPath.Visible:=cbShowAdvanced.Checked; edRpcPath.Visible:=cbShowAdvanced.Checked; {$ifndef LCLgtk2} tabConnection.TabVisible:=cbShowAdvanced.Checked; {$endif LCLgtk2} tabProxy.TabVisible:=cbShowAdvanced.Checked; tabPaths.TabVisible:=cbShowAdvanced.Checked; tabMisc.TabVisible:=cbShowAdvanced.Checked; cbShowAdvanced.Visible:=not cbShowAdvanced.Checked; Page.ActivePage:=tabConnection; end; procedure TConnOptionsForm.btNewClick(Sender: TObject); begin EndEdit; if (FCurConn <> '') and not Validate then exit; SaveConnSettings(FCurConn); LoadConnSettings(''); BeginEdit; edConnection.Text:=''; Page.ActivePage:=tabConnection; ActiveControl:=edHost; end; procedure TConnOptionsForm.btDelClick(Sender: TObject); var i: integer; begin if edConnection.Visible or (cbConnection.Text = '') then exit; if MessageDlg('', Format(SDelConnection, [cbConnection.Text]), mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then exit; if FCurConn <> '' then begin Ini.EraseSection('Connection.' + FCurConn); Ini.EraseSection('Connection'); Ini.EraseSection('AddTorrent.' + FCurConn); i:=cbConnection.ItemIndex; if i >= 0 then begin cbConnection.Items.Delete(i); if i >= cbConnection.Items.Count then begin i:=cbConnection.Items.Count - 1; if i < 0 then i:=0; end; end else i:=0; if i < cbConnection.Items.Count then cbConnection.ItemIndex:=i else cbConnection.ItemIndex:=-1; end else cbConnection.ItemIndex:=-1; if cbConnection.ItemIndex >= 0 then begin if FCurConn = ActiveConnection then ActiveConnection:=''; LoadConnSettings(cbConnection.Items[cbConnection.ItemIndex]); if ActiveConnection = '' then ActiveConnection:=FCurConn; end else begin FCurConn:=''; btNewClick(nil); end; SaveConnectionsList; end; procedure TConnOptionsForm.cbUseProxyClick(Sender: TObject); begin EnableControls(cbUseProxy.Checked, [txProxy, edProxy, txProxyPort, edProxyPort, cbUseSocks5, cbProxyAuth]); cbProxyAuthClick(nil); end; procedure TConnOptionsForm.edHostChange(Sender: TObject); begin if edConnection.Visible and (edConnection.Text = FCurHost) then edConnection.Text:=edHost.Text; FCurHost:=edHost.Text; end; procedure TConnOptionsForm.FormCreate(Sender: TObject); var i, cnt: integer; s: string; begin Page.ActivePageIndex:=0; txConnHelp.Caption:=Format(txConnHelp.Caption, [AppName]); ActiveControl:=edHost; Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@btOKClick; edConnection:=TEdit.Create(cbConnection.Parent); edConnection.Visible:=False; edConnection.BoundsRect:=cbConnection.BoundsRect; edConnection.Parent:=cbConnection.Parent; cnt:=Ini.ReadInteger('Hosts', 'Count', 0); for i:=1 to cnt do begin s:=Ini.ReadString('Hosts', Format('Host%d', [i]), ''); if s <> '' then cbConnection.Items.Add(s); end; cbShowAdvanced.Top:=edRpcPath.Top; end; procedure TConnOptionsForm.FormShow(Sender: TObject); begin if edConnection.Visible then exit; if cbConnection.Items.Count = 0 then begin btNewClick(nil); exit; end; cbConnection.ItemIndex:=cbConnection.Items.IndexOf(ActiveConnection); if cbConnection.ItemIndex < 0 then cbConnection.ItemIndex:=0; LoadConnSettings(cbConnection.Text); end; procedure TConnOptionsForm.tabPathsShow(Sender: TObject); var R: TRect; begin R:=edPaths.BoundsRect; R.Top:=txPaths.BoundsRect.Bottom + 8; edPaths.BoundsRect:=R; end; function TConnOptionsForm.Validate: boolean; begin Result:=False; edHost.Text:=Trim(edHost.Text); if Trim(edHost.Text) = '' then begin Page.ActivePage:=tabConnection; edHost.SetFocus; MessageDlg(sNoHost, mtError, [mbOK], 0); exit; end; edProxy.Text:=Trim(edProxy.Text); if tabProxy.TabVisible and cbUseProxy.Checked and (edProxy.Text = '') then begin Page.ActivePage:=tabProxy; edProxy.SetFocus; MessageDlg(sNoProxy, mtError, [mbOK], 0); exit; end; Result:=True; end; procedure TConnOptionsForm.EndEdit; procedure RenameSection(const OldName, NewName: string); var i: integer; sl: TStringList; begin sl:=TStringList.Create; with Ini do try ReadSectionValues(OldName, sl); for i:=0 to sl.Count - 1 do WriteString(NewName, sl.Names[i], sl.ValueFromIndex[i]); EraseSection(OldName); finally sl.Free; end; end; var NewName, s: string; i, p: integer; begin if not edConnection.Visible then exit; NewName:=Trim(edConnection.Text); if NewName = '' then NewName:=Trim(edHost.Text); if NewName <> FCurConn then begin if FCurConn <> '' then begin p:=cbConnection.Items.IndexOf(FCurConn); if p >= 0 then cbConnection.Items.Delete(p); end else p:=-1; i:=1; s:=NewName; while cbConnection.Items.IndexOf(NewName) >= 0 do begin Inc(i); NewName:=Format('%s (%d)', [s, i]); end; if FCurConn <> '' then begin RenameSection('Connection.' + FCurConn, 'Connection.' + NewName); RenameSection('AddTorrent.' + FCurConn, 'AddTorrent.' + NewName); end; if p >= 0 then cbConnection.Items.Insert(p, NewName) else cbConnection.Items.Add(NewName); if (FCurConn = ActiveConnection) or (FCurConn = '') then ActiveConnection:=NewName; FCurConn:=NewName; SaveConnectionsList; end; cbConnection.ItemIndex:=cbConnection.Items.IndexOf(NewName); cbConnection.Visible:=True; edConnection.Visible:=False; end; procedure TConnOptionsForm.SaveConnectionsList; var i: integer; begin with Ini do begin WriteString('Hosts', 'CurHost', ActiveConnection); WriteInteger('Hosts', 'Count', cbConnection.Items.Count); for i:=0 to cbConnection.Items.Count - 1 do WriteString('Hosts', Format('Host%d', [i + 1]), cbConnection.Items[i]); UpdateFile; end; end; procedure TConnOptionsForm.BeginEdit; var i: integer; begin i:=cbConnection.ItemIndex; if i >= 0 then edConnection.Text:=cbConnection.Items[i] else edConnection.Text:=''; edConnection.Visible:=True; cbConnection.Visible:=False; end; procedure TConnOptionsForm.LoadConnSettings(const ConnName: string); var Sec, s: string; begin with Ini do begin Sec:='Connection.' + ConnName; if (ConnName <> '') and not SectionExists(Sec) then Sec:='Connection'; edHost.Text:=ReadString(Sec, 'Host', ''); FCurHost:=edHost.Text; edPort.Value:=ReadInteger(Sec, 'Port', 9091); cbSSL.Checked:=ReadBool(Sec, 'UseSSL', False); edUserName.Text:=ReadString(Sec, 'UserName', ''); cbAuth.Checked:=edUserName.Text <> ''; if cbAuth.Checked then begin s:=ReadString(Sec, 'Password', ''); cbAskPassword.Checked:=s = '-'; if not cbAskPassword.Checked then if s <> '' then edPassword.Text:='******' else edPassword.Text:=''; end; cbAuthClick(nil); edRpcPath.Text:=ReadString(Sec, 'RpcPath', DefaultRpcPath); cbUseProxy.Checked:=ReadBool(Sec, 'UseProxy', False); cbUseSocks5.Checked:=ReadBool(Sec, 'UseSockProxy', False); edProxy.Text:=ReadString(Sec, 'ProxyHost', ''); edProxyPort.Value:=ReadInteger(Sec, 'ProxyPort', 8080); edProxyUserName.Text:=ReadString(Sec, 'ProxyUser', ''); cbProxyAuth.Checked:=edProxyUserName.Text <> ''; if cbProxyAuth.Checked then if ReadString(Sec, 'ProxyPass', '') <> '' then edProxyPassword.Text:='******' else edProxyPassword.Text:=''; edPaths.Text:=StringReplace(ReadString(Sec, 'PathMap', ''), '|', LineEnding, [rfReplaceAll]); edDownSpeeds.Text:=ReadString(Sec, 'DownSpeeds', DefSpeeds); edUpSpeeds.Text:=ReadString(Sec, 'UpSpeeds', DefSpeeds); cbUseProxyClick(nil); end; FCurConn:=ConnName; FCurHost:=edHost.Text; end; procedure TConnOptionsForm.SaveConnSettings(const ConnName: string); var Sec: string; i: integer; s: string; begin if ConnName = '' then exit; if ConnName = ActiveConnection then if IsConnSettingsChanged(ConnName) then ActiveSettingChanged:=True; with Ini do begin Sec:='Connection.' + ConnName; WriteString(Sec, 'Host', Trim(edHost.Text)); WriteBool(Sec, 'UseSSL', cbSSL.Checked); WriteInteger(Sec, 'Port', edPort.Value); if not cbAuth.Checked then begin edUserName.Text:=''; edPassword.Text:=''; cbAskPassword.Checked:=False; end; WriteString(Sec, 'UserName', edUserName.Text); if cbAskPassword.Checked then WriteString(Sec, 'Password', '-') else if edPassword.Text <> '******' then begin if edPassword.Text = '' then s:='' else s:=EncodeBase64(edPassword.Text); WriteString(Sec, 'Password', s); end; if (edRpcPath.Text = DefaultRpcPath) or (edRpcPath.Text = '') then DeleteKey(Sec, 'RpcPath') else WriteString(Sec, 'RpcPath', edRpcPath.Text); WriteBool(Sec, 'UseProxy', cbUseProxy.Checked); WriteBool(Sec, 'UseSockProxy', cbUseSocks5.Checked); WriteString(Sec, 'ProxyHost', Trim(edProxy.Text)); WriteInteger(Sec, 'ProxyPort', edProxyPort.Value); if cbProxyAuth.Checked then begin edProxyUserName.Text:=''; edProxyPassword.Text:=''; end; WriteString(Sec, 'ProxyUser', edProxyUserName.Text); if edProxyPassword.Text <> '******' then begin if edProxyPassword.Text = '' then s:='' else s:=EncodeBase64(edProxyPassword.Text); WriteString(Sec, 'ProxyPass', s); end; WriteString(Sec, 'PathMap', StringReplace(edPaths.Text, LineEnding, '|', [rfReplaceAll])); WriteString(Sec, 'DownSpeeds', Trim(edDownSpeeds.Text)); WriteString(Sec, 'UpSpeeds', Trim(edUpSpeeds.Text)); i:=cbConnection.Items.IndexOf(ConnName); if i < 0 then cbConnection.Items.Insert(0, ConnName); UpdateFile; end; end; function TConnOptionsForm.IsConnSettingsChanged(const ConnName: string): boolean; var Sec: string; begin with Ini do begin Sec:='Connection.' + ConnName; if not SectionExists(Sec) then Sec:='Connection'; Result:=(edPort.Value <> ReadInteger(Sec, 'Port', 9091)) or (edHost.Text <> ReadString(Sec, 'Host', '')) or (cbSSL.Checked <> ReadBool(Sec, 'UseSSL', False)) or (edUserName.Text <> ReadString(Sec, 'UserName', '')) or ((ReadString(Sec, 'Password', '') = '') and (edPassword.Text <> '')) or ((ReadString(Sec, 'Password', '') <> '') and (edPassword.Text <> '******')) or (edRpcPath.Text <> ReadString(Sec, 'RpcPath', DefaultRpcPath)) or (cbUseProxy.Checked <> ReadBool(Sec, 'UseProxy', False)) or (edProxy.Text <> ReadString(Sec, 'ProxyHost', '')) or (edProxyPort.Value <> ReadInteger(Sec, 'ProxyPort', 8080)) or (edProxyUserName.Text <> ReadString(Sec, 'ProxyUser', '')) or ((ReadString(Sec, 'ProxyPass', '') = '') and (edProxyPassword.Text <> '')) or ((ReadString(Sec, 'ProxyPass', '') <> '') and (edProxyPassword.Text <> '******')) or (edPaths.Text <> StringReplace(ReadString(Sec, 'PathMap', ''), '|', LineEnding, [rfReplaceAll])) or (edDownSpeeds.Text <> ReadString(Sec, 'DownSpeeds', '')) or (edUpSpeeds.Text <> ReadString(Sec, 'UpSpeeds', '')) ; end; end; initialization {$I connoptions.lrs} end. TransGUI/trcomp.pas0000644000000000000000000000053711427210747013233 0ustar rootroot{ This file was automatically created by Lazarus. do not edit! This source is only used to compile and install the package. } unit trcomp; interface uses VarGrid, LazarusPackageIntf; implementation procedure Register; begin RegisterUnit('VarGrid', @VarGrid.Register); end; initialization RegisterPackage('trcomp', @Register); end. TransGUI/restranslator.pas0000644000000000000000000005552212230561601014625 0ustar rootroot{************************************************************ Copyright (c) 2010 Alex Cherednichenko, aka Alex7Che. Copyright (c) 2011-2013 Yury Sidorov. Published at GNU General Public License as Free Software. ************************************************************} unit ResTranslator; {$MODE objfpc}{$H+} interface uses Classes, StrUtils, SysUtils, FileUtil, LResources, TypInfo, LCLProc; type TWordDelimitersOptions = set of (wdIgnoreLeading, wdIgnoreTrailing); TResTranslator = class; TTranslateStringEvent = procedure(Sender: TResTranslator; const ResourceName: AnsiString; var Accept: boolean); TTranslateStringOption = (tsoNew, tsoUsed, tsoExternal); TTranslateStringOptions = set of TTranslateStringOption; { TTranslateStringList } TTranslateStringList = class(TStringList) private function CorrectGetName(Index: integer): string; function CorrectGetValue(const Name: string): string; function GetOptions(Index: integer): TTranslateStringOptions; function NormaliseQuotedStr(const S: string): string; function ScanQuotSep(P: PChar):integer; procedure SetOptions(Index: integer; const AValue: TTranslateStringOptions); protected function DoCompareText(const s1,s2 : string) : PtrInt; override; public constructor Create(const FileName: string); overload; function IndexOfName(const Name: string; var Offset: integer): integer; function IndexOfName(const Name: string): integer; override; procedure LoadFromFile(const FileName: string); override; procedure SaveToFile(const FileName: string); override; procedure Merge(Source: TTranslateStringList; const NamesOnly: boolean = false); property CValues[const Name: string]: string read CorrectGetValue; property CNames[Index: integer]: string read CorrectGetName; property Options[Index: integer]: TTranslateStringOptions read GetOptions write SetOptions; end; { TResTranslator } TResTranslator = class(TAbstractTranslator) private FIgnoreDelimiters: TWordDelimitersOptions; FOnTranslateString: TTranslateStringEvent; FStrResLst: TTranslateStringList; FTranslationFile: string; FModified: boolean; FTranslationLanguage: AnsiString; FWordDelims: TSysCharset; function GetStrings: TStringList; procedure SetIgnoreDelimiters(const AValue: TWordDelimitersOptions); procedure SetOnTranslateString(const AValue: TTranslateStringEvent); procedure SetWordDelims(const AValue: TSysCharset); function InternalTranslateString(const Value: AnsiString; IsExternal: boolean = False): AnsiString; public constructor Create(TranslationFile: AnsiString); destructor Destroy; override; procedure TranslateStringProperty(Sender: TObject; const Instance: TPersistent; PropInfo: PPropInfo; var Content: string); override; procedure SaveFile; overload; procedure SaveFile(const aFileName: string); overload; property Modified: boolean Read FModified; property Strings: TStringList Read GetStrings; property IgnoreDelimiters: TWordDelimitersOptions Read FIgnoreDelimiters Write SetIgnoreDelimiters; property WordDelims: TSysCharset Read FWordDelims Write SetWordDelims; property TranslationLanguage: AnsiString read FTranslationLanguage; property OnTranslateString: TTranslateStringEvent Read FOnTranslateString Write SetOnTranslateString; end; function LoadTranslationFile(const TranslationFile: AnsiString; const OnTranslate: TTranslateStringEvent = nil): AnsiString; procedure SaveTranslationFile; overload; procedure SaveTranslationFile(const FileName: AnsiString); overload; procedure MakeTranslationFile; overload; procedure MakeTranslationFile(Language: AnsiString); overload; procedure MakeTranslationFile(const FileName, Language: AnsiString); overload; procedure SupplementTranslationFile(const FileName: AnsiString); procedure SupplementTranslationFiles; overload; procedure SupplementTranslationFiles(const TranslationFilesPath: AnsiString); overload; function LoadDefaultTranslationFile(const OnTranslate: TTranslateStringEvent = nil): TFileName; function LoadDefaultTranslationFile(const TranslationFilesPath: AnsiString; const OnTranslate: TTranslateStringEvent = nil): TFileName; function LoadLanguageTranslation(const Language: AnsiString; const OnTranslate: TTranslateStringEvent = nil): TFileName; function LoadLanguageTranslation(const Language, TranslationFilesPath: AnsiString; const OnTranslate: TTranslateStringEvent = nil): TFileName; function TranslateString(const Value: AnsiString; IsExternal: boolean = False): AnsiString; function ExtractLangName(const FileName: TFilename): AnsiString; function GetAvailableTranslations: TStringList; function GetAvailableTranslations(const SearchPath: AnsiString): TStringList; function GetTranslationFileName(const Language: AnsiString; AvailableTranslations: TStringList): AnsiString; function DefaultLangDir: AnsiString; function IsTranslationFileValid(const TranslationFile: AnsiString): boolean; const sLanguageIDName = 'TranslationLanguage'; implementation uses Forms, utils; const LineSeparator = '###################'; { procedures and functions } function IsQuoted(const S: AnsiString; QuoteChar: char): boolean; inline; var L: integer; begin L:= Length(S); if L > 1 then Result := (S[1] = QuoteChar) and (S[L] = QuoteChar) else Result := false; end; function HasSeparator(const S: AnsiString; Separator: char): boolean; inline; begin Result := Pos(Separator, S) > 0; end; function ExtractLangName(const FileName: TFilename): AnsiString; begin with TTranslateStringList.Create(FileName) do try Result := AnsiDequotedStr(CValues[sLanguageIDName], QuoteChar); finally Free; end; end; function GetAvailableTranslations: TStringList; begin Result:= GetAvailableTranslations(DefaultLangDir); end; function GetAvailableTranslations(const SearchPath: AnsiString): TStringList; var Sr: TSearchRec; LangName, s: AnsiString; begin Result:= TStringList.Create; if FindFirstUTF8(IncludeTrailingPathDelimiter(SearchPath) + '*', faArchive or faReadOnly, Sr) = 0 then with Result do begin NameValueSeparator:= '='; QuoteChar:= '"'; repeat if ExtractFileExt(Sr.Name) = '.template' then continue; s:=IncludeTrailingPathDelimiter(ExtractFilePath(SearchPath)) + Sr.Name; if IsTranslationFileValid(s) then begin LangName:= ExtractLangName(s); if LangName <> '' then Add(LangName + NameValueSeparator + Sr.Name); end; until FindNextUTF8(Sr) <> 0; FindClose(Sr); end; end; var FDefaultLangDir: AnsiString; function DefaultLangDir: AnsiString; {$ifdef unix} function _IsLangDir(const dir: string): boolean; var sr: TSearchRec; begin Result:=FindFirstUtf8(dir + ExtractFileNameOnly(ParamStrUtf8(0)) + '.*', faAnyFile, sr) = 0; FindClose(sr); end; var s: string; {$endif unix} begin if FDefaultLangDir = '' then begin FDefaultLangDir:=ExtractFilePath(ParamStrUtf8(0)) + 'lang' + DirectorySeparator; {$ifdef unix} if not _IsLangDir(FDefaultLangDir) then begin s:='/usr/share/' + ExtractFileNameOnly(ParamStrUtf8(0)) + '/lang/'; if _IsLangDir(s) then FDefaultLangDir:=s else begin s:='/usr/local/share/' + ExtractFileNameOnly(ParamStrUtf8(0)) + '/lang/'; if _IsLangDir(s) then FDefaultLangDir:=s; end; end; {$endif unix} end; Result:=FDefaultLangDir; end; function GetResStrings(Name, Value: AnsiString; Hash: longint; P: pointer): AnsiString; var Accept: boolean; begin with TResTranslator(P) do begin Accept := True; if Assigned(OnTranslateString) then OnTranslateString(TResTranslator(P), Name, Accept); if Accept then Result := InternalTranslateString(Value) else Result := Value; end; end; function LoadTranslationFile(const TranslationFile: AnsiString; const OnTranslate: TTranslateStringEvent = nil): AnsiString; begin LRSTranslator := TResTranslator.Create(TranslationFile); TResTranslator(LRSTranslator).OnTranslateString := OnTranslate; SetResourceStrings(@GetResStrings, LRSTranslator); Result := TResTranslator(LRSTranslator).TranslationLanguage; end; procedure SupplementTranslationFiles; overload; begin SupplementTranslationFiles(DefaultLangDir); end; procedure MakeTranslationFile; overload; begin MakeTranslationFile('???'); end; procedure MakeTranslationFile(Language: AnsiString); overload; var lLang, sLang, s: string; begin LCLGetLanguageIDs(lLang, sLang); sLang:=AnsiLowerCase(sLang); s:=ExtractFileNameOnly(ParamStrUtf8(0)); if (sLang <> '') and not FileExistsUTF8(DefaultLangDir + s + '.' + sLang) then s:=s + '.' + sLang else s:=s + '.lng'; MakeTranslationFile(DefaultLangDir + s, Language); end; procedure MakeTranslationFile(const FileName, Language: AnsiString); var Dst: TTranslateStringList; begin if Assigned(LRSTranslator) and (LRSTranslator is TResTranslator) then begin Dst := TTranslateStringList.Create; try Dst.Values[sLanguageIDName]:= Language; with LRSTranslator as TResTranslator do Dst.Merge(Strings as TTranslateStringList, true); ForceDirectories(ExtractFilePath(FileName)); Dst.SaveToFile(FileName); finally Dst.Free; end; end; end; procedure SupplementTranslationFile(const FileName: AnsiString); var Dst: TTranslateStringList; begin if Assigned(LRSTranslator) and (LRSTranslator is TResTranslator) then begin Dst := TTranslateStringList.Create(FileName); try with LRSTranslator as TResTranslator do Dst.Merge(Strings as TTranslateStringList, true); Dst.SaveToFile(FileName); finally Dst.Free; end; end; end; procedure SupplementTranslationFiles(const TranslationFilesPath: AnsiString); var Sl: TStringList; i: integer; s: string; begin if Assigned(LRSTranslator) and (LRSTranslator is TResTranslator) then begin Sl := GetAvailableTranslations(TranslationFilesPath); with Sl do for i := 0 to Count - 1 do SupplementTranslationFile(IncludeTrailingPathDelimiter(TranslationFilesPath) + ValueFromIndex[i]); // Supplement template file s:=IncludeTrailingPathDelimiter(TranslationFilesPath) + ExtractFileNameOnly(ParamStrUtf8(0)) + '.template'; if FileExistsUTF8(s) then SupplementTranslationFile(s); end; end; const InvalidLangExt: array[1..6] of string = ('ua', 'by', 'cn', 'cz', 'se', 'tw'); function IsTranslationFileValid(const TranslationFile: AnsiString): boolean; var s: string; i: integer; begin Result:=FileExistsUTF8(TranslationFile); if not Result then exit; s:=LowerCase(ExtractFileExt(TranslationFile)); Delete(s, 1, 1); for i:=Low(InvalidLangExt) to High(InvalidLangExt) do if s = InvalidLangExt[i] then begin Result:=False; exit; end; end; function LoadDefaultTranslationFile(const OnTranslate: TTranslateStringEvent): TFileName; begin Result := LoadDefaultTranslationFile(DefaultLangDir, OnTranslate); end; function LoadDefaultTranslationFile(const TranslationFilesPath: AnsiString; const OnTranslate: TTranslateStringEvent): TFileName; var lLang, sLang, s: string; i: integer; begin LCLGetLanguageIDs(lLang, sLang); lLang:=LowerCase(lLang); sLang:=LowerCase(sLang); {$ifdef windows} if sLang = 'ch' then begin sLang:='zh'; lLang:=StringReplace(lLang, 'ch_', 'zh_', []); end; {$endif windows} i:=Pos('.', lLang); if i > 0 then SetLength(lLang, i - 1); s:=IncludeTrailingPathDelimiter(TranslationFilesPath) + ExtractFileNameOnly(ParamStrUtf8(0))+ '.'; Result := s + lLang; // First check full language name (uk_ua) if not IsTranslationFileValid(Result) then begin Result := s + sLang; // Check fallback language name (uk) if not IsTranslationFileValid(Result) then begin // Finally use country name (ua) i:=Pos('_', lLang); if i > 0 then lLang:=Copy(lLang, i + 1, MaxInt); Result := s + lLang; if not IsTranslationFileValid(Result) then begin Result:=''; exit; end; end; end; Result := LoadTranslationFile(Result, OnTranslate); end; function LoadLanguageTranslation(const Language: AnsiString; const OnTranslate: TTranslateStringEvent): TFileName; begin Result := LoadLanguageTranslation(Language, DefaultLangDir, OnTranslate); end; function LoadLanguageTranslation(const Language, TranslationFilesPath: AnsiString; const OnTranslate: TTranslateStringEvent): TFileName; var Sl: TStringList; begin Sl:= GetAvailableTranslations(TranslationFilesPath); Result:= GetTranslationFileName(Language, Sl); if Result <> '' then Result := IncludeTrailingPathDelimiter(TranslationFilesPath) + Result; if FileExistsUTF8(Result) then LoadTranslationFile(Result, OnTranslate); end; function GetTranslationFileName(const Language: AnsiString; AvailableTranslations: TStringList): AnsiString; var i: integer; aName, aValue: string; begin Result := ''; if Assigned(AvailableTranslations) then with AvailableTranslations do for i := 0 to Count - 1 do begin GetNameValue(i, aName, aValue); if AnsiSameText(AnsiDequotedStr(Language, QuoteChar), AnsiDequotedStr(aName, QuoteChar)) then begin Result:= AnsiDequotedStr(aValue, QuoteChar); Break; end; end; end; procedure SaveTranslationFile; overload; begin if Assigned(LRSTranslator) and (LRSTranslator is TResTranslator) then with LRSTranslator as TResTranslator do if Modified then SaveFile; end; procedure SaveTranslationFile(const FileName: AnsiString); overload; begin if Assigned(LRSTranslator) and (LRSTranslator is TResTranslator) then with LRSTranslator as TResTranslator do if Modified then SaveFile(FileName); end; function TranslateString(const Value: AnsiString; IsExternal: boolean): AnsiString; begin if Assigned(LRSTranslator) and (LRSTranslator is TResTranslator) then with LRSTranslator as TResTranslator do result := InternalTranslateString(Value, IsExternal) else result := Value; end; { TTranslateStringList } function TTranslateStringList.CorrectGetValue(const Name: string): string; var Index: integer; offset: integer; begin Index := IndexOfName(Name, offset); if Index >= 0 then begin Result := Copy(Strings[Index], offset, MaxInt); Options[Index]:=Options[Index] + [tsoUsed]; end else result := ''; end; function TTranslateStringList.GetOptions(Index: integer): TTranslateStringOptions; begin Result:=TTranslateStringOptions(cardinal(ptruint(Objects[Index]))); end; function TTranslateStringList.CorrectGetName(Index: integer): string; var Offset: integer; s: string; begin CheckSpecialChars; Result := ''; s := Strings[Index]; Offset := ScanQuotSep(PChar(s)); if (Offset > 0) then Result := NormaliseQuotedStr(LeftStr(s, offset)); end; function TTranslateStringList.ScanQuotSep(P: PChar): integer; var i, len: integer; QuoteCount: integer; begin result := 0; QuoteCount := 0; i := 0; len:=strlen(P); while (i < len) and (result = 0) do begin if P[i] = QuoteChar then inc(QuoteCount) else if (P[i] = NameValueSeparator) and not odd(QuoteCount) then result := i; inc(i); end; end; procedure TTranslateStringList.SetOptions(Index: integer; const AValue: TTranslateStringOptions); begin Objects[Index]:=TObject(ptruint(cardinal(AValue))); end; function TTranslateStringList.DoCompareText(const s1, s2: string): PtrInt; begin if CaseSensitive then result:=AnsiCompareText(s1,s2) else result:=AnsiCompareText(UTF8UpperCase(s1),UTF8UpperCase(s2)); end; constructor TTranslateStringList.Create(const FileName: string); begin inherited Create; CheckSpecialChars; LoadFromFile(FileName); end; function TTranslateStringList.NormaliseQuotedStr(const S: string): string; begin if not HasSeparator(S, NameValueSeparator) then Result := AnsiDequotedStr(S, QuoteChar) else if not IsQuoted(S, QuoteChar) then Result := AnsiQuotedStr(S, QuoteChar) else Result := S; end; function TTranslateStringList.IndexOfName(const Name: string; var Offset: integer): integer; var s, n: string; begin CheckSpecialChars; result := 0; n:=NormaliseQuotedStr(Name); while (result < Count) do begin s:=Strings[result]; Offset := ScanQuotSep(PChar(s)); if (Offset > 0) and (n = Copy(s, 1, Offset)) then begin inc(Offset, 2); exit; end; inc(result); end; result := -1; end; function TTranslateStringList.IndexOfName(const Name: string): integer; var i: integer; begin Result:=IndexOfName(Name, i); end; procedure TTranslateStringList.LoadFromFile(const FileName: string); var FS: TFileStreamUTF8; buff: array[1..3] of char; i, j, k: integer; s, esep: string; begin FS:= TFileStreamUTF8.Create(FileName, fmOpenRead); try // Skip UTF8 header buff := ''; FS.Read(buff, SizeOf(UTF8FileHeader)); if buff <> UTF8FileHeader then FS.Position:=0; LoadFromStream(FS); finally FS.Free; end; i:=IndexOf(LineSeparator); if i >= 0 then Delete(i); // Normalize quotations esep:=NameValueSeparator + NameValueSeparator; for i:=0 to Count - 1 do begin s:=Strings[i]; j:=ScanQuotSep(PChar(s)); if j > 0 then begin k:=j + 2; if Copy(s, j + 1, 2) = esep then begin Options[i]:=[tsoExternal]; Inc(k); end; Strings[i]:=NormaliseQuotedStr(Copy(s, 1, j)) + NameValueSeparator + NormaliseQuotedStr(Copy(s, k, MaxInt)); end; end; end; procedure TTranslateStringList.SaveToFile(const FileName: string); var FS: TFileStreamUTF8; i, j: integer; s, esep: string; begin ForceDirectories(ExtractFilePath(FileName)); FS := TFileStreamUTF8.Create(FileName, fmCreate); try FS.WriteBuffer(UTF8FileHeader, SizeOf(UTF8FileHeader)); esep:=NameValueSeparator + NameValueSeparator; for i:=0 to Count - 1 do begin s:=Strings[i]; if tsoExternal in Options[i] then begin j:=ScanQuotSep(PChar(s)); if j > 0 then s:=NormaliseQuotedStr(Copy(s, 1, j)) + esep + NormaliseQuotedStr(Copy(s, j + 2, MaxInt)); end; if s <> '' then FS.WriteBuffer(s[1], Length(s)); s:=LineEnding; FS.WriteBuffer(s[1], Length(s)); end; finally FS.Free; end; end; procedure TTranslateStringList.Merge(Source: TTranslateStringList; const NamesOnly: boolean = false); var i, j: integer; n: string; begin CheckSpecialChars; Source.Sort; for i:=0 to Count - 1 do Options[i]:=[]; for i:=0 to Source.Count - 1 do begin if Source.Options[i]*[tsoUsed, tsoExternal] = [] then continue; n:=Source.CNames[i]; if n <> '' then begin j:=IndexOfName(n); if j < 0 then begin // New string if NamesOnly then j:=Add(n + NameValueSeparator + n) else j:=Add(Source.Strings[i]); end; Options[j]:=Source.Options[i] + [tsoUsed]; end; end; // Delete unused strings i:=0; while i < Count do begin n:=CNames[i]; if (Options[i] = []) and (n <> '') and (CompareText(n, sLanguageIDName) <> 0) then Delete(i) else Inc(i); end; end; { TResTranslator } constructor TResTranslator.Create(TranslationFile: AnsiString); begin inherited Create; FTranslationFile := TranslationFile; FIgnoreDelimiters := [wdIgnoreTrailing]; FWordDelims := ['.', ',', ':']; FStrResLst := TTranslateStringList.Create; with FStrResLst do begin Duplicates := dupIgnore; CaseSensitive := False; CheckSpecialChars; if FileExistsUTF8(FTranslationFile) then begin LoadFromFile(FTranslationFile); FTranslationLanguage := AnsiDequotedStr(CValues[AnsiQuotedStr(sLanguageIDName, QuoteChar)], QuoteChar); end; end; end; destructor TResTranslator.Destroy; begin FStrResLst.Free; inherited Destroy; end; function TResTranslator.InternalTranslateString(const Value: AnsiString; IsExternal: boolean): AnsiString; function IsAlpha(Ch: char): boolean; inline; begin Result := Ch in ['A'..'Z', 'a'..'z']; end; function HasAlpha: boolean; var i: integer; begin Result := False; i := 1; while not Result and (i <= Length(Value)) do begin Result := IsAlpha(Value[i]); Inc(i); end; end; var ClearValue: AnsiString; Original, s, n: AnsiString; i: integer; begin Original := Value; ClearValue := StringReplace(AdjustLineBreaks(Value), LineEnding, '~', [rfReplaceAll]); Result := ClearValue; if wdIgnoreLeading in IgnoreDelimiters then RemoveLeadingChars(ClearValue, FWordDelims); if wdIgnoreTrailing in IgnoreDelimiters then RemoveTrailingChars(ClearValue, FWordDelims); if HasAlpha then begin with FStrResLst do begin if HasSeparator(ClearValue, NameValueSeparator) then n := AnsiQuotedStr(ClearValue, QuoteChar) else n := ClearValue; s:=CValues[n]; if (s = '') then begin i:=Add(n + NameValueSeparator + n); Options[i]:=[tsoNew, tsoUsed]; FModified := True; Result := Original; end else begin Result := StringReplace(Result, ClearValue, AnsiDequotedStr(s, QuoteChar), [rfReplaceAll]); Result := StringReplace(Result, '~', LineEnding, [rfReplaceAll]); end; if IsExternal then begin i:=IndexOfName(n); if i >= 0 then Options[i]:=Options[i] + [tsoExternal]; end; end; end; end; procedure TResTranslator.SetIgnoreDelimiters(const AValue: TWordDelimitersOptions); begin if FIgnoreDelimiters = AValue then exit; FIgnoreDelimiters := AValue; end; function TResTranslator.GetStrings: TStringList; begin Result := FStrResLst; end; procedure TResTranslator.SetOnTranslateString(const AValue: TTranslateStringEvent); begin if FOnTranslateString = AValue then exit; FOnTranslateString := AValue; end; procedure TResTranslator.SetWordDelims(const AValue: TSysCharset); begin if FWordDelims = AValue then exit; FWordDelims := AValue; end; procedure TResTranslator.TranslateStringProperty(Sender: TObject; const Instance: TPersistent; PropInfo: PPropInfo; var Content: string); var Accept: boolean; ResourceName: AnsiString; OwnerName: AnsiString; begin if Sender is TReader and Assigned(TReader(Sender).Owner) then OwnerName := TReader(Sender).Owner.GetNamePath; if Instance.InheritsFrom(TForm) then ResourceName := OwnerName + '.' + PropInfo^.Name else ResourceName := OwnerName + '.' + Instance.GetNamePath + '.' + PropInfo^.Name; Accept := True; if Assigned(OnTranslateString) then OnTranslateString(Self, ResourceName, Accept); if (PropInfo^.Name = 'Caption') and (Instance.GetNamePath = Content) then Accept:=False else if PropInfo^.Name = 'Name' then Accept:=False; if Accept then Content := InternalTranslateString(Content); end; procedure TResTranslator.SaveFile; begin SaveTranslationFile(FTranslationFile); end; procedure TResTranslator.SaveFile(const aFileName: string); begin FStrResLst.SaveToFile(aFileName); end; finalization FreeAndNil(LRSTranslator); end. TransGUI/baseform.lfm0000644000000000000000000000015712256577645013534 0ustar rootrootobject BaseForm: TBaseForm Left = 234 Height = 240 Top = 132 Width = 320 LCLVersion = '1.0.15.0' end TransGUI/about.lfm0000644000000000000000000016464412261763702013047 0ustar rootrootinherited AboutForm: TAboutForm Left = 421 Height = 349 Top = 188 Width = 451 HorzScrollBar.Page = 423 VertScrollBar.Page = 316 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog BorderWidth = 8 Caption = 'About' ClientHeight = 349 ClientWidth = 451 Constraints.MinHeight = 330 Constraints.MinWidth = 350 OnCreate = FormCreate Position = poMainFormCenter object Page: TPageControl[0] Left = 8 Height = 299 Top = 8 Width = 435 ActivePage = tabAbout Align = alClient TabIndex = 0 TabOrder = 0 object tabAbout: TTabSheet Caption = 'About' ClientHeight = 273 ClientWidth = 427 object imgTransmission: TImage Left = 10 Height = 48 Top = 14 Width = 48 AutoSize = True Picture.Data = { 1754506F727461626C654E6574776F726B477261706869637D0B000089504E47 0D0A1A0A0000000D49484452000000300000003008060000005702F987000000 0473424954080808087C086488000000097048597300000DD700000DD7014228 9B780000001974455874536F667477617265007777772E696E6B73636170652E 6F72679BEE3C1A00000AFA494441546881D599EB6F5CC775C07F6766F6FD2497 942891122D8512E9C88AAD872D288E51C44E1CA44DBE0432ADA04D931645D104 4180C4823F150554D4FDE0A62860C429907FA0962A1445BE2406DAD46E6238AA CBC4B2AA476C29922D4B862C71C97D2FF73E4E3FEC92E26B9794642BC80148DE CB7BE7CCF9CD9C39E7CC5C51557E9FC5FCAE0DB85BF9BD07701FB5C2EF7FFFF9 3FC384CF01A9C5FF57A421C8DF1FF9DEB32F7C94FDC97AD6C08B2207813FB4F0 30F0C900362BD895DA84C67347D9F3C823E4725910E958AF54AA55A67E7992F8 DFFC2DE2792B9B42E0E00381F31E4C05F0F27754FFEBAE01FE59E4B9F4D6ADDF DBF5F8E3B1DCD6AD1211E93A6D01702C1DE3F057FF18CF6B31AF5B448844A2FC DBBF1EE38F4A75125DFAF4014F95F2D5ABFA9B575E69CC5CBCF82FDF54FD8B5E F6ADE942013CFBE4638F39999AC23F760C82A08736871CFD6B32E92CBEEF2D7B E43061082FBC00D56A57639CB5A4474765CBA38F264F5CBAF4273F1279E62F55 4B770C00B8607696DAE9D36BBE188461E7AF8F1FF84B9E891114A5EE7984AD56 6F45E7CF931C19C14622A137379700EE0A20BC78F1A2F1F379FC52097AB85C08 28100401BEBF14C0884115AE00915EBD896053296257AFD26AB56C0B7AD2AE09 60E047672F5CF85A261E4FB96C1647F7D8ABAEAD2EF00382650081B1A8088D6C 9696595D83021EE00701B50B176AA2FA1FDF552DDE15C0B754BFF903919FCC54 AB87041E0C61BB408CF680AF06100BC260850BD9D0A1AA7C502EB7A45259318D 0A46C013B824703A807FFFB6EAB1B5EC5B571EF8B6EA8F811FCFDFFF5024DD5A A5AD373E1E1323578320B0CB5DC8B9002291E6DC17BFF8A03D7EFCC3E56D5B10 3CAB5A598F3DB70DB05CBEA5BA6A18397AF4683C85ACBA0682C047510DF7EF2F 3F73ECD8EC9DF4BB9A7CE499183A8B38580E10B49DFC23968F09C05F31037EAF FC7117F2F1CDC00A17FA1D004C1E3E6480AF1963BEA3AA636B291311F6EDDB6B BB018461903875FAD4DB4F7FF5A99ECE24224D557D5E555F3CFED289E61D0300 4FC762B11747464652996C16992FCED680F0431F3F585A4A04818F18C3AE071E C82C5F0C8BD5AA42B3D1C85E79FFFDA3B56AEDC8E4E14363C75F3A51BB230063 CCF3DBB76D4F45A211EAF51A61B822F4AF90BE7CBEAB0B69A8D4AA5542EDAE47 4448C4E34C4C4CA4DE7EFB6DA9942BDF05FEEEB601260F1FEA8B44221B53E914 33B333A82AA5D912E57209CFF3576D2322ECDFBF8F300857F87C1006A82A972E 5DC25BA59C06C8A4D3A4D269C2304415B66CD9923C7BE6EC913B0200B6259289 A61FF8118072B94C241AE1C92F7C1E63572F059A8D2633C5AE751761A87CFAD1 83EDBDC28A6721B3A559DE383985751611A150282022C9C9C387FA8FBF7462D5 92A217C0F644228948BB142E972A3CF5F457E8EBCB776D70E1E2657A2D1311B0 36CAD6D12D5DDE1825E2229C7AF334E9741A630CF178BC51AFD77702BFBC6D80 54229900692FCCC0636464B8E7429E9D29F750D79672B5423A9DEEFA7CD3A621 A6DEF835D6B6377CA954CAD6EBF51DB70D608CF964329572460C61A82493A935 A3D0952B57C8673354AA55664BB75C4915442CF5468399D2EA9B9979E92FF4D3 683670D62108E9743A71F3E6CD896EEFF7029848A552F881471004E47259CC7C 19ACF381F056385485F7DEBF861D1DA154AE303D3DB3B4231BA15E6B70E3C39B 3D01DAB3A36DCDA2A4D36963ACD973DB0022725F2291A05A0BF0FD807C5F1E67 DD32B359D8E02850AFD6A9D6EA944B158ACB0062D138956A8D7ABDDE732050C8 E573789E87AA2193C902327E5B0093870F99783C5E8846A3B8A625F03D060706 702E72ABC3257D2AD54A95582C42BD56A7542E335D9CE9F0B55F4C2653546B35 C418023F24168B761D88814201AFD5C208A4D319C22018EE06D06D7335128BC5 3D630CD639020DE9EFEFC73A87B30E672338E770CE75FE17617A7A864C264DA9 5C69CFC0CC2C37A78B4C176798992D51AE5429976B64D249664B25ACEBE8B09D 1F1769EB728EA14D9B68B53C10211E8FE39C0B270F1FDABCEE1900B6A752294F 4412CE5A3CCFA3D05F5870A15B83766B0C67674B583154AA352EBFFB1E85FE81 794F46102EBF778552A542A190A754AEB275ABEBCCE232275265707090F3E7CE E33A912893C978C5627127706DBD00DB72B9AC13C05ACB5CA349A1E342F3EEB3 68F902509C2EA21A6080D367CE608C69472D6D838661482A9940439F6AA5B264 3074994B0E6D1CA2D198C35A8B00F97C9F2B168B3B8057D605202263996C2E81 08C65AEAF53A8383838B3AD54523D6FE552AB737599F7A601CEB1CCB03AE02BE E7512A9529CD968844220B80A12ABAA83EDAB871884AA58C750E15C8E7F30963 CCAED56C5D15C039B72B9BC988088441403C16271E8B2F3166A06F70219AF881 8FD7F2A8D56AB49A73744DC7AA8815E69A0D86068717DA8761C8F59B1F201DDD A9B4239948E2792D0C2972B9AC586B1F5C37803166473A93C6F77D5A2D8F7C5F DF82FBA8B63B7CF5E7AF3035F5064110748E0E1D20A433DDB32CC0CCEC2CBE1F F08FFFF40F80128D46D9B3670F1313F7638C5998DB810D051A8D26B99C92CFF7 01EC583780B5764B221EA7D168D06A35290C166EC5EEF673F6EEDDCBD8D8182F BFFC53AE5DBBC6FFFEEA4D6AB53A73ADD52BCD7969369BFCE69D0B4CEC1C636C 6C07070F1E241E6FCFEEA235CDE64D2354AB158C08B96C16DFF7374E1E3E648F BF74624999BB0260F2F0A16426934944A3315A2D8F66738E8D1B36AE6A4C369B E5A9A726397FEE1C89449C772E5EE2DCF977BAEE1B360C16F883471E65686888 271E7F82FE42A12BE8F0F0664E9D3A853116E7222493C9B95AAD360AFCB62700 B02D9BCB3545246D9DA5E5791406BA77043071FFFD6C1D1DE5673FFB4FB68C0C F3DAEB6F7073FA56F59B49A738F0F05E86360CF0D9CF3EC17DDBB6F5D407B071 6813F5D75FC73A0B06FAFBFA835AADB6633D00DBF3B99C1A6370D631D798A3D0 DF1B0020994CF2A52F7D99772F5F26994870F1D265DE3C759607768DF389EDA3 3CBCFF111E7AE8A1852A732D19DAB88172A5DC2EEA5418181C885D79FFCA4EE0 E5B500B6F5F7F74755156B2D2DBF85ACB35380D1FBEEE34FBFFE75FEFBD55719 DD32CCF6ED9FE0339F798C6432B96E1D00896412AFE5B5810506060662CEB9DD CBDF5B524A8888B1D6EEEACBF7C58C3138E7D834B489D75EFB05737373EBEE3C 1A89F2B9CF7D9E6F7CE3CF79F2C92FDCB6F1AACAFF9C3CC9E0E0065CE7C0B82F DF8FB5F6415956D32FCC8088C8EEDDBB73C6D8BDF32710D65AC6C77772F2E449 8E1C7986443C8112A22AA08A8AB2907F3AF7289D0D407B0BB958C23044DA4D3B D7C2F22F44F3597B707003FBF73FDCCEC662C8F7F51184E1D8D881031911A968 A7E102C0AE5DBB22128B0DAB865BC25029168B0BCA77EFFE14E3E313EDE34215 44B49DAC165F03CC5F7632EC7CA69D4FCB8BEF975FCF7348E77E3E6C4F4F4F63 AD259BCD12F87E3E1B8B8EECDBB7EF1DDA27F1B700B2D9ACAD05415F1886914A A5CCCC4C1B60E7CE7152C9251F1CEF99349B4DCE9D3F8BB36EE17B4324D4C2F5 4CE6B72B000054D5A82AE55289EB1F5E4755B9FEE1F57B6FF93271CEB53F51A9 12FABEC3DD327BB5FD40D8F25A44A3B17B67E11AD26B2FBE02C0F383B76EDCBC A1F97C7EDD31FBE31411219D4E53ABB54F17435D9AE617E622168B693D0C8372 A5FAD3772FBFFBE94422111FDE3CBC224ADC7311A1D59AE3C2850BA8EA294FCC 92C3DE05801B376E84D174BA7863A634158D447E70E6FFCEFC95B53662BA7C90 BB57A2AAF8BE6F55F5F27471F68746B558A854160A3A59F4355DC60E1CC8E49D DB14FA7E9F15494463D1DEB5F13D923008BC7A1016250C67C246E3DA5B6FBD55 9FCF03FF0F6EF4D8137C7F96BD0000000049454E44AE426082 } end object imgSynapse: TImage Cursor = crHandPoint Left = 124 Height = 42 Top = 215 Width = 102 Anchors = [akLeft, akBottom] AutoSize = True OnClick = imgSynapseClick Picture.Data = { 1754506F727461626C654E6574776F726B477261706869633E10000089504E47 0D0A1A0A0000000D49484452000000660000002A0803000000769BDE47000003 00504C5445B0191AB73639BE181EB51F29D058609A6366F8E7E8D16770DA7E85 C7787FFAEDEEBA2233BE3849DA8994F8E8EAC02840CE495EDB7182D1717FD47E 8CE29BA6E5A3ADF3D9DDF4DBDFF8DFE3B91C38CF204082172B5B101DAA1F3972 1626D12B4951141FD53C58AC3148C13850C14A6059232DE08D9BE8B4BDEFC7CE F1CDD3F1D0D6BE2040812336581824CB5269D15870803E4BD2677CE1758BCF6C 80D57386D87689D9798CD07486DE92A2E6A8B5ECBCC5EDC3CBF2D3D9F6DDE2F4 DCE1F6E2E6F7E4E8CF284FC0304F43121D662131842D41AB3C55CE5F78D76B82 DA8095EFC8D1F5DFE4C018405B1224C0284FAB2446581D2CC0405ECB5674F3D6 DDF6DAE1F8E0E6811835C73E63D5758EAB1C46C0204FC32A57D5BFC6E1D1D6DC CAD0560B2B580C423A2C378F38809A4C8CDED6DE5B0E5D8F5790AA82AC98679D B692BBC3B3C5652A6F5D0F726F2F807C418B7D438C7E448D814990844D938650 94885396A177ACB08CB9C0A4C7DBCBDFE4D8E7EEE6F04A2056733A837F478F88 54978D5B9BAA83B6BA9AC4CCB5D3D8C7DDE2D4E6E9DFECE7DDEA2D093A996AA9 C6ABCFD5C1DCD1BDD8DCCBE2DFD0E4E8DCECEBE2EEF3EDF5560E715F107F5F10 7D400B556F278C732D8F7936938041998E56A4D9C6E0671C866F288C83469C87 4C9FE6DAEBE3D6E99C93B77A6F9E6059933D3975403F8F2B2A6053529AACADCC 8285B7F4F5F9BCC4DE6C7FB33F5B9F8B9CC53A548D4D54606C8BB64F77A76392 C21F69AE387EB7244D708BB4D774AAD2C1D8EA1F7EBE4698CF5EA0D09BC3DF84 BBDFCFE3F0185A8006415E108FCF1F95CF2899D247AADB53B0DF67B6DF92CFED AAD6ECD6ECF7EAF6FC0F90CF0F8EC934A3D9297496BBE1F20087C107648C0C82 B419A8E21CA1DA3AB5E73FB0DE5DBDE575C4E3C9E8F5DEF1F90098D214516661 B2C317666A8AB6B4056548206249244C3C36614F8BA7876F8C5FB2B499C4C6A9 F4E9DAFAF2E8ECDCCBF4DFCEE5C8B5E8B89ED9906ECBA798E2A791C85B37DD98 81D8B5ABCC664CD17962BD948AF3DAD4BF4630A7817AAF3421BB3726AF231CB0 2B23000000B29299E7000000097048597300000B1200000B1201D2DD7EFC0000 000974455874436F6D6D656E740000892A8D0600000CCF49444154789CBD977D 5C53F7BDC753488D570A8110509CECAE3DC73C0A478A55C803818C1642309804 1573EF6E5C379EAC67C3873C31539290702121E03DCED5BA4B655AEF552FD4AD 6AC10758071A0243C0B64E82AC4E3729950407D5B6A968F73D49B46EBD7FEF73 7EE7F0E39CD72BEFD7E7FBF0FB9D43F9FA9F22CAD7BF3D1BD2364335C8DA0BF2 F5FA7CBEAE5F750F80C6C7C90B8C6E5D6348C73DC7DF79A75137E039DEED1B1C FC4180C94C642526B25E4C6495B1CAB2620BB3625FCC5ACBCCCA4A6096AD95AD 892D4B94956D7E2184E9E9E93966AD24A5FBD5C0808FFCF5E3DD1ECF80E7D237 BA76E1AD882E5C3BDF01D73FFCE6D4B877F03F92626333166766C8B00C59C162 4CB60E46416C4156A20CCBCA285887251664AE2B50AC02CCD9B33D5D5D5DC7AB 407555A73C9E0BA73A8E1CFE51C7C8C8C845525761F45FBD7AF5CCFE9FEC0FE9 7FAF9EA9D95F73E6A377CF787CDE7F673116A32A0CC390F842A4104B4091421A 9291C1484FA431949B55884AA5429197F349CC6057F7C79E8EC315A08E4BE7DF 359BCD3BCC35E68BFDFD97FB868727872749751EC50982D8B7BDBC7C3B71E528 4CCF4D769E19F179EFC5D2A8D23C145568A44A0D8229E9C54AA418C1103E0AA7 0206AAE497AC5A19C678BABB2F8089F397AE9D3A52610EE9DDFEFEFEBEBE61F8 FDCECECEA36FE76BF3CBB76FDF9AFFECB3DAF2F2ADE5C4C9D10F4F8F8E4E4DDD BF1F5C88A244A5D0632851D1D1D114500C5DC3877F290F6362A261121D9DFA9D F580E9190408602E749C3AD51181986B8E9C3F730E28E7F090F2EFFC75FA0BAD F65EE0AB2F025F693FD59EBCF5C9CDD3B7A66FDF999D9D7B78672E703F260AA6 B3943B73B38185187E319F42DEA40482D1147FCC9630A6274C395551F16A0D10 C2A41A7C077E06A24542F6E1DA19AD7626FFAF5F68B76ED5069EFDE2AB1B37EF DCBEF167FF947F7E7E61C11FF5708642F13FA0443DF25328F31CCA739894E27F B430F3687E9632F77974EAA63412D3D515C21C364372CC8FEDE0E471E46867C4 CD4CBE76E6CF01ED7682285F1DC80FDCBA7D1BCEE9A94FEFDEFD3CE87F743FF0 68E1D3BB9FC3348A169CBB8BD0680FFDF39FCD0583B377030F9E2B797965D84D 88028C279030272202272068F7A66E7EA985DC6FD77EA90DDC9ABE39FD97E9A9 A94F21520B77FC8160FC02390DFA1F32A23E9F0D2A180FC970069F0BDE093E83 864B208CE9200B8D3453118184FD440441DBFAF6CD1970B3AF7CF53DED27B7A7 6F4E7D320398F9FBCF2FF867EF3FA22EF81F227CB8D028B39FCD4B19147FF0E1 ECBC12B85234755308D31B3663AE78DACC530C480E048D206EDC0E68B5DA7CFF EAAD7F3931357A6B06DC406E9E59F02F0482CF3FF03F084605FD94E0C2CC8374 348409DC471FF81F2995A961375DBDE1983D1D3412627E12345CEB87781DBD31 35732F10585DDE7973FAE6951BD3531F4F41683E8312B81F202B6DEEB305A8AF C07C8A9281923537F70C150A0355A46EDAF08D9B9D55BA4A9D4EB7FBFFE5946F 7D8DD8B7EFF4FB3FD46AB7966F3FF7E1E887C370FAFE343F1F5C88894E4989A1 A7502851312951D037291ABAA698EC9BE8187E4C740C9D1EE99B2EB237ABF4B0 6E1A40969D91B43C6506C63E58033A27CF410D10A393972F5F1EBE3C79C9D733 C7A022F4E574188AE5F0EBA1834EE7F3A143F93051D0151ABEA6248C01371D55 D57A83C11652F5B75243900B0DE8282C3AF0E7CA24B9065DBEEAF11D9B63C44B 11854623D52C47107A1E9247E72BA49A3CA5869FA754C09CCE47A4E0262D8C39 5CA1AB36185CAEA61F1B8D4E5755C48BF909E431A873F23409EB1BEE1F1EEE1F 19F76E9BA329A9F1A8944AE52B314C49C5680A25239E8F3130298386295094AF 445124D237BDD0FFFAEA6DAEFACA1D2ED32193DBA2D3D535EE36E33FD9B96BD7 AE3DBB77BDB1EB8D3DFBDBDE6CDB0F84D3C41BE72F79AE5DEAEBBBFA91CFCB9E 5D0C51CBCC4CA015539548C19A02D9DA82445481210C8CB63936333153894995 68C9CB640978BBAA2A2AAA0DB61FD875669BC3D160B2189A9AAAAD7BF0D72B2D 160B6ED1EB2DFAB72CA47E419C3EF366776F97CFE7F58D5C1DF18D0560534B90 315959499B312483C9946527B1B26508A66014B2D664B3629358599B113452D0 EF90986D2EBB4357616B68004CA5ABC966D869AED3575BADB8D56A3558C36A24 7E71EC1DDFB11EEFE0A0D77BED9AEFAC40989DCDCE662765B35FA46666656733 05EC6C015B8621EB8040DE4E623395114C572560AC867A7BAB717715B90DFF54 E7AC77D91A718BC1D064256C4D2E9BABF14D97CB656B221AADDE1EEBC5C1B1B1 B1C1718FF7C71C8E40C0920905420E1BC966B3D9021953C015B2698CC42CF827 A3AC4828142622E1BEB102E648A5C1696AAD755BC822DBB1D3E932D6EBF06A9B CD65C58DCE83CDCD6FB6C1A5D9493435FD9F4D3F393E01F279BCC95C21A70853 F1B8221E572310B085498A35225E91205D11CBCC660BD25570BF288B5F12AAB4 6AD24DD53697C9515BEB68AA238BCCE5AA77EB71ABABDEA8C39BDDEE43A6B6B6 F6F696F676C2E9723A8F4D0E0C0D0D4D0C8E77F9391C9E1A53A9D539A5C90A6E 5111974D2F538B789C743AC452C04BC7727272D402456A045307AB99CE666CA8 AD6D6D0043B8596F73BB0DB8D3E876EB7093FD504B6B08D3DA42B8C1DAB149DF D0D0F5EB83BE6D222E4F2D5661A54B37E66E5C0EF3527156BC0A53A5FF9B82A4 A85F924924B972353DBCDF54EBAA6A2ACC3B2A9D6ED28EA95E8F9B1BC18D11B7 BBEDEE2AC2ED6E7038DADE6A6F6F753888B603070E9C1A9F00CAF531EF36112F 39676341424E695CDC4684BDB1541217C75D43438BA999023657228F5BB22437 2E4ECE7F21D43716081AB966D619203BAD0EB771B7B9AAC96934E12668A23ADC DEDCDED2D2D666B7B7B438A0430FE8C7203143BFFFFD84772E99C713CBD7C992 9317C549A8055C39FCF2C665BC423AA6E408D471B94B96C42DCA8DCBE1BFB26A 43D84D64B9B4B85B5B6B1D6E2B6E76399BDB2B1D0D871AFE9B701F341D020C64 A8A585F865B5EBECD8C4D85018C3E3F2C462D5E264917C91848AA4172D55E7E6 262F136562520EBB2857C266B2982FC5C6F25F791C34738D4E67D15B7575F5A4 1F176EB6398D767D6B6BAB03C70F1E747F83A976BAC6C606C687AE03E6582C17 30CB54096AC02C8355006309E512716EB23003E108B88B72D761EB16A7676682 9BB450D0EACC158D7AABC15669B690716BC0712B04CD565B5BEB24C842683FF4 1863779E1D1B1C1E0861B631385C91585CB0582D922C92A36BD71626AC65E7E4 E42E2B6231386CDE1249014D22E289B891DCE8C9A0C1D2697359F046BB09EC98 711DB831EEDD5B6B030CC4EF09A6FDE0C484378439313447130A45C96255815A 24962CA38BA0556311D652B95C94880A84BC1C098688A0CA85F492486EEA2068 D54DAE7A8BB9D108DD0391AA72C1A2507B62AF1E8756798271102D90FEB09B13 0DB154A180AB1663985A2DDF28D788840241E1F232B558BEF4453E60968A551A 5E118FCB51840BDA42BAA9B23639DD06B3C56E6F6DADC7F11D3628EFBD27F65A 08C2E66AB63FC1405F5E1F1CF65D87A07DC9500A84DCD26519D09E1289842EE4 088532BA0C9CC90BE94C0EAFB43403DCF078CCC87E036E6ACCAF5B6DF5F60693 C96D72B45A6193B1B90E92181D81DB6C071F635A8909B231C72680728247038C 7AE9329E485C9A1B27C84B52177193D93CB524B75495B946A896E4088B4A79EA A5898AF0466081A041726CF5C6860687C9E130FD14307A9B91C4B41144930D92 D3D6D6DC6C6F3F741AFA1F3406983B5CC070786209370EDA65D14B2826148978 4BD5E22512194665648944F2D24562919C892AC37DD315C298EB6C46B0D36032 D4C0BEB9CB6A331E020CEC9A4D24E7C001A88466F7647F2F24A7D707A9891350 A5103471EE3A96A4542EF8570C5132391C75B288CB92628A4C4C96942C4ACE29 624A11345CD0BDDD21CC0EBC4A076922ADC01660B5391B6AF756137F2778A51E BE3872F1238FAF3729961A2F650B3862498246958E52355404C1A8CFBF9420C5 60F79432308C9F08DF36C5D813CCC0C0EB4F5E9AC9F7E6CE4E5C074D04A5D6F4 4BFC690AF912303CDCD737E2F11E839700A5345BC011252F56C66B102A3F9E4A A562280329A6C5D3691843C9A066D21568318AA2CA1742B9F1793A8EBC1A7E99 7D15C73B2727FB8E1EF9CDE1B7748D3BFFDE0B7C39F50DC3D1FF07F87A62509F 93E6A932CAD624C4C723CBA5CAE58A78340FC150BA063EA394344483D2429F51 79A832D2370397FACF8DEE796DCFC993A3F07174E54352574EBEF65FE4F1B4DE 8647E4B8F8F1C795FF82BE02AF792529FC2D255B52E14C2929494D8159E82C49 D992929A9A9A5202972D70A67EF77B80193937FAC107EFBFF7FE074FEB7F7EFE B39FFF83DE0B3F797FF4DDC3CFAE7E79D5A6EF6C82014778FCC33C3C0D69D577 577C1F30E73EF8E3EFBEA5FFFCB6224FFEF8DEAF7FBD323F2D6DC5FAF52BD2D2 D6AF589FB67EFD860DEB576C580967DA8AB4951BE084A7E4AD0DF0286DC5F7BE 4F62FE29FA1B8C064B4CAD4156FE0000000049454E44AE426082 } end object imgLazarus: TImage Cursor = crHandPoint Left = 6 Height = 47 Top = 210 Width = 102 Anchors = [akLeft, akBottom] AutoSize = True OnClick = imgLazarusClick Picture.Data = { 1754506F727461626C654E6574776F726B477261706869635917000089504E47 0D0A1A0A0000000D49484452000000660000002F08060000001188BFC6000000 0473424954080808087C0864880000000970485973000002A2000002A201B69B BD6D0000001974455874536F667477617265007777772E696E6B73636170652E 6F72679BEE3C1A000016D649444154789CED5C795C1457B6FE7AA14168D92142 8B202222A8114450594451518CBB463413B74932C6306E8979D1319A4CE2324E 342E8986E4E9689291108321E20E068D0AA2A80415D9646941F6ADA11B9AEEAA F3FE682929AB414CF2E6CDE4F9FD7E05DC73CF3DE7D43D773D752FA21EE6F2F5 62B168ADB39393140F41F85F047599EC9AF957EAFAD7807EB16A22426969999E 61B04524EF69A9292BAFEC616961F6DBDAF70CBF0825A5B5E8D7D7B9456C636D 2D79E6947F1FB8F6B6834C26134B1FCF983973262A2B2BD1BF7F7F884422E8F5 7A545454C0CBCB0B9B366D82A5A5251886C1810307101B1B0B33333328140AB0 2C0BB55A8DE8E868040707A3BEBE1E7E7E7E387CF830468E1C0900A8ABAB4350 5010A45229323232606A6A0A00888D8DC5AE5DBB909A9A8AC58B17E3EEDDBBF0 F6F61618BD6EDD3A787A7A62F6ECD9A8A9A98197971700C31050535383B6B636 6CDCB811FEFEFE686E6E86878707468C18016B6B6B9E1C3B3B3B6CDFBE1D4AA5 12010101080D0D85B9B9391886417373334422115E7DF5554C9A34E98915D9D8 D88801030660E4C891B0B2B202117136BDFFFEFBE8DBB72FB66DDB86B8B8381C 397204EEEEEE5CD9E4E464AC5AB50AFBF6ED437070305FB08B4B1F2D75406868 28AD5DBBB62389743A1DB9BABAD2B66DDB888868FEFCF9347DFA7452A9543CBE BCBC3CF2F6F6A69D3B771211D1B265CB68FEFCF95CFEBA75EB68E5CA95141111 41BB77EFE6E8C3860DA37DFBF61111D194295368D9B265D415C68C19431B366C 10D0972F5F4EA3468D222222954A4500E8F2E5CB9DCA292C2C2400949B9BCBA3 DFB87183ACADADE9F0E1C35DDA41445457574700283D3D9D478F8A8AA2A54B97 72E9CF3FFF9C9C9D9DE9CA952B444474E0C001F2F2F2A2DBB76F0B64F63097B7 8A5C5CFA6895CA1259BBA3468F1E0D1B1B1B44454571AD3C2B2B0B172E5C404A 4A0A4C4C4CE0E0E080D4D4548C183142D0823EFDF453ECDCB913F9F9F9282E2E 868F8F0F8A8A8A209148E0E5E585ACAC2C14151561F6ECD9282C2CC48D1B3730 67CE1C141616C2D4D41453A74E455E5E9E40B6A3A323B66DDB0600183B762CDA DADAB856A6D7EBD1DCDC8CEAEA6AAC5CB912212121686A6A82A5A525222323E1 E0E0C093356DDA34CC983103454545707777476E6E2E3C3D3D793C4B962C4179 79394E9D3AD5658FA9AFAF87ADAD2DC68F1F0F5B5B5B300C83AAAA2AD4D6D622 3131117DFBF6E578535252B070E1428C1F3F1E4AA512717171B0B5B515C834B7 E8A9150C650020954A616666069148045F5F5F2C58B00072B99CAB044747479C 3C79D2A8634E9C38817EFDFA0100DCDCDC3073E64CECDFBF1F2A950A51515170 727282939313BCBDBD111F1F8FC4C444AC59B3861BD600202C2C0C3B77EEE4C9 158BC5BC7460602056AE5C09AD568BA4A424ECDDBB176BD7AE454848088F6FCD 9A35023BA552A3AFCD435555151C1D1D9FC8D78E0F3FFC10010101605916F5F5 F5D8B76F1F424343919B9B0B73737300C0983163B065CB16444747A3A6A60612 89A45379462DF4F4F4C4F4E9D38D17904AF1E5975F62F9F2E590C964080D0DC5 800103909191813367CEA0B2B212070E1CE0F8DF79E71DCC9C39134D4D4DB87A F52A47DFB0610356AD5A85AAAA2A1E3F0068341A5454540874DBDADAC2D2D212 002097CBD1AB572F00C02BAFBC82909010444646223535153B76ECE0CAD4D5D5 1995E5E2E2C2FD5D565606994C06AD568B8A8A0A1C397204F9F9F9D8B76F9FD1 3A3086070F1EA0B8B81800A052A9505D5D0D53535341E59B9A9A422A9576E914 00100C65070F1E848B8B0BC2C3C3BB2CA8D168F0E38F3F223737176565657073 7383B7B737C68E1D2B68DD31313190CBE578E9A59778F45DBB76C1C3C3039327 4FE668B1B1B1C8CDCD35AA73C2840918356A140E1D3A04575757848585F1F22B 2B2B11131383C8C8480C1932049B376FEED4FE37DF7C130CC3F07AA6542A8595 9515060F1E8CE0E0E06EF5ACD6D6566CDDBA954B8BC562C8E572B8B9B961DCB8 715C436A477676364E9C3881356BD6742AD3DCA2A756A450F4D69696DE9775CA F50CFF72F430976BA555555574E58E12BD6C7B7219DCAE951EDBC51240861F02 3A40EDECC6CB53873DB1113EEA4010D03BEAE68B78A4FBF1F21DE94FB05D2857 C847649CDEB95CEAF8CBB8DCC77513E1EACF4A68B55AC31C73F956093A82650D 954C440F9F477F779D679C4ED42EF34979DDA31BB7A3ABBCEED8D85DFBBB63CB AFAB2F00E04F064F81BB372FFFD2A2CFD00D3C7976EB04595792515E928FB0A9 0B7F95013213096452295ADB7468D5EABBE415894470E9650D22C283AA4668DB 185EBEABB30D0043EB6E1F26DA5B634DBD1A8D4D2D1DF44AA178CE0A00E19EB2 966BA9EDE8E36C0313A9D8402740A7675153DF8C06552B8FCFC9C11216E63254 5437A141D5C2A35BCA4D515EAD425DC323BAADB539FA38DBC0DCCC049535CDC8 2DACC463AA0174C331B19FFE15D316AE8299B99CA335D45642AD6AC0DD1B9740 44BFCA3963033C3039642092AEE4E168F2ED2E79FD062AF0E7970C9BCAB85399 389692CDCBDFB27A32246291D1B29F7F7B05C753EE70E969E13E583C2B0000F0 D74FCFE25246218F7FD3EA49E8DD8B1FCA018056AD1EFB8F5CC157DF67000096 2F0A41F8284F7CF869128E9ECEE2F8A21706E385B13ED8B237195F275C87A94C 8A756F8CC39CC8A13C7905253578FD2F4750505CC3A33FD13165C5793811BB17 7D3C7CE0171C01408CA4EFBE80B2C05089174F1E86D44486A089514F12F5AB31 29E4516C6C62B0174E5DCC459BEE512FDBFAC58F10015C2F891C3D107EDE0AA8 5BDA703BAF9CE3934AC5981A3E084404914884A8485F8163DAB1E3C0799457A9 201289E0E46889A5F347E18D3F04233D5389DCC2AA6EDB3E71B417E6440E45EA 8D62C4FC33156A8D160B6707604AB80F36AF998C17DF38C4E3EFD2312CC3A0BE A602B59565B871F1B421121034112E1E3EB87DED3CC797F2C3415858DA60C888 085E79755303CCE556DD36BE2B78F4B183471F7B14286B50D7A841C0E03E08F5 EF8BE4B47C8E272BF70137E146040F809FB702AD6D7A7CB03709250FEA39BED1 C3FBC1CEDA1CC9A979E8DBDB0E9E7D1D3074A00237EE9409F4DECE2B4741492D 37F987057AC0D75B01473BF9533986610DE39589440C9D9EC1EDDC72BCBBFD14 5252F3A1D6B409F8BB9CFCAB2BEE83651E8DE3E713BF06CB32F0F0F117F0DEBA 922CA09D8EDD09B5AAAEDBC6778589C186DE92949A87B3A9790080C9A3BD2136 32748D1EDE0FAFCC1E019D9EC1969864E43C568133270C06001C3B7707C7CE19 7AFEDCC78698764C1B37188B670720FAE5606C7A3312CF0F744679950A69378B 9FCAFE933F66E3566E39863FDF078777BD8CD4A32BF1FECA89D0332C922F0B37 D45D3AA6B4308797AEAFAEC0CD4B6720919A0878ADED7BF1D2377E4A44555911 2E241E7CAA173006475B39FC062AD0D0D4826B774A91575C8D9207F578CE4E8E C0C17D78BC0183FBE08DF9416089F0D181F3C8CA2DE7E5FBF9F4869BC21677EF 5522BFA40629E9055035B762D8201778B8DA0B7487F8BB63DE0B7E887AC117A3 FCFA62CBBE7398B7F22B300C0B00D033869E2002BF81B4A7F50FF9582244451F C2A2B70E23FE74165AB43A4C9B3008BBDF9B89CF36BF28D0DBA563B4AD2D02DA 8F09076165EB00B9D5A3A8A8582C817FD8542E9D9F750549DFED4353430DEE5E BF004D7363576A9E8809419E108944E86961864FFF32039F6D9CFD7045054C19 E3C3F10D19E08C950B43211201BBBFBA886BB7EE0B64CD1C6FE82D1EAEF6F86E CF42C47EFC322CCC0D818F792FF80AF8DFDAFA0326BFFA05CEFC9403998904B3 260E81CCE4519CABAE410D003033E5CF0AE60F65B6AFD426840CC08259C3915F 5C8375DB8E634CD42798FACA7E3436B5627CF000F47BAC517439C77878FB4222 9182611E4DB0DA560DC4620996BCFD316E5F3B0F9661E0313810BD5CFA813534 0EA49EFD86E3675906C539D7E1E537A62B559D426E2E43B06F5FE81916672EE7 76580603A1FEEE7053D86088A7135AB43ABCB57834A4123162E2D270E97A9140 967B6F3B0C1DE88C06550B9252F3783BEF99E38720C4DF1DCE8E9628ABE43724 9D8EC196CFCEC1D1BE277CBD15D8B2E6052CFFEB51E8F52CCAAB540080A9E37C 70EAFC5DD436A8E1E2640D5F6F0500A0ACC2206BDAF8411833B23F067A3C870D 3B4E41D3D20686612133918008D0B4F0E7992E1DE3A87043D4B27771FADB2FA0 696E8495AD03A62F7E0B0060EBE88C90C879821D6B696136CA4BF278720AEF3E D931E181FD3166B8078F76E5E712D436A8213391202DB30447CEFCCCDB6DAB35 6D888A1C8AA96307A1B4A201A632C3EB2C991580250F97C2EDF829A3905B4A1F 3F9F8DC3893778B65BC9CD1011E285B9937DB1E3C079817D7A86C5FA1D27F18F BFCDC3B041BDF15AD448ECFDFA328E9DBB83E91306A3BF9B0392BE7A1D750D6A D8D958000052D2F2B9D5E0DF3F4FC190810A4C1D370893C206A2AE4183E7EC0D 61B0FFFEE60AE7E076F01C73F372327C83C6F118060D0F858F7FA820C4D0196E A52709682CD3F9C6B1405983931773B8789321866468C7F7CB1B602937C30F29 77909EA514943D77251F2626121011940FEAD1A4D672BDA9630C8B009494D5C1 D9D112878FDFC4C90B7705B20E27DE40656D33DADAF4108980A3676EC1B2A719 EA1A1F0DE70DAA16BCFDB74484F8BB83615998994AA16969C34B2BBF4644A817 5C1536B0B33647654D336EE79523A5C38AB1B8B40E13177C8617C27DD0CFD50E A632294ACAEA91F1F37D5CFDB944608FC8C444D6BAE5CB645300F864E33298CB AD505A9883A1A3C661CCB49761D6C3E2A9623F293F1C40DAD96F794AA62F590F 779FC067B1B26EDA723FFD43FE174C977E5EB878EA3B00C04F27E370333519B3 5FFD2FF41F349CDF03581685D937909B7505AD1A35F43A2D7ABB0F847FD80C84 4D5D8C1E165628CAB901B15882E16366A077BFC18630C933741B3CC70C1812C0 3906009A1A6A7168FB3B78E1A53F23307C1A0040555F8D7FFC7D0D6A2B4B7982 B2AFFF84EAF2FB88981B8D80B133307CCC0C5E6B7886A703CF319E43026065EB 80C6BA6A8EC6B22C8E7DB50BCF8F1A075333739C8CDD2B704A3B6E5E3A0946AF 43E89485B0E8293C64F07B837BEF9E18D2DF0EA93F57A0A2BA05D3C3FB72DF63 743A062A751BD2B32AD1D8645871393B5820C45FC1CDA1F4F00701389A5400B5 46C7C9E639462C1663FCCC8588DFBF9D176DEDEDEE055333C38182CA52E331A5 76645D4942567A32DC3C872238F20F70721DF0AB2BE0DF15AECE969830CA05F7 4A55A8AC69C1C4E03E6852B7E15E6913645231FA2A7A62F6F87ED8F575166EE7 D7C2D1CE1C53C2DC505AD98C8A1A4D878F662408BE0A96CB23C2A7426E658B0B 27E2C0B20CAC6C1C3029EA75884422300C83FA6AE1C1060188509C7B133A9D16 F3976FFB4D2AE13F05F72BD4D8F3CF2C10112C7A48B179C508BC36C7072BB6FC C4F1FC74AD0C89E78B04937F4718DDC778FB0561A06F9060C570F6C8E76059C6 5811A3A850E683581610FDE2EF71FFD168D6E89053D480E1831C616BF5E81872 E870053CFBDA703DA6BC5A8D4309FC257CB73F94655FBF844BA78F3C95616E03 86422416FFBF9EFC757A4338A463C846D5DC8607556A6E8EA9691086BEBAED98 3BD72F3E954166E63D113076D65395F9BD412C12C1BDB7255AB57A9457AB616F 6DE8359977AB0543D9E3E8B6637C8326203355B8AB7F1C66E6720C0A0847D0C4 9760DAC3C2A8D2DF33EC6DCC1019E20A998918FD5DADD0CBDE1C078EDEE5EDE3 BC3DEC20168B1E6D2A01A4DD2C4779B59AE3E9B6633C7C8661F4E4F9B874E65B 30FA47211699590FB8F61F0C57CFE7D1A7FFF37054184EB3B72BFD3DA35ED58A 7C65239A353A1080BC92460084C1FD6DD1A663505DDF8A8473D7917DCFF091AE 59A3434E613D4C65123CEFE5C00BA26617D4F11CC30BC9B4A3AB30426DE50354 DC2F0411416E6507A73EFD0191E829421ACF42324F1D92E90E6C1C9C606DEF24 10F80CBF2DA400A02CC88646DDC41189ED10E5E576A986302DFBF0B781DE2192 CBF3BA91F2ED2D071DCB1BCAB22C09E8440FCB707474D0471DEC78744C894B13 19A7830CEFD64177FB7BA0BD057376F3CBB3F438BDE37B77426FD7C7D51FBF0E 0CFA3AD201B1C43078892412898E61985F7CBEEC197E7B884462BD9488FFB1DA C4C40423478EE4EE74B4A3B1B111696969108BC5F0F7F7E72EDC1011777D2137 37971BD66C6C6CE0E7E7071313FEF9009D4E87AB57AFA2A9A909767676F0F5F5 159CAA6F6B6B437A7A3AD46A358F2E954AE1EFEFCF5DDDD3E974A8ADAD454E4E 0E5A5BF907F10060D8B061707373C3A953A7A0D16804F9F6F6F6F0F1F1819393 131A1A1A90999969F4CA86ABAB2BBCBCBC60696989828202E4E4E4A0A545B8F7 B0B0B0C0A44993B80B591D616A6A8A8888085CBE7C19B5B5B582B21D41800862 B1448F87BD0D00AD5EBD9A8888727272E8F4E9D3DC131717475656563475EA54 2222BA77EF1E25242450626222A5A5A511CBB2949999498E8E8E0480BEFDF65B 22224A4F4FE7C9494C4CA4B0B0300240274F9E2422A2D4D4541ECFB163C768E4 C891D4D12E00B468D122CEB6848404CAC8C820B55A4D2A958AE6CC99C3E30D0B 0B2386618888E8F4E9D30259AB57AF268D4643494949B47BF76E4A4E4E268661 68C78E1D1C8F42A1A053A74E11CBB254545444DF7FFF3D29954AAAADADA5E8E8 6881CC3367CE101111C330347AF4685EDEBC79F38888E8830F3E1094133C22B1 5EE098F5EBD71311D19B6FBE69B4D0CB2FBF4C4444BB77EFE6D10F1D3A444444 2FBEF82201A063C78E111151484848A7069C3B778E8888FCFCFC9E6C2C402B56 AC2022A2F7DE7B8FA30505051111515A5A1A47333131A1ECEC6C522A95B479F3 662232DC1BED28ABB8B8986A6A6AC8C7C787A3AD5AB58A626262080089C562CA CECEE6BD130092C964B47FFF7ED2E974E4E4E4C4D1A3A2A2888868C3860D5455 554577EFDE25994CC6E5FFF18F7F2422A28F3EFAE8D739E6F6EDDB9490904009 0909141F1F4FE1E1E13CC7DCBA758B3EFBEC33FAE28B2FE8D8B1635457574771 7171646262C273CCCD9B37E9FCF9F3DCF3C30F3F90999919CF311919193C9EF8 F878924AA59D3AE6F8F1E3B474E9527AFBEDB729353595888866CD9AC5F1AD59 B3868888162D5A443D7AF420A55249151515646D6DCDF1444444701753EBEBEB E9C2850BB475EB56CE51BEBEBE5CEF148944025B3ADA2797CBA9ACAC8C0A0B0B 492693719762DF7DF7DD5FEC984E27FDD4D4541C3D7A1480E1DEE5B56BD778F9 252525484949C1F8F1E33165CA141C3C78102B56AC804EA7E3F1C5C4C4203333 934BD7D7D70BE6833D7BF6F06E91D5D4D440AFEFFC9C80BDBD3DBCBCBCD0D4D4 849D3B7762D1A245C8CB331C00512814D8B061035A5A5A606B6B8B3FFDE94FB8 75EB16222323B165CB16BCFEFAEB000C5700030303616A6A0A5F5F5F04040460 D5AA5558BD7A35424242D0DCDC0CC0308F3DBE1D707575C5DCB973B177EF5E34 373763E3C68D707676C6F1E3C7B16CD932482412B4B6B662DDBA75888D8D4541 4141A7EFD2297E8BA12C3A3A9A8888323333C9D2D2F25F3A943DFE7CF3CD3744 44B46BD72E7AEFBDF7B8273B3B9B5896A5C0C040B2B2B2A2969616BA78F122D9 D8D87043D4E9D3A7898868DCB87104801213138988E89D77DE21B95C4E62B198 860D1B46F7EEDDA3E6E666522814E4EDED4D6D6D6D74EBD62D9EBE4F3EF98488 88CE9E3DCBEB311F7FFC31999999718FB191C1E850B660C102625996E6CE9D6B F4C5C3C3C38961187AFBEDB779F4B56BD712CBB2B47EFD7A02401F7CF00169B5 BC7F21C04D8CED65B76FDF4E6D6D6D021EBD5E6F74729D356B16B12C4B4B962C 316A9BB5B53569B55A8A8F8F17E40506069256ABA53D7BF670EF71F1E2457AF0 E001E5E7E753535313E5E4E4D06BAFBDC695313737A7CD9B379352A924966549 A3D1504B4B0B9D397386860D1B460068D3A64DA4D56A29303050A0333E3E9E18 86216767670A0A0AA2FAFA7AC1BBB6B4B450444484C03122B158A263D967FB98 7F2B88C47AA9D4C42446A7A3A520327EB1E477047A32CBFFBD7011482492C6FC 0FD2F4EAAD4CE1114F0000000049454E44AE426082 } end object txAppName: TLabel Left = 86 Height = 17 Top = 14 Width = 75 Caption = 'txAppName' Font.Height = -13 Font.Style = [fsBold] ParentColor = False ParentFont = False end object txAuthor: TLabel Left = 86 Height = 14 Top = 66 Width = 198 Caption = 'Copyright (c) 2008-2014 by Yury Sidorov' ParentColor = False end object txVersion: TLabel Left = 86 Height = 14 Top = 38 Width = 55 Caption = 'Version %s' ParentColor = False end object Bevel1: TBevel Left = 6 Height = 7 Top = 189 Width = 416 Anchors = [akLeft, akRight, akBottom] Shape = bsTopLine end object imgDonate: TImage Cursor = crHandPoint Left = 86 Height = 26 Hint = 'Donate via PayPal,WebMoney,Credit card' Top = 138 Width = 92 AutoSize = True OnClick = imgDonateClick ParentShowHint = False Picture.Data = { 1754506F727461626C654E6574776F726B477261706869638404000089504E47 0D0A1A0A0000000D494844520000005C0000001A08030000000D30CB1B000001 80504C5445909187EC8B35F4AA9EFFCA782F6CB3D7DADC2399D5FFBE5AE53632 FFAC2C81ABD1FEE1A8E2E3E4284A6C5E7287D5E7F4B6B9BA7A8785B7D2E68A6D 4DF9D4D1FFEFD3FFB23C49B2E3FFF6E5E75B35777063D6C9A8D89A376AC4EF8D CCEC4853543F5577FEE9C0CADDEEBBB0922F3C726193C7E5F4FB6D3A5C49647C B83741B88B3EEC625FB97A46A17E49153A67CF87454083BCFFECC9FEDE9EF2A9 76FED287686451F7C0BF8D9AAFF1E6E5ADAA9EEC9842B5BDC9FEB745528ABF2B 200C5EA4D5A4ACB6403727ABB2B8BEC1C1FEF5F2F5C6A8F29C6AE2EDF6F6C298 F3FAFDCACED378879FF1F6FA2B7DC15EBDEBFFA926FFF3DCFFD696FFE2B4FFF9 EEFEE7BAFFFDF8FEE6B5FFAF34FEE4B0FEDFA20057A0C9C6C1C8CCCDC3C7C8C7 CACCBD6728C5BB9E4E4535F2F3F6EFA635EED8A8EEDCB297BBDAF1F1EF70A1CA 9B9D9C847E73EED39AFEDA981062A6FBE4D0BA4E41FFE0B0A69D8DE7DBD7D1D2 D3DCCDCF9DA6B3DECA9D5E5163F9D1B7AEB0A4EAA121003366000000FF9933FF FFFFFFFFFF7B0ABBB20000008074524E53FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0038054B67000000097048597300000B 1200000B1201D2DD7EFC0000000974455874436F6D6D656E740000892A8D0600 00020949444154789CB5964D8BA34010861B3C4810031246161474F1D208D310 D143C0FF904C7657939BA01EC2E4305E02CB4B337F7DABFD984DA2CC491FB0AC AED2A789C16ED9678F9C91C1C906B5763CFC9987EDDBA0EFE4323EFC9A930FF9 5F2E8F3F66E62007B9DC1E66E745767219BD2C816CE5E976112A2597F1CF3117 10C2AA275AF7387FBF694A92DBDAEF31164CCE05F844EB8E8B28BEE9DA9F4CA6 7A3686A3C8B25AC0A1BC2E8AA1EC1475765711A8DBA23361C8AEA924791A9F46 0015450B4C057A4097D3A980B9A6CCE92BCEC9518FEE525111BC1A2BF44E9E5E 8D270AF8EA64C132D6F0778DC0CD60E4087CAAECE00716B811709856C5B1A62A 7B36185ADACBD3F8A9C3B0EEE537884A258C66E10685A6BDA051033537FD1EC3 08D4E881484F5BB9ADD0E3F33D6B30753251D03494908606415B3917F44F830A 671FBB3383F0B90FFFE1F6484B957490DB76AAC77134E023A0C86046A4A58CA3 7907A248857701AB206F1499B851DFB788E6EBD66BACE9BD92E4F904409E971B 20CC5FE1E5B90B51862A51411DA580ABAA39F5132ABB53929CE4AB31213A5E57 2BD22489A06C8364D50617484C405D25366E69521F66396159497A4327E49E62 E3AADCF5003324AF17EEFB4063CFA31675DCB69FB8936EF5FAEF1742B6ABA2BD 08DDAA480BC012C87EB3D016407E6D73F1DBDCC8FB0DFA382B917CFCB488B7B3 6D701F72FFF0DDB2CC47D13F947E4C789D660D2C0000000049454E44AE426082 } ShowHint = True Transparent = True end object txHomePage: TLabel Cursor = crHandPoint Left = 86 Height = 14 Top = 86 Width = 55 Caption = 'Home page' Font.Color = clBlue Font.Style = [fsUnderline] ParentColor = False ParentFont = False OnClick = txHomePageClick end object txDonate: TLabel Left = 86 Height = 14 Top = 118 Width = 195 Caption = 'Donate to support further development:' ParentColor = False end end object tabLicense: TTabSheet Caption = 'License' ClientHeight = 273 ClientWidth = 427 object edLicense: TMemo Left = 4 Height = 265 Top = 4 Width = 419 Align = alClient BorderSpacing.Around = 4 Lines.Strings = ( #9#9' GNU GENERAL PUBLIC LICENSE' #9#9' Version 2, June 1991' '' ' Copyright (C) 1989, 1991 Free Software Foundation, Inc.,' ' 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA' ' Everyone is permitted to copy and distribute verbatim copies' ' of this license document, but changing it is not allowed.' '' #9#9#9' Preamble' '' ' The licenses for most software are designed to take away your' 'freedom to share and change it. By contrast, the GNU General Public' 'License is intended to guarantee your freedom to share and change free' 'software--to make sure the software is free for all its users. This' 'General Public License applies to most of the Free Software' 'Foundation''s software and to any other program whose authors commit to' 'using it. (Some other Free Software Foundation software is covered by' 'the GNU Lesser General Public License instead.) You can apply it to' 'your programs, too.' '' ' When we speak of free software, we are referring to freedom, not' 'price. Our General Public Licenses are designed to make sure that you' 'have the freedom to distribute copies of free software (and charge for' 'this service if you wish), that you receive source code or can get it' 'if you want it, that you can change the software or use pieces of it' 'in new free programs; and that you know you can do these things.' '' ' To protect your rights, we need to make restrictions that forbid' 'anyone to deny you these rights or to ask you to surrender the rights.' 'These restrictions translate to certain responsibilities for you if you' 'distribute copies of the software, or if you modify it.' '' ' For example, if you distribute copies of such a program, whether' 'gratis or for a fee, you must give the recipients all the rights that' 'you have. You must make sure that they, too, receive or can get the' 'source code. And you must show them these terms so they know their' 'rights.' '' ' We protect your rights with two steps: (1) copyright the software, and' '(2) offer you this license which gives you legal permission to copy,' 'distribute and/or modify the software.' '' ' Also, for each author''s protection and ours, we want to make certain' 'that everyone understands that there is no warranty for this free' 'software. If the software is modified by someone else and passed on, we' 'want its recipients to know that what they have is not the original, so' 'that any problems introduced by others will not reflect on the original' 'authors'' reputations.' '' ' Finally, any free program is threatened constantly by software' 'patents. We wish to avoid the danger that redistributors of a free' 'program will individually obtain patent licenses, in effect making the' 'program proprietary. To prevent this, we have made it clear that any' 'patent must be licensed for everyone''s free use or not licensed at all.' '' ' The precise terms and conditions for copying, distribution and' 'modification follow.' '' #9#9' GNU GENERAL PUBLIC LICENSE' ' TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION' '' ' 0. This License applies to any program or other work which contains' 'a notice placed by the copyright holder saying it may be distributed' 'under the terms of this General Public License. The "Program", below,' 'refers to any such program or work, and a "work based on the Program"' 'means either the Program or any derivative work under copyright law:' 'that is to say, a work containing the Program or a portion of it,' 'either verbatim or with modifications and/or translated into another' 'language. (Hereinafter, translation is included without limitation in' 'the term "modification".) Each licensee is addressed as "you".' '' 'Activities other than copying, distribution and modification are not' 'covered by this License; they are outside its scope. The act of' 'running the Program is not restricted, and the output from the Program' 'is covered only if its contents constitute a work based on the' 'Program (independent of having been made by running the Program).' 'Whether that is true depends on what the Program does.' '' ' 1. You may copy and distribute verbatim copies of the Program''s' 'source code as you receive it, in any medium, provided that you' 'conspicuously and appropriately publish on each copy an appropriate' 'copyright notice and disclaimer of warranty; keep intact all the' 'notices that refer to this License and to the absence of any warranty;' 'and give any other recipients of the Program a copy of this License' 'along with the Program.' '' 'You may charge a fee for the physical act of transferring a copy, and' 'you may at your option offer warranty protection in exchange for a fee.' '' ' 2. You may modify your copy or copies of the Program or any portion' 'of it, thus forming a work based on the Program, and copy and' 'distribute such modifications or work under the terms of Section 1' 'above, provided that you also meet all of these conditions:' '' ' a) You must cause the modified files to carry prominent notices' ' stating that you changed the files and the date of any change.' '' ' b) You must cause any work that you distribute or publish, that in' ' whole or in part contains or is derived from the Program or any' ' part thereof, to be licensed as a whole at no charge to all third' ' parties under the terms of this License.' '' ' c) If the modified program normally reads commands interactively' ' when run, you must cause it, when started running for such' ' interactive use in the most ordinary way, to print or display an' ' announcement including an appropriate copyright notice and a' ' notice that there is no warranty (or else, saying that you provide' ' a warranty) and that users may redistribute the program under' ' these conditions, and telling the user how to view a copy of this' ' License. (Exception: if the Program itself is interactive but' ' does not normally print such an announcement, your work based on' ' the Program is not required to print an announcement.)' '' 'These requirements apply to the modified work as a whole. If' 'identifiable sections of that work are not derived from the Program,' 'and can be reasonably considered independent and separate works in' 'themselves, then this License, and its terms, do not apply to those' 'sections when you distribute them as separate works. But when you' 'distribute the same sections as part of a whole which is a work based' 'on the Program, the distribution of the whole must be on the terms of' 'this License, whose permissions for other licensees extend to the' 'entire whole, and thus to each and every part regardless of who wrote it.' '' 'Thus, it is not the intent of this section to claim rights or contest' 'your rights to work written entirely by you; rather, the intent is to' 'exercise the right to control the distribution of derivative or' 'collective works based on the Program.' '' 'In addition, mere aggregation of another work not based on the Program' 'with the Program (or with a work based on the Program) on a volume of' 'a storage or distribution medium does not bring the other work under' 'the scope of this License.' '' ' 3. You may copy and distribute the Program (or a work based on it,' 'under Section 2) in object code or executable form under the terms of' 'Sections 1 and 2 above provided that you also do one of the following:' '' ' a) Accompany it with the complete corresponding machine-readable' ' source code, which must be distributed under the terms of Sections' ' 1 and 2 above on a medium customarily used for software interchange; or,' '' ' b) Accompany it with a written offer, valid for at least three' ' years, to give any third party, for a charge no more than your' ' cost of physically performing source distribution, a complete' ' machine-readable copy of the corresponding source code, to be' ' distributed under the terms of Sections 1 and 2 above on a medium' ' customarily used for software interchange; or,' '' ' c) Accompany it with the information you received as to the offer' ' to distribute corresponding source code. (This alternative is' ' allowed only for noncommercial distribution and only if you' ' received the program in object code or executable form with such' ' an offer, in accord with Subsection b above.)' '' 'The source code for a work means the preferred form of the work for' 'making modifications to it. For an executable work, complete source' 'code means all the source code for all modules it contains, plus any' 'associated interface definition files, plus the scripts used to' 'control compilation and installation of the executable. However, as a' 'special exception, the source code distributed need not include' 'anything that is normally distributed (in either source or binary' 'form) with the major components (compiler, kernel, and so on) of the' 'operating system on which the executable runs, unless that component' 'itself accompanies the executable.' '' 'If distribution of executable or object code is made by offering' 'access to copy from a designated place, then offering equivalent' 'access to copy the source code from the same place counts as' 'distribution of the source code, even though third parties are not' 'compelled to copy the source along with the object code.' '' ' 4. You may not copy, modify, sublicense, or distribute the Program' 'except as expressly provided under this License. Any attempt' 'otherwise to copy, modify, sublicense or distribute the Program is' 'void, and will automatically terminate your rights under this License.' 'However, parties who have received copies, or rights, from you under' 'this License will not have their licenses terminated so long as such' 'parties remain in full compliance.' '' ' 5. You are not required to accept this License, since you have not' 'signed it. However, nothing else grants you permission to modify or' 'distribute the Program or its derivative works. These actions are' 'prohibited by law if you do not accept this License. Therefore, by' 'modifying or distributing the Program (or any work based on the' 'Program), you indicate your acceptance of this License to do so, and' 'all its terms and conditions for copying, distributing or modifying' 'the Program or works based on it.' '' ' 6. Each time you redistribute the Program (or any work based on the' 'Program), the recipient automatically receives a license from the' 'original licensor to copy, distribute or modify the Program subject to' 'these terms and conditions. You may not impose any further' 'restrictions on the recipients'' exercise of the rights granted herein.' 'You are not responsible for enforcing compliance by third parties to' 'this License.' '' ' 7. If, as a consequence of a court judgment or allegation of patent' 'infringement or for any other reason (not limited to patent issues),' 'conditions are imposed on you (whether by court order, agreement or' 'otherwise) that contradict the conditions of this License, they do not' 'excuse you from the conditions of this License. If you cannot' 'distribute so as to satisfy simultaneously your obligations under this' 'License and any other pertinent obligations, then as a consequence you' 'may not distribute the Program at all. For example, if a patent' 'license would not permit royalty-free redistribution of the Program by' 'all those who receive copies directly or indirectly through you, then' 'the only way you could satisfy both it and this License would be to' 'refrain entirely from distribution of the Program.' '' 'If any portion of this section is held invalid or unenforceable under' 'any particular circumstance, the balance of the section is intended to' 'apply and the section as a whole is intended to apply in other' 'circumstances.' '' 'It is not the purpose of this section to induce you to infringe any' 'patents or other property right claims or to contest validity of any' 'such claims; this section has the sole purpose of protecting the' 'integrity of the free software distribution system, which is' 'implemented by public license practices. Many people have made' 'generous contributions to the wide range of software distributed' 'through that system in reliance on consistent application of that' 'system; it is up to the author/donor to decide if he or she is willing' 'to distribute software through any other system and a licensee cannot' 'impose that choice.' '' 'This section is intended to make thoroughly clear what is believed to' 'be a consequence of the rest of this License.' '' ' 8. If the distribution and/or use of the Program is restricted in' 'certain countries either by patents or by copyrighted interfaces, the' 'original copyright holder who places the Program under this License' 'may add an explicit geographical distribution limitation excluding' 'those countries, so that distribution is permitted only in or among' 'countries not thus excluded. In such case, this License incorporates' 'the limitation as if written in the body of this License.' '' ' 9. The Free Software Foundation may publish revised and/or new versions' 'of the General Public License from time to time. Such new versions will' 'be similar in spirit to the present version, but may differ in detail to' 'address new problems or concerns.' '' 'Each version is given a distinguishing version number. If the Program' 'specifies a version number of this License which applies to it and "any' 'later version", you have the option of following the terms and conditions' 'either of that version or of any later version published by the Free' 'Software Foundation. If the Program does not specify a version number of' 'this License, you may choose any version ever published by the Free Software' 'Foundation.' '' ' 10. If you wish to incorporate parts of the Program into other free' 'programs whose distribution conditions are different, write to the author' 'to ask for permission. For software which is copyrighted by the Free' 'Software Foundation, write to the Free Software Foundation; we sometimes' 'make exceptions for this. Our decision will be guided by the two goals' 'of preserving the free status of all derivatives of our free software and' 'of promoting the sharing and reuse of software generally.' '' #9#9#9' NO WARRANTY' '' ' 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY' 'FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN' 'OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES' 'PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED' 'OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF' 'MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS' 'TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE' 'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,' 'REPAIR OR CORRECTION.' '' ' 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING' 'WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR' 'REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,' 'INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING' 'OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED' 'TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY' 'YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER' 'PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE' 'POSSIBILITY OF SUCH DAMAGES.' '' #9#9' END OF TERMS AND CONDITIONS' '' #9' How to Apply These Terms to Your New Programs' '' ' If you develop a new program, and you want it to be of the greatest' 'possible use to the public, the best way to achieve this is to make it' 'free software which everyone can redistribute and change under these terms.' '' ' To do so, attach the following notices to the program. It is safest' 'to attach them to the start of each source file to most effectively' 'convey the exclusion of warranty; and each file should have at least' 'the "copyright" line and a pointer to where the full notice is found.' '' ' ' ' Copyright (C) ' '' ' This program is free software; you can redistribute it and/or modify' ' it under the terms of the GNU General Public License as published by' ' the Free Software Foundation; either version 2 of the License, or' ' (at your option) any later version.' '' ' This program is distributed in the hope that it will be useful,' ' but WITHOUT ANY WARRANTY; without even the implied warranty of' ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the' ' GNU General Public License for more details.' '' ' You should have received a copy of the GNU General Public License along' ' with this program; if not, write to the Free Software Foundation, Inc.,' ' 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.' '' 'Also add information on how to contact you by electronic and paper mail.' '' 'If the program is interactive, make it output a short notice like this' 'when it starts in an interactive mode:' '' ' Gnomovision version 69, Copyright (C) year name of author' ' Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w''.' ' This is free software, and you are welcome to redistribute it' ' under certain conditions; type `show c'' for details.' '' 'The hypothetical commands `show w'' and `show c'' should show the appropriate' 'parts of the General Public License. Of course, the commands you use may' 'be called something other than `show w'' and `show c''; they could even be' 'mouse-clicks or menu items--whatever suits your program.' '' 'You should also get your employer (if you work as a programmer) or your' 'school, if any, to sign a "copyright disclaimer" for the program, if' 'necessary. Here is a sample; alter the names:' '' ' Yoyodyne, Inc., hereby disclaims all copyright interest in the program' ' `Gnomovision'' (which makes passes at compilers) written by James Hacker.' '' ' , 1 April 1989' ' Ty Coon, President of Vice' '' 'This General Public License does not permit incorporating your program into' 'proprietary programs. If your program is a subroutine library, you may' 'consider it more useful to permit linking proprietary applications with the' 'library. If this is what you want to do, use the GNU Lesser General' 'Public License instead of this License.' ) ReadOnly = True ScrollBars = ssBoth TabOrder = 0 WordWrap = False end end end object Buttons: TButtonPanel[1] Left = 8 Height = 26 Top = 315 Width = 435 BorderSpacing.Top = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 0 ShowButtons = [pbOK] ShowBevel = False end end TransGUI/about.pas0000644000000000000000000001525412261763702013044 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit About; {$mode objfpc}{$H+} interface uses BaseForm, Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, ExtCtrls, ButtonPanel; resourcestring SErrorCheckingVersion = 'Error checking for new version.'; SNewVersionFound = 'A new version of %s is available.' + LineEnding + 'Your current version: %s' + LineEnding + 'The new version: %s' + LineEnding + LineEnding + 'Do you wish to open the Downloads web page?'; SLatestVersion = 'No updates have been found.' + LineEnding + 'You are running the latest version of %s.'; type { TAboutForm } TAboutForm = class(TBaseForm) Bevel1: TBevel; Buttons: TButtonPanel; edLicense: TMemo; imgDonate: TImage; imgTransmission: TImage; imgSynapse: TImage; imgLazarus: TImage; txDonate: TLabel; txHomePage: TLabel; txAuthor: TLabel; txVersion: TLabel; txAppName: TLabel; Page: TPageControl; tabAbout: TTabSheet; tabLicense: TTabSheet; procedure FormCreate(Sender: TObject); procedure imgDonateClick(Sender: TObject); procedure imgLazarusClick(Sender: TObject); procedure imgSynapseClick(Sender: TObject); procedure txHomePageClick(Sender: TObject); private { private declarations } public { public declarations } end; procedure CheckNewVersion(Async: boolean = True); procedure GoHomePage; procedure GoDonate; implementation uses Main, utils, httpsend; type { TCheckVersionThread } TCheckVersionThread = class(TThread) private FHttp: THTTPSend; FError: string; FVersion: string; FExit: boolean; procedure CheckResult; function GetIntVersion(const Ver: string): integer; protected procedure Execute; override; end; var CheckVersionThread: TCheckVersionThread; procedure CheckNewVersion(Async: boolean); begin if CheckVersionThread <> nil then exit; Ini.WriteInteger('Interface', 'LastNewVersionCheck', Trunc(Now)); CheckVersionThread:=TCheckVersionThread.Create(True); CheckVersionThread.FreeOnTerminate:=True; if Async then CheckVersionThread.Suspended:=False else begin CheckVersionThread.Execute; CheckVersionThread.FExit:=True; CheckVersionThread.Suspended:=False; end; end; procedure GoHomePage; begin AppBusy; OpenURL('http://code.google.com/p/transmisson-remote-gui'); AppNormal; end; procedure GoDonate; begin AppBusy; OpenURL('http://code.google.com/p/transmisson-remote-gui/wiki/Donate'); AppNormal; end; { TCheckVersionThread } procedure TCheckVersionThread.CheckResult; begin ForceAppNormal; if FError <> '' then begin MessageDlg(SErrorCheckingVersion + LineEnding + FError, mtError, [mbOK], 0); exit; end; if GetIntVersion(AppVersion) >= GetIntVersion(FVersion) then begin MessageDlg(Format(SLatestVersion, [AppName]), mtInformation, [mbOK], 0); exit; end; if MessageDlg(Format(SNewVersionFound, [AppName, AppVersion, FVersion]), mtConfirmation, mbYesNo, 0) <> mrYes then exit; Application.ProcessMessages; AppBusy; OpenURL('http://code.google.com/p/transmisson-remote-gui/wiki/Download?tm=2'); AppNormal; end; function TCheckVersionThread.GetIntVersion(const Ver: string): integer; var v: string; vi, i, j: integer; begin Result:=0; v:=Ver; for i:=1 to 3 do begin if v = '' then vi:=0 else begin j:=Pos('.', v); if j = 0 then j:=MaxInt; vi:=StrToIntDef(Copy(v, 1, j - 1), 0); Delete(v, 1, j); end; Result:=Result shl 8 or vi; end; end; procedure TCheckVersionThread.Execute; begin if not FExit then begin try FHttp:=THTTPSend.Create; try if RpcObj.Http.ProxyHost <> '' then begin FHttp.ProxyHost:=RpcObj.Http.ProxyHost; FHttp.ProxyPort:=RpcObj.Http.ProxyPort; FHttp.ProxyUser:=RpcObj.Http.ProxyUser; FHttp.ProxyPass:=RpcObj.Http.ProxyPass; end; if FHttp.HTTPMethod('GET', 'http://transmisson-remote-gui.googlecode.com/svn/wiki/version.txt') then begin if FHttp.ResultCode = 200 then begin SetString(FVersion, FHttp.Document.Memory, FHttp.Document.Size); FVersion:=Trim(FVersion); end else FError:=Format('HTTP error: %d', [FHttp.ResultCode]); end else FError:=FHttp.Sock.LastErrorDesc; finally FHttp.Free; end; except FError:=Exception(ExceptObject).Message; end; if (FError <> '') or (GetIntVersion(FVersion) > GetIntVersion(AppVersion)) or Suspended then if Suspended then CheckResult else Synchronize(@CheckResult); end; if not Suspended then CheckVersionThread:=nil; end; { TAboutForm } procedure TAboutForm.imgSynapseClick(Sender: TObject); begin AppBusy; OpenURL('http://synapse.ararat.cz'); AppNormal; end; procedure TAboutForm.txHomePageClick(Sender: TObject); begin GoHomePage; end; procedure TAboutForm.FormCreate(Sender: TObject); {$ifdef lclcarbon} var s: string; {$endif lclcarbon} begin txAppName.Font.Size:=Font.Size + 2; txHomePage.Font.Size:=Font.Size; BorderStyle:=bsSizeable; txAppName.Caption:=AppName; txVersion.Caption:=Format(txVersion.Caption, [AppVersion]); Page.ActivePageIndex:=0; {$ifdef lclcarbon} s:=edLicense.Text; edLicense.Text:=''; edLicense.HandleNeeded; edLicense.Text:=s; Buttons.BorderSpacing.Right:=Buttons.BorderSpacing.Right + ScaleInt(12); {$endif lclcarbon} end; procedure TAboutForm.imgDonateClick(Sender: TObject); begin GoDonate; end; procedure TAboutForm.imgLazarusClick(Sender: TObject); begin AppBusy; OpenURL('http://www.lazarus.freepascal.org'); AppNormal; end; initialization {$I about.lrs} end. TransGUI/colsetup.lfm0000644000000000000000000000353312256577645013575 0ustar rootrootinherited ColSetupForm: TColSetupForm Left = 401 Height = 326 Top = 193 Width = 355 HorzScrollBar.Page = 399 VertScrollBar.Page = 299 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Columns setup' ClientHeight = 326 ClientWidth = 355 Constraints.MinHeight = 200 Constraints.MinWidth = 260 OnCreate = FormCreate Position = poMainFormCenter object Buttons: TButtonPanel[0] Left = 8 Height = 36 Top = 282 Width = 339 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] end object Panel1: TPanel[1] Left = 8 Height = 266 Top = 8 Width = 339 Align = alClient BorderSpacing.Around = 8 BevelOuter = bvNone ClientHeight = 266 ClientWidth = 339 TabOrder = 0 object lstColumns: TCheckListBox Left = 0 Height = 266 Top = 0 Width = 256 Anchors = [akTop, akLeft, akRight, akBottom] ItemHeight = 0 OnClick = lstColumnsClick OnClickCheck = lstColumnsClickCheck TabOrder = 0 end object btUp: TButton Left = 264 Height = 23 Top = 0 Width = 75 Anchors = [akTop, akRight] Caption = 'Up' OnClick = btUpClick TabOrder = 1 end object btDown: TButton Left = 264 Height = 23 Top = 28 Width = 75 Anchors = [akTop, akRight] Caption = 'Down' OnClick = btDownClick TabOrder = 2 end end end TransGUI/torrprops.pas0000644000000000000000000000527012261763702014001 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit TorrProps; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, Spin, ButtonPanel, ComCtrls, BaseForm; type { TTorrPropsForm } TTorrPropsForm = class(TBaseForm) Buttons: TButtonPanel; cbIdleSeedLimit: TCheckBox; cbMaxDown: TCheckBox; cbMaxUp: TCheckBox; cbSeedRatio: TCheckBox; edIdleSeedLimit: TSpinEdit; edMaxUp: TSpinEdit; edPeerLimit: TSpinEdit; edSeedRatio: TFloatSpinEdit; edMaxDown: TSpinEdit; edTrackers: TMemo; txTrackers: TLabel; Page: TPageControl; tabGeneral: TTabSheet; tabAdvanced: TTabSheet; txKbs1: TLabel; txKbs2: TLabel; txMinutes: TLabel; txName: TLabel; txPeerLimit: TLabel; procedure cbIdleSeedLimitClick(Sender: TObject); procedure cbMaxDownClick(Sender: TObject); procedure cbMaxUpClick(Sender: TObject); procedure cbSeedRatioClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } end; implementation uses main; { TTorrPropsForm } procedure TTorrPropsForm.cbMaxDownClick(Sender: TObject); begin edMaxDown.Enabled:=cbMaxDown.Checked; end; procedure TTorrPropsForm.cbIdleSeedLimitClick(Sender: TObject); begin edIdleSeedLimit.Enabled:=cbIdleSeedLimit.State = cbChecked; end; procedure TTorrPropsForm.cbMaxUpClick(Sender: TObject); begin edMaxUp.Enabled:=cbMaxUp.Checked; end; procedure TTorrPropsForm.cbSeedRatioClick(Sender: TObject); begin edSeedRatio.Enabled:=cbSeedRatio.State = cbChecked; end; procedure TTorrPropsForm.FormCreate(Sender: TObject); begin Page.ActivePageIndex:=0; end; initialization {$I torrprops.lrs} end. TransGUI/LICENSE.txt0000644000000000000000000004310311366572451013046 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. TransGUI/varlist.pas0000644000000000000000000002276312261763702013421 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit varlist; {$mode objfpc}{$H+} interface uses Classes, SysUtils, variants; type TVarList = class; TCompareVarRowsEvent = function(Sender: TVarList; Row1, Row2: PVariant; DescendingSort: boolean): integer of object; { TVarList } TVarList = class(TList) private FColCnt: integer; FExtraColumns: integer; FOnCompareVarRows: TCompareVarRowsEvent; FOnDataChanged: TNotifyEvent; FUpdateLockCnt: integer; function GetItemPtr(ACol, ARow: integer): PVariant; function GetItems(ACol, ARow: integer): variant; function GetRowCnt: integer; function GetRowOptions(ARow: integer): integer; function GetRows(ARow: integer): PVariant; function GetRow(ARow: integer): PVariant; procedure SetColCnt(const AValue: integer); procedure SetExtraColumns(const AValue: integer); procedure SetItems(ACol, ARow: integer; const AValue: variant); procedure SetRowCnt(const AValue: integer); procedure SetRowOptions(ARow: integer; const AValue: integer); function IntCols: integer; procedure CheckColIndex(ColIndex: integer); protected procedure DoDataChanged; virtual; public constructor Create(AColCnt, ARowCnt: integer); destructor Destroy; override; procedure Clear; override; procedure Delete(Index: Integer); procedure Sort(ACol: integer; Descending: boolean = False); reintroduce; function IndexOf(ACol: integer; const Value: variant): integer; function SortedIndexOf(ACol: integer; const Value: variant): integer; function Find(ACol: integer; const Value: variant; var Index: Integer): Boolean; procedure BeginUpdate; procedure EndUpdate; procedure InsertRow(ARow: integer); function IsUpdating: boolean; function GetRowItem(ARow: PVariant; ACol: integer): variant; property Items[ACol, ARow: integer]: variant read GetItems write SetItems; default; property ItemPtrs[ACol, ARow: integer]: PVariant read GetItemPtr; property Rows[ARow: integer]: PVariant read GetRows; property RowOptions[ARow: integer]: integer read GetRowOptions write SetRowOptions; property ColCnt: integer read FColCnt write SetColCnt; property RowCnt: integer read GetRowCnt write SetRowCnt; property Count: integer read GetRowCnt; property OnDataChanged: TNotifyEvent read FOnDataChanged write FOnDataChanged; property OnCompareVarRows: TCompareVarRowsEvent read FOnCompareVarRows write FOnCompareVarRows; property ExtraColumns: integer read FExtraColumns write SetExtraColumns; end; function CompareVariants(const v1, v2: variant): integer; implementation uses Math; { TVarList } function TVarList.GetItems(ACol, ARow: integer): variant; begin CheckColIndex(ACol); Result:=GetRow(ARow)[ACol + IntCols]; end; function TVarList.GetItemPtr(ACol, ARow: integer): PVariant; begin CheckColIndex(ACol); Result:=GetRow(ARow) + (ACol + IntCols); end; function TVarList.GetRowCnt: integer; begin Result:=inherited GetCount; end; function TVarList.GetRowOptions(ARow: integer): integer; begin Result:=GetRow(ARow)[0]; end; function TVarList.GetRows(ARow: integer): PVariant; begin Result:=GetRow(ARow); end; function TVarList.GetRow(ARow: integer): PVariant; var v: PVariant; sz: integer; begin if ARow >= Count then SetRowCnt(ARow + 1); v:=Get(ARow); if v = nil then begin sz:=SizeOf(variant)*(FColCnt + IntCols); v:=GetMem(sz); FillChar(v^, sz, 0); v[0]:=0; Put(ARow, v); end; Result:=v; end; procedure TVarList.SetColCnt(const AValue: integer); var i, j, ocnt, ncnt: integer; p: PVariant; begin if FColCnt = AValue then exit; ocnt:=FColCnt + IntCols; FColCnt:=AValue; ncnt:=FColCnt + IntCols; for i:=0 to Count - 1 do begin p:=GetRow(i); for j:=ncnt to ocnt - 1 do VarClear(p[j]); ReAllocMem(p, ncnt*SizeOf(variant)); if ncnt > ocnt then FillChar(p[ocnt], (ncnt - ocnt)*SizeOf(variant), 0); end; end; procedure TVarList.SetExtraColumns(const AValue: integer); begin if FExtraColumns=AValue then exit; if RowCnt <> 0 then raise Exception.Create('Unable to set extra columns.'); FExtraColumns:=AValue; end; procedure TVarList.SetItems(ACol, ARow: integer; const AValue: variant); begin GetRow(ARow)[ACol + IntCols]:=AValue; DoDataChanged; end; procedure TVarList.SetRowCnt(const AValue: integer); begin BeginUpdate; try while Count > AValue do Delete(Count - 1); SetCount(AValue); finally EndUpdate; end; end; procedure TVarList.SetRowOptions(ARow: integer; const AValue: integer); begin GetRow(ARow)[0]:=AValue; end; function TVarList.IntCols: integer; begin Result:=FExtraColumns + 1; end; procedure TVarList.CheckColIndex(ColIndex: integer); begin if (ColIndex + IntCols < 0) or (ColIndex >= ColCnt) then raise Exception.CreateFmt('Invalid column index (%d).', [ColIndex]); end; procedure TVarList.DoDataChanged; begin if Assigned(FOnDataChanged) and (FUpdateLockCnt = 0) then FOnDataChanged(Self); end; constructor TVarList.Create(AColCnt, ARowCnt: integer); begin inherited Create; FColCnt:=AColCnt; RowCnt:=ARowCnt; end; destructor TVarList.Destroy; begin FOnDataChanged:=nil; inherited Destroy; end; procedure TVarList.Clear; var i: integer; v: PVariant; begin for i:=0 to Count - 1 do begin v:=inherited Get(i); if v <> nil then begin VarClear(v^); FreeMem(v); end; end; inherited Clear; DoDataChanged; end; procedure TVarList.Delete(Index: Integer); var v: PVariant; i: integer; begin v:=inherited Get(Index); if v <> nil then begin for i:=0 to ColCnt + IntCols - 1 do VarClear(v[i]); FreeMem(v); end; inherited Delete(Index); DoDataChanged; end; function CompareVariants(const v1, v2: variant): integer; var v1e, v2e: boolean; begin v1e:=VarIsNull(v1) or VarIsEmpty(v1); v2e:=VarIsNull(v2) or VarIsEmpty(v2); if v1e and v2e then Result:=0 else if v1e and not v2e then Result:=-1 else if not v1e and v2e then Result:=1 else case VarType(v1) of varInteger,varsmallint,varshortint,varbyte,varword,varlongword,varint64,varqword: Result:=Int64(v1) - Int64(v2); varDouble,varSingle,varDate: Result:=Sign(double(v1) - double(v2)); else Result:=AnsiCompareText(v1, v2); end; end; var _SortColumn: integer; _SortDesc: boolean; _IntCols: integer; _List: TVarList; function CompareItems(Item1, Item2: Pointer): Integer; var v1, v2: PVariant; i: integer; begin if Item1 = Item2 then begin Result:=0; exit; end; v1:=Item1; v2:=Item2; if Assigned(_List.OnCompareVarRows) then Result:=_List.OnCompareVarRows(_List, v1, v2, _SortDesc) else Result:=0; if Result = 0 then begin Result:=CompareVariants(v1[_SortColumn], v2[_SortColumn]); i:=_IntCols; while (Result = 0) and (i < _List.ColCnt + _IntCols) do begin if i <> _SortColumn then Result:=CompareVariants(v1[i], v2[i]); Inc(i); end; if _SortDesc then Result:=-Result; end; end; procedure TVarList.Sort(ACol: integer; Descending: boolean); begin _SortColumn:=ACol + IntCols; _SortDesc:=Descending; _IntCols:=IntCols; _List:=Self; inherited Sort(@CompareItems); DoDataChanged; end; function TVarList.IndexOf(ACol: integer; const Value: variant): integer; var i: integer; begin for i:=0 to RowCnt - 1 do if CompareVariants(Items[ACol, i], Value) = 0 then begin Result:=i; exit; end; Result:=-1; end; function TVarList.SortedIndexOf(ACol: integer; const Value: variant): integer; begin Result:=-1; if not Find(ACol, Value, Result) then Result:=-1; end; function TVarList.Find(ACol: integer; const Value: variant; var Index: Integer): Boolean; var L, R, I: Integer; CompareRes: PtrInt; begin Result := false; L := 0; R := Count - 1; while (L<=R) do begin I := L + (R - L) div 2; CompareRes := CompareVariants(Value, Items[ACol, I]); if (CompareRes>0) then L := I+1 else begin R := I-1; if (CompareRes=0) then begin Result := true; L := I; // forces end of while loop end; end; end; Index := L; end; procedure TVarList.BeginUpdate; begin Inc(FUpdateLockCnt); end; procedure TVarList.EndUpdate; begin Dec(FUpdateLockCnt); if FUpdateLockCnt = 0 then DoDataChanged; end; procedure TVarList.InsertRow(ARow: integer); begin inherited Insert(ARow, nil); end; function TVarList.IsUpdating: boolean; begin Result:=FUpdateLockCnt > 0; end; function TVarList.GetRowItem(ARow: PVariant; ACol: integer): variant; begin CheckColIndex(ACol); Result:=ARow[ACol + IntCols]; end; end. TransGUI/download.lfm0000644000000000000000000000276012226736740013535 0ustar rootrootinherited DownloadForm: TDownloadForm Left = 390 Height = 106 Top = 278 Width = 404 HorzScrollBar.Page = 403 HorzScrollBar.Range = 392 VertScrollBar.Page = 105 VertScrollBar.Range = 95 BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Downloading' ClientHeight = 106 ClientWidth = 404 OnClose = FormClose OnCreate = FormCreate OnDestroy = FormDestroy OnResize = FormResize OnShow = FormShow Position = poMainFormCenter object txFileName: TLabel[0] Left = 12 Height = 14 Top = 8 Width = 54 Caption = 'txFileName' ParentColor = False end object txBytes: TLabel[1] Left = 12 Height = 14 Top = 28 Width = 38 Caption = 'txBytes' ParentColor = False end object txPercent: TLabel[2] Left = 344 Height = 14 Top = 28 Width = 48 Alignment = taRightJustify Anchors = [akTop, akRight] Caption = 'txPercent' ParentColor = False end object btCancel: TButton[3] Left = 160 Height = 23 Top = 72 Width = 75 AutoSize = True Cancel = True Caption = 'Cancel' Constraints.MinWidth = 75 Default = True OnClick = btCancelClick TabOrder = 0 end object pbDownload: TProgressBar[4] Left = 12 Height = 16 Top = 44 Width = 380 Anchors = [akTop, akLeft, akRight] Smooth = True Step = 1 TabOrder = 1 end object UpdateTimer: TTimer[5] Interval = 300 OnTimer = UpdateTimerTimer left = 36 top = 68 end end TransGUI/addtorrent.pas0000644000000000000000000006254112261763702014101 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit AddTorrent; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, Spin, VarGrid, Grids, ButtonPanel, ExtCtrls, BaseForm, varlist, fpjson; resourcestring SSize = 'Size'; SSelectDownloadFolder = 'Select a folder for download'; SInvalidName = 'Invalid name specified.'; type TFilesTree = class; { TAddTorrentForm } TAddTorrentForm = class(TBaseForm) btSelectAll: TButton; btSelectNone: TButton; btBrowse: TButton; Buttons: TButtonPanel; cbStartTorrent: TCheckBox; cbDestFolder: TComboBox; edSaveAs: TEdit; gbSaveAs: TGroupBox; gbContents: TGroupBox; edPeerLimit: TSpinEdit; DiskSpaceTimer: TTimer; txSaveAs: TLabel; txSize: TLabel; txDiskSpace: TLabel; txPeerLimit: TLabel; lvFiles: TVarGrid; txDestFolder: TLabel; procedure btBrowseClick(Sender: TObject); procedure btSelectAllClick(Sender: TObject); procedure btSelectNoneClick(Sender: TObject); procedure cbDestFolderChange(Sender: TObject); procedure DiskSpaceTimerTimer(Sender: TObject); procedure edSaveAsChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure OKButtonClick(Sender: TObject); private FDiskSpaceCaption: string; FTree: TFilesTree; procedure TreeStateChanged(Sender: TObject); procedure UpdateSize; public OrigCaption: string; property FilesTree: TFilesTree read FTree; end; TFolderInfo = record Size: double; DoneSize: double; Priority: integer; chk: TCheckBoxState; end; { TFilesTree } TFilesTree = class(TComponent) private FCheckboxes: boolean; FDownloadDir: string; FGrid: TVarGrid; FHasFolders: boolean; FIsPlain: boolean; FOnStateChange: TNotifyEvent; FFiles: TVarList; FTorrentId: integer; FLastFileCount: integer; FCommonPathLen: integer; FHasDone: boolean; FHasPriority: boolean; procedure CollapseFolder(ARow: integer); procedure DoCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); procedure DoCheckBoxClick(Sender: TVarGrid; ACol, ARow, ADataCol: integer); procedure DoDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean); procedure DoQuickSearch(Sender: TVarGrid; var SearchText: string; var ARow: integer); procedure DoTreeButtonClick(Sender: TVarGrid; ACol, ARow, ADataCol: integer); procedure DoAfterSort(Sender: TObject); procedure ExpandFolder(ARow: integer); function GetChecked(ARow: integer): TCheckBoxState; function GetExpanded(ARow: integer): boolean; function GetLevel(ARow: integer): integer; procedure SetCheckboxes(const AValue: boolean); procedure IntSetChecked(ARow: integer; const AValue: TCheckBoxState); procedure SetChecked(ARow: integer; const AValue: TCheckBoxState); procedure SetExpanded(ARow: integer; const AValue: boolean); procedure SetIsPlain(const AValue: boolean); procedure TreeChanged; procedure DoOnStateChange; function DoCompareVarRows(Sender: TVarList; Row1, Row2: PVariant; DescendingSort: boolean): integer; procedure SetRowOption(ARow, AOption: integer; DoSet: boolean); public constructor Create(AGrid: TVarGrid); reintroduce; destructor Destroy; override; function IsFolder(ARow: integer): boolean; procedure CollapseAll; procedure FillTree(ATorrentId: integer; files, priorities, wanted: TJSONArray); procedure SetStateAll(AState: TCheckBoxState); procedure EnsureRowVisible(ARow: integer); function GetFullPath(ARow: integer; AbsolutePath: boolean = True): string; function UpdateSummary: TFolderInfo; procedure Clear; property Grid: TVarGrid read FGrid; property HasFolders: boolean read FHasFolders; property Checkboxes: boolean read FCheckboxes write SetCheckboxes; property IsPlain: boolean read FIsPlain write SetIsPlain; property DownloadDir: string read FDownloadDir write FDownloadDir; property Expanded[ARow: integer]: boolean read GetExpanded write SetExpanded; property Checked[ARow: integer]: TCheckBoxState read GetChecked write SetChecked; property RowLevel[ARow: integer]: integer read GetLevel; property OnStateChange: TNotifyEvent read FOnStateChange write FOnStateChange; end; const // Files list columns idxFileName = 0; idxFileSize = 1; idxFileDone = 2; idxFileProgress = 3; idxFilePriority = 4; idxFileId = -1; idxFileFullPath = -2; idxFileLevel = -3; idxFileIndex = -4; FilesExtraColumns = 4; implementation uses lclintf, lcltype, main, variants, Utils, rpc, lclproc; const roChecked = $030000; roCollapsed = $040000; roHidden = $080000; roTag = $100000; roCheckedShift = 16; TR_PRI_MIXED = -1001; // psedudo priority { TFilesTree } constructor TFilesTree.Create(AGrid: TVarGrid); begin inherited Create(AGrid); FGrid:=AGrid; FFiles:=FGrid.Items; FGrid.OnCheckBoxClick:=@DoCheckBoxClick; FGrid.OnTreeButtonClick:=@DoTreeButtonClick; FGrid.OnCellAttributes:=@DoCellAttributes; FGrid.OnAfterSort:=@DoAfterSort; FGrid.OnQuickSearch:=@DoQuickSearch; FGrid.OnDrawCell:=@DoDrawCell; end; destructor TFilesTree.Destroy; begin inherited Destroy; end; function TFilesTree.IsFolder(ARow: integer): boolean; begin Result:=VarIsEmpty(FGrid.Items[idxFileId, ARow]); end; procedure TFilesTree.CollapseAll; var i: integer; begin FGrid.BeginUpdate; try for i:=0 to FGrid.Items.Count - 1 do begin if IsFolder(i) then SetRowOption(i, roCollapsed, True); if integer(FGrid.Items[idxFileLevel, i]) > 0 then begin FGrid.RowVisible[i]:=False; FGrid.RowSelected[i]:=False; SetRowOption(i, roHidden, True); end; end; TreeChanged; finally FGrid.EndUpdate; end; end; procedure TFilesTree.FillTree(ATorrentId: integer; files, priorities, wanted: TJSONArray); procedure _AddFolders(list: TVarList; const path: string; var idx: integer; cnt, level: integer); var s, ss: string; j: integer; p: PChar; begin while idx < cnt do begin s:=ExtractFilePath(UTF8Encode(widestring(list[idxFileFullPath, idx]))); if s = '' then begin Inc(idx); continue; end; if (path <> '') and (Pos(path, s) <> 1) then break; if s = path then begin list[idxFileLevel, idx]:=level; Inc(idx); end else begin ss:=Copy(s, Length(path) + 1, MaxInt); p:=PChar(ss); while (p^ <> #0) and not (p^ in ['/','\']) do Inc(p); if p^ <> #0 then begin SetLength(ss, p - PChar(ss) + 1); j:=list.Count; list[idxFileLevel, j]:=level; list[idxFileFullPath, j]:=UTF8Decode(path + ss); _AddFolders(list, path + ss, idx, cnt, level + 1); ss:=ExcludeTrailingPathDelimiter(ss); list[idxFileName, j]:=UTF8Decode(ExtractFileName(ss)); end; end; end; end; var i, row: integer; FullRefresh: boolean; f: TJSONObject; s, ss, path: string; ff: double; begin if files = nil then begin FGrid.Items.Clear; exit; end; FHasDone:=FGrid.Columns.Count > idxFileDone; FHasPriority:=FHasDone and (priorities <> nil) and (wanted <> nil); FullRefresh:=(FTorrentId <> ATorrentId) or (FLastFileCount <> files.Count); FLastFileCount:=files.Count; FTorrentId:=ATorrentId; FIsPlain:=FGrid.SortColumn <> idxFileName; FFiles.BeginUpdate; try FFiles.OnCompareVarRows:=nil; if FullRefresh then FFiles.Clear else begin for i:=0 to FFiles.Count - 1 do SetRowOption(i, roTag, False); FFiles.Sort(idxFileId); end; // Detecting top level folder to be removed FCommonPathLen:=0; path:=''; if files.Count > 0 then begin s:=UTF8Encode(files.Objects[0].Strings['name']); FCommonPathLen:=Pos(RemotePathDelimiter, s); if FCommonPathLen > 0 then path:=Copy(s, 1, FCommonPathLen); end; FHasFolders:=False; for i:=0 to files.Count - 1 do begin f:=files.Objects[i]; if FullRefresh then begin row:=i; FFiles[idxFileLevel, row]:=0; end else if not FFiles.Find(idxFileId, i, row) then begin FFiles.InsertRow(row); FFiles[idxFileLevel, row]:=0; end; SetRowOption(row, roTag, True); FFiles[idxFileId, row]:=i; s:=UTF8Encode(f.Strings['name']); FFiles[idxFileFullPath, row]:=UTF8Decode(ExtractFilePath(s)); if FCommonPathLen > 0 then s:=Copy(s, FCommonPathLen + 1, MaxInt); ss:=ExtractFileName(s); if ss <> s then FHasFolders:=True; FFiles[idxFileName, row]:=UTF8Decode(ss); ff:=f.Floats['length']; FFiles[idxFileSize, row]:=ff; if FHasDone then begin FFiles[idxFileDone, row]:=f.Floats['bytesCompleted']; if ff = 0 then ff:=100.0 else ff:=double(FFiles[idxFileDone, row])*100.0/ff; FFiles[idxFileProgress, row]:=Int(ff*10.0)/10.0; if FHasPriority then begin if wanted.Integers[i] = 0 then begin FFiles[idxFilePriority, row]:=TR_PRI_SKIP; IntSetChecked(row, cbUnchecked); end else begin FFiles[idxFilePriority, row]:=priorities.Integers[i]; IntSetChecked(row, cbChecked); end; end; end; end; if not FullRefresh then begin i:=0; while i < FFiles.Count do if not IsFolder(i) and not LongBool(FFiles.RowOptions[i] and roTag) then FFiles.Delete(i) else Inc(i); end; if HasFolders and FullRefresh then begin FFiles.Sort(idxFileFullPath); i:=0; _AddFolders(FFiles, path, i, FFiles.Count, 0); end; FFiles.OnCompareVarRows:=@DoCompareVarRows; FGrid.Sort; if FullRefresh and (FFiles.Count > 0) then begin FGrid.Row:=0; if HasFolders then begin i:=FFiles.RowCnt + FGrid.FixedRows; if FGrid.RowCount <> i then FGrid.RowCount:=i; CollapseAll; end else TreeChanged; end else TreeChanged; if not IsPlain then UpdateSummary; finally FFiles.EndUpdate; end; end; procedure TFilesTree.SetStateAll(AState: TCheckBoxState); var i: integer; begin FFiles.BeginUpdate; try for i:=0 to FFiles.Count - 1 do IntSetChecked(i, AState); finally FFiles.EndUpdate; end; DoOnStateChange; end; procedure TFilesTree.EnsureRowVisible(ARow: integer); var i, level: integer; begin if not FGrid.RowVisible[ARow] then begin FGrid.BeginUpdate; try level:=FFiles[idxFileLevel, ARow] - 1; for i:=ARow downto 0 do begin if IsFolder(i) and (FFiles[idxFileLevel, i] = level) then begin ExpandFolder(i); if level = 0 then break; Dec(level); end; end; finally FGrid.EndUpdate; end; end; FGrid.EnsureRowVisible(ARow); end; function TFilesTree.GetFullPath(ARow: integer; AbsolutePath: boolean): string; begin if AbsolutePath then begin Result:=FDownloadDir; if Copy(Result, Length(Result), 1) <> RemotePathDelimiter then Result:=Result + RemotePathDelimiter; end else Result:=''; Result:=Result + UTF8Encode(widestring(FFiles[idxFileFullPath, ARow])); if IsFolder(ARow) then Result:=Copy(Result, 1, Length(Result) - 1) else Result:=Result + UTF8Encode(widestring(FFiles[idxFileName, ARow])); end; function TFilesTree.UpdateSummary: TFolderInfo; function _UpdateSummary(var idx: integer; cnt, level: integer): TFolderInfo; var i, j: integer; IsFirst: boolean; begin FillChar(Result, SizeOf(Result), 0); IsFirst:=True; while idx < cnt do begin if FFiles[idxFileLevel, idx] <> level then break; i:=idx; Inc(idx); if IsFolder(i) then begin with _UpdateSummary(idx, cnt, level + 1) do begin FFiles[idxFileSize, i]:=Size; if FHasDone then begin FFiles[idxFileDone, i]:=DoneSize; if Size = 0 then DoneSize:=100.0 else DoneSize:=DoneSize*100.0/Size; FFiles[idxFileProgress, i]:=Int(DoneSize*10.0)/10.0; end; if FHasPriority then begin FFiles[idxFilePriority, i]:=Priority; IntSetChecked(i, chk); end; end; end; with Result do begin Size:=Size + FFiles[idxFileSize, i]; if FHasDone then DoneSize:=DoneSize + FFiles[idxFileDone, i]; if FHasPriority then begin j:=FFiles[idxFilePriority, i]; if IsFirst then begin IsFirst:=False; Priority:=j; chk:=Checked[i]; end else begin if Priority <> j then Priority:=TR_PRI_MIXED; if chk <> Checked[i] then chk:=cbGrayed; end; end; end; end; end; var i: integer; begin FFiles.BeginUpdate; try i:=0; Result:=_UpdateSummary(i, FFiles.Count, 0); finally FFiles.EndUpdate; end; end; procedure TFilesTree.Clear; begin FLastFileCount:=0; FTorrentId:=0; FFiles.Clear; end; procedure TFilesTree.DoCheckBoxClick(Sender: TVarGrid; ACol, ARow, ADataCol: integer); begin if Checked[ARow] = cbChecked then Checked[ARow]:=cbUnchecked else Checked[ARow]:=cbChecked; end; procedure TFilesTree.DoTreeButtonClick(Sender: TVarGrid; ACol, ARow, ADataCol: integer); begin Expanded[ARow]:=not Expanded[ARow]; end; procedure TFilesTree.DoAfterSort(Sender: TObject); var p: boolean; begin p:=FGrid.SortColumn <> idxFileName; if p <> IsPlain then IsPlain:=p else TreeChanged; end; procedure TFilesTree.CollapseFolder(ARow: integer); var i, lev: integer; begin AppBusy; FGrid.BeginUpdate; try lev:=FGrid.Items[idxFileLevel, ARow]; SetRowOption(ARow, roCollapsed, True); for i:=ARow + 1 to FGrid.Items.Count - 1 do if integer(FGrid.Items[idxFileLevel, i]) > lev then begin FGrid.RowVisible[i]:=False; FGrid.RowSelected[i]:=False; SetRowOption(i, roHidden, True); end else break; TreeChanged; finally FGrid.EndUpdate; end; AppNormal; end; procedure TFilesTree.ExpandFolder(ARow: integer); var i, j, lev: integer; begin AppBusy; FGrid.BeginUpdate; try lev:=FGrid.Items[idxFileLevel, ARow] + 1; SetRowOption(ARow, roCollapsed, False); for i:=ARow + 1 to FGrid.Items.Count - 1 do begin j:=integer(FGrid.Items[idxFileLevel, i]); if j = lev then begin FGrid.RowVisible[i]:=True; SetRowOption(i, roHidden, False); if IsFolder(i) and Expanded[i] then ExpandFolder(i); end else if j <= lev then break; end; TreeChanged; finally FGrid.EndUpdate; end; AppNormal; end; function TFilesTree.GetChecked(ARow: integer): TCheckBoxState; begin Result:=TCheckBoxState((FFiles.RowOptions[ARow] and roChecked) shr roCheckedShift); end; function TFilesTree.GetExpanded(ARow: integer): boolean; begin Result:=not LongBool(FFiles.RowOptions[ARow] and roCollapsed); end; function TFilesTree.GetLevel(ARow: integer): integer; begin Result:=FFiles[idxFileLevel, ARow]; end; procedure TFilesTree.SetCheckboxes(const AValue: boolean); begin if FCheckboxes = AValue then exit; FCheckboxes:=AValue; end; procedure TFilesTree.IntSetChecked(ARow: integer; const AValue: TCheckBoxState); begin FFiles.RowOptions[ARow]:=(FFiles.RowOptions[ARow] and not roChecked) or (integer(AValue) shl roCheckedShift); end; procedure TFilesTree.SetChecked(ARow: integer; const AValue: TCheckBoxState); var i, lev: integer; st: TCheckBoxState; begin st:=AValue; if st = cbGrayed then st:=cbUnchecked; if Checked[ARow] = st then exit; IntSetChecked(ARow, st); FGrid.InvalidateRow(ARow + FGrid.FixedRows); if not IsPlain then begin lev:=integer(FFiles[idxFileLevel, ARow]); if IsFolder(ARow) then begin FFiles.BeginUpdate; for i:=ARow + 1 to FFiles.Count - 1 do if integer(FFiles[idxFileLevel, i]) <= lev then break else IntSetChecked(i, st); FFiles.EndUpdate; end; if lev > 0 then begin i:=ARow + 1; while (i < FFiles.Count) and (integer(FFiles[idxFileLevel, i]) >= lev) do Inc(i); for i:=i - 1 downto 0 do begin if IsFolder(i) and (integer(FFiles[idxFileLevel, i]) < lev) then begin IntSetChecked(i, st); FGrid.InvalidateRow(i + FGrid.FixedRows); Dec(lev); if lev = 0 then break; end else if Checked[i] <> st then st:=cbGrayed; end; end; end; DoOnStateChange; end; procedure TFilesTree.SetExpanded(ARow: integer; const AValue: boolean); begin if GetExpanded(ARow) <> AValue then if AValue then ExpandFolder(ARow) else CollapseFolder(ARow); end; procedure TFilesTree.SetIsPlain(const AValue: boolean); begin if FIsPlain = AValue then exit; FIsPlain:=AValue; FFiles.BeginUpdate; try TreeChanged; if not FIsPlain then UpdateSummary; finally FFiles.EndUpdate; end; if FFiles.Count > 0 then FGrid.Row:=0; end; procedure TFilesTree.TreeChanged; var i, j: integer; f: boolean; begin FGrid.Items.BeginUpdate; try FGrid.RowCount:=FFiles.RowCnt + FGrid.FixedRows; j:=0; for i:=0 to FGrid.Items.Count - 1 do begin if IsPlain then f:=not IsFolder(i) else f:=not LongBool(FFiles.RowOptions[i] and roHidden); FGrid.RowVisible[i]:=f; if f then begin FGrid.Items[idxFileIndex, i]:=j; Inc(j); end; end; finally FGrid.Items.EndUpdate; end; end; procedure TFilesTree.DoOnStateChange; begin if Assigned(FOnStateChange) then FOnStateChange(Self); end; function TFilesTree.DoCompareVarRows(Sender: TVarList; Row1, Row2: PVariant; DescendingSort: boolean): integer; begin if FGrid.SortColumn <> idxFileName then begin Result:=(integer(VarIsEmpty(Sender.GetRowItem(Row1, idxFileId))) and 1) - (integer(VarIsEmpty(Sender.GetRowItem(Row2, idxFileId))) and 1); exit; end; Result:=CompareVariants(Sender.GetRowItem(Row1, idxFileFullPath), Sender.GetRowItem(Row2, idxFileFullPath)); if Result <> 0 then exit; Result:=(integer(VarIsEmpty(Sender.GetRowItem(Row2, idxFileId))) and 1) - (integer(VarIsEmpty(Sender.GetRowItem(Row1, idxFileId))) and 1); if Result <> 0 then exit; Result:=CompareVariants(Sender.GetRowItem(Row1, idxFileName), Sender.GetRowItem(Row2, idxFileName)); if DescendingSort then Result:=-Result; end; procedure TFilesTree.SetRowOption(ARow, AOption: integer; DoSet: boolean); var i: integer; begin i:=FFiles.RowOptions[ARow]; if DoSet then FFiles.RowOptions[ARow]:=i or AOption else FFiles.RowOptions[ARow]:=i and not AOption; end; procedure TFilesTree.DoCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); var i: integer; begin if ARow < 0 then exit; with CellAttribs do begin if not (gdSelected in AState) and (integer(Sender.Items[idxFileIndex, ARow]) and 1 = 1) then Sender.Canvas.Brush.Color:=FAlterColor; if Text = '' then exit; case ADataCol of 0: begin // Text:=UTF8Encode(Sender.Items[idxFileFullPath, ARow]) + ' (' + Text + ')'; if Checkboxes then begin Options:=[coDrawCheckBox]; State:=Checked[ARow]; end; if IsPlain then begin Text:=Copy(UTF8Encode(widestring(Sender.Items[idxFileFullPath, ARow])), FCommonPathLen + 1, MaxInt) + Text; end else begin Indent:=integer(Sender.Items[idxFileLevel, ARow])*16; if IsFolder(ARow) then begin Include(Options, coDrawTreeButton); Expanded:=Self.Expanded[ARow]; ImageIndex:=22; end else if HasFolders then Inc(Indent, Sender.RowHeights[ARow + Sender.FixedRows]); end; end; idxFileSize, idxFileDone: Text:=GetHumanSize(double(Sender.Items[ADataCol, ARow])); idxFileProgress: Text:=Format('%.1f%%', [double(Sender.Items[ADataCol, ARow])]); idxFilePriority: begin i:=Sender.Items[idxFilePriority, ARow]; if i = TR_PRI_MIXED then Text:='' else Text:=PriorityToStr(i, ImageIndex); end; end; end; end; procedure TFilesTree.DoDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean); begin if ARow < 0 then exit; if ADataCol = idxFileProgress then begin ADefaultDrawing:=False; DrawProgressCell(Sender, ACol, ARow, ADataCol, AState, R); end; end; procedure TFilesTree.DoQuickSearch(Sender: TVarGrid; var SearchText: string; var ARow: integer); var i: integer; s: string; v: variant; begin s:=UTF8UpperCase(SearchText); for i:=ARow to Sender.Items.Count - 1 do begin v:=Sender.Items[idxFileName, i]; if VarIsEmpty(v) or VarIsNull(v) or (IsPlain and IsFolder(i)) then continue; if Pos(s, Trim(UTF8UpperCase(UTF8Encode(widestring(v))))) > 0 then begin ARow:=i; EnsureRowVisible(ARow); break; end; end; end; { TAddTorrentForm } procedure TAddTorrentForm.FormShow(Sender: TObject); begin AppBusy; lvFiles.BeginUpdate; try btSelectAllClick(nil); { lvFiles.Sort; if lvFiles.Items.Count > 0 then lvFiles.Row:=0; } // FTree.CollapseAll; finally lvFiles.EndUpdate; end; DiskSpaceTimerTimer(nil); AppNormal; end; procedure TAddTorrentForm.OKButtonClick(Sender: TObject); begin if edSaveAs.Enabled then begin edSaveAs.Text:=Trim(edSaveAs.Text); if edSaveAs.Text = '' then begin edSaveAs.SetFocus; MessageDlg(SInvalidName, mtError, [mbOK], 0); exit; end; end; ModalResult:=mrOK; end; procedure TAddTorrentForm.UpdateSize; var i: integer; d, sz, tsz: double; s: string; begin sz:=0; tsz:=0; for i:=0 to lvFiles.Items.Count - 1 do if not FTree.IsFolder(i) then begin d:=double(lvFiles.Items[idxFileSize, i]); tsz:=tsz + d; if FTree.Checked[i] = cbChecked then sz:=sz + d; end; s:=GetHumanSize(sz); if s <> GetHumanSize(tsz) then s:=s + ' / ' + GetHumanSize(tsz); txSize.Caption:=Format('%s: %s', [SSize, s]); end; procedure TAddTorrentForm.btSelectAllClick(Sender: TObject); begin FTree.SetStateAll(cbChecked); end; procedure TAddTorrentForm.btBrowseClick(Sender: TObject); var s: string; begin s:=MainForm.SelectRemoteFolder(cbDestFolder.Text, SSelectDownloadFolder); if s <> '' then cbDestFolder.Text:=s; end; procedure TAddTorrentForm.btSelectNoneClick(Sender: TObject); begin FTree.SetStateAll(cbUnchecked); end; procedure TAddTorrentForm.cbDestFolderChange(Sender: TObject); begin DiskSpaceTimer.Enabled:=True; end; procedure TAddTorrentForm.DiskSpaceTimerTimer(Sender: TObject); var f: double; req, args: TJSONObject; begin DiskSpaceTimer.Enabled:=False; if RpcObj.RPCVersion < 15 then exit; AppBusy; f:=-1; try req:=TJSONObject.Create; args:=TJSONObject.Create; try req.Add('method', 'free-space'); args.Add('path', UTF8Decode(cbDestFolder.Text)); req.Add('arguments', args); args:=RpcObj.SendRequest(req); if args <> nil then f:=args.Floats['size-bytes']; RpcObj.Status:=''; finally args.Free; req.Free; end; except f:=-1; end; txDiskSpace.Caption:=FDiskSpaceCaption + ' ' + GetHumanSize(f); AppNormal; end; procedure TAddTorrentForm.edSaveAsChange(Sender: TObject); begin Caption:=OrigCaption + ' - ' + edSaveAs.Text; end; procedure TAddTorrentForm.TreeStateChanged(Sender: TObject); begin UpdateSize; end; procedure TAddTorrentForm.FormCreate(Sender: TObject); begin OrigCaption:=Caption; FDiskSpaceCaption:=txDiskSpace.Caption; lvFiles.Items.ExtraColumns:=FilesExtraColumns; FTree:=TFilesTree.Create(lvFiles); FTree.Checkboxes:=True; FTree.OnStateChange:=@TreeStateChanged; Buttons.OKButton.ModalResult:=mrNone; {$ifdef windows} gbSaveAs.Caption:=''; {$endif windows} {$ifdef darwin} Buttons.BorderSpacing.Right:=Buttons.BorderSpacing.Right + ScaleInt(12); {$endif darwin} end; initialization {$I addtorrent.lrs} end. TransGUI/lineinfo2.pp0000644000000000000000000002004011737136325013442 0ustar rootroot{ This file is part of the Free Pascal run time library. Copyright (c) 2000 by Peter Vreman Stabs Line Info Retriever See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} { This unit should not be compiled in objfpc mode, since this would make it dependent on objpas unit. } unit lineinfo2; interface {$S-} {$Q-} function GetLineInfo(addr:ptruint;var func,source:string;var line:longint) : boolean; implementation uses exeinfo,strings; const N_Function = $24; N_TextLine = $44; N_DataLine = $46; N_BssLine = $48; N_SourceFile = $64; N_IncludeFile = $84; maxstabs = 40; { size of the stabs buffer } var { GDB after 4.18 uses offset to function begin in text section but OS/2 version still uses 4.16 PM } StabsFunctionRelative: boolean; type pstab=^tstab; tstab=packed record strpos : longint; ntype : byte; nother : byte; ndesc : word; nvalue : dword; end; { We use static variable so almost no stack is required, and is thus more safe when an error has occured in the program } var e : TExeFile; staberr : boolean = false; stabcnt, { amount of stabs } stablen, stabofs, { absolute stab section offset in executable } stabstrlen, stabstrofs : longint; { absolute stabstr section offset in executable } dirlength : longint; { length of the dirctory part of the source file } stabs : array[0..maxstabs-1] of tstab; { buffer } funcstab, { stab with current function info } linestab, { stab with current line info } dirstab, { stab with current directory info } filestab : tstab; { stab with current file info } filename, dbgfn : string; function OpenStabs(addr : pointer) : boolean; var baseaddr : pointer; begin OpenStabs:=false; if staberr then exit; GetModuleByAddr(addr,baseaddr,filename); {$ifdef DEBUG_LINEINFO} writeln(stderr,filename,' Baseaddr: ',hexstr(ptruint(baseaddr),sizeof(baseaddr)*2)); {$endif DEBUG_LINEINFO} if not OpenExeFile(e,filename) then exit; if ReadDebugLink(e,dbgfn) then begin CloseExeFile(e); if not OpenExeFile(e,dbgfn) then exit; end; e.processaddress:=ptruint(baseaddr)-e.processaddress; StabsFunctionRelative := E.FunctionRelative; if FindExeSection(e,'.stab',stabofs,stablen) and FindExeSection(e,'.stabstr',stabstrofs,stabstrlen) then begin stabcnt:=stablen div sizeof(tstab); OpenStabs:=true; end else begin CloseExeFile(e); // staberr:=true; exit; end; end; procedure CloseStabs; begin CloseExeFile(e); end; function GetLineInfo(addr:ptruint;var func,source:string;var line:longint) : boolean; var res, stabsleft, stabscnt,i : longint; found : boolean; lastfunc : tstab; begin GetLineInfo:=false; {$ifdef DEBUG_LINEINFO} writeln(stderr,'GetLineInfo called'); {$endif DEBUG_LINEINFO} fillchar(func,high(func)+1,0); fillchar(source,high(source)+1,0); line:=0; if staberr then exit; if not e.isopen then begin if not OpenStabs(pointer(addr)) then exit; end; { correct the value to the correct address in the file } { processaddress is set in OpenStabs } addr := dword(addr - e.processaddress); {$ifdef DEBUG_LINEINFO} writeln(stderr,'Addr: ',hexstr(addr,sizeof(addr)*2)); {$endif DEBUG_LINEINFO} fillchar(funcstab,sizeof(tstab),0); fillchar(filestab,sizeof(tstab),0); fillchar(dirstab,sizeof(tstab),0); fillchar(linestab,sizeof(tstab),0); fillchar(lastfunc,sizeof(tstab),0); found:=false; seek(e.f,stabofs); stabsleft:=stabcnt; repeat if stabsleft>maxstabs then stabscnt:=maxstabs else stabscnt:=stabsleft; blockread(e.f,stabs,stabscnt*sizeof(tstab),res); stabscnt:=res div sizeof(tstab); for i:=0 to stabscnt-1 do begin case stabs[i].ntype of N_BssLine, N_DataLine, N_TextLine : begin if (stabs[i].ntype=N_TextLine) and StabsFunctionRelative then inc(stabs[i].nvalue,lastfunc.nvalue); if (stabs[i].nvalue<=addr) and (stabs[i].nvalue>linestab.nvalue) then begin { if it's equal we can stop and take the last info } if stabs[i].nvalue=addr then found:=true else linestab:=stabs[i]; end; end; N_Function : begin lastfunc:=stabs[i]; if (stabs[i].nvalue<=addr) and (stabs[i].nvalue>funcstab.nvalue) then begin funcstab:=stabs[i]; fillchar(linestab,sizeof(tstab),0); end; end; N_SourceFile, N_IncludeFile : begin if (stabs[i].nvalue<=addr) and (stabs[i].nvalue>=filestab.nvalue) then begin { if same value and type then the first one contained the directory PM } if (stabs[i].nvalue=filestab.nvalue) and (stabs[i].ntype=filestab.ntype) then dirstab:=filestab else fillchar(dirstab,sizeof(tstab),0); filestab:=stabs[i]; fillchar(linestab,sizeof(tstab),0); { if new file then func is not valid anymore PM } if stabs[i].ntype=N_SourceFile then begin fillchar(funcstab,sizeof(tstab),0); fillchar(lastfunc,sizeof(tstab),0); end; end; end; end; end; dec(stabsleft,stabscnt); until found or (stabsleft=0); { get the line,source,function info } line:=linestab.ndesc; if dirstab.ntype<>0 then begin seek(e.f,stabstrofs+dirstab.strpos); blockread(e.f,source[1],high(source)-1,res); dirlength:=strlen(@source[1]); source[0]:=chr(dirlength); end else dirlength:=0; if filestab.ntype<>0 then begin seek(e.f,stabstrofs+filestab.strpos); blockread(e.f,source[dirlength+1],high(source)-(dirlength+1),res); source[0]:=chr(strlen(@source[1])); end; if funcstab.ntype<>0 then begin seek(e.f,stabstrofs+funcstab.strpos); blockread(e.f,func[1],high(func)-1,res); func[0]:=chr(strlen(@func[1])); i:=pos(':',func); if i>0 then Delete(func,i,255); end; if e.isopen then CloseStabs; GetLineInfo:=true; end; function StabBackTraceStr(addr:Pointer):shortstring; var func, source : string; hs : string[32]; line : longint; Store : TBackTraceStrFunc; Success : boolean; begin {$ifdef DEBUG_LINEINFO} writeln(stderr,'StabBackTraceStr called'); {$endif DEBUG_LINEINFO} { reset to prevent infinite recursion if problems inside the code PM } Success:=false; Store:=BackTraceStrFunc; BackTraceStrFunc:=@SysBackTraceStr; Success:=GetLineInfo(ptruint(addr),func,source,line); { create string } {$ifdef netware} { we need addr relative to code start on netware } dec(addr,ptruint(system.NWGetCodeStart)); StabBackTraceStr:=' CodeStart + $'+HexStr(ptruint(addr),sizeof(ptruint)*2); {$else} StabBackTraceStr:=' $'+HexStr(ptruint(addr),sizeof(ptruint)*2); {$endif} if func<>'' then StabBackTraceStr:=StabBackTraceStr+' '+func; if source<>'' then begin if func<>'' then StabBackTraceStr:=StabBackTraceStr+', '; if line<>0 then begin str(line,hs); StabBackTraceStr:=StabBackTraceStr+' line '+hs; end; StabBackTraceStr:=StabBackTraceStr+' of '+source; end; if Success then BackTraceStrFunc:=Store; end; initialization // BackTraceStrFunc:=@StabBackTraceStr; finalization if e.isopen then CloseStabs; end. TransGUI/daemonoptions.lfm0000644000000000000000000003327612256577645014625 0ustar rootrootinherited DaemonOptionsForm: TDaemonOptionsForm Left = 354 Height = 345 Top = 216 Width = 617 HorzScrollBar.Page = 409 VertScrollBar.Page = 302 VertScrollBar.Range = 169 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Transmission options' ClientHeight = 345 ClientWidth = 617 Constraints.MinHeight = 340 Constraints.MinWidth = 400 OnCreate = FormCreate Position = poMainFormCenter object Page: TPageControl[0] Left = 8 Height = 295 Top = 8 Width = 601 ActivePage = tabNetwork Align = alClient BorderSpacing.Around = 8 TabIndex = 1 TabOrder = 0 object tabDownload: TTabSheet Caption = 'Download' ClientHeight = 269 ClientWidth = 593 object txDownloadDir: TLabel Left = 8 Height = 14 Top = 8 Width = 196 Caption = 'Default download folder on remote host:' ParentColor = False end object txCacheSize: TLabel Left = 8 Height = 14 Top = 191 Width = 76 Caption = 'Disk cache size:' ParentColor = False end object txMB: TLabel Left = 507 Height = 14 Top = 191 Width = 15 Anchors = [akTop, akRight] Caption = 'MB' ParentColor = False end object txMinutes: TLabel Left = 507 Height = 14 Top = 164 Width = 38 Anchors = [akTop, akRight] Caption = 'minutes' ParentColor = False end object edDownloadDir: TEdit Left = 8 Height = 21 Top = 28 Width = 577 Anchors = [akTop, akLeft, akRight] TabOrder = 0 end object cbIncompleteDir: TCheckBox Left = 8 Height = 17 Top = 80 Width = 159 Caption = 'Directory for incomplete files:' OnClick = cbIncompleteDirClick TabOrder = 2 end object edIncompleteDir: TEdit Left = 8 Height = 21 Top = 103 Width = 577 Anchors = [akTop, akLeft, akRight] TabOrder = 3 end object cbPartExt: TCheckBox Left = 8 Height = 17 Top = 56 Width = 203 Caption = 'Add .part extension to incomplete files' TabOrder = 1 end object cbSeedRatio: TCheckBox Left = 8 Height = 17 Top = 134 Width = 71 Caption = 'Seed ratio:' OnClick = cbSeedRatioClick TabOrder = 4 end object edSeedRatio: TFloatSpinEdit Left = 431 Height = 21 Top = 134 Width = 66 Anchors = [akTop, akRight] Increment = 0.1 MaxValue = 9999 MinValue = 0 TabOrder = 5 Value = 0 end object edCacheSize: TSpinEdit Left = 431 Height = 21 Top = 188 Width = 66 Anchors = [akTop, akRight] MaxValue = 9999 TabOrder = 8 end object cbIdleSeedLimit: TCheckBox Left = 8 Height = 17 Top = 163 Width = 170 Caption = 'Stop seeding when inactive for:' OnClick = cbIdleSeedLimitClick TabOrder = 6 end object edIdleSeedLimit: TSpinEdit Left = 431 Height = 21 Top = 161 Width = 66 Anchors = [akTop, akRight] MaxValue = 999999 MinValue = 1 TabOrder = 7 Value = 1 end end object tabNetwork: TTabSheet Caption = 'Network (WAN)' ClientHeight = 269 ClientWidth = 593 object txPort: TLabel Left = 8 Height = 14 Top = 13 Width = 71 Caption = 'Incoming port:' ParentColor = False end object txEncryption: TLabel Left = 8 Height = 14 Top = 69 Width = 56 Caption = 'Encryption:' ParentColor = False end object txPeerLimit: TLabel Left = 8 Height = 14 Top = 99 Width = 80 Caption = 'Global peer limit:' ParentColor = False end object edPort: TSpinEdit Left = 192 Height = 21 Top = 10 Width = 90 MaxValue = 65535 MinValue = 1 TabOrder = 0 Value = 1 end object cbEncryption: TComboBox Left = 192 Height = 21 Top = 66 Width = 393 Anchors = [akTop, akLeft, akRight] ItemHeight = 13 Style = csDropDownList TabOrder = 4 end object cbPortForwarding: TCheckBox Left = 292 Height = 17 Top = 38 Width = 128 Caption = 'Enable port forwarding' TabOrder = 3 end object cbPEX: TCheckBox Left = 192 Height = 17 Top = 126 Width = 125 Caption = 'Enable Peer Exchange' TabOrder = 6 end object edMaxPeers: TSpinEdit Left = 192 Height = 21 Top = 96 Width = 66 MaxValue = 99999 MinValue = 1 TabOrder = 5 Value = 1 end object cbDHT: TCheckBox Left = 192 Height = 17 Top = 146 Width = 73 Caption = 'Enable DHT' TabOrder = 7 end object cbRandomPort: TCheckBox Left = 292 Height = 17 Top = 13 Width = 211 Caption = 'Pick random port on Transmission launch' OnClick = cbRandomPortClick TabOrder = 1 end object btTestPort: TButton Left = 192 Height = 23 Top = 35 Width = 90 Caption = 'Test port' OnClick = btTestPortClick TabOrder = 2 end object cbBlocklist: TCheckBox Left = 8 Height = 17 Top = 210 Width = 94 Caption = 'Enable blocklist:' OnClick = cbBlocklistClick TabOrder = 10 end object edBlocklistURL: TEdit Left = 8 Height = 21 Top = 233 Width = 577 Anchors = [akTop, akLeft, akRight] TabOrder = 11 end object cbLPD: TCheckBox Left = 192 Height = 17 Top = 166 Width = 152 Caption = 'Enable Local Peer Discovery' TabOrder = 8 end object cbUTP: TCheckBox Left = 192 Height = 17 Top = 186 Width = 71 Caption = 'Enable µTP' TabOrder = 9 end end object tabBandwidth: TTabSheet Caption = 'Bandwidth' ClientHeight = 269 ClientWidth = 593 object gbBandwidth: TGroupBox Left = 8 Height = 76 Top = 6 Width = 576 Anchors = [akTop, akLeft, akRight] Caption = 'Global bandwidth settings' ClientHeight = 58 ClientWidth = 572 TabOrder = 0 object txKbs1: TLabel Left = 536 Height = 14 Top = 5 Width = 22 Anchors = [akTop, akRight] Caption = 'KB/s' ParentColor = False end object txKbs2: TLabel Left = 536 Height = 14 Top = 32 Width = 22 Anchors = [akTop, akRight] Caption = 'KB/s' ParentColor = False end object cbMaxDown: TCheckBox Left = 8 Height = 17 Top = 4 Width = 147 Caption = 'Maximum download speed:' OnClick = cbMaxDownClick TabOrder = 0 end object edMaxDown: TSpinEdit Left = 455 Height = 21 Top = 2 Width = 66 Anchors = [akTop, akRight] Increment = 10 MaxValue = 999999 TabOrder = 1 end object cbMaxUp: TCheckBox Left = 8 Height = 17 Top = 30 Width = 133 Caption = 'Maximum upload speed:' OnClick = cbMaxUpClick TabOrder = 2 end object edMaxUp: TSpinEdit Left = 455 Height = 21 Top = 28 Width = 66 Anchors = [akTop, akRight] Increment = 10 MaxValue = 999999 TabOrder = 3 end end object gbAltSpeed: TGroupBox Left = 8 Height = 175 Top = 86 Width = 576 Anchors = [akTop, akLeft, akRight] Caption = 'Alternate bandwidth settings' ClientHeight = 157 ClientWidth = 572 TabOrder = 1 object txKbs3: TLabel Left = 536 Height = 14 Top = 5 Width = 22 Anchors = [akTop, akRight] Caption = 'KB/s' ParentColor = False end object txKbs4: TLabel Left = 536 Height = 14 Top = 32 Width = 22 Anchors = [akTop, akRight] Caption = 'KB/s' ParentColor = False end object txAltDown: TLabel Left = 8 Height = 14 Top = 5 Width = 130 Caption = 'Maximum download speed:' ParentColor = False end object txAltUp: TLabel Left = 8 Height = 14 Top = 31 Width = 116 Caption = 'Maximum upload speed:' ParentColor = False end object txFrom: TLabel Left = 26 Height = 14 Top = 103 Width = 29 Caption = 'From:' ParentColor = False end object txDays: TLabel Left = 26 Height = 14 Top = 132 Width = 29 Caption = 'Days:' ParentColor = False end object txTo: TLabel Left = 150 Height = 14 Top = 104 Width = 49 Alignment = taCenter AutoSize = False Caption = 'to:' ParentColor = False end object edAltDown: TSpinEdit Left = 455 Height = 21 Top = 2 Width = 66 Anchors = [akTop, akRight] Increment = 10 MaxValue = 999999 TabOrder = 0 end object edAltUp: TSpinEdit Left = 455 Height = 21 Top = 28 Width = 66 Anchors = [akTop, akRight] Increment = 10 MaxValue = 999999 TabOrder = 1 end object cbAltEnabled: TCheckBox Left = 8 Height = 17 Top = 54 Width = 177 Caption = 'Use alternate bandwidth settings' TabOrder = 2 end object cbAutoAlt: TCheckBox Left = 8 Height = 17 Top = 76 Width = 252 Caption = 'Apply alternate bandwidth settings automatically' OnClick = cbAutoAltClick TabOrder = 3 end object edAltTimeBegin: TMaskEdit Left = 82 Height = 21 Top = 100 Width = 64 CharCase = ecNormal MaxLength = 5 TabOrder = 4 EditMask = '!99:99;1; ' Text = ' : ' SpaceChar = ' ' end object edAltTimeEnd: TMaskEdit Left = 202 Height = 21 Top = 100 Width = 64 CharCase = ecNormal MaxLength = 5 TabOrder = 5 EditMask = '!99:99;1; ' Text = ' : ' SpaceChar = ' ' end end end object tabQueue: TTabSheet Caption = 'Queue' ClientHeight = 269 ClientWidth = 593 object cbDownQueue: TCheckBox Left = 8 Height = 17 Top = 12 Width = 123 Caption = 'Download queue size:' TabOrder = 0 end object edDownQueue: TSpinEdit Left = 431 Height = 21 Top = 10 Width = 66 Anchors = [akTop, akRight] MaxValue = 999999 MinValue = 1 TabOrder = 1 Value = 1 end object cbUpQueue: TCheckBox Left = 8 Height = 17 Top = 40 Width = 109 Caption = 'Upload queue size:' TabOrder = 2 end object edUpQueue: TSpinEdit Left = 431 Height = 21 Top = 38 Width = 66 Anchors = [akTop, akRight] MaxValue = 999999 MinValue = 1 TabOrder = 3 Value = 1 end object cbStalled: TCheckBox Left = 8 Height = 17 Hint = 'Torrents that are idle for N minuets aren''t counted toward the Download queue or Upload queue' Top = 68 Width = 251 Caption = 'Consider active torrents as stalled when idle for:' ParentShowHint = False ShowHint = True TabOrder = 4 end object edStalledTime: TSpinEdit Left = 431 Height = 21 Top = 66 Width = 66 Anchors = [akTop, akRight] MaxValue = 999999 MinValue = 1 TabOrder = 5 Value = 1 end object txMinutes1: TLabel Left = 507 Height = 14 Top = 69 Width = 38 Anchors = [akTop, akRight] Caption = 'minutes' ParentColor = False end end end object Buttons: TButtonPanel[1] Left = 8 Height = 26 Top = 311 Width = 601 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end end TransGUI/addlink.lfm0000644000000000000000000000265412256577645013350 0ustar rootrootinherited AddLinkForm: TAddLinkForm Left = 354 Height = 93 Top = 193 Width = 574 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Add torrent link' ClientHeight = 93 ClientWidth = 574 OnCreate = FormCreate Position = poMainFormCenter object Buttons: TButtonPanel[0] Left = 8 Height = 26 Top = 59 Width = 558 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end object Panel1: TPanel[1] Left = 8 Height = 43 Top = 8 Width = 558 Align = alClient BorderSpacing.Around = 8 BevelOuter = bvNone ClientHeight = 43 ClientWidth = 558 TabOrder = 0 object txLink: TLabel Left = 0 Height = 14 Top = 0 Width = 183 Caption = 'URL of a .torrent file or a magnet link:' ParentColor = False end object edLink: TEdit Left = 0 Height = 21 Top = 20 Width = 558 Anchors = [akTop, akLeft, akRight] TabOrder = 0 end end end TransGUI/urllistenerosx.pas0000644000000000000000000000230412027134022015007 0ustar rootrootunit URLListenerOSX; {$mode objfpc}{$H+} {$modeswitch objectivec2} interface uses Classes, SysUtils, CocoaAll, InternetConfig, AppleEvents; type THandlerProc = procedure(const url: string); { TAppURLHandler } TAppURLHandler = objcclass(NSObject) public procedure getUrlwithReplyEvent(event: NSAppleEventDescriptor; eventReply: NSAppleEventDescriptor); message 'getUrl:withReplyEvent:'; public callBack: THandlerProc; end; procedure RegisterURLHandler(HandlerProc: THandlerProc); var handler : TAppURLHandler; eventManager: NSAppleEventManager; implementation { TAppURLHandler } procedure TAppURLHandler.getUrlwithReplyEvent(event: NSAppleEventDescriptor; eventReply: NSAppleEventDescriptor); var url : NSString; begin url:=event.paramDescriptorForKeyword(keyDirectObject).stringValue; callBack(url.UTF8String); end; procedure RegisterURLHandler(HandlerProc: THandlerProc); begin handler:=TAppURLHandler.alloc.init; handler.callBack:=HandlerProc; eventManager:=NSAppleEventManager.sharedAppleEventManager; eventManager.setEventHandler_andSelector_forEventClass_andEventID(handler,ObjCSelector(handler.getUrlwithReplyEvent), kInternetEventClass,kAEGetURL); end; end. TransGUI/units/0000755000000000000000000000000012261774331012360 5ustar rootrootTransGUI/synapse/0000755000000000000000000000000012261774331012700 5ustar rootrootTransGUI/synapse/licence.txt0000644000000000000000000000415411366572451015053 0ustar rootrootCopyright (c)1999-2002, Lukas Gebauer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Lukas Gebauer nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. TransGUI/synapse/source/0000755000000000000000000000000012261774331014200 5ustar rootrootTransGUI/synapse/source/lib/0000755000000000000000000000000012261774331014746 5ustar rootrootTransGUI/synapse/source/lib/ssdotnet.pas0000644000000000000000000010427211366572451017330 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.000.002 | |==============================================================================| | Content: Socket Independent Platform Layer - .NET definition include | |==============================================================================| | Copyright (c)2004, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2004. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@exclude} {$IFDEF CIL} interface uses SyncObjs, SysUtils, Classes, System.Net, System.Net.Sockets; const DLLStackName = ''; WinsockLevel = $0202; function InitSocketInterface(stack: string): Boolean; function DestroySocketInterface: Boolean; type u_char = Char; u_short = Word; u_int = Integer; u_long = Longint; pu_long = ^u_long; pu_short = ^u_short; PSockAddr = IPEndPoint; DWORD = integer; ULong = cardinal; TMemory = Array of byte; TLinger = LingerOption; TSocket = socket; TAddrFamily = AddressFamily; const WSADESCRIPTION_LEN = 256; WSASYS_STATUS_LEN = 128; type PWSAData = ^TWSAData; TWSAData = packed record wVersion: Word; wHighVersion: Word; szDescription: array[0..WSADESCRIPTION_LEN] of Char; szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char; iMaxSockets: Word; iMaxUdpDg: Word; // lpVendorInfo: PChar; end; const MSG_NOSIGNAL = 0; INVALID_SOCKET = nil; AF_UNSPEC = AddressFamily.Unspecified; AF_INET = AddressFamily.InterNetwork; AF_INET6 = AddressFamily.InterNetworkV6; SOCKET_ERROR = integer(-1); FIONREAD = integer($4004667f); FIONBIO = integer($8004667e); FIOASYNC = integer($8004667d); SOMAXCONN = integer($7fffffff); IPPROTO_IP = ProtocolType.IP; IPPROTO_ICMP = ProtocolType.Icmp; IPPROTO_IGMP = ProtocolType.Igmp; IPPROTO_TCP = ProtocolType.Tcp; IPPROTO_UDP = ProtocolType.Udp; IPPROTO_RAW = ProtocolType.Raw; IPPROTO_IPV6 = ProtocolType.IPV6; // IPPROTO_ICMPV6 = ProtocolType.Icmp; //?? SOCK_STREAM = SocketType.Stream; SOCK_DGRAM = SocketType.Dgram; SOCK_RAW = SocketType.Raw; SOCK_RDM = SocketType.Rdm; SOCK_SEQPACKET = SocketType.Seqpacket; SOL_SOCKET = SocketOptionLevel.Socket; SOL_IP = SocketOptionLevel.Ip; IP_OPTIONS = SocketOptionName.IPOptions; IP_HDRINCL = SocketOptionName.HeaderIncluded; IP_TOS = SocketOptionName.TypeOfService; { set/get IP Type Of Service } IP_TTL = SocketOptionName.IpTimeToLive; { set/get IP Time To Live } IP_MULTICAST_IF = SocketOptionName.MulticastInterface; { set/get IP multicast interface } IP_MULTICAST_TTL = SocketOptionName.MulticastTimeToLive; { set/get IP multicast timetolive } IP_MULTICAST_LOOP = SocketOptionName.MulticastLoopback; { set/get IP multicast loopback } IP_ADD_MEMBERSHIP = SocketOptionName.AddMembership; { add an IP group membership } IP_DROP_MEMBERSHIP = SocketOptionName.DropMembership; { drop an IP group membership } IP_DONTFRAGMENT = SocketOptionName.DontFragment; { set/get IP Don't Fragment flag } IPV6_UNICAST_HOPS = 8; // TTL IPV6_MULTICAST_IF = 9; // set/get IP multicast i/f IPV6_MULTICAST_HOPS = 10; // set/get IP multicast ttl IPV6_MULTICAST_LOOP = 11; // set/get IP multicast loopback IPV6_JOIN_GROUP = 12; // add an IP group membership IPV6_LEAVE_GROUP = 13; // drop an IP group membership SO_DEBUG = SocketOptionName.Debug; { turn on debugging info recording } SO_ACCEPTCONN = SocketOptionName.AcceptConnection; { socket has had listen() } SO_REUSEADDR = SocketOptionName.ReuseAddress; { allow local address reuse } SO_KEEPALIVE = SocketOptionName.KeepAlive; { keep connections alive } SO_DONTROUTE = SocketOptionName.DontRoute; { just use interface addresses } SO_BROADCAST = SocketOptionName.Broadcast; { permit sending of broadcast msgs } SO_USELOOPBACK = SocketOptionName.UseLoopback; { bypass hardware when possible } SO_LINGER = SocketOptionName.Linger; { linger on close if data present } SO_OOBINLINE = SocketOptionName.OutOfBandInline; { leave received OOB data in line } SO_DONTLINGER = SocketOptionName.DontLinger; { Additional options. } SO_SNDBUF = SocketOptionName.SendBuffer; { send buffer size } SO_RCVBUF = SocketOptionName.ReceiveBuffer; { receive buffer size } SO_SNDLOWAT = SocketOptionName.SendLowWater; { send low-water mark } SO_RCVLOWAT = SocketOptionName.ReceiveLowWater; { receive low-water mark } SO_SNDTIMEO = SocketOptionName.SendTimeout; { send timeout } SO_RCVTIMEO = SocketOptionName.ReceiveTimeout; { receive timeout } SO_ERROR = SocketOptionName.Error; { get error status and clear } SO_TYPE = SocketOptionName.Type; { get socket type } { WinSock 2 extension -- new options } // SO_GROUP_ID = $2001; { ID of a socket group} // SO_GROUP_PRIORITY = $2002; { the relative priority within a group} // SO_MAX_MSG_SIZE = $2003; { maximum message size } // SO_PROTOCOL_INFOA = $2004; { WSAPROTOCOL_INFOA structure } // SO_PROTOCOL_INFOW = $2005; { WSAPROTOCOL_INFOW structure } // SO_PROTOCOL_INFO = SO_PROTOCOL_INFOA; // PVD_CONFIG = $3001; {configuration info for service provider } { Option for opening sockets for synchronous access. } // SO_OPENTYPE = $7008; // SO_SYNCHRONOUS_ALERT = $10; // SO_SYNCHRONOUS_NONALERT = $20; { Other NT-specific options. } // SO_MAXDG = $7009; // SO_MAXPATHDG = $700A; // SO_UPDATE_ACCEPT_CONTEXT = $700B; // SO_CONNECT_TIME = $700C; { All Windows Sockets error constants are biased by WSABASEERR from the "normal" } WSABASEERR = 10000; { Windows Sockets definitions of regular Microsoft C error constants } WSAEINTR = (WSABASEERR+4); WSAEBADF = (WSABASEERR+9); WSAEACCES = (WSABASEERR+13); WSAEFAULT = (WSABASEERR+14); WSAEINVAL = (WSABASEERR+22); WSAEMFILE = (WSABASEERR+24); { Windows Sockets definitions of regular Berkeley error constants } WSAEWOULDBLOCK = (WSABASEERR+35); WSAEINPROGRESS = (WSABASEERR+36); WSAEALREADY = (WSABASEERR+37); WSAENOTSOCK = (WSABASEERR+38); WSAEDESTADDRREQ = (WSABASEERR+39); WSAEMSGSIZE = (WSABASEERR+40); WSAEPROTOTYPE = (WSABASEERR+41); WSAENOPROTOOPT = (WSABASEERR+42); WSAEPROTONOSUPPORT = (WSABASEERR+43); WSAESOCKTNOSUPPORT = (WSABASEERR+44); WSAEOPNOTSUPP = (WSABASEERR+45); WSAEPFNOSUPPORT = (WSABASEERR+46); WSAEAFNOSUPPORT = (WSABASEERR+47); WSAEADDRINUSE = (WSABASEERR+48); WSAEADDRNOTAVAIL = (WSABASEERR+49); WSAENETDOWN = (WSABASEERR+50); WSAENETUNREACH = (WSABASEERR+51); WSAENETRESET = (WSABASEERR+52); WSAECONNABORTED = (WSABASEERR+53); WSAECONNRESET = (WSABASEERR+54); WSAENOBUFS = (WSABASEERR+55); WSAEISCONN = (WSABASEERR+56); WSAENOTCONN = (WSABASEERR+57); WSAESHUTDOWN = (WSABASEERR+58); WSAETOOMANYREFS = (WSABASEERR+59); WSAETIMEDOUT = (WSABASEERR+60); WSAECONNREFUSED = (WSABASEERR+61); WSAELOOP = (WSABASEERR+62); WSAENAMETOOLONG = (WSABASEERR+63); WSAEHOSTDOWN = (WSABASEERR+64); WSAEHOSTUNREACH = (WSABASEERR+65); WSAENOTEMPTY = (WSABASEERR+66); WSAEPROCLIM = (WSABASEERR+67); WSAEUSERS = (WSABASEERR+68); WSAEDQUOT = (WSABASEERR+69); WSAESTALE = (WSABASEERR+70); WSAEREMOTE = (WSABASEERR+71); { Extended Windows Sockets error constant definitions } WSASYSNOTREADY = (WSABASEERR+91); WSAVERNOTSUPPORTED = (WSABASEERR+92); WSANOTINITIALISED = (WSABASEERR+93); WSAEDISCON = (WSABASEERR+101); WSAENOMORE = (WSABASEERR+102); WSAECANCELLED = (WSABASEERR+103); WSAEEINVALIDPROCTABLE = (WSABASEERR+104); WSAEINVALIDPROVIDER = (WSABASEERR+105); WSAEPROVIDERFAILEDINIT = (WSABASEERR+106); WSASYSCALLFAILURE = (WSABASEERR+107); WSASERVICE_NOT_FOUND = (WSABASEERR+108); WSATYPE_NOT_FOUND = (WSABASEERR+109); WSA_E_NO_MORE = (WSABASEERR+110); WSA_E_CANCELLED = (WSABASEERR+111); WSAEREFUSED = (WSABASEERR+112); { Error return codes from gethostbyname() and gethostbyaddr() (when using the resolver). Note that these errors are retrieved via WSAGetLastError() and must therefore follow the rules for avoiding clashes with error numbers from specific implementations or language run-time systems. For this reason the codes are based at WSABASEERR+1001. Note also that [WSA]NO_ADDRESS is defined only for compatibility purposes. } { Authoritative Answer: Host not found } WSAHOST_NOT_FOUND = (WSABASEERR+1001); HOST_NOT_FOUND = WSAHOST_NOT_FOUND; { Non-Authoritative: Host not found, or SERVERFAIL } WSATRY_AGAIN = (WSABASEERR+1002); TRY_AGAIN = WSATRY_AGAIN; { Non recoverable errors, FORMERR, REFUSED, NOTIMP } WSANO_RECOVERY = (WSABASEERR+1003); NO_RECOVERY = WSANO_RECOVERY; { Valid name, no data record of requested type } WSANO_DATA = (WSABASEERR+1004); NO_DATA = WSANO_DATA; { no address, look for MX record } WSANO_ADDRESS = WSANO_DATA; NO_ADDRESS = WSANO_ADDRESS; EWOULDBLOCK = WSAEWOULDBLOCK; EINPROGRESS = WSAEINPROGRESS; EALREADY = WSAEALREADY; ENOTSOCK = WSAENOTSOCK; EDESTADDRREQ = WSAEDESTADDRREQ; EMSGSIZE = WSAEMSGSIZE; EPROTOTYPE = WSAEPROTOTYPE; ENOPROTOOPT = WSAENOPROTOOPT; EPROTONOSUPPORT = WSAEPROTONOSUPPORT; ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT; EOPNOTSUPP = WSAEOPNOTSUPP; EPFNOSUPPORT = WSAEPFNOSUPPORT; EAFNOSUPPORT = WSAEAFNOSUPPORT; EADDRINUSE = WSAEADDRINUSE; EADDRNOTAVAIL = WSAEADDRNOTAVAIL; ENETDOWN = WSAENETDOWN; ENETUNREACH = WSAENETUNREACH; ENETRESET = WSAENETRESET; ECONNABORTED = WSAECONNABORTED; ECONNRESET = WSAECONNRESET; ENOBUFS = WSAENOBUFS; EISCONN = WSAEISCONN; ENOTCONN = WSAENOTCONN; ESHUTDOWN = WSAESHUTDOWN; ETOOMANYREFS = WSAETOOMANYREFS; ETIMEDOUT = WSAETIMEDOUT; ECONNREFUSED = WSAECONNREFUSED; ELOOP = WSAELOOP; ENAMETOOLONG = WSAENAMETOOLONG; EHOSTDOWN = WSAEHOSTDOWN; EHOSTUNREACH = WSAEHOSTUNREACH; ENOTEMPTY = WSAENOTEMPTY; EPROCLIM = WSAEPROCLIM; EUSERS = WSAEUSERS; EDQUOT = WSAEDQUOT; ESTALE = WSAESTALE; EREMOTE = WSAEREMOTE; type TVarSin = IPEndpoint; { function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6):boolean; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); var in6addr_any, in6addr_loopback : TInAddr6; } {procedure FD_CLR(Socket: TSocket; var FDSet: TFDSet); function FD_ISSET(Socket: TSocket; var FDSet: TFDSet): Boolean; procedure FD_SET(Socket: TSocket; var FDSet: TFDSet); procedure FD_ZERO(var FDSet: TFDSet); } {=============================================================================} function WSAStartup(wVersionRequired: Word; var WSData: TWSAData): Integer; function WSACleanup: Integer; function WSAGetLastError: Integer; function WSAGetLastErrorDesc: String; function GetHostName: string; function Shutdown(s: TSocket; how: Integer): Integer; // function SetSockOpt(s: TSocket; level, optname: Integer; optval: PChar; // optlen: Integer): Integer; function SetSockOpt(s: TSocket; level, optname: Integer; optval: TMemory; optlen: Integer): Integer; function SetSockOptObj(s: TSocket; level, optname: Integer; optval: TObject): Integer; function GetSockOpt(s: TSocket; level, optname: Integer; optval: TMemory; var optlen: Integer): Integer; // function SendTo(s: TSocket; const Buf; len, flags: Integer; addrto: PSockAddr; // tolen: Integer): Integer; /// function SendTo(s: TSocket; const Buf; len, flags: Integer; addrto: TVarSin): Integer; /// function Send(s: TSocket; const Buf; len, flags: Integer): Integer; /// function Recv(s: TSocket; var Buf; len, flags: Integer): Integer; // function RecvFrom(s: TSocket; var Buf; len, flags: Integer; from: PSockAddr; // var fromlen: Integer): Integer; /// function RecvFrom(s: TSocket; var Buf; len, flags: Integer; from: TVarSin): Integer; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; function ntohs(netshort: u_short): u_short; function ntohl(netlong: u_long): u_long; function Listen(s: TSocket; backlog: Integer): Integer; function IoctlSocket(s: TSocket; cmd: DWORD; var arg: integer): Integer; function htons(hostshort: u_short): u_short; function htonl(hostlong: u_long): u_long; // function GetSockName(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; function GetSockName(s: TSocket; var name: TVarSin): Integer; // function GetPeerName(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; function GetPeerName(s: TSocket; var name: TVarSin): Integer; // function Connect(s: TSocket; name: PSockAddr; namelen: Integer): Integer; function Connect(s: TSocket; const name: TVarSin): Integer; function CloseSocket(s: TSocket): Integer; // function Bind(s: TSocket; addr: PSockAddr; namelen: Integer): Integer; function Bind(s: TSocket; const addr: TVarSin): Integer; // function Accept(s: TSocket; addr: PSockAddr; var addrlen: Integer): TSocket; function Accept(s: TSocket; var addr: TVarSin): TSocket; function Socket(af, Struc, Protocol: Integer): TSocket; // Select = function(nfds: Integer; readfds, writefds, exceptfds: PFDSet; // timeout: PTimeVal): Longint; // {$IFDEF LINUX}cdecl{$ELSE}stdcall{$ENDIF}; // TWSAIoctl = function (s: TSocket; dwIoControlCode: DWORD; lpvInBuffer: Pointer; // cbInBuffer: DWORD; lpvOutBuffer: Pointer; cbOutBuffer: DWORD; // lpcbBytesReturned: PDWORD; lpOverlapped: Pointer; // lpCompletionRoutine: pointer): u_int; // stdcall; function GetPortService(value: string): integer; function IsNewApi(Family: TAddrFamily): Boolean; function SetVarSin(var Sin: TVarSin; IP, Port: string; Family: TAddrFamily; SockProtocol, SockType: integer; PreferIP4: Boolean): integer; function GetSinIP(Sin: TVarSin): string; function GetSinPort(Sin: TVarSin): Integer; procedure ResolveNameToIP(Name: string; Family: TAddrFamily; SockProtocol, SockType: integer; const IPList: TStrings); function ResolveIPToName(IP: string; Family: TAddrFamily; SockProtocol, SockType: integer): string; function ResolvePort(Port: string; Family: TAddrFamily; SockProtocol, SockType: integer): Word; var SynSockCS: SyncObjs.TCriticalSection; SockEnhancedApi: Boolean; SockWship6Api: Boolean; {==============================================================================} implementation threadvar WSALastError: integer; WSALastErrorDesc: string; var services: Array [0..139, 0..1] of string = ( ('echo', '7'), ('discard', '9'), ('sink', '9'), ('null', '9'), ('systat', '11'), ('users', '11'), ('daytime', '13'), ('qotd', '17'), ('quote', '17'), ('chargen', '19'), ('ttytst', '19'), ('source', '19'), ('ftp-data', '20'), ('ftp', '21'), ('telnet', '23'), ('smtp', '25'), ('mail', '25'), ('time', '37'), ('timeserver', '37'), ('rlp', '39'), ('nameserver', '42'), ('name', '42'), ('nickname', '43'), ('whois', '43'), ('domain', '53'), ('bootps', '67'), ('dhcps', '67'), ('bootpc', '68'), ('dhcpc', '68'), ('tftp', '69'), ('gopher', '70'), ('finger', '79'), ('http', '80'), ('www', '80'), ('www-http', '80'), ('kerberos', '88'), ('hostname', '101'), ('hostnames', '101'), ('iso-tsap', '102'), ('rtelnet', '107'), ('pop2', '109'), ('postoffice', '109'), ('pop3', '110'), ('sunrpc', '111'), ('rpcbind', '111'), ('portmap', '111'), ('auth', '113'), ('ident', '113'), ('tap', '113'), ('uucp-path', '117'), ('nntp', '119'), ('usenet', '119'), ('ntp', '123'), ('epmap', '135'), ('loc-srv', '135'), ('netbios-ns', '137'), ('nbname', '137'), ('netbios-dgm', '138'), ('nbdatagram', '138'), ('netbios-ssn', '139'), ('nbsession', '139'), ('imap', '143'), ('imap4', '143'), ('pcmail-srv', '158'), ('snmp', '161'), ('snmptrap', '162'), ('snmp-trap', '162'), ('print-srv', '170'), ('bgp', '179'), ('irc', '194'), ('ipx', '213'), ('ldap', '389'), ('https', '443'), ('mcom', '443'), ('microsoft-ds', '445'), ('kpasswd', '464'), ('isakmp', '500'), ('ike', '500'), ('exec', '512'), ('biff', '512'), ('comsat', '512'), ('login', '513'), ('who', '513'), ('whod', '513'), ('cmd', '514'), ('shell', '514'), ('syslog', '514'), ('printer', '515'), ('spooler', '515'), ('talk', '517'), ('ntalk', '517'), ('efs', '520'), ('router', '520'), ('route', '520'), ('routed', '520'), ('timed', '525'), ('timeserver', '525'), ('tempo', '526'), ('newdate', '526'), ('courier', '530'), ('rpc', '530'), ('conference', '531'), ('chat', '531'), ('netnews', '532'), ('readnews', '532'), ('netwall', '533'), ('uucp', '540'), ('uucpd', '540'), ('klogin', '543'), ('kshell', '544'), ('krcmd', '544'), ('new-rwho', '550'), ('new-who', '550'), ('remotefs', '556'), ('rfs', '556'), ('rfs_server', '556'), ('rmonitor', '560'), ('rmonitord', '560'), ('monitor', '561'), ('ldaps', '636'), ('sldap', '636'), ('doom', '666'), ('kerberos-adm', '749'), ('kerberos-iv', '750'), ('kpop', '1109'), ('phone', '1167'), ('ms-sql-s', '1433'), ('ms-sql-m', '1434'), ('wins', '1512'), ('ingreslock', '1524'), ('ingres', '1524'), ('l2tp', '1701'), ('pptp', '1723'), ('radius', '1812'), ('radacct', '1813'), ('nfsd', '2049'), ('nfs', '2049'), ('knetd', '2053'), ('gds_db', '3050'), ('man', '9535') ); {function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; begin Result := ((a^.s_un_dw.s_dw1 = 0) and (a^.s_un_dw.s_dw2 = 0) and (a^.s_un_dw.s_dw3 = 0) and (a^.s_un_dw.s_dw4 = 0)); end; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; begin Result := ((a^.s_un_dw.s_dw1 = 0) and (a^.s_un_dw.s_dw2 = 0) and (a^.s_un_dw.s_dw3 = 0) and (a^.s_un_b.s_b13 = char(0)) and (a^.s_un_b.s_b14 = char(0)) and (a^.s_un_b.s_b15 = char(0)) and (a^.s_un_b.s_b16 = char(1))); end; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; begin Result := ((a^.s_un_b.s_b1 = u_char($FE)) and (a^.s_un_b.s_b2 = u_char($80))); end; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; begin Result := ((a^.s_un_b.s_b1 = u_char($FE)) and (a^.s_un_b.s_b2 = u_char($C0))); end; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; begin Result := (a^.s_un_b.s_b1 = char($FF)); end; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6): boolean; begin Result := (CompareMem( a, b, sizeof(TInAddr6))); end; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); end; procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); a^.s_un_b.s_b16 := char(1); end; } {=============================================================================} procedure NullErr; begin WSALastError := 0; WSALastErrorDesc := ''; end; procedure GetErrCode(E: System.Exception); var SE: System.Net.Sockets.SocketException; begin if E is System.Net.Sockets.SocketException then begin SE := E as System.Net.Sockets.SocketException; WSALastError := SE.ErrorCode; WSALastErrorDesc := SE.Message; end end; function WSAStartup(wVersionRequired: Word; var WSData: TWSAData): Integer; begin NullErr; with WSData do begin wVersion := wVersionRequired; wHighVersion := $202; szDescription := 'Synsock - Synapse Platform Independent Socket Layer'; szSystemStatus := 'Running on .NET'; iMaxSockets := 32768; iMaxUdpDg := 8192; end; Result := 0; end; function WSACleanup: Integer; begin NullErr; Result := 0; end; function WSAGetLastError: Integer; begin Result := WSALastError; end; function WSAGetLastErrorDesc: String; begin Result := WSALastErrorDesc; end; function GetHostName: string; begin Result := System.Net.DNS.GetHostName; end; function Shutdown(s: TSocket; how: Integer): Integer; begin Result := 0; NullErr; try s.ShutDown(SocketShutdown(how)); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function SetSockOpt(s: TSocket; level, optname: Integer; optval: Tmemory; optlen: Integer): Integer; begin Result := 0; NullErr; try s.SetSocketOption(SocketOptionLevel(level), SocketOptionName(optname), optval); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function SetSockOptObj(s: TSocket; level, optname: Integer; optval: TObject): Integer; begin Result := 0; NullErr; try s.SetSocketOption(SocketOptionLevel(level), SocketOptionName(optname), optval); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function GetSockOpt(s: TSocket; level, optname: Integer; optval: Tmemory; var optlen: Integer): Integer; begin Result := 0; NullErr; try s.GetSocketOption(SocketOptionLevel(level), SocketOptionName(optname), optval); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; //function SendTo(s: TSocket; const Buf; len, flags: Integer; addrto: TVarSin): Integer; begin NullErr; try result := s.SendTo(Buf, len, SocketFlags(flags), addrto); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; //function Send(s: TSocket; const Buf; len, flags: Integer): Integer; begin NullErr; try result := s.Send(Buf, len, SocketFlags(flags)); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; //function Recv(s: TSocket; var Buf; len, flags: Integer): Integer; begin NullErr; try result := s.Receive(Buf, len, SocketFlags(flags)); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; //function RecvFrom(s: TSocket; var Buf; len, flags: Integer; from: PSockAddr; // var fromlen: Integer): Integer; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; //function RecvFrom(s: TSocket; var Buf; len, flags: Integer; from: TVarSin): Integer; var EP: EndPoint; begin NullErr; try EP := from; result := s.ReceiveFrom(Buf, len, SocketFlags(flags), EndPoint(EP)); from := EP as IPEndPoint; except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function ntohs(netshort: u_short): u_short; begin Result := IPAddress.NetworkToHostOrder(NetShort); end; function ntohl(netlong: u_long): u_long; begin Result := IPAddress.NetworkToHostOrder(NetLong); end; function Listen(s: TSocket; backlog: Integer): Integer; begin Result := 0; NullErr; try s.Listen(backlog); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function IoctlSocket(s: TSocket; cmd: DWORD; var arg: integer): Integer; var inv, outv: TMemory; begin Result := 0; NullErr; try if cmd = DWORD(FIONBIO) then s.Blocking := arg = 0 else begin inv := BitConverter.GetBytes(arg); outv := BitConverter.GetBytes(integer(0)); s.IOControl(cmd, inv, outv); arg := BitConverter.ToInt32(outv, 0); end; except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function htons(hostshort: u_short): u_short; begin Result := IPAddress.HostToNetworkOrder(Hostshort); end; function htonl(hostlong: u_long): u_long; begin Result := IPAddress.HostToNetworkOrder(HostLong); end; //function GetSockName(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; function GetSockName(s: TSocket; var name: TVarSin): Integer; begin Result := 0; NullErr; try Name := s.localEndPoint as IPEndpoint; except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; //function GetPeerName(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; function GetPeerName(s: TSocket; var name: TVarSin): Integer; begin Result := 0; NullErr; try Name := s.RemoteEndPoint as IPEndpoint; except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; //function Connect(s: TSocket; name: PSockAddr; namelen: Integer): Integer; function Connect(s: TSocket; const name: TVarSin): Integer; begin Result := 0; NullErr; try s.Connect(name); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; function CloseSocket(s: TSocket): Integer; begin Result := 0; NullErr; try s.Close; except on e: System.Net.Sockets.SocketException do begin Result := integer(SOCKET_ERROR); end; end; end; //function Bind(s: TSocket; addr: PSockAddr; namelen: Integer): Integer; function Bind(s: TSocket; const addr: TVarSin): Integer; begin Result := 0; NullErr; try s.Bind(addr); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := integer(SOCKET_ERROR); end; end; end; //function Accept(s: TSocket; addr: PSockAddr; var addrlen: Integer): TSocket; function Accept(s: TSocket; var addr: TVarSin): TSocket; begin NullErr; try result := s.Accept(); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := nil; end; end; end; function Socket(af, Struc, Protocol: Integer): TSocket; begin NullErr; try result := TSocket.Create(AddressFamily(af), SocketType(Struc), ProtocolType(Protocol)); except on e: System.Net.Sockets.SocketException do begin GetErrCode(e); Result := nil; end; end; end; {=============================================================================} function GetPortService(value: string): integer; var n: integer; begin Result := 0; value := Lowercase(value); for n := 0 to High(Services) do if services[n, 0] = value then begin Result := strtointdef(services[n, 1], 0); break; end; if Result = 0 then Result := StrToIntDef(value, 0); end; {=============================================================================} function IsNewApi(Family: TAddrFamily): Boolean; begin Result := true; end; function SetVarSin(var Sin: TVarSin; IP, Port: string; Family: TAddrFamily; SockProtocol, SockType: integer; PreferIP4: Boolean): integer; var IPs: array of IPAddress; n: integer; ip4, ip6: string; sip: string; begin sip := ''; ip4 := ''; ip6 := ''; IPs := Dns.Resolve(IP).AddressList; for n :=low(IPs) to high(IPs) do begin if (ip4 = '') and (IPs[n].AddressFamily = AF_INET) then ip4 := IPs[n].toString; if (ip6 = '') and (IPs[n].AddressFamily = AF_INET6) then ip6 := IPs[n].toString; if (ip4 <> '') and (ip6 <> '') then break; end; case Family of AF_UNSPEC: begin if (ip4 <> '') and (ip6 <> '') then begin if PreferIP4 then sip := ip4 else Sip := ip6; end else begin sip := ip4; if (ip6 <> '') then sip := ip6; end; end; AF_INET: sip := ip4; AF_INET6: sip := ip6; end; sin := TVarSin.Create(IPAddress.Parse(sip), GetPortService(Port)); end; function GetSinIP(Sin: TVarSin): string; begin Result := Sin.Address.ToString; end; function GetSinPort(Sin: TVarSin): Integer; begin Result := Sin.Port; end; procedure ResolveNameToIP(Name: string; Family: TAddrFamily; SockProtocol, SockType: integer; const IPList: TStrings); var IPs :array of IPAddress; n: integer; begin IPList.Clear; IPs := Dns.Resolve(Name).AddressList; for n := low(IPs) to high(IPs) do begin if not(((Family = AF_INET6) and (IPs[n].AddressFamily = AF_INET)) or ((Family = AF_INET) and (IPs[n].AddressFamily = AF_INET6))) then begin IPList.Add(IPs[n].toString); end; end; end; function ResolvePort(Port: string; Family: TAddrFamily; SockProtocol, SockType: integer): Word; var n: integer; begin Result := StrToIntDef(port, 0); if Result = 0 then begin port := Lowercase(port); for n := 0 to High(Services) do if services[n, 0] = port then begin Result := strtointdef(services[n, 1], 0); break; end; end; end; function ResolveIPToName(IP: string; Family: TAddrFamily; SockProtocol, SockType: integer): string; begin Result := Dns.GetHostByAddress(IP).HostName; end; {=============================================================================} function InitSocketInterface(stack: string): Boolean; begin Result := True; end; function DestroySocketInterface: Boolean; begin NullErr; Result := True; end; initialization begin SynSockCS := SyncObjs.TCriticalSection.Create; // SET_IN6_IF_ADDR_ANY (@in6addr_any); // SET_LOOPBACK_ADDR6 (@in6addr_loopback); end; finalization begin NullErr; SynSockCS.Free; end; {$ENDIF} TransGUI/synapse/source/lib/sswin32.pas0000644000000000000000000015161411366572451016777 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.002.003 | |==============================================================================| | Content: Socket Independent Platform Layer - Win32 definition include | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2003-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@exclude} //{$DEFINE WINSOCK1} {Note about define WINSOCK1: If you activate this compiler directive, then socket interface level 1.1 is used instead default level 2.2. Level 2.2 is not available on old W95, however you can install update. } //{$DEFINE FORCEOLDAPI} {Note about define FORCEOLDAPI: If you activate this compiler directive, then is allways used old socket API for name resolution. If you leave this directive inactive, then the new API is used, when running system allows it. For IPv6 support you must have new API! } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF VER125} {$DEFINE BCB} {$ENDIF} {$IFDEF BCB} {$ObjExportAll On} (*$HPPEMIT '/* EDE 2003-02-19 */' *) (*$HPPEMIT 'namespace Synsock { using System::Shortint; }' *) (*$HPPEMIT '#undef h_addr' *) (*$HPPEMIT '#undef IOCPARM_MASK' *) (*$HPPEMIT '#undef FD_SETSIZE' *) (*$HPPEMIT '#undef IOC_VOID' *) (*$HPPEMIT '#undef IOC_OUT' *) (*$HPPEMIT '#undef IOC_IN' *) (*$HPPEMIT '#undef IOC_INOUT' *) (*$HPPEMIT '#undef FIONREAD' *) (*$HPPEMIT '#undef FIONBIO' *) (*$HPPEMIT '#undef FIOASYNC' *) (*$HPPEMIT '#undef IPPROTO_IP' *) (*$HPPEMIT '#undef IPPROTO_ICMP' *) (*$HPPEMIT '#undef IPPROTO_IGMP' *) (*$HPPEMIT '#undef IPPROTO_TCP' *) (*$HPPEMIT '#undef IPPROTO_UDP' *) (*$HPPEMIT '#undef IPPROTO_RAW' *) (*$HPPEMIT '#undef IPPROTO_MAX' *) (*$HPPEMIT '#undef INADDR_ANY' *) (*$HPPEMIT '#undef INADDR_LOOPBACK' *) (*$HPPEMIT '#undef INADDR_BROADCAST' *) (*$HPPEMIT '#undef INADDR_NONE' *) (*$HPPEMIT '#undef INVALID_SOCKET' *) (*$HPPEMIT '#undef SOCKET_ERROR' *) (*$HPPEMIT '#undef WSADESCRIPTION_LEN' *) (*$HPPEMIT '#undef WSASYS_STATUS_LEN' *) (*$HPPEMIT '#undef IP_OPTIONS' *) (*$HPPEMIT '#undef IP_TOS' *) (*$HPPEMIT '#undef IP_TTL' *) (*$HPPEMIT '#undef IP_MULTICAST_IF' *) (*$HPPEMIT '#undef IP_MULTICAST_TTL' *) (*$HPPEMIT '#undef IP_MULTICAST_LOOP' *) (*$HPPEMIT '#undef IP_ADD_MEMBERSHIP' *) (*$HPPEMIT '#undef IP_DROP_MEMBERSHIP' *) (*$HPPEMIT '#undef IP_DONTFRAGMENT' *) (*$HPPEMIT '#undef IP_DEFAULT_MULTICAST_TTL' *) (*$HPPEMIT '#undef IP_DEFAULT_MULTICAST_LOOP' *) (*$HPPEMIT '#undef IP_MAX_MEMBERSHIPS' *) (*$HPPEMIT '#undef SOL_SOCKET' *) (*$HPPEMIT '#undef SO_DEBUG' *) (*$HPPEMIT '#undef SO_ACCEPTCONN' *) (*$HPPEMIT '#undef SO_REUSEADDR' *) (*$HPPEMIT '#undef SO_KEEPALIVE' *) (*$HPPEMIT '#undef SO_DONTROUTE' *) (*$HPPEMIT '#undef SO_BROADCAST' *) (*$HPPEMIT '#undef SO_USELOOPBACK' *) (*$HPPEMIT '#undef SO_LINGER' *) (*$HPPEMIT '#undef SO_OOBINLINE' *) (*$HPPEMIT '#undef SO_DONTLINGER' *) (*$HPPEMIT '#undef SO_SNDBUF' *) (*$HPPEMIT '#undef SO_RCVBUF' *) (*$HPPEMIT '#undef SO_SNDLOWAT' *) (*$HPPEMIT '#undef SO_RCVLOWAT' *) (*$HPPEMIT '#undef SO_SNDTIMEO' *) (*$HPPEMIT '#undef SO_RCVTIMEO' *) (*$HPPEMIT '#undef SO_ERROR' *) (*$HPPEMIT '#undef SO_OPENTYPE' *) (*$HPPEMIT '#undef SO_SYNCHRONOUS_ALERT' *) (*$HPPEMIT '#undef SO_SYNCHRONOUS_NONALERT' *) (*$HPPEMIT '#undef SO_MAXDG' *) (*$HPPEMIT '#undef SO_MAXPATHDG' *) (*$HPPEMIT '#undef SO_UPDATE_ACCEPT_CONTEXT' *) (*$HPPEMIT '#undef SO_CONNECT_TIME' *) (*$HPPEMIT '#undef SO_TYPE' *) (*$HPPEMIT '#undef SOCK_STREAM' *) (*$HPPEMIT '#undef SOCK_DGRAM' *) (*$HPPEMIT '#undef SOCK_RAW' *) (*$HPPEMIT '#undef SOCK_RDM' *) (*$HPPEMIT '#undef SOCK_SEQPACKET' *) (*$HPPEMIT '#undef TCP_NODELAY' *) (*$HPPEMIT '#undef AF_UNSPEC' *) (*$HPPEMIT '#undef SOMAXCONN' *) (*$HPPEMIT '#undef AF_INET' *) (*$HPPEMIT '#undef AF_MAX' *) (*$HPPEMIT '#undef PF_UNSPEC' *) (*$HPPEMIT '#undef PF_INET' *) (*$HPPEMIT '#undef PF_MAX' *) (*$HPPEMIT '#undef MSG_OOB' *) (*$HPPEMIT '#undef MSG_PEEK' *) (*$HPPEMIT '#undef WSABASEERR' *) (*$HPPEMIT '#undef WSAEINTR' *) (*$HPPEMIT '#undef WSAEBADF' *) (*$HPPEMIT '#undef WSAEACCES' *) (*$HPPEMIT '#undef WSAEFAULT' *) (*$HPPEMIT '#undef WSAEINVAL' *) (*$HPPEMIT '#undef WSAEMFILE' *) (*$HPPEMIT '#undef WSAEWOULDBLOCK' *) (*$HPPEMIT '#undef WSAEINPROGRESS' *) (*$HPPEMIT '#undef WSAEALREADY' *) (*$HPPEMIT '#undef WSAENOTSOCK' *) (*$HPPEMIT '#undef WSAEDESTADDRREQ' *) (*$HPPEMIT '#undef WSAEMSGSIZE' *) (*$HPPEMIT '#undef WSAEPROTOTYPE' *) (*$HPPEMIT '#undef WSAENOPROTOOPT' *) (*$HPPEMIT '#undef WSAEPROTONOSUPPORT' *) (*$HPPEMIT '#undef WSAESOCKTNOSUPPORT' *) (*$HPPEMIT '#undef WSAEOPNOTSUPP' *) (*$HPPEMIT '#undef WSAEPFNOSUPPORT' *) (*$HPPEMIT '#undef WSAEAFNOSUPPORT' *) (*$HPPEMIT '#undef WSAEADDRINUSE' *) (*$HPPEMIT '#undef WSAEADDRNOTAVAIL' *) (*$HPPEMIT '#undef WSAENETDOWN' *) (*$HPPEMIT '#undef WSAENETUNREACH' *) (*$HPPEMIT '#undef WSAENETRESET' *) (*$HPPEMIT '#undef WSAECONNABORTED' *) (*$HPPEMIT '#undef WSAECONNRESET' *) (*$HPPEMIT '#undef WSAENOBUFS' *) (*$HPPEMIT '#undef WSAEISCONN' *) (*$HPPEMIT '#undef WSAENOTCONN' *) (*$HPPEMIT '#undef WSAESHUTDOWN' *) (*$HPPEMIT '#undef WSAETOOMANYREFS' *) (*$HPPEMIT '#undef WSAETIMEDOUT' *) (*$HPPEMIT '#undef WSAECONNREFUSED' *) (*$HPPEMIT '#undef WSAELOOP' *) (*$HPPEMIT '#undef WSAENAMETOOLONG' *) (*$HPPEMIT '#undef WSAEHOSTDOWN' *) (*$HPPEMIT '#undef WSAEHOSTUNREACH' *) (*$HPPEMIT '#undef WSAENOTEMPTY' *) (*$HPPEMIT '#undef WSAEPROCLIM' *) (*$HPPEMIT '#undef WSAEUSERS' *) (*$HPPEMIT '#undef WSAEDQUOT' *) (*$HPPEMIT '#undef WSAESTALE' *) (*$HPPEMIT '#undef WSAEREMOTE' *) (*$HPPEMIT '#undef WSASYSNOTREADY' *) (*$HPPEMIT '#undef WSAVERNOTSUPPORTED' *) (*$HPPEMIT '#undef WSANOTINITIALISED' *) (*$HPPEMIT '#undef WSAEDISCON' *) (*$HPPEMIT '#undef WSAENOMORE' *) (*$HPPEMIT '#undef WSAECANCELLED' *) (*$HPPEMIT '#undef WSAEEINVALIDPROCTABLE' *) (*$HPPEMIT '#undef WSAEINVALIDPROVIDER' *) (*$HPPEMIT '#undef WSAEPROVIDERFAILEDINIT' *) (*$HPPEMIT '#undef WSASYSCALLFAILURE' *) (*$HPPEMIT '#undef WSASERVICE_NOT_FOUND' *) (*$HPPEMIT '#undef WSATYPE_NOT_FOUND' *) (*$HPPEMIT '#undef WSA_E_NO_MORE' *) (*$HPPEMIT '#undef WSA_E_CANCELLED' *) (*$HPPEMIT '#undef WSAEREFUSED' *) (*$HPPEMIT '#undef WSAHOST_NOT_FOUND' *) (*$HPPEMIT '#undef HOST_NOT_FOUND' *) (*$HPPEMIT '#undef WSATRY_AGAIN' *) (*$HPPEMIT '#undef TRY_AGAIN' *) (*$HPPEMIT '#undef WSANO_RECOVERY' *) (*$HPPEMIT '#undef NO_RECOVERY' *) (*$HPPEMIT '#undef WSANO_DATA' *) (*$HPPEMIT '#undef NO_DATA' *) (*$HPPEMIT '#undef WSANO_ADDRESS' *) (*$HPPEMIT '#undef ENAMETOOLONG' *) (*$HPPEMIT '#undef ENOTEMPTY' *) (*$HPPEMIT '#undef FD_CLR' *) (*$HPPEMIT '#undef FD_ISSET' *) (*$HPPEMIT '#undef FD_SET' *) (*$HPPEMIT '#undef FD_ZERO' *) (*$HPPEMIT '#undef NO_ADDRESS' *) (*$HPPEMIT '#undef ADDR_ANY' *) (*$HPPEMIT '#undef SO_GROUP_ID' *) (*$HPPEMIT '#undef SO_GROUP_PRIORITY' *) (*$HPPEMIT '#undef SO_MAX_MSG_SIZE' *) (*$HPPEMIT '#undef SO_PROTOCOL_INFOA' *) (*$HPPEMIT '#undef SO_PROTOCOL_INFOW' *) (*$HPPEMIT '#undef SO_PROTOCOL_INFO' *) (*$HPPEMIT '#undef PVD_CONFIG' *) (*$HPPEMIT '#undef AF_INET6' *) (*$HPPEMIT '#undef PF_INET6' *) {$ENDIF} interface uses SyncObjs, SysUtils, Classes, Windows; function InitSocketInterface(stack: String): Boolean; function DestroySocketInterface: Boolean; const {$IFDEF WINSOCK1} WinsockLevel = $0101; {$ELSE} WinsockLevel = $0202; {$ENDIF} type u_short = Word; u_int = Integer; u_long = Longint; pu_long = ^u_long; pu_short = ^u_short; {$IFDEF FPC} TSocket = ptruint; {$ELSE} TSocket = u_int; {$ENDIF} TAddrFamily = integer; TMemory = pointer; const {$IFDEF WINSOCK1} DLLStackName = 'wsock32.dll'; {$ELSE} DLLStackName = 'ws2_32.dll'; {$ENDIF} DLLwship6 = 'wship6.dll'; cLocalhost = '127.0.0.1'; cAnyHost = '0.0.0.0'; cBroadcast = '255.255.255.255'; c6Localhost = '::1'; c6AnyHost = '::0'; c6Broadcast = 'ffff::1'; cAnyPort = '0'; const FD_SETSIZE = 64; type PFDSet = ^TFDSet; TFDSet = record fd_count: u_int; fd_array: array[0..FD_SETSIZE-1] of TSocket; end; const FIONREAD = $4004667f; FIONBIO = $8004667e; FIOASYNC = $8004667d; type PTimeVal = ^TTimeVal; TTimeVal = record tv_sec: Longint; tv_usec: Longint; end; const IPPROTO_IP = 0; { Dummy } IPPROTO_ICMP = 1; { Internet Control Message Protocol } IPPROTO_IGMP = 2; { Internet Group Management Protocol} IPPROTO_TCP = 6; { TCP } IPPROTO_UDP = 17; { User Datagram Protocol } IPPROTO_IPV6 = 41; IPPROTO_ICMPV6 = 58; IPPROTO_RM = 113; IPPROTO_RAW = 255; IPPROTO_MAX = 256; type PInAddr = ^TInAddr; TInAddr = record case integer of 0: (S_bytes: packed array [0..3] of byte); 1: (S_addr: u_long); end; PSockAddrIn = ^TSockAddrIn; TSockAddrIn = record case Integer of 0: (sin_family: u_short; sin_port: u_short; sin_addr: TInAddr; sin_zero: array[0..7] of byte); 1: (sa_family: u_short; sa_data: array[0..13] of byte) end; TIP_mreq = record imr_multiaddr: TInAddr; { IP multicast address of group } imr_interface: TInAddr; { local IP address of interface } end; PInAddr6 = ^TInAddr6; TInAddr6 = record case integer of 0: (S6_addr: packed array [0..15] of byte); 1: (u6_addr8: packed array [0..15] of byte); 2: (u6_addr16: packed array [0..7] of word); 3: (u6_addr32: packed array [0..3] of integer); end; PSockAddrIn6 = ^TSockAddrIn6; TSockAddrIn6 = record sin6_family: u_short; // AF_INET6 sin6_port: u_short; // Transport level port number sin6_flowinfo: u_long; // IPv6 flow information sin6_addr: TInAddr6; // IPv6 address sin6_scope_id: u_long; // Scope Id: IF number for link-local // SITE id for site-local end; TIPv6_mreq = record ipv6mr_multiaddr: TInAddr6; // IPv6 multicast address. ipv6mr_interface: integer; // Interface index. padding: integer; end; PHostEnt = ^THostEnt; THostEnt = record h_name: PAnsiChar; h_aliases: ^PAnsiChar; h_addrtype: Smallint; h_length: Smallint; case integer of 0: (h_addr_list: ^PAnsiChar); 1: (h_addr: ^PInAddr); end; PNetEnt = ^TNetEnt; TNetEnt = record n_name: PAnsiChar; n_aliases: ^PAnsiChar; n_addrtype: Smallint; n_net: u_long; end; PServEnt = ^TServEnt; TServEnt = record s_name: PAnsiChar; s_aliases: ^PAnsiChar; {$ifdef WIN64} s_proto: PAnsiChar; s_port: Smallint; {$else} s_port: Smallint; s_proto: PAnsiChar; {$endif} end; PProtoEnt = ^TProtoEnt; TProtoEnt = record p_name: PAnsiChar; p_aliases: ^PAnsichar; p_proto: Smallint; end; const INADDR_ANY = $00000000; INADDR_LOOPBACK = $7F000001; INADDR_BROADCAST = $FFFFFFFF; INADDR_NONE = $FFFFFFFF; ADDR_ANY = INADDR_ANY; INVALID_SOCKET = TSocket(NOT(0)); SOCKET_ERROR = -1; Const {$IFDEF WINSOCK1} IP_OPTIONS = 1; IP_MULTICAST_IF = 2; { set/get IP multicast interface } IP_MULTICAST_TTL = 3; { set/get IP multicast timetolive } IP_MULTICAST_LOOP = 4; { set/get IP multicast loopback } IP_ADD_MEMBERSHIP = 5; { add an IP group membership } IP_DROP_MEMBERSHIP = 6; { drop an IP group membership } IP_TTL = 7; { set/get IP Time To Live } IP_TOS = 8; { set/get IP Type Of Service } IP_DONTFRAGMENT = 9; { set/get IP Don't Fragment flag } {$ELSE} IP_OPTIONS = 1; IP_HDRINCL = 2; IP_TOS = 3; { set/get IP Type Of Service } IP_TTL = 4; { set/get IP Time To Live } IP_MULTICAST_IF = 9; { set/get IP multicast interface } IP_MULTICAST_TTL = 10; { set/get IP multicast timetolive } IP_MULTICAST_LOOP = 11; { set/get IP multicast loopback } IP_ADD_MEMBERSHIP = 12; { add an IP group membership } IP_DROP_MEMBERSHIP = 13; { drop an IP group membership } IP_DONTFRAGMENT = 14; { set/get IP Don't Fragment flag } {$ENDIF} IP_DEFAULT_MULTICAST_TTL = 1; { normally limit m'casts to 1 hop } IP_DEFAULT_MULTICAST_LOOP = 1; { normally hear sends if a member } IP_MAX_MEMBERSHIPS = 20; { per socket; must fit in one mbuf } SOL_SOCKET = $ffff; {options for socket level } { Option flags per-socket. } SO_DEBUG = $0001; { turn on debugging info recording } SO_ACCEPTCONN = $0002; { socket has had listen() } SO_REUSEADDR = $0004; { allow local address reuse } SO_KEEPALIVE = $0008; { keep connections alive } SO_DONTROUTE = $0010; { just use interface addresses } SO_BROADCAST = $0020; { permit sending of broadcast msgs } SO_USELOOPBACK = $0040; { bypass hardware when possible } SO_LINGER = $0080; { linger on close if data present } SO_OOBINLINE = $0100; { leave received OOB data in line } SO_DONTLINGER = $ff7f; { Additional options. } SO_SNDBUF = $1001; { send buffer size } SO_RCVBUF = $1002; { receive buffer size } SO_SNDLOWAT = $1003; { send low-water mark } SO_RCVLOWAT = $1004; { receive low-water mark } SO_SNDTIMEO = $1005; { send timeout } SO_RCVTIMEO = $1006; { receive timeout } SO_ERROR = $1007; { get error status and clear } SO_TYPE = $1008; { get socket type } { WinSock 2 extension -- new options } SO_GROUP_ID = $2001; { ID of a socket group} SO_GROUP_PRIORITY = $2002; { the relative priority within a group} SO_MAX_MSG_SIZE = $2003; { maximum message size } SO_PROTOCOL_INFOA = $2004; { WSAPROTOCOL_INFOA structure } SO_PROTOCOL_INFOW = $2005; { WSAPROTOCOL_INFOW structure } SO_PROTOCOL_INFO = SO_PROTOCOL_INFOA; PVD_CONFIG = $3001; {configuration info for service provider } { Option for opening sockets for synchronous access. } SO_OPENTYPE = $7008; SO_SYNCHRONOUS_ALERT = $10; SO_SYNCHRONOUS_NONALERT = $20; { Other NT-specific options. } SO_MAXDG = $7009; SO_MAXPATHDG = $700A; SO_UPDATE_ACCEPT_CONTEXT = $700B; SO_CONNECT_TIME = $700C; SOMAXCONN = $7fffffff; IPV6_UNICAST_HOPS = 8; // ??? IPV6_MULTICAST_IF = 9; // set/get IP multicast i/f IPV6_MULTICAST_HOPS = 10; // set/get IP multicast ttl IPV6_MULTICAST_LOOP = 11; // set/get IP multicast loopback IPV6_JOIN_GROUP = 12; // add an IP group membership IPV6_LEAVE_GROUP = 13; // drop an IP group membership MSG_NOSIGNAL = 0; // getnameinfo constants NI_MAXHOST = 1025; NI_MAXSERV = 32; NI_NOFQDN = $1; NI_NUMERICHOST = $2; NI_NAMEREQD = $4; NI_NUMERICSERV = $8; NI_DGRAM = $10; const SOCK_STREAM = 1; { stream socket } SOCK_DGRAM = 2; { datagram socket } SOCK_RAW = 3; { raw-protocol interface } SOCK_RDM = 4; { reliably-delivered message } SOCK_SEQPACKET = 5; { sequenced packet stream } { TCP options. } TCP_NODELAY = $0001; { Address families. } AF_UNSPEC = 0; { unspecified } AF_INET = 2; { internetwork: UDP, TCP, etc. } AF_INET6 = 23; { Internetwork Version 6 } AF_MAX = 24; { Protocol families, same as address families for now. } PF_UNSPEC = AF_UNSPEC; PF_INET = AF_INET; PF_INET6 = AF_INET6; PF_MAX = AF_MAX; type { Structure used by kernel to store most addresses. } PSockAddr = ^TSockAddr; TSockAddr = TSockAddrIn; { Structure used by kernel to pass protocol information in raw sockets. } PSockProto = ^TSockProto; TSockProto = record sp_family: u_short; sp_protocol: u_short; end; type PAddrInfo = ^TAddrInfo; TAddrInfo = record ai_flags: integer; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST. ai_family: integer; // PF_xxx. ai_socktype: integer; // SOCK_xxx. ai_protocol: integer; // 0 or IPPROTO_xxx for IPv4 and IPv6. ai_addrlen: u_int; // Length of ai_addr. ai_canonname: PAnsiChar; // Canonical name for nodename. ai_addr: PSockAddr; // Binary address. ai_next: PAddrInfo; // Next structure in linked list. end; const // Flags used in "hints" argument to getaddrinfo(). AI_PASSIVE = $1; // Socket address will be used in bind() call. AI_CANONNAME = $2; // Return canonical name in first ai_canonname. AI_NUMERICHOST = $4; // Nodename must be a numeric address string. type { Structure used for manipulating linger option. } PLinger = ^TLinger; TLinger = record l_onoff: u_short; l_linger: u_short; end; const MSG_OOB = $01; // Process out-of-band data. MSG_PEEK = $02; // Peek at incoming messages. const { All Windows Sockets error constants are biased by WSABASEERR from the "normal" } WSABASEERR = 10000; { Windows Sockets definitions of regular Microsoft C error constants } WSAEINTR = (WSABASEERR+4); WSAEBADF = (WSABASEERR+9); WSAEACCES = (WSABASEERR+13); WSAEFAULT = (WSABASEERR+14); WSAEINVAL = (WSABASEERR+22); WSAEMFILE = (WSABASEERR+24); { Windows Sockets definitions of regular Berkeley error constants } WSAEWOULDBLOCK = (WSABASEERR+35); WSAEINPROGRESS = (WSABASEERR+36); WSAEALREADY = (WSABASEERR+37); WSAENOTSOCK = (WSABASEERR+38); WSAEDESTADDRREQ = (WSABASEERR+39); WSAEMSGSIZE = (WSABASEERR+40); WSAEPROTOTYPE = (WSABASEERR+41); WSAENOPROTOOPT = (WSABASEERR+42); WSAEPROTONOSUPPORT = (WSABASEERR+43); WSAESOCKTNOSUPPORT = (WSABASEERR+44); WSAEOPNOTSUPP = (WSABASEERR+45); WSAEPFNOSUPPORT = (WSABASEERR+46); WSAEAFNOSUPPORT = (WSABASEERR+47); WSAEADDRINUSE = (WSABASEERR+48); WSAEADDRNOTAVAIL = (WSABASEERR+49); WSAENETDOWN = (WSABASEERR+50); WSAENETUNREACH = (WSABASEERR+51); WSAENETRESET = (WSABASEERR+52); WSAECONNABORTED = (WSABASEERR+53); WSAECONNRESET = (WSABASEERR+54); WSAENOBUFS = (WSABASEERR+55); WSAEISCONN = (WSABASEERR+56); WSAENOTCONN = (WSABASEERR+57); WSAESHUTDOWN = (WSABASEERR+58); WSAETOOMANYREFS = (WSABASEERR+59); WSAETIMEDOUT = (WSABASEERR+60); WSAECONNREFUSED = (WSABASEERR+61); WSAELOOP = (WSABASEERR+62); WSAENAMETOOLONG = (WSABASEERR+63); WSAEHOSTDOWN = (WSABASEERR+64); WSAEHOSTUNREACH = (WSABASEERR+65); WSAENOTEMPTY = (WSABASEERR+66); WSAEPROCLIM = (WSABASEERR+67); WSAEUSERS = (WSABASEERR+68); WSAEDQUOT = (WSABASEERR+69); WSAESTALE = (WSABASEERR+70); WSAEREMOTE = (WSABASEERR+71); { Extended Windows Sockets error constant definitions } WSASYSNOTREADY = (WSABASEERR+91); WSAVERNOTSUPPORTED = (WSABASEERR+92); WSANOTINITIALISED = (WSABASEERR+93); WSAEDISCON = (WSABASEERR+101); WSAENOMORE = (WSABASEERR+102); WSAECANCELLED = (WSABASEERR+103); WSAEEINVALIDPROCTABLE = (WSABASEERR+104); WSAEINVALIDPROVIDER = (WSABASEERR+105); WSAEPROVIDERFAILEDINIT = (WSABASEERR+106); WSASYSCALLFAILURE = (WSABASEERR+107); WSASERVICE_NOT_FOUND = (WSABASEERR+108); WSATYPE_NOT_FOUND = (WSABASEERR+109); WSA_E_NO_MORE = (WSABASEERR+110); WSA_E_CANCELLED = (WSABASEERR+111); WSAEREFUSED = (WSABASEERR+112); { Error return codes from gethostbyname() and gethostbyaddr() (when using the resolver). Note that these errors are retrieved via WSAGetLastError() and must therefore follow the rules for avoiding clashes with error numbers from specific implementations or language run-time systems. For this reason the codes are based at WSABASEERR+1001. Note also that [WSA]NO_ADDRESS is defined only for compatibility purposes. } { Authoritative Answer: Host not found } WSAHOST_NOT_FOUND = (WSABASEERR+1001); HOST_NOT_FOUND = WSAHOST_NOT_FOUND; { Non-Authoritative: Host not found, or SERVERFAIL } WSATRY_AGAIN = (WSABASEERR+1002); TRY_AGAIN = WSATRY_AGAIN; { Non recoverable errors, FORMERR, REFUSED, NOTIMP } WSANO_RECOVERY = (WSABASEERR+1003); NO_RECOVERY = WSANO_RECOVERY; { Valid name, no data record of requested type } WSANO_DATA = (WSABASEERR+1004); NO_DATA = WSANO_DATA; { no address, look for MX record } WSANO_ADDRESS = WSANO_DATA; NO_ADDRESS = WSANO_ADDRESS; EWOULDBLOCK = WSAEWOULDBLOCK; EINPROGRESS = WSAEINPROGRESS; EALREADY = WSAEALREADY; ENOTSOCK = WSAENOTSOCK; EDESTADDRREQ = WSAEDESTADDRREQ; EMSGSIZE = WSAEMSGSIZE; EPROTOTYPE = WSAEPROTOTYPE; ENOPROTOOPT = WSAENOPROTOOPT; EPROTONOSUPPORT = WSAEPROTONOSUPPORT; ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT; EOPNOTSUPP = WSAEOPNOTSUPP; EPFNOSUPPORT = WSAEPFNOSUPPORT; EAFNOSUPPORT = WSAEAFNOSUPPORT; EADDRINUSE = WSAEADDRINUSE; EADDRNOTAVAIL = WSAEADDRNOTAVAIL; ENETDOWN = WSAENETDOWN; ENETUNREACH = WSAENETUNREACH; ENETRESET = WSAENETRESET; ECONNABORTED = WSAECONNABORTED; ECONNRESET = WSAECONNRESET; ENOBUFS = WSAENOBUFS; EISCONN = WSAEISCONN; ENOTCONN = WSAENOTCONN; ESHUTDOWN = WSAESHUTDOWN; ETOOMANYREFS = WSAETOOMANYREFS; ETIMEDOUT = WSAETIMEDOUT; ECONNREFUSED = WSAECONNREFUSED; ELOOP = WSAELOOP; ENAMETOOLONG = WSAENAMETOOLONG; EHOSTDOWN = WSAEHOSTDOWN; EHOSTUNREACH = WSAEHOSTUNREACH; ENOTEMPTY = WSAENOTEMPTY; EPROCLIM = WSAEPROCLIM; EUSERS = WSAEUSERS; EDQUOT = WSAEDQUOT; ESTALE = WSAESTALE; EREMOTE = WSAEREMOTE; EAI_ADDRFAMILY = 1; // Address family for nodename not supported. EAI_AGAIN = 2; // Temporary failure in name resolution. EAI_BADFLAGS = 3; // Invalid value for ai_flags. EAI_FAIL = 4; // Non-recoverable failure in name resolution. EAI_FAMILY = 5; // Address family ai_family not supported. EAI_MEMORY = 6; // Memory allocation failure. EAI_NODATA = 7; // No address associated with nodename. EAI_NONAME = 8; // Nodename nor servname provided, or not known. EAI_SERVICE = 9; // Servname not supported for ai_socktype. EAI_SOCKTYPE = 10; // Socket type ai_socktype not supported. EAI_SYSTEM = 11; // System error returned in errno. const WSADESCRIPTION_LEN = 256; WSASYS_STATUS_LEN = 128; type PWSAData = ^TWSAData; TWSAData = record wVersion: Word; wHighVersion: Word; {$ifdef win64} iMaxSockets : Word; iMaxUdpDg : Word; lpVendorInfo : PAnsiChar; szDescription : array[0..WSADESCRIPTION_LEN] of AnsiChar; szSystemStatus : array[0..WSASYS_STATUS_LEN] of AnsiChar; {$else} szDescription: array[0..WSADESCRIPTION_LEN] of AnsiChar; szSystemStatus: array[0..WSASYS_STATUS_LEN] of AnsiChar; iMaxSockets: Word; iMaxUdpDg: Word; lpVendorInfo: PAnsiChar; {$endif} end; function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6):boolean; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); var in6addr_any, in6addr_loopback : TInAddr6; procedure FD_CLR(Socket: TSocket; var FDSet: TFDSet); function FD_ISSET(Socket: TSocket; var FDSet: TFDSet): Boolean; procedure FD_SET(Socket: TSocket; var FDSet: TFDSet); procedure FD_ZERO(var FDSet: TFDSet); {=============================================================================} type TWSAStartup = function(wVersionRequired: Word; var WSData: TWSAData): Integer; stdcall; TWSACleanup = function: Integer; stdcall; TWSAGetLastError = function: Integer; stdcall; TGetServByName = function(name, proto: PAnsiChar): PServEnt; stdcall; TGetServByPort = function(port: Integer; proto: PAnsiChar): PServEnt; stdcall; TGetProtoByName = function(name: PAnsiChar): PProtoEnt; stdcall; TGetProtoByNumber = function(proto: Integer): PProtoEnt; stdcall; TGetHostByName = function(name: PAnsiChar): PHostEnt; stdcall; TGetHostByAddr = function(addr: Pointer; len, Struc: Integer): PHostEnt; stdcall; TGetHostName = function(name: PAnsiChar; len: Integer): Integer; stdcall; TShutdown = function(s: TSocket; how: Integer): Integer; stdcall; TSetSockOpt = function(s: TSocket; level, optname: Integer; optval: PAnsiChar; optlen: Integer): Integer; stdcall; TGetSockOpt = function(s: TSocket; level, optname: Integer; optval: PAnsiChar; var optlen: Integer): Integer; stdcall; TSendTo = function(s: TSocket; const Buf; len, flags: Integer; addrto: PSockAddr; tolen: Integer): Integer; stdcall; TSend = function(s: TSocket; const Buf; len, flags: Integer): Integer; stdcall; TRecv = function(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; TRecvFrom = function(s: TSocket; var Buf; len, flags: Integer; from: PSockAddr; var fromlen: Integer): Integer; stdcall; Tntohs = function(netshort: u_short): u_short; stdcall; Tntohl = function(netlong: u_long): u_long; stdcall; TListen = function(s: TSocket; backlog: Integer): Integer; stdcall; TIoctlSocket = function(s: TSocket; cmd: DWORD; var arg: Integer): Integer; stdcall; TInet_ntoa = function(inaddr: TInAddr): PAnsiChar; stdcall; TInet_addr = function(cp: PAnsiChar): u_long; stdcall; Thtons = function(hostshort: u_short): u_short; stdcall; Thtonl = function(hostlong: u_long): u_long; stdcall; TGetSockName = function(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; stdcall; TGetPeerName = function(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; stdcall; TConnect = function(s: TSocket; name: PSockAddr; namelen: Integer): Integer; stdcall; TCloseSocket = function(s: TSocket): Integer; stdcall; TBind = function(s: TSocket; addr: PSockAddr; namelen: Integer): Integer; stdcall; TAccept = function(s: TSocket; addr: PSockAddr; var addrlen: Integer): TSocket; stdcall; TTSocket = function(af, Struc, Protocol: Integer): TSocket; stdcall; TSelect = function(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Longint; stdcall; TGetAddrInfo = function(NodeName: PAnsiChar; ServName: PAnsiChar; Hints: PAddrInfo; var Addrinfo: PAddrInfo): integer; stdcall; TFreeAddrInfo = procedure(ai: PAddrInfo); stdcall; TGetNameInfo = function( addr: PSockAddr; namelen: Integer; host: PAnsiChar; hostlen: DWORD; serv: PAnsiChar; servlen: DWORD; flags: integer): integer; stdcall; T__WSAFDIsSet = function (s: TSocket; var FDSet: TFDSet): Bool; stdcall; TWSAIoctl = function (s: TSocket; dwIoControlCode: DWORD; lpvInBuffer: Pointer; cbInBuffer: DWORD; lpvOutBuffer: Pointer; cbOutBuffer: DWORD; lpcbBytesReturned: PDWORD; lpOverlapped: Pointer; lpCompletionRoutine: pointer): u_int; stdcall; var WSAStartup: TWSAStartup = nil; WSACleanup: TWSACleanup = nil; WSAGetLastError: TWSAGetLastError = nil; GetServByName: TGetServByName = nil; GetServByPort: TGetServByPort = nil; GetProtoByName: TGetProtoByName = nil; GetProtoByNumber: TGetProtoByNumber = nil; GetHostByName: TGetHostByName = nil; GetHostByAddr: TGetHostByAddr = nil; ssGetHostName: TGetHostName = nil; Shutdown: TShutdown = nil; SetSockOpt: TSetSockOpt = nil; GetSockOpt: TGetSockOpt = nil; ssSendTo: TSendTo = nil; ssSend: TSend = nil; ssRecv: TRecv = nil; ssRecvFrom: TRecvFrom = nil; ntohs: Tntohs = nil; ntohl: Tntohl = nil; Listen: TListen = nil; IoctlSocket: TIoctlSocket = nil; Inet_ntoa: TInet_ntoa = nil; Inet_addr: TInet_addr = nil; htons: Thtons = nil; htonl: Thtonl = nil; ssGetSockName: TGetSockName = nil; ssGetPeerName: TGetPeerName = nil; ssConnect: TConnect = nil; CloseSocket: TCloseSocket = nil; ssBind: TBind = nil; ssAccept: TAccept = nil; Socket: TTSocket = nil; Select: TSelect = nil; GetAddrInfo: TGetAddrInfo = nil; FreeAddrInfo: TFreeAddrInfo = nil; GetNameInfo: TGetNameInfo = nil; __WSAFDIsSet: T__WSAFDIsSet = nil; WSAIoctl: TWSAIoctl = nil; var SynSockCS: SyncObjs.TCriticalSection; SockEnhancedApi: Boolean; SockWship6Api: Boolean; type TVarSin = packed record case integer of 0: (AddressFamily: u_short); 1: ( case sin_family: u_short of AF_INET: (sin_port: u_short; sin_addr: TInAddr; sin_zero: array[0..7] of byte); AF_INET6: (sin6_port: u_short; sin6_flowinfo: u_long; sin6_addr: TInAddr6; sin6_scope_id: u_long); ); end; function SizeOfVarSin(sin: TVarSin): integer; function Bind(s: TSocket; const addr: TVarSin): Integer; function Connect(s: TSocket; const name: TVarSin): Integer; function GetSockName(s: TSocket; var name: TVarSin): Integer; function GetPeerName(s: TSocket; var name: TVarSin): Integer; function GetHostName: AnsiString; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; function Accept(s: TSocket; var addr: TVarSin): TSocket; function IsNewApi(Family: integer): Boolean; function SetVarSin(var Sin: TVarSin; IP, Port: AnsiString; Family, SockProtocol, SockType: integer; PreferIP4: Boolean): integer; function GetSinIP(Sin: TVarSin): AnsiString; function GetSinPort(Sin: TVarSin): Integer; procedure ResolveNameToIP(Name: AnsiString; Family, SockProtocol, SockType: integer; const IPList: TStrings); function ResolveIPToName(IP: AnsiString; Family, SockProtocol, SockType: integer): AnsiString; function ResolvePort(Port: AnsiString; Family, SockProtocol, SockType: integer): Word; {==============================================================================} implementation var SynSockCount: Integer = 0; LibHandle: THandle = 0; Libwship6Handle: THandle = 0; function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr32[0] = 0) and (a^.u6_addr32[1] = 0) and (a^.u6_addr32[2] = 0) and (a^.u6_addr32[3] = 0)); end; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr32[0] = 0) and (a^.u6_addr32[1] = 0) and (a^.u6_addr32[2] = 0) and (a^.u6_addr8[12] = 0) and (a^.u6_addr8[13] = 0) and (a^.u6_addr8[14] = 0) and (a^.u6_addr8[15] = 1)); end; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr8[0] = $FE) and (a^.u6_addr8[1] = $80)); end; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr8[0] = $FE) and (a^.u6_addr8[1] = $C0)); end; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; begin Result := (a^.u6_addr8[0] = $FF); end; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6): boolean; begin Result := (CompareMem( a, b, sizeof(TInAddr6))); end; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); end; procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); a^.u6_addr8[15] := 1; end; {=============================================================================} procedure FD_CLR(Socket: TSocket; var FDSet: TFDSet); var I: Integer; begin I := 0; while I < FDSet.fd_count do begin if FDSet.fd_array[I] = Socket then begin while I < FDSet.fd_count - 1 do begin FDSet.fd_array[I] := FDSet.fd_array[I + 1]; Inc(I); end; Dec(FDSet.fd_count); Break; end; Inc(I); end; end; function FD_ISSET(Socket: TSocket; var FDSet: TFDSet): Boolean; begin Result := __WSAFDIsSet(Socket, FDSet); end; procedure FD_SET(Socket: TSocket; var FDSet: TFDSet); begin if FDSet.fd_count < FD_SETSIZE then begin FDSet.fd_array[FDSet.fd_count] := Socket; Inc(FDSet.fd_count); end; end; procedure FD_ZERO(var FDSet: TFDSet); begin FDSet.fd_count := 0; end; {=============================================================================} function SizeOfVarSin(sin: TVarSin): integer; begin case sin.sin_family of AF_INET: Result := SizeOf(TSockAddrIn); AF_INET6: Result := SizeOf(TSockAddrIn6); else Result := 0; end; end; {=============================================================================} function Bind(s: TSocket; const addr: TVarSin): Integer; begin Result := ssBind(s, @addr, SizeOfVarSin(addr)); end; function Connect(s: TSocket; const name: TVarSin): Integer; begin Result := ssConnect(s, @name, SizeOfVarSin(name)); end; function GetSockName(s: TSocket; var name: TVarSin): Integer; var len: integer; begin len := SizeOf(name); FillChar(name, len, 0); Result := ssGetSockName(s, @name, Len); end; function GetPeerName(s: TSocket; var name: TVarSin): Integer; var len: integer; begin len := SizeOf(name); FillChar(name, len, 0); Result := ssGetPeerName(s, @name, Len); end; function GetHostName: AnsiString; var s: AnsiString; begin Result := ''; setlength(s, 255); ssGetHostName(pAnsichar(s), Length(s) - 1); Result := PAnsichar(s); end; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; begin Result := ssSend(s, Buf^, len, flags); end; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; begin Result := ssRecv(s, Buf^, len, flags); end; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; begin Result := ssSendTo(s, Buf^, len, flags, @addrto, SizeOfVarSin(addrto)); end; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; var x: integer; begin x := SizeOf(from); Result := ssRecvFrom(s, Buf^, len, flags, @from, x); end; function Accept(s: TSocket; var addr: TVarSin): TSocket; var x: integer; begin x := SizeOf(addr); Result := ssAccept(s, @addr, x); end; {=============================================================================} function IsNewApi(Family: integer): Boolean; begin Result := SockEnhancedApi; if not Result then Result := (Family = AF_INET6) and SockWship6Api; end; function SetVarSin(var Sin: TVarSin; IP, Port: AnsiString; Family, SockProtocol, SockType: integer; PreferIP4: Boolean): integer; type pu_long = ^u_long; var ProtoEnt: PProtoEnt; ServEnt: PServEnt; HostEnt: PHostEnt; r: integer; Hints1, Hints2: TAddrInfo; Sin1, Sin2: TVarSin; TwoPass: boolean; function GetAddr(const IP, port: AnsiString; Hints: TAddrInfo; var Sin: TVarSin): integer; var Addr: PAddrInfo; begin Addr := nil; try FillChar(Sin, Sizeof(Sin), 0); if Hints.ai_socktype = SOCK_RAW then begin Hints.ai_socktype := 0; Hints.ai_protocol := 0; Result := synsock.GetAddrInfo(PAnsiChar(IP), nil, @Hints, Addr); end else begin if (IP = cAnyHost) or (IP = c6AnyHost) then begin Hints.ai_flags := AI_PASSIVE; Result := synsock.GetAddrInfo(nil, PAnsiChar(Port), @Hints, Addr); end else if (IP = cLocalhost) or (IP = c6Localhost) then begin Result := synsock.GetAddrInfo(nil, PAnsiChar(Port), @Hints, Addr); end else begin Result := synsock.GetAddrInfo(PAnsiChar(IP), PAnsiChar(Port), @Hints, Addr); end; end; if Result = 0 then if (Addr <> nil) then Move(Addr^.ai_addr^, Sin, Addr^.ai_addrlen); finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; begin Result := 0; FillChar(Sin, Sizeof(Sin), 0); if not IsNewApi(family) then begin SynSockCS.Enter; try Sin.sin_family := AF_INET; ProtoEnt := synsock.GetProtoByNumber(SockProtocol); ServEnt := nil; if (ProtoEnt <> nil) and (StrToIntDef(string(Port),-1) =-1) then ServEnt := synsock.GetServByName(PAnsiChar(Port), ProtoEnt^.p_name); if ServEnt = nil then Sin.sin_port := synsock.htons(StrToIntDef(string(Port), 0)) else Sin.sin_port := ServEnt^.s_port; if IP = cBroadcast then Sin.sin_addr.s_addr := u_long(INADDR_BROADCAST) else begin Sin.sin_addr.s_addr := synsock.inet_addr(PAnsiChar(IP)); if Sin.sin_addr.s_addr = u_long(INADDR_NONE) then begin HostEnt := synsock.GetHostByName(PAnsiChar(IP)); Result := synsock.WSAGetLastError; if HostEnt <> nil then Sin.sin_addr.S_addr := u_long(Pu_long(HostEnt^.h_addr_list^)^); end; end; finally SynSockCS.Leave; end; end else begin FillChar(Hints1, Sizeof(Hints1), 0); FillChar(Hints2, Sizeof(Hints2), 0); TwoPass := False; if Family = AF_UNSPEC then begin if PreferIP4 then begin Hints1.ai_family := AF_INET; Hints2.ai_family := AF_INET6; TwoPass := True; end else begin Hints2.ai_family := AF_INET; Hints1.ai_family := AF_INET6; TwoPass := True; end; end else Hints1.ai_family := Family; Hints1.ai_socktype := SockType; Hints1.ai_protocol := SockProtocol; Hints2.ai_socktype := Hints1.ai_socktype; Hints2.ai_protocol := Hints1.ai_protocol; r := GetAddr(IP, Port, Hints1, Sin1); Result := r; sin := sin1; if r <> 0 then if TwoPass then begin r := GetAddr(IP, Port, Hints2, Sin2); Result := r; if r = 0 then sin := sin2; end; end; end; function GetSinIP(Sin: TVarSin): AnsiString; var p: PAnsiChar; host, serv: AnsiString; hostlen, servlen: integer; r: integer; begin Result := ''; if not IsNewApi(Sin.AddressFamily) then begin p := synsock.inet_ntoa(Sin.sin_addr); if p <> nil then Result := p; end else begin hostlen := NI_MAXHOST; servlen := NI_MAXSERV; setlength(host, hostlen); setlength(serv, servlen); r := getnameinfo(@sin, SizeOfVarSin(sin), PAnsiChar(host), hostlen, PAnsiChar(serv), servlen, NI_NUMERICHOST + NI_NUMERICSERV); if r = 0 then Result := PAnsiChar(host); end; end; function GetSinPort(Sin: TVarSin): Integer; begin if (Sin.sin_family = AF_INET6) then Result := synsock.ntohs(Sin.sin6_port) else Result := synsock.ntohs(Sin.sin_port); end; procedure ResolveNameToIP(Name: AnsiString; Family, SockProtocol, SockType: integer; const IPList: TStrings); type TaPInAddr = array[0..250] of PInAddr; PaPInAddr = ^TaPInAddr; var Hints: TAddrInfo; Addr: PAddrInfo; AddrNext: PAddrInfo; r: integer; host, serv: AnsiString; hostlen, servlen: integer; RemoteHost: PHostEnt; IP: u_long; PAdrPtr: PaPInAddr; i: Integer; s: String; InAddr: TInAddr; begin IPList.Clear; if not IsNewApi(Family) then begin IP := synsock.inet_addr(PAnsiChar(Name)); if IP = u_long(INADDR_NONE) then begin SynSockCS.Enter; try RemoteHost := synsock.GetHostByName(PAnsiChar(Name)); if RemoteHost <> nil then begin PAdrPtr := PAPInAddr(RemoteHost^.h_addr_list); i := 0; while PAdrPtr^[i] <> nil do begin InAddr := PAdrPtr^[i]^; s := Format('%d.%d.%d.%d', [InAddr.S_bytes[0], InAddr.S_bytes[1], InAddr.S_bytes[2], InAddr.S_bytes[3]]); IPList.Add(s); Inc(i); end; end; finally SynSockCS.Leave; end; end else IPList.Add(string(Name)); end else begin Addr := nil; try FillChar(Hints, Sizeof(Hints), 0); Hints.ai_family := AF_UNSPEC; Hints.ai_socktype := SockType; Hints.ai_protocol := SockProtocol; Hints.ai_flags := 0; r := synsock.GetAddrInfo(PAnsiChar(Name), nil, @Hints, Addr); if r = 0 then begin AddrNext := Addr; while not(AddrNext = nil) do begin if not(((Family = AF_INET6) and (AddrNext^.ai_family = AF_INET)) or ((Family = AF_INET) and (AddrNext^.ai_family = AF_INET6))) then begin hostlen := NI_MAXHOST; servlen := NI_MAXSERV; setlength(host, hostlen); setlength(serv, servlen); r := getnameinfo(AddrNext^.ai_addr, AddrNext^.ai_addrlen, PAnsiChar(host), hostlen, PAnsiChar(serv), servlen, NI_NUMERICHOST + NI_NUMERICSERV); if r = 0 then begin host := PAnsiChar(host); IPList.Add(string(host)); end; end; AddrNext := AddrNext^.ai_next; end; end; finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; if IPList.Count = 0 then IPList.Add(cAnyHost); end; function ResolvePort(Port: AnsiString; Family, SockProtocol, SockType: integer): Word; var ProtoEnt: PProtoEnt; ServEnt: PServEnt; Hints: TAddrInfo; Addr: PAddrInfo; r: integer; begin Result := 0; if not IsNewApi(Family) then begin SynSockCS.Enter; try ProtoEnt := synsock.GetProtoByNumber(SockProtocol); ServEnt := nil; if ProtoEnt <> nil then ServEnt := synsock.GetServByName(PAnsiChar(Port), ProtoEnt^.p_name); if ServEnt = nil then Result := StrToIntDef(string(Port), 0) else Result := synsock.htons(ServEnt^.s_port); finally SynSockCS.Leave; end; end else begin Addr := nil; try FillChar(Hints, Sizeof(Hints), 0); Hints.ai_family := AF_UNSPEC; Hints.ai_socktype := SockType; Hints.ai_protocol := Sockprotocol; Hints.ai_flags := AI_PASSIVE; r := synsock.GetAddrInfo(nil, PAnsiChar(Port), @Hints, Addr); if (r = 0) and Assigned(Addr) then begin if Addr^.ai_family = AF_INET then Result := synsock.htons(Addr^.ai_addr^.sin_port); if Addr^.ai_family = AF_INET6 then Result := synsock.htons(PSockAddrIn6(Addr^.ai_addr)^.sin6_port); end; finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; end; function ResolveIPToName(IP: AnsiString; Family, SockProtocol, SockType: integer): AnsiString; var Hints: TAddrInfo; Addr: PAddrInfo; r: integer; host, serv: AnsiString; hostlen, servlen: integer; RemoteHost: PHostEnt; IPn: u_long; begin Result := IP; if not IsNewApi(Family) then begin IPn := synsock.inet_addr(PAnsiChar(IP)); if IPn <> u_long(INADDR_NONE) then begin SynSockCS.Enter; try RemoteHost := GetHostByAddr(@IPn, SizeOf(IPn), AF_INET); if RemoteHost <> nil then Result := RemoteHost^.h_name; finally SynSockCS.Leave; end; end; end else begin Addr := nil; try FillChar(Hints, Sizeof(Hints), 0); Hints.ai_family := AF_UNSPEC; Hints.ai_socktype := SockType; Hints.ai_protocol := SockProtocol; Hints.ai_flags := 0; r := synsock.GetAddrInfo(PAnsiChar(IP), nil, @Hints, Addr); if (r = 0) and Assigned(Addr)then begin hostlen := NI_MAXHOST; servlen := NI_MAXSERV; setlength(host, hostlen); setlength(serv, servlen); r := getnameinfo(Addr^.ai_addr, Addr^.ai_addrlen, PAnsiChar(host), hostlen, PAnsiChar(serv), servlen, NI_NUMERICSERV); if r = 0 then Result := PAnsiChar(host); end; finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; end; {=============================================================================} function InitSocketInterface(stack: String): Boolean; begin Result := False; SockEnhancedApi := False; if stack = '' then stack := DLLStackName; SynSockCS.Enter; try if SynSockCount = 0 then begin SockEnhancedApi := False; SockWship6Api := False; LibHandle := LoadLibrary(PChar(Stack)); if LibHandle <> 0 then begin WSAIoctl := GetProcAddress(LibHandle, PAnsiChar(AnsiString('WSAIoctl'))); __WSAFDIsSet := GetProcAddress(LibHandle, PAnsiChar(AnsiString('__WSAFDIsSet'))); CloseSocket := GetProcAddress(LibHandle, PAnsiChar(AnsiString('closesocket'))); IoctlSocket := GetProcAddress(LibHandle, PAnsiChar(AnsiString('ioctlsocket'))); WSAGetLastError := GetProcAddress(LibHandle, PAnsiChar(AnsiString('WSAGetLastError'))); WSAStartup := GetProcAddress(LibHandle, PAnsiChar(AnsiString('WSAStartup'))); WSACleanup := GetProcAddress(LibHandle, PAnsiChar(AnsiString('WSACleanup'))); ssAccept := GetProcAddress(LibHandle, PAnsiChar(AnsiString('accept'))); ssBind := GetProcAddress(LibHandle, PAnsiChar(AnsiString('bind'))); ssConnect := GetProcAddress(LibHandle, PAnsiChar(AnsiString('connect'))); ssGetPeerName := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getpeername'))); ssGetSockName := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getsockname'))); GetSockOpt := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getsockopt'))); Htonl := GetProcAddress(LibHandle, PAnsiChar(AnsiString('htonl'))); Htons := GetProcAddress(LibHandle, PAnsiChar(AnsiString('htons'))); Inet_Addr := GetProcAddress(LibHandle, PAnsiChar(AnsiString('inet_addr'))); Inet_Ntoa := GetProcAddress(LibHandle, PAnsiChar(AnsiString('inet_ntoa'))); Listen := GetProcAddress(LibHandle, PAnsiChar(AnsiString('listen'))); Ntohl := GetProcAddress(LibHandle, PAnsiChar(AnsiString('ntohl'))); Ntohs := GetProcAddress(LibHandle, PAnsiChar(AnsiString('ntohs'))); ssRecv := GetProcAddress(LibHandle, PAnsiChar(AnsiString('recv'))); ssRecvFrom := GetProcAddress(LibHandle, PAnsiChar(AnsiString('recvfrom'))); Select := GetProcAddress(LibHandle, PAnsiChar(AnsiString('select'))); ssSend := GetProcAddress(LibHandle, PAnsiChar(AnsiString('send'))); ssSendTo := GetProcAddress(LibHandle, PAnsiChar(AnsiString('sendto'))); SetSockOpt := GetProcAddress(LibHandle, PAnsiChar(AnsiString('setsockopt'))); ShutDown := GetProcAddress(LibHandle, PAnsiChar(AnsiString('shutdown'))); Socket := GetProcAddress(LibHandle, PAnsiChar(AnsiString('socket'))); GetHostByAddr := GetProcAddress(LibHandle, PAnsiChar(AnsiString('gethostbyaddr'))); GetHostByName := GetProcAddress(LibHandle, PAnsiChar(AnsiString('gethostbyname'))); GetProtoByName := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getprotobyname'))); GetProtoByNumber := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getprotobynumber'))); GetServByName := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getservbyname'))); GetServByPort := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getservbyport'))); ssGetHostName := GetProcAddress(LibHandle, PAnsiChar(AnsiString('gethostname'))); {$IFNDEF FORCEOLDAPI} GetAddrInfo := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getaddrinfo'))); FreeAddrInfo := GetProcAddress(LibHandle, PAnsiChar(AnsiString('freeaddrinfo'))); GetNameInfo := GetProcAddress(LibHandle, PAnsiChar(AnsiString('getnameinfo'))); SockEnhancedApi := Assigned(GetAddrInfo) and Assigned(FreeAddrInfo) and Assigned(GetNameInfo); if not SockEnhancedApi then begin LibWship6Handle := LoadLibrary(PChar(DLLWship6)); if LibWship6Handle <> 0 then begin GetAddrInfo := GetProcAddress(LibWship6Handle, PAnsiChar(AnsiString('getaddrinfo'))); FreeAddrInfo := GetProcAddress(LibWship6Handle, PAnsiChar(AnsiString('freeaddrinfo'))); GetNameInfo := GetProcAddress(LibWship6Handle, PAnsiChar(AnsiString('getnameinfo'))); SockWship6Api := Assigned(GetAddrInfo) and Assigned(FreeAddrInfo) and Assigned(GetNameInfo); end; end; {$ENDIF} Result := True; end; end else Result := True; if Result then Inc(SynSockCount); finally SynSockCS.Leave; end; end; function DestroySocketInterface: Boolean; begin SynSockCS.Enter; try Dec(SynSockCount); if SynSockCount < 0 then SynSockCount := 0; if SynSockCount = 0 then begin if LibHandle <> 0 then begin FreeLibrary(libHandle); LibHandle := 0; end; if LibWship6Handle <> 0 then begin FreeLibrary(LibWship6Handle); LibWship6Handle := 0; end; end; finally SynSockCS.Leave; end; Result := True; end; initialization begin SynSockCS := SyncObjs.TCriticalSection.Create; SET_IN6_IF_ADDR_ANY (@in6addr_any); SET_LOOPBACK_ADDR6 (@in6addr_loopback); end; finalization begin SynSockCS.Free; end;TransGUI/synapse/source/lib/pingsend.pas0000644000000000000000000005104511366572451017273 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 004.000.002 | |==============================================================================| | Content: PING sender | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(ICMP PING implementation.) Allows create PING and TRACEROUTE. Or you can diagnose your network. This unit using IpHlpApi (on WinXP or higher) if available. Otherwise it trying to use RAW sockets. Warning: For use of RAW sockets you must have some special rights on some systems. So, it working allways when you have administator/root rights. Otherwise you can have problems! Note: This unit is NOT portable to .NET! Use native .NET classes for Ping instead. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$R-} {$H+} {$IFDEF CIL} Sorry, this unit is not for .NET! {$ENDIF} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit pingsend; interface uses SysUtils, synsock, blcksock, synautil, synafpc, synaip {$IFDEF MSWINDOWS} , windows {$ENDIF} ; const ICMP_ECHO = 8; ICMP_ECHOREPLY = 0; ICMP_UNREACH = 3; ICMP_TIME_EXCEEDED = 11; //rfc-2292 ICMP6_ECHO = 128; ICMP6_ECHOREPLY = 129; ICMP6_UNREACH = 1; ICMP6_TIME_EXCEEDED = 3; type {:List of possible ICMP reply packet types.} TICMPError = ( IE_NoError, IE_Other, IE_TTLExceed, IE_UnreachOther, IE_UnreachRoute, IE_UnreachAdmin, IE_UnreachAddr, IE_UnreachPort ); {:@abstract(Implementation of ICMP PING and ICMPv6 PING.)} TPINGSend = class(TSynaClient) private FSock: TICMPBlockSocket; FBuffer: Ansistring; FSeq: Integer; FId: Integer; FPacketSize: Integer; FPingTime: Integer; FIcmpEcho: Byte; FIcmpEchoReply: Byte; FIcmpUnreach: Byte; FReplyFrom: string; FReplyType: byte; FReplyCode: byte; FReplyError: TICMPError; FReplyErrorDesc: string; FTTL: Byte; Fsin: TVarSin; function Checksum(Value: AnsiString): Word; function Checksum6(Value: AnsiString): Word; function ReadPacket: Boolean; procedure TranslateError; procedure TranslateErrorIpHlp(value: integer); function InternalPing(const Host: string): Boolean; function InternalPingIpHlp(const Host: string): Boolean; function IsHostIP6(const Host: string): Boolean; procedure GenErrorDesc; public {:Send ICMP ping to host and count @link(pingtime). If ping OK, result is @true.} function Ping(const Host: string): Boolean; constructor Create; destructor Destroy; override; published {:Size of PING packet. Default size is 32 bytes.} property PacketSize: Integer read FPacketSize Write FPacketSize; {:Time between request and reply.} property PingTime: Integer read FPingTime; {:From this address is sended reply for your PING request. It maybe not your requested destination, when some error occured!} property ReplyFrom: string read FReplyFrom; {:ICMP type of PING reply. Each protocol using another values! For IPv4 and IPv6 are used different values!} property ReplyType: byte read FReplyType; {:ICMP code of PING reply. Each protocol using another values! For IPv4 and IPv6 are used different values! For protocol independent value look to @link(ReplyError)} property ReplyCode: byte read FReplyCode; {:Return type of returned ICMP message. This value is independent on used protocol!} property ReplyError: TICMPError read FReplyError; {:Return human readable description of returned packet type.} property ReplyErrorDesc: string read FReplyErrorDesc; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TICMPBlockSocket read FSock; {:TTL value for ICMP query} property TTL: byte read FTTL write FTTL; end; {:A very useful function and example of its use would be found in the TPINGSend object. Use it to ping to any host. If successful, returns the ping time in milliseconds. Returns -1 if an error occurred.} function PingHost(const Host: string): Integer; {:A very useful function and example of its use would be found in the TPINGSend object. Use it to TraceRoute to any host.} function TraceRouteHost(const Host: string): string; implementation type {:Record for ICMP ECHO packet header.} TIcmpEchoHeader = packed record i_type: Byte; i_code: Byte; i_checkSum: Word; i_Id: Word; i_seq: Word; TimeStamp: integer; end; {:record used internally by TPingSend for compute checksum of ICMPv6 packet pseudoheader.} TICMP6Packet = packed record in_source: TInAddr6; in_dest: TInAddr6; Length: integer; free0: Byte; free1: Byte; free2: Byte; proto: Byte; end; {$IFDEF MSWINDOWS} const DLLIcmpName = 'iphlpapi.dll'; type TIP_OPTION_INFORMATION = record TTL: Byte; TOS: Byte; Flags: Byte; OptionsSize: Byte; OptionsData: PAnsiChar; end; PIP_OPTION_INFORMATION = ^TIP_OPTION_INFORMATION; TICMP_ECHO_REPLY = record Address: TInAddr; Status: integer; RoundTripTime: integer; DataSize: Word; Reserved: Word; Data: pointer; Options: TIP_OPTION_INFORMATION; end; PICMP_ECHO_REPLY = ^TICMP_ECHO_REPLY; TICMPV6_ECHO_REPLY = record Address: TSockAddrIn6; Status: integer; RoundTripTime: integer; end; PICMPV6_ECHO_REPLY = ^TICMPV6_ECHO_REPLY; TIcmpCreateFile = function: integer; stdcall; TIcmpCloseHandle = function(handle: integer): boolean; stdcall; TIcmpSendEcho2 = function(handle: integer; Event: pointer; ApcRoutine: pointer; ApcContext: pointer; DestinationAddress: TInAddr; RequestData: pointer; RequestSize: integer; RequestOptions: PIP_OPTION_INFORMATION; ReplyBuffer: pointer; ReplySize: integer; Timeout: Integer): integer; stdcall; TIcmp6CreateFile = function: integer; stdcall; TIcmp6SendEcho2 = function(handle: integer; Event: pointer; ApcRoutine: pointer; ApcContext: pointer; SourceAddress: PSockAddrIn6; DestinationAddress: PSockAddrIn6; RequestData: pointer; RequestSize: integer; RequestOptions: PIP_OPTION_INFORMATION; ReplyBuffer: pointer; ReplySize: integer; Timeout: Integer): integer; stdcall; var IcmpDllHandle: TLibHandle = 0; IcmpHelper4: boolean = false; IcmpHelper6: boolean = false; IcmpCreateFile: TIcmpCreateFile = nil; IcmpCloseHandle: TIcmpCloseHandle = nil; IcmpSendEcho2: TIcmpSendEcho2 = nil; Icmp6CreateFile: TIcmp6CreateFile = nil; Icmp6SendEcho2: TIcmp6SendEcho2 = nil; {$ENDIF} {==============================================================================} constructor TPINGSend.Create; begin inherited Create; FSock := TICMPBlockSocket.Create; FSock.Owner := self; FTimeout := 5000; FPacketSize := 32; FSeq := 0; Randomize; FTTL := 128; end; destructor TPINGSend.Destroy; begin FSock.Free; inherited Destroy; end; function TPINGSend.ReadPacket: Boolean; begin FBuffer := FSock.RecvPacket(Ftimeout); Result := FSock.LastError = 0; end; procedure TPINGSend.GenErrorDesc; begin case FReplyError of IE_NoError: FReplyErrorDesc := ''; IE_Other: FReplyErrorDesc := 'Unknown error'; IE_TTLExceed: FReplyErrorDesc := 'TTL Exceeded'; IE_UnreachOther: FReplyErrorDesc := 'Unknown unreachable'; IE_UnreachRoute: FReplyErrorDesc := 'No route to destination'; IE_UnreachAdmin: FReplyErrorDesc := 'Administratively prohibited'; IE_UnreachAddr: FReplyErrorDesc := 'Address unreachable'; IE_UnreachPort: FReplyErrorDesc := 'Port unreachable'; end; end; function TPINGSend.IsHostIP6(const Host: string): Boolean; var f: integer; begin f := AF_UNSPEC; if IsIp(Host) then f := AF_INET else if IsIp6(Host) then f := AF_INET6; synsock.SetVarSin(Fsin, host, '0', f, IPPROTO_UDP, SOCK_DGRAM, Fsock.PreferIP4); result := Fsin.sin_family = AF_INET6; end; function TPINGSend.Ping(const Host: string): Boolean; var b: boolean; begin FPingTime := -1; FReplyFrom := ''; FReplyType := 0; FReplyCode := 0; FReplyError := IE_Other; GenErrorDesc; FBuffer := StringOfChar(#55, SizeOf(TICMPEchoHeader) + FPacketSize); {$IFDEF MSWINDOWS} b := IsHostIP6(host); if not(b) and IcmpHelper4 then result := InternalPingIpHlp(host) else if b and IcmpHelper6 then result := InternalPingIpHlp(host) else result := InternalPing(host); {$ELSE} result := InternalPing(host); {$ENDIF} end; function TPINGSend.InternalPing(const Host: string): Boolean; var IPHeadPtr: ^TIPHeader; IpHdrLen: Integer; IcmpEchoHeaderPtr: ^TICMPEchoHeader; t: Boolean; x: cardinal; IcmpReqHead: string; begin Result := False; FSock.TTL := FTTL; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(Host, '0'); if FSock.LastError <> 0 then Exit; FSock.SizeRecvBuffer := 60 * 1024; if FSock.IP6used then begin FIcmpEcho := ICMP6_ECHO; FIcmpEchoReply := ICMP6_ECHOREPLY; FIcmpUnreach := ICMP6_UNREACH; end else begin FIcmpEcho := ICMP_ECHO; FIcmpEchoReply := ICMP_ECHOREPLY; FIcmpUnreach := ICMP_UNREACH; end; IcmpEchoHeaderPtr := Pointer(FBuffer); with IcmpEchoHeaderPtr^ do begin i_type := FIcmpEcho; i_code := 0; i_CheckSum := 0; FId := System.Random(32767); i_Id := FId; TimeStamp := GetTick; Inc(FSeq); i_Seq := FSeq; if fSock.IP6used then i_CheckSum := CheckSum6(FBuffer) else i_CheckSum := CheckSum(FBuffer); end; FSock.SendString(FBuffer); // remember first 8 bytes of ICMP packet IcmpReqHead := Copy(FBuffer, 1, 8); x := GetTick; repeat t := ReadPacket; if not t then break; if fSock.IP6used then begin {$IFNDEF MSWINDOWS} IcmpEchoHeaderPtr := Pointer(FBuffer); {$ELSE} //WinXP SP1 with networking update doing this think by another way ;-O // FBuffer := StringOfChar(#0, 4) + FBuffer; IcmpEchoHeaderPtr := Pointer(FBuffer); // IcmpEchoHeaderPtr^.i_type := FIcmpEchoReply; {$ENDIF} end else begin IPHeadPtr := Pointer(FBuffer); IpHdrLen := (IPHeadPtr^.VerLen and $0F) * 4; IcmpEchoHeaderPtr := @FBuffer[IpHdrLen + 1]; end; //check for timeout if TickDelta(x, GetTick) > FTimeout then begin t := false; Break; end; //it discard sometimes possible 'echoes' of previosly sended packet //or other unwanted ICMP packets... until (IcmpEchoHeaderPtr^.i_type <> FIcmpEcho) and ((IcmpEchoHeaderPtr^.i_id = FId) or (Pos(IcmpReqHead, FBuffer) > 0)); if t then begin FPingTime := TickDelta(x, GetTick); FReplyFrom := FSock.GetRemoteSinIP; FReplyType := IcmpEchoHeaderPtr^.i_type; FReplyCode := IcmpEchoHeaderPtr^.i_code; TranslateError; Result := True; end; end; function TPINGSend.Checksum(Value: AnsiString): Word; var CkSum: integer; Num, Remain: Integer; n, i: Integer; begin Num := Length(Value) div 2; Remain := Length(Value) mod 2; CkSum := 0; i := 1; for n := 0 to Num - 1 do begin CkSum := CkSum + Synsock.HtoNs(DecodeInt(Value, i)); inc(i, 2); end; if Remain <> 0 then CkSum := CkSum + Ord(Value[Length(Value)]); CkSum := (CkSum shr 16) + (CkSum and $FFFF); CkSum := CkSum + (CkSum shr 16); Result := Word(not CkSum); end; function TPINGSend.Checksum6(Value: AnsiString): Word; const IOC_OUT = $40000000; IOC_IN = $80000000; IOC_INOUT = (IOC_IN or IOC_OUT); IOC_WS2 = $08000000; SIO_ROUTING_INTERFACE_QUERY = 20 or IOC_WS2 or IOC_INOUT; var ICMP6Ptr: ^TICMP6Packet; s: AnsiString; b: integer; ip6: TSockAddrIn6; x: integer; begin Result := 0; {$IFDEF MSWINDOWS} s := StringOfChar(#0, SizeOf(TICMP6Packet)) + Value; ICMP6Ptr := Pointer(s); x := synsock.WSAIoctl(FSock.Socket, SIO_ROUTING_INTERFACE_QUERY, @FSock.RemoteSin, SizeOf(FSock.RemoteSin), @ip6, SizeOf(ip6), @b, nil, nil); if x <> -1 then ICMP6Ptr^.in_dest := ip6.sin6_addr else ICMP6Ptr^.in_dest := FSock.LocalSin.sin6_addr; ICMP6Ptr^.in_source := FSock.RemoteSin.sin6_addr; ICMP6Ptr^.Length := synsock.htonl(Length(Value)); ICMP6Ptr^.proto := IPPROTO_ICMPV6; Result := Checksum(s); {$ENDIF} end; procedure TPINGSend.TranslateError; begin if fSock.IP6used then begin case FReplyType of ICMP6_ECHOREPLY: FReplyError := IE_NoError; ICMP6_TIME_EXCEEDED: FReplyError := IE_TTLExceed; ICMP6_UNREACH: case FReplyCode of 0: FReplyError := IE_UnreachRoute; 3: FReplyError := IE_UnreachAddr; 4: FReplyError := IE_UnreachPort; 1: FReplyError := IE_UnreachAdmin; else FReplyError := IE_UnreachOther; end; else FReplyError := IE_Other; end; end else begin case FReplyType of ICMP_ECHOREPLY: FReplyError := IE_NoError; ICMP_TIME_EXCEEDED: FReplyError := IE_TTLExceed; ICMP_UNREACH: case FReplyCode of 0: FReplyError := IE_UnreachRoute; 1: FReplyError := IE_UnreachAddr; 3: FReplyError := IE_UnreachPort; 13: FReplyError := IE_UnreachAdmin; else FReplyError := IE_UnreachOther; end; else FReplyError := IE_Other; end; end; GenErrorDesc; end; procedure TPINGSend.TranslateErrorIpHlp(value: integer); begin case value of 11000, 0: FReplyError := IE_NoError; 11013: FReplyError := IE_TTLExceed; 11002: FReplyError := IE_UnreachRoute; 11003: FReplyError := IE_UnreachAddr; 11005: FReplyError := IE_UnreachPort; 11004: FReplyError := IE_UnreachAdmin; else FReplyError := IE_Other; end; GenErrorDesc; end; function TPINGSend.InternalPingIpHlp(const Host: string): Boolean; {$IFDEF MSWINDOWS} var PingIp6: boolean; PingHandle: integer; r: integer; ipo: TIP_OPTION_INFORMATION; RBuff: Ansistring; ip4reply: PICMP_ECHO_REPLY; ip6reply: PICMPV6_ECHO_REPLY; ip6: TSockAddrIn6; begin Result := False; PingIp6 := Fsin.sin_family = AF_INET6; if pingIp6 then PingHandle := Icmp6CreateFile else PingHandle := IcmpCreateFile; if PingHandle <> -1 then begin try ipo.TTL := FTTL; ipo.TOS := 0; ipo.Flags := 0; ipo.OptionsSize := 0; ipo.OptionsData := nil; setlength(RBuff, 4096); if pingIp6 then begin FillChar(ip6, sizeof(ip6), 0); r := Icmp6SendEcho2(PingHandle, nil, nil, nil, @ip6, @Fsin, PAnsichar(FBuffer), length(FBuffer), @ipo, pAnsichar(RBuff), length(RBuff), FTimeout); if r > 0 then begin RBuff := #0 + #0 + RBuff; ip6reply := PICMPV6_ECHO_REPLY(pointer(RBuff)); FPingTime := ip6reply^.RoundTripTime; ip6reply^.Address.sin6_family := AF_INET6; FReplyFrom := GetSinIp(TVarSin(ip6reply^.Address)); TranslateErrorIpHlp(ip6reply^.Status); Result := True; end; end else begin r := IcmpSendEcho2(PingHandle, nil, nil, nil, Fsin.sin_addr, PAnsichar(FBuffer), length(FBuffer), @ipo, pAnsichar(RBuff), length(RBuff), FTimeout); if r > 0 then begin ip4reply := PICMP_ECHO_REPLY(pointer(RBuff)); FPingTime := ip4reply^.RoundTripTime; FReplyFrom := IpToStr(swapbytes(ip4reply^.Address.S_addr)); TranslateErrorIpHlp(ip4reply^.Status); Result := True; end; end finally IcmpCloseHandle(PingHandle); end; end; end; {$ELSE} begin result := false; end; {$ENDIF} {==============================================================================} function PingHost(const Host: string): Integer; begin with TPINGSend.Create do try Result := -1; if Ping(Host) then if ReplyError = IE_NoError then Result := PingTime; finally Free; end; end; function TraceRouteHost(const Host: string): string; var Ping: TPingSend; ttl : byte; begin Result := ''; Ping := TPINGSend.Create; try ttl := 1; repeat ping.TTL := ttl; inc(ttl); if ttl > 30 then Break; if not ping.Ping(Host) then begin Result := Result + cAnyHost+ ' Timeout' + CRLF; continue; end; if (ping.ReplyError <> IE_NoError) and (ping.ReplyError <> IE_TTLExceed) then begin Result := Result + Ping.ReplyFrom + ' ' + Ping.ReplyErrorDesc + CRLF; break; end; Result := Result + Ping.ReplyFrom + ' ' + IntToStr(Ping.PingTime) + CRLF; until ping.ReplyError = IE_NoError; finally Ping.Free; end; end; {$IFDEF MSWINDOWS} initialization begin IcmpHelper4 := false; IcmpHelper6 := false; IcmpDllHandle := LoadLibrary(DLLIcmpName); if IcmpDllHandle <> 0 then begin IcmpCreateFile := GetProcAddress(IcmpDLLHandle, 'IcmpCreateFile'); IcmpCloseHandle := GetProcAddress(IcmpDLLHandle, 'IcmpCloseHandle'); IcmpSendEcho2 := GetProcAddress(IcmpDLLHandle, 'IcmpSendEcho2'); Icmp6CreateFile := GetProcAddress(IcmpDLLHandle, 'Icmp6CreateFile'); Icmp6SendEcho2 := GetProcAddress(IcmpDLLHandle, 'Icmp6SendEcho2'); IcmpHelper4 := assigned(IcmpCreateFile) and assigned(IcmpCloseHandle) and assigned(IcmpSendEcho2); IcmpHelper6 := assigned(Icmp6CreateFile) and assigned(Icmp6SendEcho2); end; end; finalization begin FreeLibrary(IcmpDllHandle); end; {$ENDIF} end. TransGUI/synapse/source/lib/ftptsend.pas0000644000000000000000000002751311366572451017316 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.001 | |==============================================================================| | Content: Trivial FTP (TFTP) client and server | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2003-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {: @abstract(TFTP client and server protocol) Used RFC: RFC-1350 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit ftptsend; interface uses SysUtils, Classes, blcksock, synautil; const cTFTPProtocol = '69'; cTFTP_RRQ = word(1); cTFTP_WRQ = word(2); cTFTP_DTA = word(3); cTFTP_ACK = word(4); cTFTP_ERR = word(5); type {:@abstract(Implementation of TFTP client and server) Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TTFTPSend = class(TSynaClient) private FSock: TUDPBlockSocket; FErrorCode: integer; FErrorString: string; FData: TMemoryStream; FRequestIP: string; FRequestPort: string; function SendPacket(Cmd: word; Serial: word; const Value: string): Boolean; function RecvPacket(Serial: word; var Value: string): Boolean; public constructor Create; destructor Destroy; override; {:Upload @link(data) as file to TFTP server.} function SendFile(const Filename: string): Boolean; {:Download file from TFTP server to @link(data).} function RecvFile(const Filename: string): Boolean; {:Acts as TFTP server and wait for client request. When some request incoming within Timeout, result is @true and parametres is filled with information from request. You must handle this request, validate it, and call @link(ReplyError), @link(ReplyRecv) or @link(ReplySend) for send reply to TFTP Client.} function WaitForRequest(var Req: word; var filename: string): Boolean; {:send error to TFTP client, when you acts as TFTP server.} procedure ReplyError(Error: word; Description: string); {:Accept uploaded file from TFTP client to @link(data), when you acts as TFTP server.} function ReplyRecv: Boolean; {:Accept download request file from TFTP client and send content of @link(data), when you acts as TFTP server.} function ReplySend: Boolean; published {:Code of TFTP error.} property ErrorCode: integer read FErrorCode; {:Human readable decription of TFTP error. (if is sended by remote side)} property ErrorString: string read FErrorString; {:MemoryStream with datas for sending or receiving} property Data: TMemoryStream read FData; {:Address of TFTP remote side.} property RequestIP: string read FRequestIP write FRequestIP; {:Port of TFTP remote side.} property RequestPort: string read FRequestPort write FRequestPort; end; implementation constructor TTFTPSend.Create; begin inherited Create; FSock := TUDPBlockSocket.Create; FSock.Owner := self; FTargetPort := cTFTPProtocol; FData := TMemoryStream.Create; FErrorCode := 0; FErrorString := ''; end; destructor TTFTPSend.Destroy; begin FSock.Free; FData.Free; inherited Destroy; end; function TTFTPSend.SendPacket(Cmd: word; Serial: word; const Value: string): Boolean; var s, sh: string; begin FErrorCode := 0; FErrorString := ''; Result := false; if Cmd <> 2 then s := CodeInt(Cmd) + CodeInt(Serial) + Value else s := CodeInt(Cmd) + Value; FSock.SendString(s); s := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then if length(s) >= 4 then begin sh := CodeInt(4) + CodeInt(Serial); if Pos(sh, s) = 1 then Result := True else if s[1] = #5 then begin FErrorCode := DecodeInt(s, 3); Delete(s, 1, 4); FErrorString := SeparateLeft(s, #0); end; end; end; function TTFTPSend.RecvPacket(Serial: word; var Value: string): Boolean; var s: string; ser: word; begin FErrorCode := 0; FErrorString := ''; Result := False; Value := ''; s := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then if length(s) >= 4 then if DecodeInt(s, 1) = 3 then begin ser := DecodeInt(s, 3); if ser = Serial then begin Delete(s, 1, 4); Value := s; S := CodeInt(4) + CodeInt(ser); FSock.SendString(s); Result := FSock.LastError = 0; end else begin S := CodeInt(5) + CodeInt(5) + 'Unexcepted serial#' + #0; FSock.SendString(s); end; end; if DecodeInt(s, 1) = 5 then begin FErrorCode := DecodeInt(s, 3); Delete(s, 1, 4); FErrorString := SeparateLeft(s, #0); end; end; function TTFTPSend.SendFile(const Filename: string): Boolean; var s: string; ser: word; n, n1, n2: integer; begin Result := False; FErrorCode := 0; FErrorString := ''; FSock.CloseSocket; FSock.Connect(FTargetHost, FTargetPort); try if FSock.LastError = 0 then begin s := Filename + #0 + 'octet' + #0; if not Sendpacket(2, 0, s) then Exit; ser := 1; FData.Position := 0; n1 := FData.Size div 512; n2 := FData.Size mod 512; for n := 1 to n1 do begin s := ReadStrFromStream(FData, 512); // SetLength(s, 512); // FData.Read(pointer(s)^, 512); if not Sendpacket(3, ser, s) then Exit; inc(ser); end; s := ReadStrFromStream(FData, n2); // SetLength(s, n2); // FData.Read(pointer(s)^, n2); if not Sendpacket(3, ser, s) then Exit; Result := True; end; finally FSock.CloseSocket; end; end; function TTFTPSend.RecvFile(const Filename: string): Boolean; var s: string; ser: word; begin Result := False; FErrorCode := 0; FErrorString := ''; FSock.CloseSocket; FSock.Connect(FTargetHost, FTargetPort); try if FSock.LastError = 0 then begin s := CodeInt(1) + Filename + #0 + 'octet' + #0; FSock.SendString(s); if FSock.LastError <> 0 then Exit; FData.Clear; ser := 1; repeat if not RecvPacket(ser, s) then Exit; inc(ser); WriteStrToStream(FData, s); // FData.Write(pointer(s)^, length(s)); until length(s) <> 512; FData.Position := 0; Result := true; end; finally FSock.CloseSocket; end; end; function TTFTPSend.WaitForRequest(var Req: word; var filename: string): Boolean; var s: string; begin Result := False; FErrorCode := 0; FErrorString := ''; FSock.CloseSocket; FSock.Bind('0.0.0.0', FTargetPort); if FSock.LastError = 0 then begin s := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then if Length(s) >= 4 then begin FRequestIP := FSock.GetRemoteSinIP; FRequestPort := IntToStr(FSock.GetRemoteSinPort); Req := DecodeInt(s, 1); delete(s, 1, 2); filename := Trim(SeparateLeft(s, #0)); s := SeparateRight(s, #0); s := SeparateLeft(s, #0); Result := lowercase(trim(s)) = 'octet'; end; end; end; procedure TTFTPSend.ReplyError(Error: word; Description: string); var s: string; begin FSock.CloseSocket; FSock.Connect(FRequestIP, FRequestPort); s := CodeInt(5) + CodeInt(Error) + Description + #0; FSock.SendString(s); FSock.CloseSocket; end; function TTFTPSend.ReplyRecv: Boolean; var s: string; ser: integer; begin Result := False; FErrorCode := 0; FErrorString := ''; FSock.CloseSocket; FSock.Connect(FRequestIP, FRequestPort); try s := CodeInt(4) + CodeInt(0); FSock.SendString(s); FData.Clear; ser := 1; repeat if not RecvPacket(ser, s) then Exit; inc(ser); WriteStrToStream(FData, s); // FData.Write(pointer(s)^, length(s)); until length(s) <> 512; FData.Position := 0; Result := true; finally FSock.CloseSocket; end; end; function TTFTPSend.ReplySend: Boolean; var s: string; ser: word; n, n1, n2: integer; begin Result := False; FErrorCode := 0; FErrorString := ''; FSock.CloseSocket; FSock.Connect(FRequestIP, FRequestPort); try ser := 1; FData.Position := 0; n1 := FData.Size div 512; n2 := FData.Size mod 512; for n := 1 to n1 do begin s := ReadStrFromStream(FData, 512); // SetLength(s, 512); // FData.Read(pointer(s)^, 512); if not Sendpacket(3, ser, s) then Exit; inc(ser); end; s := ReadStrFromStream(FData, n2); // SetLength(s, n2); // FData.Read(pointer(s)^, n2); if not Sendpacket(3, ser, s) then Exit; Result := True; finally FSock.CloseSocket; end; end; {==============================================================================} end. TransGUI/synapse/source/lib/synafpc.pas0000644000000000000000000001204211366572451017121 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.002 | |==============================================================================| | Content: Utils for FreePascal compatibility | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2003-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@exclude} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} unit synafpc; interface uses {$IFDEF FPC} dynlibs, sysutils; {$ELSE} {$IFDEF MSWINDOWS} Windows; {$ELSE} SysUtils; {$ENDIF} {$ENDIF} {$IFDEF FPC} type TLibHandle = dynlibs.TLibHandle; function LoadLibrary(ModuleName: PChar): TLibHandle; function FreeLibrary(Module: TLibHandle): LongBool; function GetProcAddress(Module: TLibHandle; Proc: PChar): Pointer; function GetModuleFileName(Module: TLibHandle; Buffer: PChar; BufLen: Integer): Integer; {$ELSE} type {$IFDEF CIL} TLibHandle = Integer; {$ELSE} TLibHandle = HModule; {$ENDIF} {$IFDEF VER100} LongWord = DWord; {$ENDIF} {$ENDIF} procedure Sleep(milliseconds: Cardinal); implementation {==============================================================================} {$IFDEF FPC} function LoadLibrary(ModuleName: PChar): TLibHandle; begin Result := dynlibs.LoadLibrary(Modulename); end; function FreeLibrary(Module: TLibHandle): LongBool; begin Result := dynlibs.UnloadLibrary(Module); end; function GetProcAddress(Module: TLibHandle; Proc: PChar): Pointer; begin Result := dynlibs.GetProcedureAddress(Module, Proc); end; function GetModuleFileName(Module: TLibHandle; Buffer: PChar; BufLen: Integer): Integer; begin Result := 0; end; {$ELSE} {$ENDIF} procedure Sleep(milliseconds: Cardinal); begin {$IFDEF MSWINDOWS} {$IFDEF FPC} sysutils.sleep(milliseconds); {$ELSE} windows.sleep(milliseconds); {$ENDIF} {$ELSE} sysutils.sleep(milliseconds); {$ENDIF} end; end. TransGUI/synapse/source/lib/synsock.pas0000644000000000000000000000757211366572451017163 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 005.002.000 | |==============================================================================| | Content: Socket Independent Platform Layer | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2001-20010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@exclude} unit synsock; {$MINENUMSIZE 4} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {$IFDEF CIL} {$I ssdotnet.pas} {$ELSE} {$IFDEF MSWINDOWS} {$I sswin32.pas} {$ELSE} {$IFDEF WINCE} {$I sswin32.pas} //not complete yet! {$ELSE} {$IFDEF FPC} {$I ssfpc.pas} {$ELSE} {$I sslinux.pas} {$ENDIF} {$ENDIF} {$ENDIF} {$ENDIF} end. TransGUI/synapse/source/lib/laz_synapse.lpk0000644000000000000000000001071011466757142020014 0ustar rootroot TransGUI/synapse/source/lib/dnssend.pas0000644000000000000000000004546311366572451017131 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.007.006 | |==============================================================================| | Content: DNS client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {: @abstract(DNS client by UDP or TCP) Support for sending DNS queries by UDP or TCP protocol. It can retrieve zone transfers too! Used RFC: RFC-1035, RFC-1183, RFC1706, RFC1712, RFC2163, RFC2230 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit dnssend; interface uses SysUtils, Classes, blcksock, synautil, synaip, synsock; const cDnsProtocol = '53'; QTYPE_A = 1; QTYPE_NS = 2; QTYPE_MD = 3; QTYPE_MF = 4; QTYPE_CNAME = 5; QTYPE_SOA = 6; QTYPE_MB = 7; QTYPE_MG = 8; QTYPE_MR = 9; QTYPE_NULL = 10; QTYPE_WKS = 11; // QTYPE_PTR = 12; QTYPE_HINFO = 13; QTYPE_MINFO = 14; QTYPE_MX = 15; QTYPE_TXT = 16; QTYPE_RP = 17; QTYPE_AFSDB = 18; QTYPE_X25 = 19; QTYPE_ISDN = 20; QTYPE_RT = 21; QTYPE_NSAP = 22; QTYPE_NSAPPTR = 23; QTYPE_SIG = 24; // RFC-2065 QTYPE_KEY = 25; // RFC-2065 QTYPE_PX = 26; QTYPE_GPOS = 27; QTYPE_AAAA = 28; QTYPE_LOC = 29; // RFC-1876 QTYPE_NXT = 30; // RFC-2065 QTYPE_SRV = 33; QTYPE_NAPTR = 35; // RFC-2168 QTYPE_KX = 36; QTYPE_SPF = 99; QTYPE_AXFR = 252; QTYPE_MAILB = 253; // QTYPE_MAILA = 254; // QTYPE_ALL = 255; type {:@abstract(Implementation of DNS protocol by UDP or TCP protocol.) Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TDNSSend = class(TSynaClient) private FID: Word; FRCode: Integer; FBuffer: AnsiString; FSock: TUDPBlockSocket; FTCPSock: TTCPBlockSocket; FUseTCP: Boolean; FAnswerInfo: TStringList; FNameserverInfo: TStringList; FAdditionalInfo: TStringList; FAuthoritative: Boolean; FTruncated: Boolean; function CompressName(const Value: AnsiString): AnsiString; function CodeHeader: AnsiString; function CodeQuery(const Name: AnsiString; QType: Integer): AnsiString; function DecodeLabels(var From: Integer): AnsiString; function DecodeString(var From: Integer): AnsiString; function DecodeResource(var i: Integer; const Info: TStringList; QType: Integer): AnsiString; function RecvTCPResponse(const WorkSock: TBlockSocket): AnsiString; function DecodeResponse(const Buf: AnsiString; const Reply: TStrings; QType: Integer):boolean; public constructor Create; destructor Destroy; override; {:Query a DNSHost for QType resources correspond to a name. Supported QType values are: Qtype_A, Qtype_NS, Qtype_MD, Qtype_MF, Qtype_CNAME, Qtype_SOA, Qtype_MB, Qtype_MG, Qtype_MR, Qtype_NULL, Qtype_PTR, Qtype_HINFO, Qtype_MINFO, Qtype_MX, Qtype_TXT, Qtype_RP, Qtype_AFSDB, Qtype_X25, Qtype_ISDN, Qtype_RT, Qtype_NSAP, Qtype_NSAPPTR, Qtype_PX, Qtype_GPOS, Qtype_KX. Type for zone transfers QTYPE_AXFR is supported too, but only in TCP mode! "Name" is domain name or host name for queried resource. If "name" is IP address, automatically convert to reverse domain form (.in-addr.arpa). If result is @true, Reply contains resource records. One record on one line. If Resource record have multiple fields, they are stored on line divided by comma. (example: MX record contains value 'rs.cesnet.cz' with preference number 10, string in Reply is: '10,rs.cesnet.cz'). All numbers or IP address in resource are converted to string form.} function DNSQuery(Name: AnsiString; QType: Integer; const Reply: TStrings): Boolean; published {:Socket object used for UDP operation. Good for seting OnStatus hook, etc.} property Sock: TUDPBlockSocket read FSock; {:Socket object used for TCP operation. Good for seting OnStatus hook, etc.} property TCPSock: TTCPBlockSocket read FTCPSock; {:if @true, then is used TCP protocol instead UDP. It is needed for zone transfers, etc.} property UseTCP: Boolean read FUseTCP Write FUseTCP; {:After DNS operation contains ResultCode of DNS operation. Values are: 0-no error, 1-format error, 2-server failure, 3-name error, 4-not implemented, 5-refused.} property RCode: Integer read FRCode; {:@True, if answer is authoritative.} property Authoritative: Boolean read FAuthoritative; {:@True, if answer is truncated to 512 bytes.} property Truncated: Boolean read FTRuncated; {:Detailed informations from name server reply. One record per line. Record have comma delimited entries with type number, TTL and data filelds. This information contains detailed information about query reply.} property AnswerInfo: TStringList read FAnswerInfo; {:Detailed informations from name server reply. One record per line. Record have comma delimited entries with type number, TTL and data filelds. This information contains detailed information about nameserver.} property NameserverInfo: TStringList read FNameserverInfo; {:Detailed informations from name server reply. One record per line. Record have comma delimited entries with type number, TTL and data filelds. This information contains detailed additional information.} property AdditionalInfo: TStringList read FAdditionalInfo; end; {:A very useful function, and example of it's use is found in the TDNSSend object. This function is used to get mail servers for a domain and sort them by preference numbers. "Servers" contains only the domain names of the mail servers in the right order (without preference number!). The first domain name will always be the highest preferenced mail server. Returns boolean @TRUE if all went well.} function GetMailServers(const DNSHost, Domain: AnsiString; const Servers: TStrings): Boolean; implementation constructor TDNSSend.Create; begin inherited Create; FSock := TUDPBlockSocket.Create; FSock.Owner := self; FTCPSock := TTCPBlockSocket.Create; FTCPSock.Owner := self; FUseTCP := False; FTimeout := 10000; FTargetPort := cDnsProtocol; FAnswerInfo := TStringList.Create; FNameserverInfo := TStringList.Create; FAdditionalInfo := TStringList.Create; Randomize; end; destructor TDNSSend.Destroy; begin FAnswerInfo.Free; FNameserverInfo.Free; FAdditionalInfo.Free; FTCPSock.Free; FSock.Free; inherited Destroy; end; function TDNSSend.CompressName(const Value: AnsiString): AnsiString; var n: Integer; s: AnsiString; begin Result := ''; if Value = '' then Result := #0 else begin s := ''; for n := 1 to Length(Value) do if Value[n] = '.' then begin Result := Result + AnsiChar(Length(s)) + s; s := ''; end else s := s + Value[n]; if s <> '' then Result := Result + AnsiChar(Length(s)) + s; Result := Result + #0; end; end; function TDNSSend.CodeHeader: AnsiString; begin FID := Random(32767); Result := CodeInt(FID); // ID Result := Result + CodeInt($0100); // flags Result := Result + CodeInt(1); // QDCount Result := Result + CodeInt(0); // ANCount Result := Result + CodeInt(0); // NSCount Result := Result + CodeInt(0); // ARCount end; function TDNSSend.CodeQuery(const Name: AnsiString; QType: Integer): AnsiString; begin Result := CompressName(Name); Result := Result + CodeInt(QType); Result := Result + CodeInt(1); // Type INTERNET end; function TDNSSend.DecodeString(var From: Integer): AnsiString; var Len: integer; begin Len := Ord(FBuffer[From]); Inc(From); Result := Copy(FBuffer, From, Len); Inc(From, Len); end; function TDNSSend.DecodeLabels(var From: Integer): AnsiString; var l, f: Integer; begin Result := ''; while True do begin if From >= Length(FBuffer) then Break; l := Ord(FBuffer[From]); Inc(From); if l = 0 then Break; if Result <> '' then Result := Result + '.'; if (l and $C0) = $C0 then begin f := l and $3F; f := f * 256 + Ord(FBuffer[From]) + 1; Inc(From); Result := Result + DecodeLabels(f); Break; end else begin Result := Result + Copy(FBuffer, From, l); Inc(From, l); end; end; end; function TDNSSend.DecodeResource(var i: Integer; const Info: TStringList; QType: Integer): AnsiString; var Rname: AnsiString; RType, Len, j, x, y, z, n: Integer; R: AnsiString; t1, t2, ttl: integer; ip6: TIp6bytes; begin Result := ''; R := ''; Rname := DecodeLabels(i); RType := DecodeInt(FBuffer, i); Inc(i, 4); t1 := DecodeInt(FBuffer, i); Inc(i, 2); t2 := DecodeInt(FBuffer, i); Inc(i, 2); ttl := t1 * 65536 + t2; Len := DecodeInt(FBuffer, i); Inc(i, 2); // i point to begin of data j := i; i := i + len; // i point to next record if Length(FBuffer) >= (i - 1) then case RType of QTYPE_A: begin R := IntToStr(Ord(FBuffer[j])); Inc(j); R := R + '.' + IntToStr(Ord(FBuffer[j])); Inc(j); R := R + '.' + IntToStr(Ord(FBuffer[j])); Inc(j); R := R + '.' + IntToStr(Ord(FBuffer[j])); end; QTYPE_AAAA: begin for n := 0 to 15 do ip6[n] := ord(FBuffer[j + n]); R := IP6ToStr(ip6); end; QTYPE_NS, QTYPE_MD, QTYPE_MF, QTYPE_CNAME, QTYPE_MB, QTYPE_MG, QTYPE_MR, QTYPE_PTR, QTYPE_X25, QTYPE_NSAP, QTYPE_NSAPPTR: R := DecodeLabels(j); QTYPE_SOA: begin R := DecodeLabels(j); R := R + ',' + DecodeLabels(j); for n := 1 to 5 do begin x := DecodeInt(FBuffer, j) * 65536 + DecodeInt(FBuffer, j + 2); Inc(j, 4); R := R + ',' + IntToStr(x); end; end; QTYPE_NULL: begin end; QTYPE_WKS: begin end; QTYPE_HINFO: begin R := DecodeString(j); R := R + ',' + DecodeString(j); end; QTYPE_MINFO, QTYPE_RP, QTYPE_ISDN: begin R := DecodeLabels(j); R := R + ',' + DecodeLabels(j); end; QTYPE_MX, QTYPE_AFSDB, QTYPE_RT, QTYPE_KX: begin x := DecodeInt(FBuffer, j); Inc(j, 2); R := IntToStr(x); R := R + ',' + DecodeLabels(j); end; QTYPE_TXT, QTYPE_SPF: begin R := ''; while j < i do R := R + DecodeString(j); end; QTYPE_GPOS: begin R := DecodeLabels(j); R := R + ',' + DecodeLabels(j); R := R + ',' + DecodeLabels(j); end; QTYPE_PX: begin x := DecodeInt(FBuffer, j); Inc(j, 2); R := IntToStr(x); R := R + ',' + DecodeLabels(j); R := R + ',' + DecodeLabels(j); end; QTYPE_SRV: // Author: Dan begin x := DecodeInt(FBuffer, j); Inc(j, 2); y := DecodeInt(FBuffer, j); Inc(j, 2); z := DecodeInt(FBuffer, j); Inc(j, 2); R := IntToStr(x); // Priority R := R + ',' + IntToStr(y); // Weight R := R + ',' + IntToStr(z); // Port R := R + ',' + DecodeLabels(j); // Server DNS Name end; end; if R <> '' then Info.Add(RName + ',' + IntToStr(RType) + ',' + IntToStr(ttl) + ',' + R); if QType = RType then Result := R; end; function TDNSSend.RecvTCPResponse(const WorkSock: TBlockSocket): AnsiString; var l: integer; begin Result := ''; l := WorkSock.recvbyte(FTimeout) * 256 + WorkSock.recvbyte(FTimeout); if l > 0 then Result := WorkSock.RecvBufferStr(l, FTimeout); end; function TDNSSend.DecodeResponse(const Buf: AnsiString; const Reply: TStrings; QType: Integer):boolean; var n, i: Integer; flag, qdcount, ancount, nscount, arcount: Integer; s: AnsiString; begin Result := False; Reply.Clear; FAnswerInfo.Clear; FNameserverInfo.Clear; FAdditionalInfo.Clear; FAuthoritative := False; if (Length(Buf) > 13) and (FID = DecodeInt(Buf, 1)) then begin Result := True; flag := DecodeInt(Buf, 3); FRCode := Flag and $000F; FAuthoritative := (Flag and $0400) > 0; FTruncated := (Flag and $0200) > 0; if FRCode = 0 then begin qdcount := DecodeInt(Buf, 5); ancount := DecodeInt(Buf, 7); nscount := DecodeInt(Buf, 9); arcount := DecodeInt(Buf, 11); i := 13; //begin of body if (qdcount > 0) and (Length(Buf) > i) then //skip questions for n := 1 to qdcount do begin while (Buf[i] <> #0) and ((Ord(Buf[i]) and $C0) <> $C0) do Inc(i); Inc(i, 5); end; if (ancount > 0) and (Length(Buf) > i) then // decode reply for n := 1 to ancount do begin s := DecodeResource(i, FAnswerInfo, QType); if s <> '' then Reply.Add(s); end; if (nscount > 0) and (Length(Buf) > i) then // decode nameserver info for n := 1 to nscount do DecodeResource(i, FNameserverInfo, QType); if (arcount > 0) and (Length(Buf) > i) then // decode additional info for n := 1 to arcount do DecodeResource(i, FAdditionalInfo, QType); end; end; end; function TDNSSend.DNSQuery(Name: AnsiString; QType: Integer; const Reply: TStrings): Boolean; var WorkSock: TBlockSocket; t: TStringList; b: boolean; begin Result := False; if IsIP(Name) then Name := ReverseIP(Name) + '.in-addr.arpa'; if IsIP6(Name) then Name := ReverseIP6(Name) + '.ip6.arpa'; FBuffer := CodeHeader + CodeQuery(Name, QType); if FUseTCP then WorkSock := FTCPSock else WorkSock := FSock; WorkSock.Bind(FIPInterface, cAnyPort); WorkSock.Connect(FTargetHost, FTargetPort); if FUseTCP then FBuffer := Codeint(length(FBuffer)) + FBuffer; WorkSock.SendString(FBuffer); if FUseTCP then FBuffer := RecvTCPResponse(WorkSock) else FBuffer := WorkSock.RecvPacket(FTimeout); if FUseTCP and (QType = QTYPE_AXFR) then //zone transfer begin t := TStringList.Create; try repeat b := DecodeResponse(FBuffer, Reply, QType); if (t.Count > 1) and (AnswerInfo.Count > 0) then //find end of transfer b := b and (t[0] <> AnswerInfo[AnswerInfo.count - 1]); if b then begin t.AddStrings(AnswerInfo); FBuffer := RecvTCPResponse(WorkSock); if FBuffer = '' then Break; if WorkSock.LastError <> 0 then Break; end; until not b; Reply.Assign(t); Result := True; finally t.free; end; end else //normal query if WorkSock.LastError = 0 then Result := DecodeResponse(FBuffer, Reply, QType); end; {==============================================================================} function GetMailServers(const DNSHost, Domain: AnsiString; const Servers: TStrings): Boolean; var DNS: TDNSSend; t: TStringList; n, m, x: Integer; begin Result := False; Servers.Clear; t := TStringList.Create; DNS := TDNSSend.Create; try DNS.TargetHost := DNSHost; if DNS.DNSQuery(Domain, QType_MX, t) then begin { normalize preference number to 5 digits } for n := 0 to t.Count - 1 do begin x := Pos(',', t[n]); if x > 0 then for m := 1 to 6 - x do t[n] := '0' + t[n]; end; { sort server list } t.Sorted := True; { result is sorted list without preference numbers } for n := 0 to t.Count - 1 do begin x := Pos(',', t[n]); Servers.Add(Copy(t[n], x + 1, Length(t[n]) - x)); end; Result := True; end; finally DNS.Free; t.Free; end; end; end. TransGUI/synapse/source/lib/httpsend.pas0000644000000000000000000006457011466757142017327 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 003.012.004 | |==============================================================================| | Content: HTTP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(HTTP protocol client) Used RFC: RFC-1867, RFC-1947, RFC-2388, RFC-2616 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit httpsend; interface uses SysUtils, Classes, blcksock, synautil, synaip, synacode, synsock; const cHttpProtocol = '80'; type {:These encoding types are used internally by the THTTPSend object to identify the transfer data types.} TTransferEncoding = (TE_UNKNOWN, TE_IDENTITY, TE_CHUNKED); {:abstract(Implementation of HTTP protocol.)} THTTPSend = class(TSynaClient) protected FSock: TTCPBlockSocket; FTransferEncoding: TTransferEncoding; FAliveHost: string; FAlivePort: string; FHeaders: TStringList; FDocument: TMemoryStream; FMimeType: string; FProtocol: string; FKeepAlive: Boolean; FKeepAliveTimeout: integer; FStatus100: Boolean; FProxyHost: string; FProxyPort: string; FProxyUser: string; FProxyPass: string; FResultCode: Integer; FResultString: string; FUserAgent: string; FCookies: TStringList; FDownloadSize: integer; FUploadSize: integer; FRangeStart: integer; FRangeEnd: integer; FAddPortNumberToHost: Boolean; function ReadUnknown: Boolean; function ReadIdentity(Size: Integer): Boolean; function ReadChunked: Boolean; procedure ParseCookies; function PrepareHeaders: AnsiString; function InternalDoConnect(needssl: Boolean): Boolean; function InternalConnect(needssl: Boolean): Boolean; public constructor Create; destructor Destroy; override; {:Reset headers and document and Mimetype.} procedure Clear; {:Decode ResultCode and ResultString from Value.} procedure DecodeStatus(const Value: string); {:Connects to host define in URL and access to resource defined in URL by method. If Document is not empty, send it to server as part of HTTP request. Server response is in Document and headers. Connection may be authorised by username and password in URL. If you define proxy properties, connection is made by this proxy. If all OK, result is @true, else result is @false. If you use in URL 'https:' instead only 'http:', then your request is made by SSL/TLS connection (if you not specify port, then port 443 is used instead standard port 80). If you use SSL/TLS request and you have defined HTTP proxy, then HTTP-tunnel mode is automaticly used .} function HTTPMethod(const Method, URL: string): Boolean; {:You can call this method from OnStatus event for break current data transfer. (or from another thread.)} procedure Abort; published {:Before HTTP operation you may define any non-standard headers for HTTP request, except of: 'Expect: 100-continue', 'Content-Length', 'Content-Type', 'Connection', 'Authorization', 'Proxy-Authorization' and 'Host' headers. After HTTP operation contains full headers of returned document.} property Headers: TStringList read FHeaders; {:This is stringlist with name-value stringlist pairs. Each this pair is one cookie. After HTTP request is returned cookies parsed to this stringlist. You can leave this cookies untouched for next HTTP request. You can also save this stringlist for later use.} property Cookies: TStringList read FCookies; {:Stream with document to send (before request, or with document received from HTTP server (after request).} property Document: TMemoryStream read FDocument; {:If you need download only part of requested document, here specify possition of subpart begin. If here 0, then is requested full document.} property RangeStart: integer read FRangeStart Write FRangeStart; {:If you need download only part of requested document, here specify possition of subpart end. If here 0, then is requested document from rangeStart to end of document. (for broken download restoration, for example.)} property RangeEnd: integer read FRangeEnd Write FRangeEnd; {:Mime type of sending data. Default is: 'text/html'.} property MimeType: string read FMimeType Write FMimeType; {:Define protocol version. Possible values are: '1.1', '1.0' (default) and '0.9'.} property Protocol: string read FProtocol Write FProtocol; {:If @true (default value), keepalives in HTTP protocol 1.1 is enabled.} property KeepAlive: Boolean read FKeepAlive Write FKeepAlive; {:Define timeout for keepalives in seconds!} property KeepAliveTimeout: integer read FKeepAliveTimeout Write FKeepAliveTimeout; {:if @true, then server is requested for 100status capability when uploading data. Default is @false (off).} property Status100: Boolean read FStatus100 Write FStatus100; {:Address of proxy server (IP address or domain name) where you want to connect in @link(HTTPMethod) method.} property ProxyHost: string read FProxyHost Write FProxyHost; {:Port number for proxy connection. Default value is 8080.} property ProxyPort: string read FProxyPort Write FProxyPort; {:Username for connect to proxy server where you want to connect in HTTPMethod method.} property ProxyUser: string read FProxyUser Write FProxyUser; {:Password for connect to proxy server where you want to connect in HTTPMethod method.} property ProxyPass: string read FProxyPass Write FProxyPass; {:Here you can specify custom User-Agent indentification. By default is used: 'Mozilla/4.0 (compatible; Synapse)'} property UserAgent: string read FUserAgent Write FUserAgent; {:After successful @link(HTTPMethod) method contains result code of operation.} property ResultCode: Integer read FResultCode; {:After successful @link(HTTPMethod) method contains string after result code.} property ResultString: string read FResultString; {:if this value is not 0, then data download pending. In this case you have here total sice of downloaded data. It is good for draw download progressbar from OnStatus event.} property DownloadSize: integer read FDownloadSize; {:if this value is not 0, then data upload pending. In this case you have here total sice of uploaded data. It is good for draw upload progressbar from OnStatus event.} property UploadSize: integer read FUploadSize; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; {:To have possibility to switch off port number in 'Host:' HTTP header, by default @TRUE. Some buggy servers not like port informations in this header.} property AddPortNumberToHost: Boolean read FAddPortNumberToHost write FAddPortNumberToHost; end; {:A very usefull function, and example of use can be found in the THTTPSend object. It implements the GET method of the HTTP protocol. This function sends the GET method for URL document to an HTTP server. Returned document is in the "Response" stringlist (without any headers). Returns boolean TRUE if all went well.} function HttpGetText(const URL: string; const Response: TStrings): Boolean; {:A very usefull function, and example of use can be found in the THTTPSend object. It implements the GET method of the HTTP protocol. This function sends the GET method for URL document to an HTTP server. Returned document is in the "Response" stream. Returns boolean TRUE if all went well.} function HttpGetBinary(const URL: string; const Response: TStream): Boolean; {:A very useful function, and example of use can be found in the THTTPSend object. It implements the POST method of the HTTP protocol. This function sends the SEND method for a URL document to an HTTP server. The document to be sent is located in "Data" stream. The returned document is in the "Data" stream. Returns boolean TRUE if all went well.} function HttpPostBinary(const URL: string; const Data: TStream): Boolean; {:A very useful function, and example of use can be found in the THTTPSend object. It implements the POST method of the HTTP protocol. This function is good for POSTing form data. It sends the POST method for a URL document to an HTTP server. You must prepare the form data in the same manner as you would the URL data, and pass this prepared data to "URLdata". The following is a sample of how the data would appear: 'name=Lukas&field1=some%20data'. The information in the field must be encoded by EncodeURLElement function. The returned document is in the "Data" stream. Returns boolean TRUE if all went well.} function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean; {:A very useful function, and example of use can be found in the THTTPSend object. It implements the POST method of the HTTP protocol. This function sends the POST method for a URL document to an HTTP server. This function simulate posting of file by HTML form used method 'multipart/form-data'. Posting file is in DATA stream. Its name is Filename string. Fieldname is for name of formular field with file. (simulate HTML INPUT FILE) The returned document is in the ResultData Stringlist. Returns boolean TRUE if all went well.} function HttpPostFile(const URL, FieldName, FileName: string; const Data: TStream; const ResultData: TStrings): Boolean; implementation constructor THTTPSend.Create; begin inherited Create; FHeaders := TStringList.Create; FCookies := TStringList.Create; FDocument := TMemoryStream.Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.ConvertLineEnd := True; FSock.SizeRecvBuffer := c64k; FSock.SizeSendBuffer := c64k; FTimeout := 90000; FTargetPort := cHttpProtocol; FProxyHost := ''; FProxyPort := '8080'; FProxyUser := ''; FProxyPass := ''; FAliveHost := ''; FAlivePort := ''; FProtocol := '1.0'; FKeepAlive := True; FStatus100 := False; FUserAgent := 'Mozilla/4.0 (compatible; Synapse)'; FDownloadSize := 0; FUploadSize := 0; FAddPortNumberToHost := true; FKeepAliveTimeout := 300; Clear; end; destructor THTTPSend.Destroy; begin FSock.Free; FDocument.Free; FCookies.Free; FHeaders.Free; inherited Destroy; end; procedure THTTPSend.Clear; begin FRangeStart := 0; FRangeEnd := 0; FDocument.Clear; FHeaders.Clear; FMimeType := 'text/html'; end; procedure THTTPSend.DecodeStatus(const Value: string); var s, su: string; begin s := Trim(SeparateRight(Value, ' ')); su := Trim(SeparateLeft(s, ' ')); FResultCode := StrToIntDef(su, 0); FResultString := Trim(SeparateRight(s, ' ')); if FResultString = s then FResultString := ''; end; function THTTPSend.PrepareHeaders: AnsiString; begin if FProtocol = '0.9' then Result := FHeaders[0] + CRLF else {$IFNDEF MSWINDOWS} Result := {$IFDEF UNICODE}AnsiString{$ENDIF}(AdjustLineBreaks(FHeaders.Text, tlbsCRLF)); {$ELSE} Result := FHeaders.Text; {$ENDIF} end; function THTTPSend.InternalDoConnect(needssl: Boolean): Boolean; begin Result := False; FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError <> 0 then Exit; FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError <> 0 then Exit; if needssl then begin FSock.SSLDoConnect; if FSock.LastError <> 0 then Exit; end; FAliveHost := FTargetHost; FAlivePort := FTargetPort; Result := True; end; function THTTPSend.InternalConnect(needssl: Boolean): Boolean; begin if FSock.Socket = INVALID_SOCKET then Result := InternalDoConnect(needssl) else if (FAliveHost <> FTargetHost) or (FAlivePort <> FTargetPort) or FSock.CanRead(0) then Result := InternalDoConnect(needssl) else Result := True; end; function THTTPSend.HTTPMethod(const Method, URL: string): Boolean; var Sending, Receiving: Boolean; status100: Boolean; status100error: string; ToClose: Boolean; Size: Integer; Prot, User, Pass, Host, Port, Path, Para, URI: string; s, su: AnsiString; HttpTunnel: Boolean; n: integer; pp: string; UsingProxy: boolean; l: TStringList; x: integer; begin {initial values} Result := False; FResultCode := 500; FResultString := ''; FDownloadSize := 0; FUploadSize := 0; URI := ParseURL(URL, Prot, User, Pass, Host, Port, Path, Para); if User = '' then begin User := FUsername; Pass := FPassword; end; if UpperCase(Prot) = 'HTTPS' then begin HttpTunnel := FProxyHost <> ''; FSock.HTTPTunnelIP := FProxyHost; FSock.HTTPTunnelPort := FProxyPort; FSock.HTTPTunnelUser := FProxyUser; FSock.HTTPTunnelPass := FProxyPass; end else begin HttpTunnel := False; FSock.HTTPTunnelIP := ''; FSock.HTTPTunnelPort := ''; FSock.HTTPTunnelUser := ''; FSock.HTTPTunnelPass := ''; end; UsingProxy := (FProxyHost <> '') and not(HttpTunnel); Sending := FDocument.Size > 0; {Headers for Sending data} status100 := FStatus100 and Sending and (FProtocol = '1.1'); if status100 then FHeaders.Insert(0, 'Expect: 100-continue'); if Sending then begin FHeaders.Insert(0, 'Content-Length: ' + IntToStr(FDocument.Size)); if FMimeType <> '' then FHeaders.Insert(0, 'Content-Type: ' + FMimeType); end; { setting User-agent } if FUserAgent <> '' then FHeaders.Insert(0, 'User-Agent: ' + FUserAgent); { setting Ranges } if (FRangeStart > 0) or (FRangeEnd > 0) then begin if FRangeEnd >= FRangeStart then FHeaders.Insert(0, 'Range: bytes=' + IntToStr(FRangeStart) + '-' + IntToStr(FRangeEnd)) else FHeaders.Insert(0, 'Range: bytes=' + IntToStr(FRangeStart) + '-'); end; { setting Cookies } s := ''; for n := 0 to FCookies.Count - 1 do begin if s <> '' then s := s + '; '; s := s + FCookies[n]; end; if s <> '' then FHeaders.Insert(0, 'Cookie: ' + s); { setting KeepAlives } pp := ''; if UsingProxy then pp := 'Proxy-'; if FKeepAlive then begin FHeaders.Insert(0, pp + 'Connection: keep-alive'); FHeaders.Insert(0, 'Keep-Alive: ' + IntToStr(FKeepAliveTimeout)); end else FHeaders.Insert(0, pp + 'Connection: close'); { set target servers/proxy, authorizations, etc... } if User <> '' then FHeaders.Insert(0, 'Authorization: Basic ' + EncodeBase64(User + ':' + Pass)); if UsingProxy and (FProxyUser <> '') then FHeaders.Insert(0, 'Proxy-Authorization: Basic ' + EncodeBase64(FProxyUser + ':' + FProxyPass)); if isIP6(Host) then s := '[' + Host + ']' else s := Host; if FAddPortNumberToHost and (Port <> '80') then FHeaders.Insert(0, 'Host: ' + s + ':' + Port) else FHeaders.Insert(0, 'Host: ' + s); if UsingProxy then URI := Prot + '://' + s + ':' + Port + URI; if URI = '/*' then URI := '*'; if FProtocol = '0.9' then FHeaders.Insert(0, UpperCase(Method) + ' ' + URI) else FHeaders.Insert(0, UpperCase(Method) + ' ' + URI + ' HTTP/' + FProtocol); if UsingProxy then begin FTargetHost := FProxyHost; FTargetPort := FProxyPort; end else begin FTargetHost := Host; FTargetPort := Port; end; if FHeaders[FHeaders.Count - 1] <> '' then FHeaders.Add(''); { connect } if not InternalConnect(UpperCase(Prot) = 'HTTPS') then begin FAliveHost := ''; FAlivePort := ''; Exit; end; { reading Status } FDocument.Position := 0; Status100Error := ''; if status100 then begin { send Headers } FSock.SendString(PrepareHeaders); if FSock.LastError <> 0 then Exit; repeat s := FSock.RecvString(FTimeout); if s <> '' then Break; until FSock.LastError <> 0; DecodeStatus(s); Status100Error := s; repeat s := FSock.recvstring(FTimeout); if s = '' then Break; until FSock.LastError <> 0; if (FResultCode >= 100) and (FResultCode < 200) then begin { we can upload content } Status100Error := ''; FUploadSize := FDocument.Size; FSock.SendBuffer(FDocument.Memory, FDocument.Size); end; end else { upload content } if sending then begin if FDocument.Size >= c64k then begin FSock.SendString(PrepareHeaders); FUploadSize := FDocument.Size; FSock.SendBuffer(FDocument.Memory, FDocument.Size); end else begin s := PrepareHeaders + ReadStrFromStream(FDocument, FDocument.Size); FUploadSize := Length(s); FSock.SendString(s); end; end else begin { we not need to upload document, send headers only } FSock.SendString(PrepareHeaders); end; if FSock.LastError <> 0 then Exit; Clear; Size := -1; FTransferEncoding := TE_UNKNOWN; { read status } if Status100Error = '' then begin repeat repeat s := FSock.RecvString(FTimeout); if s <> '' then Break; until FSock.LastError <> 0; if Pos('HTTP/', UpperCase(s)) = 1 then begin FHeaders.Add(s); DecodeStatus(s); end else begin { old HTTP 0.9 and some buggy servers not send result } s := s + CRLF; WriteStrToStream(FDocument, s); FResultCode := 0; end; until (FSock.LastError <> 0) or (FResultCode <> 100); end else FHeaders.Add(Status100Error); { if need receive headers, receive and parse it } ToClose := FProtocol <> '1.1'; if FHeaders.Count > 0 then begin l := TStringList.Create; try repeat s := FSock.RecvString(FTimeout); l.Add(s); if s = '' then Break; until FSock.LastError <> 0; x := 0; while l.Count > x do begin s := NormalizeHeader(l, x); FHeaders.Add(s); su := UpperCase(s); if Pos('CONTENT-LENGTH:', su) = 1 then begin Size := StrToIntDef(Trim(SeparateRight(s, ' ')), -1); if (Size <> -1) and (FTransferEncoding = TE_UNKNOWN) then FTransferEncoding := TE_IDENTITY; end; if Pos('CONTENT-TYPE:', su) = 1 then FMimeType := Trim(SeparateRight(s, ' ')); if Pos('TRANSFER-ENCODING:', su) = 1 then begin s := Trim(SeparateRight(su, ' ')); if Pos('CHUNKED', s) > 0 then FTransferEncoding := TE_CHUNKED; end; if UsingProxy then begin if Pos('PROXY-CONNECTION:', su) = 1 then if Pos('CLOSE', su) > 0 then ToClose := True; end else begin if Pos('CONNECTION:', su) = 1 then if Pos('CLOSE', su) > 0 then ToClose := True; end; end; finally l.free; end; end; Result := FSock.LastError = 0; if not Result then Exit; {if need receive response body, read it} Receiving := Method <> 'HEAD'; Receiving := Receiving and (FResultCode <> 204); Receiving := Receiving and (FResultCode <> 304); if Receiving then case FTransferEncoding of TE_UNKNOWN: Result := ReadUnknown; TE_IDENTITY: Result := ReadIdentity(Size); TE_CHUNKED: Result := ReadChunked; end; FDocument.Seek(0, soFromBeginning); if ToClose then begin FSock.CloseSocket; FAliveHost := ''; FAlivePort := ''; end; ParseCookies; end; function THTTPSend.ReadUnknown: Boolean; var s: string; begin Result := false; repeat s := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then WriteStrToStream(FDocument, s); until FSock.LastError <> 0; if FSock.LastError = WSAECONNRESET then begin Result := true; FSock.ResetLastError; end; end; function THTTPSend.ReadIdentity(Size: Integer): Boolean; begin if Size > 0 then begin FDownloadSize := Size; FSock.RecvStreamSize(FDocument, FTimeout, Size); FDocument.Position := FDocument.Size; Result := FSock.LastError = 0; end else Result := true; end; function THTTPSend.ReadChunked: Boolean; var s: string; Size: Integer; begin repeat repeat s := FSock.RecvString(FTimeout); until (s <> '') or (FSock.LastError <> 0); if FSock.LastError <> 0 then Break; s := Trim(SeparateLeft(s, ' ')); s := Trim(SeparateLeft(s, ';')); Size := StrToIntDef('$' + s, 0); if Size = 0 then Break; if not ReadIdentity(Size) then break; until False; Result := FSock.LastError = 0; end; procedure THTTPSend.ParseCookies; var n: integer; s: string; sn, sv: string; begin for n := 0 to FHeaders.Count - 1 do if Pos('set-cookie:', lowercase(FHeaders[n])) = 1 then begin s := SeparateRight(FHeaders[n], ':'); s := trim(SeparateLeft(s, ';')); sn := trim(SeparateLeft(s, '=')); sv := trim(SeparateRight(s, '=')); FCookies.Values[sn] := sv; end; end; procedure THTTPSend.Abort; begin FSock.StopFlag := True; end; {==============================================================================} function HttpGetText(const URL: string; const Response: TStrings): Boolean; var HTTP: THTTPSend; begin HTTP := THTTPSend.Create; try Result := HTTP.HTTPMethod('GET', URL); if Result then Response.LoadFromStream(HTTP.Document); finally HTTP.Free; end; end; function HttpGetBinary(const URL: string; const Response: TStream): Boolean; var HTTP: THTTPSend; begin HTTP := THTTPSend.Create; try Result := HTTP.HTTPMethod('GET', URL); if Result then begin Response.Seek(0, soFromBeginning); Response.CopyFrom(HTTP.Document, 0); end; finally HTTP.Free; end; end; function HttpPostBinary(const URL: string; const Data: TStream): Boolean; var HTTP: THTTPSend; begin HTTP := THTTPSend.Create; try HTTP.Document.CopyFrom(Data, 0); HTTP.MimeType := 'Application/octet-stream'; Result := HTTP.HTTPMethod('POST', URL); Data.Size := 0; if Result then begin Data.Seek(0, soFromBeginning); Data.CopyFrom(HTTP.Document, 0); end; finally HTTP.Free; end; end; function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean; var HTTP: THTTPSend; begin HTTP := THTTPSend.Create; try WriteStrToStream(HTTP.Document, URLData); HTTP.MimeType := 'application/x-www-form-urlencoded'; Result := HTTP.HTTPMethod('POST', URL); if Result then Data.CopyFrom(HTTP.Document, 0); finally HTTP.Free; end; end; function HttpPostFile(const URL, FieldName, FileName: string; const Data: TStream; const ResultData: TStrings): Boolean; var HTTP: THTTPSend; Bound, s: string; begin Bound := IntToHex(Random(MaxInt), 8) + '_Synapse_boundary'; HTTP := THTTPSend.Create; try s := '--' + Bound + CRLF; s := s + 'content-disposition: form-data; name="' + FieldName + '";'; s := s + ' filename="' + FileName +'"' + CRLF; s := s + 'Content-Type: Application/octet-string' + CRLF + CRLF; WriteStrToStream(HTTP.Document, s); HTTP.Document.CopyFrom(Data, 0); s := CRLF + '--' + Bound + '--' + CRLF; WriteStrToStream(HTTP.Document, s); HTTP.MimeType := 'multipart/form-data; boundary=' + Bound; Result := HTTP.HTTPMethod('POST', URL); if Result then ResultData.LoadFromStream(HTTP.Document); finally HTTP.Free; end; end; end. TransGUI/synapse/source/lib/ssfpc.pas0000644000000000000000000006621711466757142016614 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.003 | |==============================================================================| | Content: Socket Independent Platform Layer - FreePascal definition include | |==============================================================================| | Copyright (c)2006-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2006-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@exclude} {$IFDEF FPC} {For FreePascal 2.x.x} //{$DEFINE FORCEOLDAPI} {Note about define FORCEOLDAPI: If you activate this compiler directive, then is allways used old socket API for name resolution. If you leave this directive inactive, then the new API is used, when running system allows it. For IPv6 support you must have new API! } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$ifdef FreeBSD} {$DEFINE SOCK_HAS_SINLEN} // BSD definition of scoketaddr {$endif} {$ifdef darwin} {$DEFINE SOCK_HAS_SINLEN} // BSD definition of scoketaddr {$endif} interface uses SyncObjs, SysUtils, Classes, synafpc, BaseUnix, Unix, termio, sockets, netdb; function InitSocketInterface(stack: string): Boolean; function DestroySocketInterface: Boolean; const DLLStackName = ''; WinsockLevel = $0202; cLocalHost = '127.0.0.1'; cAnyHost = '0.0.0.0'; c6AnyHost = '::0'; c6Localhost = '::1'; cLocalHostStr = 'localhost'; type TSocket = longint; TAddrFamily = integer; TMemory = pointer; type TFDSet = Baseunix.TFDSet; PFDSet = ^TFDSet; Ptimeval = Baseunix.ptimeval; Ttimeval = Baseunix.ttimeval; const FIONREAD = termio.FIONREAD; FIONBIO = termio.FIONBIO; FIOASYNC = termio.FIOASYNC; const IPPROTO_IP = 0; { Dummy } IPPROTO_ICMP = 1; { Internet Control Message Protocol } IPPROTO_IGMP = 2; { Internet Group Management Protocol} IPPROTO_TCP = 6; { TCP } IPPROTO_UDP = 17; { User Datagram Protocol } IPPROTO_IPV6 = 41; IPPROTO_ICMPV6 = 58; IPPROTO_RM = 113; IPPROTO_RAW = 255; IPPROTO_MAX = 256; type PInAddr = ^TInAddr; TInAddr = sockets.in_addr; PSockAddrIn = ^TSockAddrIn; TSockAddrIn = sockets.TInetSockAddr; TIP_mreq = record imr_multiaddr: TInAddr; // IP multicast address of group imr_interface: TInAddr; // local IP address of interface end; PInAddr6 = ^TInAddr6; TInAddr6 = sockets.Tin6_addr; PSockAddrIn6 = ^TSockAddrIn6; TSockAddrIn6 = sockets.TInetSockAddr6; TIPv6_mreq = record ipv6mr_multiaddr: TInAddr6; // IPv6 multicast address. ipv6mr_interface: integer; // Interface index. end; const INADDR_ANY = $00000000; INADDR_LOOPBACK = $7F000001; INADDR_BROADCAST = $FFFFFFFF; INADDR_NONE = $FFFFFFFF; ADDR_ANY = INADDR_ANY; INVALID_SOCKET = TSocket(NOT(0)); SOCKET_ERROR = -1; Const IP_TOS = sockets.IP_TOS; { int; IP type of service and precedence. } IP_TTL = sockets.IP_TTL; { int; IP time to live. } IP_HDRINCL = sockets.IP_HDRINCL; { int; Header is included with data. } IP_OPTIONS = sockets.IP_OPTIONS; { ip_opts; IP per-packet options. } // IP_ROUTER_ALERT = sockets.IP_ROUTER_ALERT; { bool } IP_RECVOPTS = sockets.IP_RECVOPTS; { bool } IP_RETOPTS = sockets.IP_RETOPTS; { bool } // IP_PKTINFO = sockets.IP_PKTINFO; { bool } // IP_PKTOPTIONS = sockets.IP_PKTOPTIONS; // IP_PMTUDISC = sockets.IP_PMTUDISC; { obsolete name? } // IP_MTU_DISCOVER = sockets.IP_MTU_DISCOVER; { int; see below } // IP_RECVERR = sockets.IP_RECVERR; { bool } // IP_RECVTTL = sockets.IP_RECVTTL; { bool } // IP_RECVTOS = sockets.IP_RECVTOS; { bool } IP_MULTICAST_IF = sockets.IP_MULTICAST_IF; { in_addr; set/get IP multicast i/f } IP_MULTICAST_TTL = sockets.IP_MULTICAST_TTL; { u_char; set/get IP multicast ttl } IP_MULTICAST_LOOP = sockets.IP_MULTICAST_LOOP; { i_char; set/get IP multicast loopback } IP_ADD_MEMBERSHIP = sockets.IP_ADD_MEMBERSHIP; { ip_mreq; add an IP group membership } IP_DROP_MEMBERSHIP = sockets.IP_DROP_MEMBERSHIP; { ip_mreq; drop an IP group membership } SOL_SOCKET = sockets.SOL_SOCKET; SO_DEBUG = sockets.SO_DEBUG; SO_REUSEADDR = sockets.SO_REUSEADDR; SO_TYPE = sockets.SO_TYPE; SO_ERROR = sockets.SO_ERROR; SO_DONTROUTE = sockets.SO_DONTROUTE; SO_BROADCAST = sockets.SO_BROADCAST; SO_SNDBUF = sockets.SO_SNDBUF; SO_RCVBUF = sockets.SO_RCVBUF; SO_KEEPALIVE = sockets.SO_KEEPALIVE; SO_OOBINLINE = sockets.SO_OOBINLINE; // SO_NO_CHECK = sockets.SO_NO_CHECK; // SO_PRIORITY = sockets.SO_PRIORITY; SO_LINGER = sockets.SO_LINGER; // SO_BSDCOMPAT = sockets.SO_BSDCOMPAT; // SO_REUSEPORT = sockets.SO_REUSEPORT; // SO_PASSCRED = sockets.SO_PASSCRED; // SO_PEERCRED = sockets.SO_PEERCRED; SO_RCVLOWAT = sockets.SO_RCVLOWAT; SO_SNDLOWAT = sockets.SO_SNDLOWAT; SO_RCVTIMEO = sockets.SO_RCVTIMEO; SO_SNDTIMEO = sockets.SO_SNDTIMEO; { Security levels - as per NRL IPv6 - don't actually do anything } // SO_SECURITY_AUTHENTICATION = sockets.SO_SECURITY_AUTHENTICATION; // SO_SECURITY_ENCRYPTION_TRANSPORT = sockets.SO_SECURITY_ENCRYPTION_TRANSPORT; // SO_SECURITY_ENCRYPTION_NETWORK = sockets.SO_SECURITY_ENCRYPTION_NETWORK; // SO_BINDTODEVICE = sockets.SO_BINDTODEVICE; { Socket filtering } // SO_ATTACH_FILTER = sockets.SO_ATTACH_FILTER; // SO_DETACH_FILTER = sockets.SO_DETACH_FILTER; SOMAXCONN = 1024; IPV6_UNICAST_HOPS = sockets.IPV6_UNICAST_HOPS; IPV6_MULTICAST_IF = sockets.IPV6_MULTICAST_IF; IPV6_MULTICAST_HOPS = sockets.IPV6_MULTICAST_HOPS; IPV6_MULTICAST_LOOP = sockets.IPV6_MULTICAST_LOOP; IPV6_JOIN_GROUP = sockets.IPV6_JOIN_GROUP; IPV6_LEAVE_GROUP = sockets.IPV6_LEAVE_GROUP; const SOCK_STREAM = 1; { stream socket } SOCK_DGRAM = 2; { datagram socket } SOCK_RAW = 3; { raw-protocol interface } SOCK_RDM = 4; { reliably-delivered message } SOCK_SEQPACKET = 5; { sequenced packet stream } { TCP options. } TCP_NODELAY = $0001; { Address families. } AF_UNSPEC = 0; { unspecified } AF_INET = 2; { internetwork: UDP, TCP, etc. } AF_INET6 = 10; { Internetwork Version 6 } AF_MAX = 24; { Protocol families, same as address families for now. } PF_UNSPEC = AF_UNSPEC; PF_INET = AF_INET; PF_INET6 = AF_INET6; PF_MAX = AF_MAX; type { Structure used for manipulating linger option. } PLinger = ^TLinger; TLinger = packed record l_onoff: integer; l_linger: integer; end; const MSG_OOB = sockets.MSG_OOB; // Process out-of-band data. MSG_PEEK = sockets.MSG_PEEK; // Peek at incoming messages. {$ifdef DARWIN} MSG_NOSIGNAL = $20000; // Do not generate SIGPIPE. // Works under MAC OS X, but is undocumented, // So FPC doesn't include it {$else} MSG_NOSIGNAL = sockets.MSG_NOSIGNAL; // Do not generate SIGPIPE. {$endif} const WSAEINTR = ESysEINTR; WSAEBADF = ESysEBADF; WSAEACCES = ESysEACCES; WSAEFAULT = ESysEFAULT; WSAEINVAL = ESysEINVAL; WSAEMFILE = ESysEMFILE; WSAEWOULDBLOCK = ESysEWOULDBLOCK; WSAEINPROGRESS = ESysEINPROGRESS; WSAEALREADY = ESysEALREADY; WSAENOTSOCK = ESysENOTSOCK; WSAEDESTADDRREQ = ESysEDESTADDRREQ; WSAEMSGSIZE = ESysEMSGSIZE; WSAEPROTOTYPE = ESysEPROTOTYPE; WSAENOPROTOOPT = ESysENOPROTOOPT; WSAEPROTONOSUPPORT = ESysEPROTONOSUPPORT; WSAESOCKTNOSUPPORT = ESysESOCKTNOSUPPORT; WSAEOPNOTSUPP = ESysEOPNOTSUPP; WSAEPFNOSUPPORT = ESysEPFNOSUPPORT; WSAEAFNOSUPPORT = ESysEAFNOSUPPORT; WSAEADDRINUSE = ESysEADDRINUSE; WSAEADDRNOTAVAIL = ESysEADDRNOTAVAIL; WSAENETDOWN = ESysENETDOWN; WSAENETUNREACH = ESysENETUNREACH; WSAENETRESET = ESysENETRESET; WSAECONNABORTED = ESysECONNABORTED; WSAECONNRESET = ESysECONNRESET; WSAENOBUFS = ESysENOBUFS; WSAEISCONN = ESysEISCONN; WSAENOTCONN = ESysENOTCONN; WSAESHUTDOWN = ESysESHUTDOWN; WSAETOOMANYREFS = ESysETOOMANYREFS; WSAETIMEDOUT = ESysETIMEDOUT; WSAECONNREFUSED = ESysECONNREFUSED; WSAELOOP = ESysELOOP; WSAENAMETOOLONG = ESysENAMETOOLONG; WSAEHOSTDOWN = ESysEHOSTDOWN; WSAEHOSTUNREACH = ESysEHOSTUNREACH; WSAENOTEMPTY = ESysENOTEMPTY; WSAEPROCLIM = -1; WSAEUSERS = ESysEUSERS; WSAEDQUOT = ESysEDQUOT; WSAESTALE = ESysESTALE; WSAEREMOTE = ESysEREMOTE; WSASYSNOTREADY = -2; WSAVERNOTSUPPORTED = -3; WSANOTINITIALISED = -4; WSAEDISCON = -5; WSAHOST_NOT_FOUND = 1; WSATRY_AGAIN = 2; WSANO_RECOVERY = 3; WSANO_DATA = -6; const WSADESCRIPTION_LEN = 256; WSASYS_STATUS_LEN = 128; type PWSAData = ^TWSAData; TWSAData = packed record wVersion: Word; wHighVersion: Word; szDescription: array[0..WSADESCRIPTION_LEN] of Char; szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char; iMaxSockets: Word; iMaxUdpDg: Word; lpVendorInfo: PChar; end; function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6):boolean; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); var in6addr_any, in6addr_loopback : TInAddr6; procedure FD_CLR(Socket: TSocket; var FDSet: TFDSet); function FD_ISSET(Socket: TSocket; var FDSet: TFDSet): Boolean; procedure FD_SET(Socket: TSocket; var FDSet: TFDSet); procedure FD_ZERO(var FDSet: TFDSet); {=============================================================================} var SynSockCS: SyncObjs.TCriticalSection; SockEnhancedApi: Boolean; SockWship6Api: Boolean; type TVarSin = packed record {$ifdef SOCK_HAS_SINLEN} sin_len : cuchar; {$endif} case integer of 0: (AddressFamily: sa_family_t); 1: ( case sin_family: sa_family_t of AF_INET: (sin_port: word; sin_addr: TInAddr; sin_zero: array[0..7] of Char); AF_INET6: (sin6_port: word; sin6_flowinfo: longword; sin6_addr: TInAddr6; sin6_scope_id: longword); ); end; function SizeOfVarSin(sin: TVarSin): integer; function WSAStartup(wVersionRequired: Word; var WSData: TWSAData): Integer; function WSACleanup: Integer; function WSAGetLastError: Integer; function GetHostName: string; function Shutdown(s: TSocket; how: Integer): Integer; function SetSockOpt(s: TSocket; level, optname: Integer; optval: TMemory; optlen: Integer): Integer; function GetSockOpt(s: TSocket; level, optname: Integer; optval: TMemory; var optlen: Integer): Integer; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; function ntohs(netshort: word): word; function ntohl(netlong: longword): longword; function Listen(s: TSocket; backlog: Integer): Integer; function IoctlSocket(s: TSocket; cmd: DWORD; var arg: integer): Integer; function htons(hostshort: word): word; function htonl(hostlong: longword): longword; function GetSockName(s: TSocket; var name: TVarSin): Integer; function GetPeerName(s: TSocket; var name: TVarSin): Integer; function Connect(s: TSocket; const name: TVarSin): Integer; function CloseSocket(s: TSocket): Integer; function Bind(s: TSocket; const addr: TVarSin): Integer; function Accept(s: TSocket; var addr: TVarSin): TSocket; function Socket(af, Struc, Protocol: Integer): TSocket; function Select(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Longint; function IsNewApi(Family: integer): Boolean; function SetVarSin(var Sin: TVarSin; IP, Port: string; Family, SockProtocol, SockType: integer; PreferIP4: Boolean): integer; function GetSinIP(Sin: TVarSin): string; function GetSinPort(Sin: TVarSin): Integer; procedure ResolveNameToIP(Name: string; Family, SockProtocol, SockType: integer; const IPList: TStrings); function ResolveIPToName(IP: string; Family, SockProtocol, SockType: integer): string; function ResolvePort(Port: string; Family, SockProtocol, SockType: integer): Word; {==============================================================================} implementation function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr32[0] = 0) and (a^.u6_addr32[1] = 0) and (a^.u6_addr32[2] = 0) and (a^.u6_addr32[3] = 0)); end; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr32[0] = 0) and (a^.u6_addr32[1] = 0) and (a^.u6_addr32[2] = 0) and (a^.u6_addr8[12] = 0) and (a^.u6_addr8[13] = 0) and (a^.u6_addr8[14] = 0) and (a^.u6_addr8[15] = 1)); end; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr8[0] = $FE) and (a^.u6_addr8[1] = $80)); end; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr8[0] = $FE) and (a^.u6_addr8[1] = $C0)); end; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; begin Result := (a^.u6_addr8[0] = $FF); end; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6): boolean; begin Result := (CompareMem( a, b, sizeof(TInAddr6))); end; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); end; procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); a^.u6_addr8[15] := 1; end; {=============================================================================} function WSAStartup(wVersionRequired: Word; var WSData: TWSAData): Integer; begin with WSData do begin wVersion := wVersionRequired; wHighVersion := $202; szDescription := 'Synsock - Synapse Platform Independent Socket Layer'; szSystemStatus := 'Running on Unix/Linux by FreePascal'; iMaxSockets := 32768; iMaxUdpDg := 8192; end; Result := 0; end; function WSACleanup: Integer; begin Result := 0; end; function WSAGetLastError: Integer; begin Result := fpGetErrno; end; function FD_ISSET(Socket: TSocket; var fdset: TFDSet): Boolean; begin Result := fpFD_ISSET(socket, fdset) <> 0; end; procedure FD_SET(Socket: TSocket; var fdset: TFDSet); begin fpFD_SET(Socket, fdset); end; procedure FD_CLR(Socket: TSocket; var fdset: TFDSet); begin fpFD_CLR(Socket, fdset); end; procedure FD_ZERO(var fdset: TFDSet); begin fpFD_ZERO(fdset); end; {=============================================================================} function SizeOfVarSin(sin: TVarSin): integer; begin case sin.sin_family of AF_INET: Result := SizeOf(TSockAddrIn); AF_INET6: Result := SizeOf(TSockAddrIn6); else Result := 0; end; end; {=============================================================================} function Bind(s: TSocket; const addr: TVarSin): Integer; begin if fpBind(s, @addr, SizeOfVarSin(addr)) = 0 then Result := 0 else Result := SOCKET_ERROR; end; function Connect(s: TSocket; const name: TVarSin): Integer; begin if fpConnect(s, @name, SizeOfVarSin(name)) = 0 then Result := 0 else Result := SOCKET_ERROR; end; function GetSockName(s: TSocket; var name: TVarSin): Integer; var len: integer; begin len := SizeOf(name); FillChar(name, len, 0); Result := fpGetSockName(s, @name, @Len); end; function GetPeerName(s: TSocket; var name: TVarSin): Integer; var len: integer; begin len := SizeOf(name); FillChar(name, len, 0); Result := fpGetPeerName(s, @name, @Len); end; function GetHostName: string; begin Result := unix.GetHostName; end; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; begin Result := fpSend(s, pointer(Buf), len, flags); end; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; begin Result := fpRecv(s, pointer(Buf), len, flags); end; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; begin Result := fpSendTo(s, pointer(Buf), len, flags, @addrto, SizeOfVarSin(addrto)); end; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; var x: integer; begin x := SizeOf(from); Result := fpRecvFrom(s, pointer(Buf), len, flags, @from, @x); end; function Accept(s: TSocket; var addr: TVarSin): TSocket; var x: integer; begin x := SizeOf(addr); Result := fpAccept(s, @addr, @x); end; function Shutdown(s: TSocket; how: Integer): Integer; begin Result := fpShutdown(s, how); end; function SetSockOpt(s: TSocket; level, optname: Integer; optval: Tmemory; optlen: Integer): Integer; begin Result := fpsetsockopt(s, level, optname, pointer(optval), optlen); end; function GetSockOpt(s: TSocket; level, optname: Integer; optval: Tmemory; var optlen: Integer): Integer; begin Result := fpgetsockopt(s, level, optname, pointer(optval), @optlen); end; function ntohs(netshort: word): word; begin Result := sockets.ntohs(NetShort); end; function ntohl(netlong: longword): longword; begin Result := sockets.ntohl(NetLong); end; function Listen(s: TSocket; backlog: Integer): Integer; begin if fpListen(s, backlog) = 0 then Result := 0 else Result := SOCKET_ERROR; end; function IoctlSocket(s: TSocket; cmd: DWORD; var arg: integer): Integer; begin Result := fpIoctl(s, cmd, @arg); end; function htons(hostshort: word): word; begin Result := sockets.htons(Hostshort); end; function htonl(hostlong: longword): longword; begin Result := sockets.htonl(HostLong); end; function CloseSocket(s: TSocket): Integer; begin Result := sockets.CloseSocket(s); end; function Socket(af, Struc, Protocol: Integer): TSocket; begin Result := fpSocket(af, struc, protocol); end; function Select(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Longint; begin Result := fpSelect(nfds, readfds, writefds, exceptfds, timeout); end; {=============================================================================} function IsNewApi(Family: integer): Boolean; begin Result := SockEnhancedApi; if not Result then Result := (Family = AF_INET6) and SockWship6Api; end; function SetVarSin(var Sin: TVarSin; IP, Port: string; Family, SockProtocol, SockType: integer; PreferIP4: Boolean): integer; var TwoPass: boolean; f1, f2: integer; function GetAddr(f:integer): integer; var a4: array [1..1] of in_addr; a6: array [1..1] of Tin6_addr; he: THostEntry; begin Result := WSAEPROTONOSUPPORT; case f of AF_INET: begin if IP = cAnyHost then begin Sin.sin_family := AF_INET; Result := 0; end else begin if lowercase(IP) = cLocalHostStr then a4[1].s_addr := htonl(INADDR_LOOPBACK) else begin a4[1].s_addr := 0; Result := WSAHOST_NOT_FOUND; a4[1] := StrTonetAddr(IP); if a4[1].s_addr = INADDR_ANY then if GetHostByName(ip, he) then a4[1]:=HostToNet(he.Addr) else Resolvename(ip, a4); end; if a4[1].s_addr <> INADDR_ANY then begin Sin.sin_family := AF_INET; sin.sin_addr := a4[1]; Result := 0; end; end; end; AF_INET6: begin if IP = c6AnyHost then begin Sin.sin_family := AF_INET6; Result := 0; end else begin if lowercase(IP) = cLocalHostStr then SET_LOOPBACK_ADDR6(@a6[1]) else begin Result := WSAHOST_NOT_FOUND; SET_IN6_IF_ADDR_ANY(@a6[1]); a6[1] := StrTonetAddr6(IP); if IN6_IS_ADDR_UNSPECIFIED(@a6[1]) then Resolvename6(ip, a6); end; if not IN6_IS_ADDR_UNSPECIFIED(@a6[1]) then begin Sin.sin_family := AF_INET6; sin.sin6_addr := a6[1]; Result := 0; end; end; end; end; end; begin Result := 0; FillChar(Sin, Sizeof(Sin), 0); Sin.sin_port := Resolveport(port, family, SockProtocol, SockType); TwoPass := False; if Family = AF_UNSPEC then begin if PreferIP4 then begin f1 := AF_INET; f2 := AF_INET6; TwoPass := True; end else begin f2 := AF_INET; f1 := AF_INET6; TwoPass := True; end; end else f1 := Family; Result := GetAddr(f1); if Result <> 0 then if TwoPass then Result := GetAddr(f2); end; function GetSinIP(Sin: TVarSin): string; begin Result := ''; case sin.AddressFamily of AF_INET: begin result := NetAddrToStr(sin.sin_addr); end; AF_INET6: begin result := NetAddrToStr6(sin.sin6_addr); end; end; end; function GetSinPort(Sin: TVarSin): Integer; begin if (Sin.sin_family = AF_INET6) then Result := synsock.ntohs(Sin.sin6_port) else Result := synsock.ntohs(Sin.sin_port); end; procedure ResolveNameToIP(Name: string; Family, SockProtocol, SockType: integer; const IPList: TStrings); var x, n: integer; a4: array [1..255] of in_addr; a6: array [1..255] of Tin6_addr; he: THostEntry; begin IPList.Clear; if (family = AF_INET) or (family = AF_UNSPEC) then begin if lowercase(name) = cLocalHostStr then IpList.Add(cLocalHost) else begin a4[1] := StrTonetAddr(name); if a4[1].s_addr = INADDR_ANY then if GetHostByName(name, he) then begin a4[1]:=HostToNet(he.Addr); x := 1; end else x := Resolvename(name, a4) else x := 1; for n := 1 to x do IpList.Add(netaddrToStr(a4[n])); end; end; if (family = AF_INET6) or (family = AF_UNSPEC) then begin if lowercase(name) = cLocalHostStr then IpList.Add(c6LocalHost) else begin a6[1] := StrTonetAddr6(name); if IN6_IS_ADDR_UNSPECIFIED(@a6[1]) then x := Resolvename6(name, a6) else x := 1; for n := 1 to x do IpList.Add(netaddrToStr6(a6[n])); end; end; if IPList.Count = 0 then IPList.Add(cLocalHost); end; function ResolvePort(Port: string; Family, SockProtocol, SockType: integer): Word; var ProtoEnt: TProtocolEntry; ServEnt: TServiceEntry; begin Result := synsock.htons(StrToIntDef(Port, 0)); if Result = 0 then begin ProtoEnt.Name := ''; GetProtocolByNumber(SockProtocol, ProtoEnt); ServEnt.port := 0; GetServiceByName(Port, ProtoEnt.Name, ServEnt); Result := ServEnt.port; end; end; function ResolveIPToName(IP: string; Family, SockProtocol, SockType: integer): string; var n: integer; a4: array [1..1] of in_addr; a6: array [1..1] of Tin6_addr; a: array [1..1] of string; begin Result := IP; a4[1] := StrToNetAddr(IP); if a4[1].s_addr <> INADDR_ANY then begin //why ResolveAddress need address in HOST order? :-O n := ResolveAddress(nettohost(a4[1]), a); if n > 0 then Result := a[1]; end else begin a6[1] := StrToNetAddr6(IP); n := ResolveAddress6(a6[1], a); if n > 0 then Result := a[1]; end; end; {=============================================================================} function InitSocketInterface(stack: string): Boolean; begin SockEnhancedApi := False; SockWship6Api := False; // Libc.Signal(Libc.SIGPIPE, TSignalHandler(Libc.SIG_IGN)); Result := True; end; function DestroySocketInterface: Boolean; begin Result := True; end; initialization begin SynSockCS := SyncObjs.TCriticalSection.Create; SET_IN6_IF_ADDR_ANY (@in6addr_any); SET_LOOPBACK_ADDR6 (@in6addr_loopback); end; finalization begin SynSockCS.Free; end; {$ENDIF} TransGUI/synapse/source/lib/synachar.pas0000644000000000000000000021453211366572451017276 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 005.002.002 | |==============================================================================| | Content: Charset conversion support | |==============================================================================| | Copyright (c)1999-2004, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2004. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {: @abstract(Charset conversion support) This unit contains a routines for lot of charset conversions. It using built-in conversion tables or external Iconv library. Iconv is used when needed conversion is known by Iconv library. When Iconv library is not found or Iconv not know requested conversion, then are internal routines used for conversion. (You can disable Iconv support from your program too!) Internal routines knows all major charsets for Europe or America. For East-Asian charsets you must use Iconv library! } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit synachar; interface uses {$IFNDEF WIN32} {$IFNDEF FPC} Libc, {$ELSE} {$IFDEF FPC_USE_LIBC} Libc, {$ENDIF} {$ENDIF} {$ELSE} Windows, {$ENDIF} SysUtils, synautil, synacode, synaicnv; type {:Type with all supported charsets.} TMimeChar = (ISO_8859_1, ISO_8859_2, ISO_8859_3, ISO_8859_4, ISO_8859_5, ISO_8859_6, ISO_8859_7, ISO_8859_8, ISO_8859_9, ISO_8859_10, ISO_8859_13, ISO_8859_14, ISO_8859_15, CP1250, CP1251, CP1252, CP1253, CP1254, CP1255, CP1256, CP1257, CP1258, KOI8_R, CP895, CP852, UCS_2, UCS_4, UTF_8, UTF_7, UTF_7mod, UCS_2LE, UCS_4LE, //next is supported by Iconv only... UTF_16, UTF_16LE, UTF_32, UTF_32LE, C99, JAVA, ISO_8859_16, KOI8_U, KOI8_RU, CP862, CP866, MAC, MACCE, MACICE, MACCRO, MACRO, MACCYR, MACUK, MACGR, MACTU, MACHEB, MACAR, MACTH, ROMAN8, NEXTSTEP, ARMASCII, GEORGIAN_AC, GEORGIAN_PS, KOI8_T, MULELAO, CP1133, TIS620, CP874, VISCII, TCVN, ISO_IR_14, JIS_X0201, JIS_X0208, JIS_X0212, GB1988_80, GB2312_80, ISO_IR_165, ISO_IR_149, EUC_JP, SHIFT_JIS, CP932, ISO_2022_JP, ISO_2022_JP1, ISO_2022_JP2, GB2312, CP936, GB18030, ISO_2022_CN, ISO_2022_CNE, HZ, EUC_TW, BIG5, CP950, BIG5_HKSCS, EUC_KR, CP949, CP1361, ISO_2022_KR, CP737, CP775, CP853, CP855, CP857, CP858, CP860, CP861, CP863, CP864, CP865, CP869, CP1125); {:Set of any charsets.} TMimeSetChar = set of TMimeChar; const {:Set of charsets supported by Iconv library only.} IconvOnlyChars: set of TMimeChar = [UTF_16, UTF_16LE, UTF_32, UTF_32LE, C99, JAVA, ISO_8859_16, KOI8_U, KOI8_RU, CP862, CP866, MAC, MACCE, MACICE, MACCRO, MACRO, MACCYR, MACUK, MACGR, MACTU, MACHEB, MACAR, MACTH, ROMAN8, NEXTSTEP, ARMASCII, GEORGIAN_AC, GEORGIAN_PS, KOI8_T, MULELAO, CP1133, TIS620, CP874, VISCII, TCVN, ISO_IR_14, JIS_X0201, JIS_X0208, JIS_X0212, GB1988_80, GB2312_80, ISO_IR_165, ISO_IR_149, EUC_JP, SHIFT_JIS, CP932, ISO_2022_JP, ISO_2022_JP1, ISO_2022_JP2, GB2312, CP936, GB18030, ISO_2022_CN, ISO_2022_CNE, HZ, EUC_TW, BIG5, CP950, BIG5_HKSCS, EUC_KR, CP949, CP1361, ISO_2022_KR, CP737, CP775, CP853, CP855, CP857, CP858, CP860, CP861, CP863, CP864, CP865, CP869, CP1125]; {:Set of charsets supported by internal routines only.} NoIconvChars: set of TMimeChar = [CP895, UTF_7mod]; {:null character replace table. (Usable for disable charater replacing.)} Replace_None: array[0..0] of Word = (0); {:Character replace table for remove Czech diakritics.} Replace_Czech: array[0..59] of Word = ( $00E1, $0061, $010D, $0063, $010F, $0064, $010E, $0044, $00E9, $0065, $011B, $0065, $00ED, $0069, $0148, $006E, $00F3, $006F, $0159, $0072, $0161, $0073, $0165, $0074, $00FA, $0075, $016F, $0075, $00FD, $0079, $017E, $007A, $00C1, $0041, $010C, $0043, $00C9, $0045, $011A, $0045, $00CD, $0049, $0147, $004E, $00D3, $004F, $0158, $0052, $0160, $0053, $0164, $0054, $00DA, $0055, $016E, $0055, $00DD, $0059, $017D, $005A ); var {:By this you can generally disable/enable Iconv support.} DisableIconv: Boolean = False; {:Default set of charsets for @link(IdealCharsetCoding) function.} IdealCharsets: TMimeSetChar = [ISO_8859_1, ISO_8859_2, ISO_8859_3, ISO_8859_4, ISO_8859_5, ISO_8859_6, ISO_8859_7, ISO_8859_8, ISO_8859_9, ISO_8859_10, KOI8_R, KOI8_U {$IFNDEF CIL} //error URW778 ??? :-O , GB2312, EUC_KR, ISO_2022_JP, EUC_TW {$ENDIF} ]; {==============================================================================} {:Convert Value from one charset to another. See: @link(CharsetConversionEx)} function CharsetConversion(const Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeChar): AnsiString; {:Convert Value from one charset to another with additional character conversion. see: @link(Replace_None) and @link(Replace_Czech)} function CharsetConversionEx(const Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeChar; const TransformTable: array of Word): AnsiString; {:Convert Value from one charset to another with additional character conversion. This funtion is similar to @link(CharsetConversionEx), but you can disable transliteration of unconvertible characters.} function CharsetConversionTrans(Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeChar; const TransformTable: array of Word; Translit: Boolean): AnsiString; {:Returns charset used by operating system.} function GetCurCP: TMimeChar; {:Returns charset used by operating system as OEM charset. (in Windows DOS box, for example)} function GetCurOEMCP: TMimeChar; {:Converting string with charset name to TMimeChar.} function GetCPFromID(Value: AnsiString): TMimeChar; {:Converting TMimeChar to string with name of charset.} function GetIDFromCP(Value: TMimeChar): AnsiString; {:return @true when value need to be converted. (It is not 7-bit ASCII)} function NeedCharsetConversion(const Value: AnsiString): Boolean; {:Finding best target charset from set of TMimeChars with minimal count of unconvertible characters.} function IdealCharsetCoding(const Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeSetChar): TMimeChar; {:Return BOM (Byte Order Mark) for given unicode charset.} function GetBOM(Value: TMimeChar): AnsiString; {:Convert binary string with unicode content to WideString.} function StringToWide(const Value: AnsiString): WideString; {:Convert WideString to binary string with unicode content.} function WideToString(const Value: WideString): AnsiString; {==============================================================================} implementation //character transcoding tables X to UCS-2 { //dummy table $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $00A1, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $00AA, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00BA, $00BB, $00BC, $00BD, $00BE, $00BF, $00C0, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $00D0, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $00DD, $00DE, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $00F0, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $00FD, $00FE, $00FF } const {Latin-1 Danish, Dutch, English, Faeroese, Finnish, French, German, Icelandic, Irish, Italian, Norwegian, Portuguese, Spanish and Swedish. } CharISO_8859_1: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $00A1, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $00AA, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00BA, $00BB, $00BC, $00BD, $00BE, $00BF, $00C0, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $00D0, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $00DD, $00DE, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $00F0, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $00FD, $00FE, $00FF ); {Latin-2 Albanian, Czech, English, German, Hungarian, Polish, Rumanian, Serbo-Croatian, Slovak, Slovene and Swedish. } CharISO_8859_2: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $0104, $02D8, $0141, $00A4, $013D, $015A, $00A7, $00A8, $0160, $015E, $0164, $0179, $00AD, $017D, $017B, $00B0, $0105, $02DB, $0142, $00B4, $013E, $015B, $02C7, $00B8, $0161, $015F, $0165, $017A, $02DD, $017E, $017C, $0154, $00C1, $00C2, $0102, $00C4, $0139, $0106, $00C7, $010C, $00C9, $0118, $00CB, $011A, $00CD, $00CE, $010E, $0110, $0143, $0147, $00D3, $00D4, $0150, $00D6, $00D7, $0158, $016E, $00DA, $0170, $00DC, $00DD, $0162, $00DF, $0155, $00E1, $00E2, $0103, $00E4, $013A, $0107, $00E7, $010D, $00E9, $0119, $00EB, $011B, $00ED, $00EE, $010F, $0111, $0144, $0148, $00F3, $00F4, $0151, $00F6, $00F7, $0159, $016F, $00FA, $0171, $00FC, $00FD, $0163, $02D9 ); {Latin-3 Afrikaans, Catalan, English, Esperanto, French, Galician, German, Italian, Maltese and Turkish. } CharISO_8859_3: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $0126, $02D8, $00A3, $00A4, $FFFD, $0124, $00A7, $00A8, $0130, $015E, $011E, $0134, $00AD, $FFFD, $017B, $00B0, $0127, $00B2, $00B3, $00B4, $00B5, $0125, $00B7, $00B8, $0131, $015F, $011F, $0135, $00BD, $FFFD, $017C, $00C0, $00C1, $00C2, $FFFD, $00C4, $010A, $0108, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $FFFD, $00D1, $00D2, $00D3, $00D4, $0120, $00D6, $00D7, $011C, $00D9, $00DA, $00DB, $00DC, $016C, $015C, $00DF, $00E0, $00E1, $00E2, $FFFD, $00E4, $010B, $0109, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $FFFD, $00F1, $00F2, $00F3, $00F4, $0121, $00F6, $00F7, $011D, $00F9, $00FA, $00FB, $00FC, $016D, $015D, $02D9 ); {Latin-4 Danish, English, Estonian, Finnish, German, Greenlandic, Lappish, Latvian, Lithuanian, Norwegian and Swedish. } CharISO_8859_4: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $0104, $0138, $0156, $00A4, $0128, $013B, $00A7, $00A8, $0160, $0112, $0122, $0166, $00AD, $017D, $00AF, $00B0, $0105, $02DB, $0157, $00B4, $0129, $013C, $02C7, $00B8, $0161, $0113, $0123, $0167, $014A, $017E, $014B, $0100, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $012E, $010C, $00C9, $0118, $00CB, $0116, $00CD, $00CE, $012A, $0110, $0145, $014C, $0136, $00D4, $00D5, $00D6, $00D7, $00D8, $0172, $00DA, $00DB, $00DC, $0168, $016A, $00DF, $0101, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $012F, $010D, $00E9, $0119, $00EB, $0117, $00ED, $00EE, $012B, $0111, $0146, $014D, $0137, $00F4, $00F5, $00F6, $00F7, $00F8, $0173, $00FA, $00FB, $00FC, $0169, $016B, $02D9 ); {CYRILLIC Bulgarian, Bielorussian, English, Macedonian, Russian, Serbo-Croatian and Ukrainian. } CharISO_8859_5: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $0401, $0402, $0403, $0404, $0405, $0406, $0407, $0408, $0409, $040A, $040B, $040C, $00AD, $040E, $040F, $0410, $0411, $0412, $0413, $0414, $0415, $0416, $0417, $0418, $0419, $041A, $041B, $041C, $041D, $041E, $041F, $0420, $0421, $0422, $0423, $0424, $0425, $0426, $0427, $0428, $0429, $042A, $042B, $042C, $042D, $042E, $042F, $0430, $0431, $0432, $0433, $0434, $0435, $0436, $0437, $0438, $0439, $043A, $043B, $043C, $043D, $043E, $043F, $0440, $0441, $0442, $0443, $0444, $0445, $0446, $0447, $0448, $0449, $044A, $044B, $044C, $044D, $044E, $044F, $2116, $0451, $0452, $0453, $0454, $0455, $0456, $0457, $0458, $0459, $045A, $045B, $045C, $00A7, $045E, $045F ); {ARABIC } CharISO_8859_6: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $FFFD, $FFFD, $FFFD, $00A4, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $060C, $00AD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $061B, $FFFD, $FFFD, $FFFD, $061F, $FFFD, $0621, $0622, $0623, $0624, $0625, $0626, $0627, $0628, $0629, $062A, $062B, $062C, $062D, $062E, $062F, $0630, $0631, $0632, $0633, $0634, $0635, $0636, $0637, $0638, $0639, $063A, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $0640, $0641, $0642, $0643, $0644, $0645, $0646, $0647, $0648, $0649, $064A, $064B, $064C, $064D, $064E, $064F, $0650, $0651, $0652, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD ); {GREEK } CharISO_8859_7: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $2018, $2019, $00A3, $FFFD, $FFFD, $00A6, $00A7, $00A8, $00A9, $FFFD, $00AB, $00AC, $00AD, $FFFD, $2015, $00B0, $00B1, $00B2, $00B3, $0384, $0385, $0386, $00B7, $0388, $0389, $038A, $00BB, $038C, $00BD, $038E, $038F, $0390, $0391, $0392, $0393, $0394, $0395, $0396, $0397, $0398, $0399, $039A, $039B, $039C, $039D, $039E, $039F, $03A0, $03A1, $FFFD, $03A3, $03A4, $03A5, $03A6, $03A7, $03A8, $03A9, $03AA, $03AB, $03AC, $03AD, $03AE, $03AF, $03B0, $03B1, $03B2, $03B3, $03B4, $03B5, $03B6, $03B7, $03B8, $03B9, $03BA, $03BB, $03BC, $03BD, $03BE, $03BF, $03C0, $03C1, $03C2, $03C3, $03C4, $03C5, $03C6, $03C7, $03C8, $03C9, $03CA, $03CB, $03CC, $03CD, $03CE, $FFFD ); {HEBREW } CharISO_8859_8: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $FFFD, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $00D7, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00F7, $00BB, $00BC, $00BD, $00BE, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $2017, $05D0, $05D1, $05D2, $05D3, $05D4, $05D5, $05D6, $05D7, $05D8, $05D9, $05DA, $05DB, $05DC, $05DD, $05DE, $05DF, $05E0, $05E1, $05E2, $05E3, $05E4, $05E5, $05E6, $05E7, $05E8, $05E9, $05EA, $FFFD, $FFFD, $200E, $200F, $FFFD ); {Latin-5 English, Finnish, French, German, Irish, Italian, Norwegian, Portuguese, Spanish, Swedish and Turkish. } CharISO_8859_9: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $0104, $02D8, $0141, $00A4, $013D, $015A, $00A7, $00A8, $0160, $015E, $0164, $0179, $00AD, $017D, $017B, $00B0, $0105, $02DB, $0142, $00B4, $013E, $015B, $02C7, $00B8, $0161, $015F, $0165, $017A, $02DD, $017E, $017C, $0154, $00C1, $00C2, $0102, $00C4, $0139, $0106, $00C7, $010C, $00C9, $0118, $00CB, $011A, $00CD, $00CE, $010E, $011E, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $0130, $015E, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $011F, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $0131, $015F, $00FF ); {Latin-6 Danish, English, Estonian, Faeroese, Finnish, German, Greenlandic, Icelandic, Lappish, Latvian, Lithuanian, Norwegian and Swedish. } CharISO_8859_10: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $0104, $0112, $0122, $012A, $0128, $0136, $00A7, $013B, $0110, $0160, $0166, $017D, $00AD, $016A, $014A, $00B0, $0105, $0113, $0123, $012B, $0129, $0137, $00B7, $013C, $0111, $0161, $0167, $017E, $2015, $016B, $014B, $0100, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $012E, $010C, $00C9, $0118, $00CB, $0116, $00CD, $00CE, $00CF, $00D0, $0145, $014C, $00D3, $00D4, $00D5, $00D6, $0168, $00D8, $0172, $00DA, $00DB, $00DC, $00DD, $00DE, $00DF, $0101, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $012F, $010D, $00E9, $0119, $00EB, $0117, $00ED, $00EE, $00EF, $00F0, $0146, $014D, $00F3, $00F4, $00F5, $00F6, $0169, $00F8, $0173, $00FA, $00FB, $00FC, $00FD, $00FE, $0138 ); CharISO_8859_13: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $201D, $00A2, $00A3, $00A4, $201E, $00A6, $00A7, $00D8, $00A9, $0156, $00AB, $00AC, $00AD, $00AE, $00C6, $00B0, $00B1, $00B2, $00B3, $201C, $00B5, $00B6, $00B7, $00F8, $00B9, $0157, $00BB, $00BC, $00BD, $00BE, $00E6, $0104, $012E, $0100, $0106, $00C4, $00C5, $0118, $0112, $010C, $00C9, $0179, $0116, $0122, $0136, $012A, $013B, $0160, $0143, $0145, $00D3, $014C, $00D5, $00D6, $00D7, $0172, $0141, $015A, $016A, $00DC, $017B, $017D, $00DF, $0105, $012F, $0101, $0107, $00E4, $00E5, $0119, $0113, $010D, $00E9, $017A, $0117, $0123, $0137, $012B, $013C, $0161, $0144, $0146, $00F3, $014D, $00F5, $00F6, $00F7, $0173, $0142, $015B, $016B, $00FC, $017C, $017E, $2019 ); CharISO_8859_14: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $1E02, $1E03, $00A3, $010A, $010B, $1E0A, $00A7, $1E80, $00A9, $1E82, $1E0B, $1EF2, $00AD, $00AE, $0178, $1E1E, $1E1F, $0120, $0121, $1E40, $1E41, $00B6, $1E56, $1E81, $1E57, $1E83, $1E60, $1EF3, $1E84, $1E85, $1E61, $00C0, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $0174, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $1E6A, $00D8, $00D9, $00DA, $00DB, $00DC, $00DD, $0176, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $0175, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $1E6B, $00F8, $00F9, $00FA, $00FB, $00FC, $00FD, $0177, $00FF ); CharISO_8859_15: array[128..255] of Word = ( $0080, $0081, $0082, $0083, $0084, $0085, $0086, $0087, $0088, $0089, $008A, $008B, $008C, $008D, $008E, $008F, $0090, $0091, $0092, $0093, $0094, $0095, $0096, $0097, $0098, $0099, $009A, $009B, $009C, $009D, $009E, $009F, $00A0, $00A1, $00A2, $00A3, $20AC, $00A5, $0160, $00A7, $0161, $00A9, $00AA, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $017D, $00B5, $00B6, $00B7, $017E, $00B9, $00BA, $00BB, $0152, $0153, $0178, $00BF, $00C0, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $00D0, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $00DD, $00DE, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $00F0, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $00FD, $00FE, $00FF ); {Eastern European } CharCP_1250: array[128..255] of Word = ( $20AC, $FFFD, $201A, $FFFD, $201E, $2026, $2020, $2021, $FFFD, $2030, $0160, $2039, $015A, $0164, $017D, $0179, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $FFFD, $2122, $0161, $203A, $015B, $0165, $017E, $017A, $00A0, $02C7, $02D8, $0141, $00A4, $0104, $00A6, $00A7, $00A8, $00A9, $015E, $00AB, $00AC, $00AD, $00AE, $017B, $00B0, $00B1, $02DB, $0142, $00B4, $00B5, $00B6, $00B7, $00B8, $0105, $015F, $00BB, $013D, $02DD, $013E, $017C, $0154, $00C1, $00C2, $0102, $00C4, $0139, $0106, $00C7, $010C, $00C9, $0118, $00CB, $011A, $00CD, $00CE, $010E, $0110, $0143, $0147, $00D3, $00D4, $0150, $00D6, $00D7, $0158, $016E, $00DA, $0170, $00DC, $00DD, $0162, $00DF, $0155, $00E1, $00E2, $0103, $00E4, $013A, $0107, $00E7, $010D, $00E9, $0119, $00EB, $011B, $00ED, $00EE, $010F, $0111, $0144, $0148, $00F3, $00F4, $0151, $00F6, $00F7, $0159, $016F, $00FA, $0171, $00FC, $00FD, $0163, $02D9 ); {Cyrillic } CharCP_1251: array[128..255] of Word = ( $0402, $0403, $201A, $0453, $201E, $2026, $2020, $2021, $20AC, $2030, $0409, $2039, $040A, $040C, $040B, $040F, $0452, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $FFFD, $2122, $0459, $203A, $045A, $045C, $045B, $045F, $00A0, $040E, $045E, $0408, $00A4, $0490, $00A6, $00A7, $0401, $00A9, $0404, $00AB, $00AC, $00AD, $00AE, $0407, $00B0, $00B1, $0406, $0456, $0491, $00B5, $00B6, $00B7, $0451, $2116, $0454, $00BB, $0458, $0405, $0455, $0457, $0410, $0411, $0412, $0413, $0414, $0415, $0416, $0417, $0418, $0419, $041A, $041B, $041C, $041D, $041E, $041F, $0420, $0421, $0422, $0423, $0424, $0425, $0426, $0427, $0428, $0429, $042A, $042B, $042C, $042D, $042E, $042F, $0430, $0431, $0432, $0433, $0434, $0435, $0436, $0437, $0438, $0439, $043A, $043B, $043C, $043D, $043E, $043F, $0440, $0441, $0442, $0443, $0444, $0445, $0446, $0447, $0448, $0449, $044A, $044B, $044C, $044D, $044E, $044F ); {Latin-1 (US, Western Europe) } CharCP_1252: array[128..255] of Word = ( $20AC, $FFFD, $201A, $0192, $201E, $2026, $2020, $2021, $02C6, $2030, $0160, $2039, $0152, $FFFD, $017D, $FFFD, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $02DC, $2122, $0161, $203A, $0153, $FFFD, $017E, $0178, $00A0, $00A1, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $00AA, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00BA, $00BB, $00BC, $00BD, $00BE, $00BF, $00C0, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $00D0, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $00DD, $00DE, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $00F0, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $00FD, $00FE, $00FF ); {Greek } CharCP_1253: array[128..255] of Word = ( $20AC, $FFFD, $201A, $0192, $201E, $2026, $2020, $2021, $FFFD, $2030, $FFFD, $2039, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $FFFD, $2122, $FFFD, $203A, $FFFD, $FFFD, $FFFD, $FFFD, $00A0, $0385, $0386, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $FFFD, $00AB, $00AC, $00AD, $00AE, $2015, $00B0, $00B1, $00B2, $00B3, $0384, $00B5, $00B6, $00B7, $0388, $0389, $038A, $00BB, $038C, $00BD, $038E, $038F, $0390, $0391, $0392, $0393, $0394, $0395, $0396, $0397, $0398, $0399, $039A, $039B, $039C, $039D, $039E, $039F, $03A0, $03A1, $FFFD, $03A3, $03A4, $03A5, $03A6, $03A7, $03A8, $03A9, $03AA, $03AB, $03AC, $03AD, $03AE, $03AF, $03B0, $03B1, $03B2, $03B3, $03B4, $03B5, $03B6, $03B7, $03B8, $03B9, $03BA, $03BB, $03BC, $03BD, $03BE, $03BF, $03C0, $03C1, $03C2, $03C3, $03C4, $03C5, $03C6, $03C7, $03C8, $03C9, $03CA, $03CB, $03CC, $03CD, $03CE, $FFFD ); {Turkish } CharCP_1254: array[128..255] of Word = ( $20AC, $FFFD, $201A, $0192, $201E, $2026, $2020, $2021, $02C6, $2030, $0160, $2039, $0152, $FFFD, $FFFD, $FFFD, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $02DC, $2122, $0161, $203A, $0153, $FFFD, $FFFD, $0178, $00A0, $00A1, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $00AA, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00BA, $00BB, $00BC, $00BD, $00BE, $00BF, $00C0, $00C1, $00C2, $00C3, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $00CC, $00CD, $00CE, $00CF, $011E, $00D1, $00D2, $00D3, $00D4, $00D5, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $0130, $015E, $00DF, $00E0, $00E1, $00E2, $00E3, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $00EC, $00ED, $00EE, $00EF, $011F, $00F1, $00F2, $00F3, $00F4, $00F5, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $0131, $015F, $00FF ); {Hebrew } CharCP_1255: array[128..255] of Word = ( $20AC, $FFFD, $201A, $0192, $201E, $2026, $2020, $2021, $02C6, $2030, $FFFD, $2039, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $02DC, $2122, $FFFD, $203A, $FFFD, $FFFD, $FFFD, $FFFD, $00A0, $00A1, $00A2, $00A3, $20AA, $00A5, $00A6, $00A7, $00A8, $00A9, $00D7, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00F7, $00BB, $00BC, $00BD, $00BE, $00BF, $05B0, $05B1, $05B2, $05B3, $05B4, $05B5, $05B6, $05B7, $05B8, $05B9, $FFFD, $05BB, $05BC, $05BD, $05BE, $05BF, $05C0, $05C1, $05C2, $05C3, $05F0, $05F1, $05F2, $05F3, $05F4, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $FFFD, $05D0, $05D1, $05D2, $05D3, $05D4, $05D5, $05D6, $05D7, $05D8, $05D9, $05DA, $05DB, $05DC, $05DD, $05DE, $05DF, $05E0, $05E1, $05E2, $05E3, $05E4, $05E5, $05E6, $05E7, $05E8, $05E9, $05EA, $FFFD, $FFFD, $200E, $200F, $FFFD ); {Arabic } CharCP_1256: array[128..255] of Word = ( $20AC, $067E, $201A, $0192, $201E, $2026, $2020, $2021, $02C6, $2030, $0679, $2039, $0152, $0686, $0698, $0688, $06AF, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $06A9, $2122, $0691, $203A, $0153, $200C, $200D, $06BA, $00A0, $060C, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $06BE, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $061B, $00BB, $00BC, $00BD, $00BE, $061F, $06C1, $0621, $0622, $0623, $0624, $0625, $0626, $0627, $0628, $0629, $062A, $062B, $062C, $062D, $062E, $062F, $0630, $0631, $0632, $0633, $0634, $0635, $0636, $00D7, $0637, $0638, $0639, $063A, $0640, $0641, $0642, $0643, $00E0, $0644, $00E2, $0645, $0646, $0647, $0648, $00E7, $00E8, $00E9, $00EA, $00EB, $0649, $064A, $00EE, $00EF, $064B, $064C, $064D, $064E, $00F4, $064F, $0650, $00F7, $0651, $00F9, $0652, $00FB, $00FC, $200E, $200F, $06D2 ); {Baltic } CharCP_1257: array[128..255] of Word = ( $20AC, $FFFD, $201A, $FFFD, $201E, $2026, $2020, $2021, $FFFD, $2030, $FFFD, $2039, $FFFD, $00A8, $02C7, $00B8, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $FFFD, $2122, $FFFD, $203A, $FFFD, $00AF, $02DB, $FFFD, $00A0, $FFFD, $00A2, $00A3, $00A4, $FFFD, $00A6, $00A7, $00D8, $00A9, $0156, $00AB, $00AC, $00AD, $00AE, $00C6, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00F8, $00B9, $0157, $00BB, $00BC, $00BD, $00BE, $00E6, $0104, $012E, $0100, $0106, $00C4, $00C5, $0118, $0112, $010C, $00C9, $0179, $0116, $0122, $0136, $012A, $013B, $0160, $0143, $0145, $00D3, $014C, $00D5, $00D6, $00D7, $0172, $0141, $015A, $016A, $00DC, $017B, $017D, $00DF, $0105, $012F, $0101, $0107, $00E4, $00E5, $0119, $0113, $010D, $00E9, $017A, $0117, $0123, $0137, $012B, $013C, $0161, $0144, $0146, $00F3, $014D, $00F5, $00F6, $00F7, $0173, $0142, $015B, $016B, $00FC, $017C, $017E, $02D9 ); {Vietnamese } CharCP_1258: array[128..255] of Word = ( $20AC, $FFFD, $201A, $0192, $201E, $2026, $2020, $2021, $02C6, $2030, $FFFD, $2039, $0152, $FFFD, $FFFD, $FFFD, $FFFD, $2018, $2019, $201C, $201D, $2022, $2013, $2014, $02DC, $2122, $FFFD, $203A, $0153, $FFFD, $FFFD, $0178, $00A0, $00A1, $00A2, $00A3, $00A4, $00A5, $00A6, $00A7, $00A8, $00A9, $00AA, $00AB, $00AC, $00AD, $00AE, $00AF, $00B0, $00B1, $00B2, $00B3, $00B4, $00B5, $00B6, $00B7, $00B8, $00B9, $00BA, $00BB, $00BC, $00BD, $00BE, $00BF, $00C0, $00C1, $00C2, $0102, $00C4, $00C5, $00C6, $00C7, $00C8, $00C9, $00CA, $00CB, $0300, $00CD, $00CE, $00CF, $0110, $00D1, $0309, $00D3, $00D4, $01A0, $00D6, $00D7, $00D8, $00D9, $00DA, $00DB, $00DC, $01AF, $0303, $00DF, $00E0, $00E1, $00E2, $0103, $00E4, $00E5, $00E6, $00E7, $00E8, $00E9, $00EA, $00EB, $0301, $00ED, $00EE, $00EF, $0111, $00F1, $0323, $00F3, $00F4, $01A1, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $01B0, $20AB, $00FF ); {Cyrillic } CharKOI8_R: array[128..255] of Word = ( $2500, $2502, $250C, $2510, $2514, $2518, $251C, $2524, $252C, $2534, $253C, $2580, $2584, $2588, $258C, $2590, $2591, $2592, $2593, $2320, $25A0, $2219, $221A, $2248, $2264, $2265, $00A0, $2321, $00B0, $00B2, $00B7, $00F7, $2550, $2551, $2552, $0451, $2553, $2554, $2555, $2556, $2557, $2558, $2559, $255A, $255B, $255C, $255D, $255E, $255F, $2560, $2561, $0401, $2562, $2563, $2564, $2565, $2566, $2567, $2568, $2569, $256A, $256B, $256C, $00A9, $044E, $0430, $0431, $0446, $0434, $0435, $0444, $0433, $0445, $0438, $0439, $043A, $043B, $043C, $043D, $043E, $043F, $044F, $0440, $0441, $0442, $0443, $0436, $0432, $044C, $044B, $0437, $0448, $044D, $0449, $0447, $044A, $042E, $0410, $0411, $0426, $0414, $0415, $0424, $0413, $0425, $0418, $0419, $041A, $041B, $041C, $041D, $041E, $041F, $042F, $0420, $0421, $0422, $0423, $0416, $0412, $042C, $042B, $0417, $0428, $042D, $0429, $0427, $042A ); {Czech (Kamenicky) } CharCP_895: array[128..255] of Word = ( $010C, $00FC, $00E9, $010F, $00E4, $010E, $0164, $010D, $011B, $011A, $0139, $00CD, $013E, $013A, $00C4, $00C1, $00C9, $017E, $017D, $00F4, $00F6, $00D3, $016F, $00DA, $00FD, $00D6, $00DC, $0160, $013D, $00DD, $0158, $0165, $00E1, $00ED, $00F3, $00FA, $0148, $0147, $016E, $00D4, $0161, $0159, $0155, $0154, $00BC, $00A7, $00AB, $00BB, $2591, $2592, $2593, $2502, $2524, $2561, $2562, $2556, $2555, $2563, $2551, $2557, $255D, $255C, $255B, $2510, $2514, $2534, $252C, $251C, $2500, $253C, $255E, $255F, $255A, $2554, $2569, $2566, $2560, $2550, $256C, $2567, $2568, $2564, $2565, $2559, $2558, $2552, $2553, $256B, $256A, $2518, $250C, $2588, $2584, $258C, $2590, $2580, $03B1, $03B2, $0393, $03C0, $03A3, $03C3, $03BC, $03C4, $03A6, $0398, $03A9, $03B4, $221E, $2205, $03B5, $2229, $2261, $00B1, $2265, $2264, $2320, $2321, $00F7, $2248, $2218, $00B7, $2219, $221A, $207F, $00B2, $25A0, $00A0 ); {Eastern European } CharCP_852: array[128..255] of Word = ( $00C7, $00FC, $00E9, $00E2, $00E4, $016F, $0107, $00E7, $0142, $00EB, $0150, $0151, $00EE, $0179, $00C4, $0106, $00C9, $0139, $013A, $00F4, $00F6, $013D, $013E, $015A, $015B, $00D6, $00DC, $0164, $0165, $0141, $00D7, $010D, $00E1, $00ED, $00F3, $00FA, $0104, $0105, $017D, $017E, $0118, $0119, $00AC, $017A, $010C, $015F, $00AB, $00BB, $2591, $2592, $2593, $2502, $2524, $00C1, $00C2, $011A, $015E, $2563, $2551, $2557, $255D, $017B, $017C, $2510, $2514, $2534, $252C, $251C, $2500, $253C, $0102, $0103, $255A, $2554, $2569, $2566, $2560, $2550, $256C, $00A4, $0111, $0110, $010E, $00CB, $010F, $0147, $00CD, $00CE, $011B, $2518, $250C, $2588, $2584, $0162, $016E, $2580, $00D3, $00DF, $00D4, $0143, $0144, $0148, $0160, $0161, $0154, $00DA, $0155, $0170, $00FD, $00DD, $0163, $00B4, $00AD, $02DD, $02DB, $02C7, $02D8, $00A7, $00F7, $00B8, $00B0, $00A8, $02D9, $0171, $0158, $0159, $25A0, $00A0 ); {==============================================================================} type TIconvChar = record Charset: TMimeChar; CharName: string; end; TIconvArr = array [0..112] of TIconvChar; const NotFoundChar = '_'; var SetTwo: set of TMimeChar = [UCS_2, UCS_2LE, UTF_7, UTF_7mod]; SetFour: set of TMimeChar = [UCS_4, UCS_4LE, UTF_8]; SetLE: set of TMimeChar = [UCS_2LE, UCS_4LE]; IconvArr: TIconvArr; {==============================================================================} function FindIconvID(const Value, Charname: string): Boolean; var s: string; begin Result := True; //exact match if Value = Charname then Exit; //Value is on begin of charname s := Value + ' '; if s = Copy(Charname, 1, Length(s)) then Exit; //Value is on end of charname s := ' ' + Value; if s = Copy(Charname, Length(Charname) - Length(s) + 1, Length(s)) then Exit; //value is somewhere inside charname if Pos( s + ' ', Charname) > 0 then Exit; Result := False; end; function GetCPFromIconvID(Value: AnsiString): TMimeChar; var n: integer; begin Result := ISO_8859_1; Value := UpperCase(Value); for n := 0 to High(IconvArr) do if FindIconvID(Value, IconvArr[n].Charname) then begin Result := IconvArr[n].Charset; Break; end; end; {==============================================================================} function GetIconvIDFromCP(Value: TMimeChar): AnsiString; var n: integer; begin Result := 'ISO-8859-1'; for n := 0 to High(IconvArr) do if IconvArr[n].Charset = Value then begin Result := Separateleft(IconvArr[n].Charname, ' '); Break; end; end; {==============================================================================} function ReplaceUnicode(Value: Word; const TransformTable: array of Word): Word; var n: integer; begin if High(TransformTable) <> 0 then for n := 0 to High(TransformTable) do if not odd(n) then if TransformTable[n] = Value then begin Value := TransformTable[n+1]; break; end; Result := Value; end; {==============================================================================} procedure CopyArray(const SourceTable: array of Word; var TargetTable: array of Word); var n: Integer; begin for n := 0 to 127 do TargetTable[n] := SourceTable[n]; end; {==============================================================================} procedure GetArray(CharSet: TMimeChar; var Result: array of Word); begin case CharSet of ISO_8859_2: CopyArray(CharISO_8859_2, Result); ISO_8859_3: CopyArray(CharISO_8859_3, Result); ISO_8859_4: CopyArray(CharISO_8859_4, Result); ISO_8859_5: CopyArray(CharISO_8859_5, Result); ISO_8859_6: CopyArray(CharISO_8859_6, Result); ISO_8859_7: CopyArray(CharISO_8859_7, Result); ISO_8859_8: CopyArray(CharISO_8859_8, Result); ISO_8859_9: CopyArray(CharISO_8859_9, Result); ISO_8859_10: CopyArray(CharISO_8859_10, Result); ISO_8859_13: CopyArray(CharISO_8859_13, Result); ISO_8859_14: CopyArray(CharISO_8859_14, Result); ISO_8859_15: CopyArray(CharISO_8859_15, Result); CP1250: CopyArray(CharCP_1250, Result); CP1251: CopyArray(CharCP_1251, Result); CP1252: CopyArray(CharCP_1252, Result); CP1253: CopyArray(CharCP_1253, Result); CP1254: CopyArray(CharCP_1254, Result); CP1255: CopyArray(CharCP_1255, Result); CP1256: CopyArray(CharCP_1256, Result); CP1257: CopyArray(CharCP_1257, Result); CP1258: CopyArray(CharCP_1258, Result); KOI8_R: CopyArray(CharKOI8_R, Result); CP895: CopyArray(CharCP_895, Result); CP852: CopyArray(CharCP_852, Result); else CopyArray(CharISO_8859_1, Result); end; end; {==============================================================================} procedure ReadMulti(const Value: AnsiString; var Index: Integer; mb: Byte; var b1, b2, b3, b4: Byte; le: boolean); Begin b1 := 0; b2 := 0; b3 := 0; b4 := 0; if Index < 0 then Index := 1; if mb > 4 then mb := 1; if (Index + mb - 1) <= Length(Value) then begin if le then Case mb Of 1: b1 := Ord(Value[Index]); 2: Begin b1 := Ord(Value[Index]); b2 := Ord(Value[Index + 1]); End; 3: Begin b1 := Ord(Value[Index]); b2 := Ord(Value[Index + 1]); b3 := Ord(Value[Index + 2]); End; 4: Begin b1 := Ord(Value[Index]); b2 := Ord(Value[Index + 1]); b3 := Ord(Value[Index + 2]); b4 := Ord(Value[Index + 3]); End; end else Case mb Of 1: b1 := Ord(Value[Index]); 2: Begin b2 := Ord(Value[Index]); b1 := Ord(Value[Index + 1]); End; 3: Begin b3 := Ord(Value[Index]); b2 := Ord(Value[Index + 1]); b1 := Ord(Value[Index + 2]); End; 4: Begin b4 := Ord(Value[Index]); b3 := Ord(Value[Index + 1]); b2 := Ord(Value[Index + 2]); b1 := Ord(Value[Index + 3]); End; end; end; Inc(Index, mb); end; {==============================================================================} function WriteMulti(b1, b2, b3, b4: Byte; mb: Byte; le: boolean): AnsiString; begin if mb > 4 then mb := 1; SetLength(Result, mb); if le then case mb Of 1: Result[1] := AnsiChar(b1); 2: begin Result[1] := AnsiChar(b1); Result[2] := AnsiChar(b2); end; 3: begin Result[1] := AnsiChar(b1); Result[2] := AnsiChar(b2); Result[3] := AnsiChar(b3); end; 4: begin Result[1] := AnsiChar(b1); Result[2] := AnsiChar(b2); Result[3] := AnsiChar(b3); Result[4] := AnsiChar(b4); end; end else case mb Of 1: Result[1] := AnsiChar(b1); 2: begin Result[2] := AnsiChar(b1); Result[1] := AnsiChar(b2); end; 3: begin Result[3] := AnsiChar(b1); Result[2] := AnsiChar(b2); Result[1] := AnsiChar(b3); end; 4: begin Result[4] := AnsiChar(b1); Result[3] := AnsiChar(b2); Result[2] := AnsiChar(b3); Result[1] := AnsiChar(b4); end; end; end; {==============================================================================} function UTF8toUCS4(const Value: AnsiString): AnsiString; var n, x, ul, m: Integer; s: AnsiString; w1, w2: Word; begin Result := ''; n := 1; while Length(Value) >= n do begin x := Ord(Value[n]); Inc(n); if x < 128 then Result := Result + WriteMulti(x, 0, 0, 0, 4, false) else begin m := 0; if (x and $E0) = $C0 then m := $1F; if (x and $F0) = $E0 then m := $0F; if (x and $F8) = $F0 then m := $07; if (x and $FC) = $F8 then m := $03; if (x and $FE) = $FC then m := $01; ul := x and m; s := IntToBin(ul, 0); while Length(Value) >= n do begin x := Ord(Value[n]); Inc(n); if (x and $C0) = $80 then s := s + IntToBin(x and $3F, 6) else begin Dec(n); Break; end; end; ul := BinToInt(s); w1 := ul div 65536; w2 := ul mod 65536; Result := Result + WriteMulti(Lo(w2), Hi(w2), Lo(w1), Hi(w1), 4, false); end; end; end; {==============================================================================} function UCS4toUTF8(const Value: AnsiString): AnsiString; var s, l, k: AnsiString; b1, b2, b3, b4: Byte; n, m, x, y: Integer; b: Byte; begin Result := ''; n := 1; while Length(Value) >= n do begin ReadMulti(Value, n, 4, b1, b2, b3, b4, false); if (b2 = 0) and (b3 = 0) and (b4 = 0) and (b1 < 128) then Result := Result + AnsiChar(b1) else begin x := (b1 + 256 * b2) + (b3 + 256 * b4) * 65536; l := IntToBin(x, 0); y := Length(l) div 6; s := ''; for m := 1 to y do begin k := Copy(l, Length(l) - 5, 6); l := Copy(l, 1, Length(l) - 6); b := BinToInt(k) or $80; s := AnsiChar(b) + s; end; b := BinToInt(l); case y of 5: b := b or $FC; 4: b := b or $F8; 3: b := b or $F0; 2: b := b or $E0; 1: b := b or $C0; end; s := AnsiChar(b) + s; Result := Result + s; end; end; end; {==============================================================================} function UTF7toUCS2(const Value: AnsiString; Modified: Boolean): AnsiString; var n, i: Integer; c: AnsiChar; s, t: AnsiString; shift: AnsiChar; table: String; begin Result := ''; n := 1; if modified then begin shift := '&'; table := TableBase64mod; end else begin shift := '+'; table := TableBase64; end; while Length(Value) >= n do begin c := Value[n]; Inc(n); if c <> shift then Result := Result + WriteMulti(Ord(c), 0, 0, 0, 2, false) else begin s := ''; while Length(Value) >= n do begin c := Value[n]; Inc(n); if c = '-' then Break; if (c = '=') or (Pos(c, table) < 1) then begin Dec(n); Break; end; s := s + c; end; if s = '' then s := WriteMulti(Ord(shift), 0, 0, 0, 2, false) else begin if modified then t := DecodeBase64mod(s) else t := DecodeBase64(s); if not odd(length(t)) then s := t else begin //ill-formed sequence t := s; s := WriteMulti(Ord(shift), 0, 0, 0, 2, false); for i := 1 to length(t) do s := s + WriteMulti(Ord(t[i]), 0, 0, 0, 2, false); end; end; Result := Result + s; end; end; end; {==============================================================================} function UCS2toUTF7(const Value: AnsiString; Modified: Boolean): AnsiString; var s: AnsiString; b1, b2, b3, b4: Byte; n, m: Integer; shift: AnsiChar; begin Result := ''; n := 1; if modified then shift := '&' else shift := '+'; while Length(Value) >= n do begin ReadMulti(Value, n, 2, b1, b2, b3, b4, false); if (b2 = 0) and (b1 < 128) then if AnsiChar(b1) = shift then Result := Result + shift + '-' else Result := Result + AnsiChar(b1) else begin s := AnsiChar(b2) + AnsiChar(b1); while Length(Value) >= n do begin ReadMulti(Value, n, 2, b1, b2, b3, b4, false); if (b2 = 0) and (b1 < 128) then begin Dec(n, 2); Break; end; s := s + AnsiChar(b2) + AnsiChar(b1); end; if modified then s := EncodeBase64mod(s) else s := EncodeBase64(s); m := Pos('=', s); if m > 0 then s := Copy(s, 1, m - 1); Result := Result + shift + s + '-'; end; end; end; {==============================================================================} function CharsetConversion(const Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeChar): AnsiString; begin Result := CharsetConversionEx(Value, CharFrom, CharTo, Replace_None); end; {==============================================================================} function CharsetConversionEx(const Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeChar; const TransformTable: array of Word): AnsiString; begin Result := CharsetConversionTrans(Value, CharFrom, CharTo, TransformTable, True); end; {==============================================================================} function InternalToUcs(const Value: AnsiString; Charfrom: TMimeChar): AnsiString; var uni: Word; n: Integer; b1, b2, b3, b4: Byte; SourceTable: array[128..255] of Word; mbf: Byte; lef: Boolean; s: AnsiString; begin if CharFrom = UTF_8 then s := UTF8toUCS4(Value) else if CharFrom = UTF_7 then s := UTF7toUCS2(Value, False) else if CharFrom = UTF_7mod then s := UTF7toUCS2(Value, True) else s := Value; GetArray(CharFrom, SourceTable); mbf := 1; if CharFrom in SetTwo then mbf := 2; if CharFrom in SetFour then mbf := 4; lef := CharFrom in SetLe; Result := ''; n := 1; while Length(s) >= n do begin ReadMulti(s, n, mbf, b1, b2, b3, b4, lef); //handle BOM if (b3 = 0) and (b4 = 0) then begin if (b1 = $FE) and (b2 = $FF) then begin lef := not lef; continue; end; if (b1 = $FF) and (b2 = $FE) then continue; end; if mbf = 1 then if b1 > 127 then begin uni := SourceTable[b1]; b1 := Lo(uni); b2 := Hi(uni); end; Result := Result + WriteMulti(b1, b2, b3, b4, 2, False); end; end; function CharsetConversionTrans(Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeChar; const TransformTable: array of Word; Translit: Boolean): AnsiString; var uni: Word; n, m: Integer; b: Byte; b1, b2, b3, b4: Byte; TargetTable: array[128..255] of Word; mbt: Byte; let: Boolean; ucsstring, s, t: AnsiString; cd: iconv_t; f: Boolean; NotNeedTransform: Boolean; FromID, ToID: string; begin NotNeedTransform := (High(TransformTable) = 0); if (CharFrom = CharTo) and NotNeedTransform then begin Result := Value; Exit; end; FromID := GetIDFromCP(CharFrom); ToID := GetIDFromCP(CharTo); cd := Iconv_t(-1); //do two-pass conversion. Transform to UCS-2 first. if not DisableIconv then cd := SynaIconvOpenIgnore('UCS-2BE', FromID); try if cd <> iconv_t(-1) then SynaIconv(cd, Value, ucsstring) else ucsstring := InternalToUcs(Value, CharFrom); finally SynaIconvClose(cd); end; //here we allways have ucstring with UCS-2 encoding //second pass... from UCS-2 to target encoding. if not DisableIconv then if translit then cd := SynaIconvOpenTranslit(ToID, 'UCS-2BE') else cd := SynaIconvOpenIgnore(ToID, 'UCS-2BE'); try if (cd <> iconv_t(-1)) and NotNeedTransform then begin if CharTo = UTF_7 then ucsstring := ucsstring + #0 + '-'; //when transformtable is not needed and Iconv know target charset, //do it fast by one call. SynaIconv(cd, ucsstring, Result); if CharTo = UTF_7 then Delete(Result, Length(Result), 1); end else begin GetArray(CharTo, TargetTable); mbt := 1; if CharTo in SetTwo then mbt := 2; if CharTo in SetFour then mbt := 4; let := CharTo in SetLe; b3 := 0; b4 := 0; Result := ''; for n:= 0 to (Length(ucsstring) div 2) - 1 do begin s := Copy(ucsstring, n * 2 + 1, 2); b2 := Ord(s[1]); b1 := Ord(s[2]); uni := b2 * 256 + b1; if not NotNeedTransform then begin uni := ReplaceUnicode(uni, TransformTable); b1 := Lo(uni); b2 := Hi(uni); s[1] := AnsiChar(b2); s[2] := AnsiChar(b1); end; if cd <> iconv_t(-1) then begin if CharTo = UTF_7 then s := s + #0 + '-'; SynaIconv(cd, s, t); if CharTo = UTF_7 then Delete(t, Length(t), 1); Result := Result + t; end else begin f := True; if mbt = 1 then if uni > 127 then begin f := False; b := 0; for m := 128 to 255 do if TargetTable[m] = uni then begin b := m; f := True; Break; end; b1 := b; b2 := 0; end else b1 := Lo(uni); if not f then if translit then begin b1 := Ord(NotFoundChar); b2 := 0; f := True; end; if f then Result := Result + WriteMulti(b1, b2, b3, b4, mbt, let) end; end; if cd = iconv_t(-1) then begin if CharTo = UTF_7 then Result := UCS2toUTF7(Result, false); if CharTo = UTF_7mod then Result := UCS2toUTF7(Result, true); if CharTo = UTF_8 then Result := UCS4toUTF8(Result); end; end; finally SynaIconvClose(cd); end; end; {==============================================================================} {$IFNDEF WIN32} function GetCurCP: TMimeChar; begin {$IFNDEF FPC} Result := GetCPFromID(nl_langinfo(_NL_CTYPE_CODESET_NAME)); {$ELSE} {$IFDEF FPC_USE_LIBC} Result := GetCPFromID(nl_langinfo(_NL_CTYPE_CODESET_NAME)); {$ELSE} //How to get system codepage without LIBC? Result := UTF_8; {$ENDIF} {$ENDIF} end; function GetCurOEMCP: TMimeChar; begin Result := GetCurCP; end; {$ELSE} function CPToMimeChar(Value: Integer): TMimeChar; begin case Value of 437, 850, 20127: Result := ISO_8859_1; //I know, it is not ideal! 737: Result := CP737; 775: Result := CP775; 852: Result := CP852; 855: Result := CP855; 857: Result := CP857; 858: Result := CP858; 860: Result := CP860; 861: Result := CP861; 862: Result := CP862; 863: Result := CP863; 864: Result := CP864; 865: Result := CP865; 866: Result := CP866; 869: Result := CP869; 874: Result := ISO_8859_15; 895: Result := CP895; 932: Result := CP932; 936: Result := CP936; 949: Result := CP949; 950: Result := CP950; 1200: Result := UCS_2LE; 1201: Result := UCS_2; 1250: Result := CP1250; 1251: Result := CP1251; 1253: Result := CP1253; 1254: Result := CP1254; 1255: Result := CP1255; 1256: Result := CP1256; 1257: Result := CP1257; 1258: Result := CP1258; 1361: Result := CP1361; 10000: Result := MAC; 10004: Result := MACAR; 10005: Result := MACHEB; 10006: Result := MACGR; 10007: Result := MACCYR; 10010: Result := MACRO; 10017: Result := MACUK; 10021: Result := MACTH; 10029: Result := MACCE; 10079: Result := MACICE; 10081: Result := MACTU; 10082: Result := MACCRO; 12000: Result := UCS_4LE; 12001: Result := UCS_4; 20866: Result := KOI8_R; 20932: Result := JIS_X0208; 20936: Result := GB2312; 21866: Result := KOI8_U; 28591: Result := ISO_8859_1; 28592: Result := ISO_8859_2; 28593: Result := ISO_8859_3; 28594: Result := ISO_8859_4; 28595: Result := ISO_8859_5; 28596, 708: Result := ISO_8859_6; 28597: Result := ISO_8859_7; 28598, 38598: Result := ISO_8859_8; 28599: Result := ISO_8859_9; 28605: Result := ISO_8859_15; 50220: Result := ISO_2022_JP; //? ISO 2022 Japanese with no halfwidth Katakana 50221: Result := ISO_2022_JP1;//? Japanese with halfwidth Katakana 50222: Result := ISO_2022_JP2;//? Japanese JIS X 0201-1989 50225: Result := ISO_2022_KR; 50227: Result := ISO_2022_CN;//? ISO 2022 Simplified Chinese 50229: Result := ISO_2022_CNE;//? ISO 2022 Traditional Chinese 51932: Result := EUC_JP; 51936: Result := GB2312; 51949: Result := EUC_KR; 52936: Result := HZ; 54936: Result := GB18030; 65000: Result := UTF_7; 65001: Result := UTF_8; 0: Result := UCS_2LE; else Result := CP1252; end; end; function GetCurCP: TMimeChar; begin Result := CPToMimeChar(GetACP); end; function GetCurOEMCP: TMimeChar; begin Result := CPToMimeChar(GetOEMCP); end; {$ENDIF} {==============================================================================} function NeedCharsetConversion(const Value: AnsiString): Boolean; var n: Integer; begin Result := False; for n := 1 to Length(Value) do if (Ord(Value[n]) > 127) or (Ord(Value[n]) = 0) then begin Result := True; Break; end; end; {==============================================================================} function IdealCharsetCoding(const Value: AnsiString; CharFrom: TMimeChar; CharTo: TMimeSetChar): TMimeChar; var n: Integer; max: Integer; s, t, u: AnsiString; CharSet: TMimeChar; begin Result := ISO_8859_1; s := Copy(Value, 1, 1024); //max first 1KB for next procedure max := 0; for n := Ord(Low(TMimeChar)) to Ord(High(TMimeChar)) do begin CharSet := TMimeChar(n); if CharSet in CharTo then begin t := CharsetConversionTrans(s, CharFrom, CharSet, Replace_None, False); u := CharsetConversionTrans(t, CharSet, CharFrom, Replace_None, False); if s = u then begin Result := CharSet; Exit; end; if Length(u) > max then begin Result := CharSet; max := Length(u); end; end; end; end; {==============================================================================} function GetBOM(Value: TMimeChar): AnsiString; begin Result := ''; case Value of UCS_2: Result := #$fe + #$ff; UCS_4: Result := #$00 + #$00 + #$fe + #$ff; UCS_2LE: Result := #$ff + #$fe; UCS_4LE: Result := #$ff + #$fe + #$00 + #$00; UTF_8: Result := #$ef + #$bb + #$bf; end; end; {==============================================================================} function GetCPFromID(Value: AnsiString): TMimeChar; begin Value := UpperCase(Value); if (Pos('KAMENICKY', Value) > 0) or (Pos('895', Value) > 0) then Result := CP895 else if Pos('MUTF-7', Value) > 0 then Result := UTF_7mod else Result := GetCPFromIconvID(Value); end; {==============================================================================} function GetIDFromCP(Value: TMimeChar): AnsiString; begin case Value of CP895: Result := 'CP-895'; UTF_7mod: Result := 'mUTF-7'; else Result := GetIconvIDFromCP(Value); end; end; {==============================================================================} function StringToWide(const Value: AnsiString): WideString; var n: integer; x, y: integer; begin SetLength(Result, Length(Value) div 2); for n := 1 to Length(Value) div 2 do begin x := Ord(Value[((n-1) * 2) + 1]); y := Ord(Value[((n-1) * 2) + 2]); Result[n] := WideChar(x * 256 + y); end; end; {==============================================================================} function WideToString(const Value: WideString): AnsiString; var n: integer; x: integer; begin SetLength(Result, Length(Value) * 2); for n := 1 to Length(Value) do begin x := Ord(Value[n]); Result[((n-1) * 2) + 1] := AnsiChar(x div 256); Result[((n-1) * 2) + 2] := AnsiChar(x mod 256); end; end; {==============================================================================} initialization begin IconvArr[0].Charset := ISO_8859_1; IconvArr[0].Charname := 'ISO-8859-1 CP819 IBM819 ISO-IR-100 ISO8859-1 ISO_8859-1 ISO_8859-1:1987 L1 LATIN1 CSISOLATIN1'; IconvArr[1].Charset := UTF_8; IconvArr[1].Charname := 'UTF-8'; IconvArr[2].Charset := UCS_2; IconvArr[2].Charname := 'ISO-10646-UCS-2 UCS-2 CSUNICODE'; IconvArr[3].Charset := UCS_2; IconvArr[3].Charname := 'UCS-2BE UNICODE-1-1 UNICODEBIG CSUNICODE11'; IconvArr[4].Charset := UCS_2LE; IconvArr[4].Charname := 'UCS-2LE UNICODELITTLE'; IconvArr[5].Charset := UCS_4; IconvArr[5].Charname := 'ISO-10646-UCS-4 UCS-4 CSUCS4'; IconvArr[6].Charset := UCS_4; IconvArr[6].Charname := 'UCS-4BE'; IconvArr[7].Charset := UCS_2LE; IconvArr[7].Charname := 'UCS-4LE'; IconvArr[8].Charset := UTF_16; IconvArr[8].Charname := 'UTF-16'; IconvArr[9].Charset := UTF_16; IconvArr[9].Charname := 'UTF-16BE'; IconvArr[10].Charset := UTF_16LE; IconvArr[10].Charname := 'UTF-16LE'; IconvArr[11].Charset := UTF_32; IconvArr[11].Charname := 'UTF-32'; IconvArr[12].Charset := UTF_32; IconvArr[12].Charname := 'UTF-32BE'; IconvArr[13].Charset := UTF_32; IconvArr[13].Charname := 'UTF-32LE'; IconvArr[14].Charset := UTF_7; IconvArr[14].Charname := 'UNICODE-1-1-UTF-7 UTF-7 CSUNICODE11UTF7'; IconvArr[15].Charset := C99; IconvArr[15].Charname := 'C99'; IconvArr[16].Charset := JAVA; IconvArr[16].Charname := 'JAVA'; IconvArr[17].Charset := ISO_8859_1; IconvArr[17].Charname := 'US-ASCII ANSI_X3.4-1968 ANSI_X3.4-1986 ASCII CP367 IBM367 ISO-IR-6 ISO646-US ISO_646.IRV:1991 US CSASCII'; IconvArr[18].Charset := ISO_8859_2; IconvArr[18].Charname := 'ISO-8859-2 ISO-IR-101 ISO8859-2 ISO_8859-2 ISO_8859-2:1987 L2 LATIN2 CSISOLATIN2'; IconvArr[19].Charset := ISO_8859_3; IconvArr[19].Charname := 'ISO-8859-3 ISO-IR-109 ISO8859-3 ISO_8859-3 ISO_8859-3:1988 L3 LATIN3 CSISOLATIN3'; IconvArr[20].Charset := ISO_8859_4; IconvArr[20].Charname := 'ISO-8859-4 ISO-IR-110 ISO8859-4 ISO_8859-4 ISO_8859-4:1988 L4 LATIN4 CSISOLATIN4'; IconvArr[21].Charset := ISO_8859_5; IconvArr[21].Charname := 'ISO-8859-5 CYRILLIC ISO-IR-144 ISO8859-5 ISO_8859-5 ISO_8859-5:1988 CSISOLATINCYRILLIC'; IconvArr[22].Charset := ISO_8859_6; IconvArr[22].Charname := 'ISO-8859-6 ARABIC ASMO-708 ECMA-114 ISO-IR-127 ISO8859-6 ISO_8859-6 ISO_8859-6:1987 CSISOLATINARABIC'; IconvArr[23].Charset := ISO_8859_7; IconvArr[23].Charname := 'ISO-8859-7 ECMA-118 ELOT_928 GREEK GREEK8 ISO-IR-126 ISO8859-7 ISO_8859-7 ISO_8859-7:1987 CSISOLATINGREEK'; IconvArr[24].Charset := ISO_8859_8; IconvArr[24].Charname := 'ISO-8859-8 HEBREW ISO_8859-8 ISO-IR-138 ISO8859-8 ISO_8859-8:1988 CSISOLATINHEBREW ISO-8859-8-I'; IconvArr[25].Charset := ISO_8859_9; IconvArr[25].Charname := 'ISO-8859-9 ISO-IR-148 ISO8859-9 ISO_8859-9 ISO_8859-9:1989 L5 LATIN5 CSISOLATIN5'; IconvArr[26].Charset := ISO_8859_10; IconvArr[26].Charname := 'ISO-8859-10 ISO-IR-157 ISO8859-10 ISO_8859-10 ISO_8859-10:1992 L6 LATIN6 CSISOLATIN6'; IconvArr[27].Charset := ISO_8859_13; IconvArr[27].Charname := 'ISO-8859-13 ISO-IR-179 ISO8859-13 ISO_8859-13 L7 LATIN7'; IconvArr[28].Charset := ISO_8859_14; IconvArr[28].Charname := 'ISO-8859-14 ISO-CELTIC ISO-IR-199 ISO8859-14 ISO_8859-14 ISO_8859-14:1998 L8 LATIN8'; IconvArr[29].Charset := ISO_8859_15; IconvArr[29].Charname := 'ISO-8859-15 ISO-IR-203 ISO8859-15 ISO_8859-15 ISO_8859-15:1998'; IconvArr[30].Charset := ISO_8859_16; IconvArr[30].Charname := 'ISO-8859-16 ISO-IR-226 ISO8859-16 ISO_8859-16 ISO_8859-16:2000'; IconvArr[31].Charset := KOI8_R; IconvArr[31].Charname := 'KOI8-R CSKOI8R'; IconvArr[32].Charset := KOI8_U; IconvArr[32].Charname := 'KOI8-U'; IconvArr[33].Charset := KOI8_RU; IconvArr[33].Charname := 'KOI8-RU'; IconvArr[34].Charset := CP1250; IconvArr[34].Charname := 'WINDOWS-1250 CP1250 MS-EE'; IconvArr[35].Charset := CP1251; IconvArr[35].Charname := 'WINDOWS-1251 CP1251 MS-CYRL'; IconvArr[36].Charset := CP1252; IconvArr[36].Charname := 'WINDOWS-1252 CP1252 MS-ANSI'; IconvArr[37].Charset := CP1253; IconvArr[37].Charname := 'WINDOWS-1253 CP1253 MS-GREEK'; IconvArr[38].Charset := CP1254; IconvArr[38].Charname := 'WINDOWS-1254 CP1254 MS-TURK'; IconvArr[39].Charset := CP1255; IconvArr[39].Charname := 'WINDOWS-1255 CP1255 MS-HEBR'; IconvArr[40].Charset := CP1256; IconvArr[40].Charname := 'WINDOWS-1256 CP1256 MS-ARAB'; IconvArr[41].Charset := CP1257; IconvArr[41].Charname := 'WINDOWS-1257 CP1257 WINBALTRIM'; IconvArr[42].Charset := CP1258; IconvArr[42].Charname := 'WINDOWS-1258 CP1258'; IconvArr[43].Charset := ISO_8859_1; IconvArr[43].Charname := '850 CP850 IBM850 CSPC850MULTILINGUAL'; IconvArr[44].Charset := CP862; IconvArr[44].Charname := '862 CP862 IBM862 CSPC862LATINHEBREW'; IconvArr[45].Charset := CP866; IconvArr[45].Charname := '866 CP866 IBM866 CSIBM866'; IconvArr[46].Charset := MAC; IconvArr[46].Charname := 'MAC MACINTOSH MACROMAN CSMACINTOSH'; IconvArr[47].Charset := MACCE; IconvArr[47].Charname := 'MACCENTRALEUROPE'; IconvArr[48].Charset := MACICE; IconvArr[48].Charname := 'MACICELAND'; IconvArr[49].Charset := MACCRO; IconvArr[49].Charname := 'MACCROATIAN'; IconvArr[50].Charset := MACRO; IconvArr[50].Charname := 'MACROMANIA'; IconvArr[51].Charset := MACCYR; IconvArr[51].Charname := 'MACCYRILLIC'; IconvArr[52].Charset := MACUK; IconvArr[52].Charname := 'MACUKRAINE'; IconvArr[53].Charset := MACGR; IconvArr[53].Charname := 'MACGREEK'; IconvArr[54].Charset := MACTU; IconvArr[54].Charname := 'MACTURKISH'; IconvArr[55].Charset := MACHEB; IconvArr[55].Charname := 'MACHEBREW'; IconvArr[56].Charset := MACAR; IconvArr[56].Charname := 'MACARABIC'; IconvArr[57].Charset := MACTH; IconvArr[57].Charname := 'MACTHAI'; IconvArr[58].Charset := ROMAN8; IconvArr[58].Charname := 'HP-ROMAN8 R8 ROMAN8 CSHPROMAN8'; IconvArr[59].Charset := NEXTSTEP; IconvArr[59].Charname := 'NEXTSTEP'; IconvArr[60].Charset := ARMASCII; IconvArr[60].Charname := 'ARMSCII-8'; IconvArr[61].Charset := GEORGIAN_AC; IconvArr[61].Charname := 'GEORGIAN-ACADEMY'; IconvArr[62].Charset := GEORGIAN_PS; IconvArr[62].Charname := 'GEORGIAN-PS'; IconvArr[63].Charset := KOI8_T; IconvArr[63].Charname := 'KOI8-T'; IconvArr[64].Charset := MULELAO; IconvArr[64].Charname := 'MULELAO-1'; IconvArr[65].Charset := CP1133; IconvArr[65].Charname := 'CP1133 IBM-CP1133'; IconvArr[66].Charset := TIS620; IconvArr[66].Charname := 'TIS-620 ISO-IR-166 TIS620 TIS620-0 TIS620.2529-1 TIS620.2533-0 TIS620.2533-1'; IconvArr[67].Charset := CP874; IconvArr[67].Charname := 'CP874 WINDOWS-874'; IconvArr[68].Charset := VISCII; IconvArr[68].Charname := 'VISCII VISCII1.1-1 CSVISCII'; IconvArr[69].Charset := TCVN; IconvArr[69].Charname := 'TCVN TCVN-5712 TCVN5712-1 TCVN5712-1:1993'; IconvArr[70].Charset := ISO_IR_14; IconvArr[70].Charname := 'ISO-IR-14 ISO646-JP JIS_C6220-1969-RO JP CSISO14JISC6220RO'; IconvArr[71].Charset := JIS_X0201; IconvArr[71].Charname := 'JISX0201-1976 JIS_X0201 X0201 CSHALFWIDTHKATAKANA'; IconvArr[72].Charset := JIS_X0208; IconvArr[72].Charname := 'ISO-IR-87 JIS0208 JIS_C6226-1983 JIS_X0208 JIS_X0208-1983 JIS_X0208-1990 X0208 CSISO87JISX0208'; IconvArr[73].Charset := JIS_X0212; IconvArr[73].Charname := 'ISO-IR-159 JIS_X0212 JIS_X0212-1990 JIS_X0212.1990-0 X0212 CSISO159JISX02121990'; IconvArr[74].Charset := GB1988_80; IconvArr[74].Charname := 'CN GB_1988-80 ISO-IR-57 ISO646-CN CSISO57GB1988'; IconvArr[75].Charset := GB2312_80; IconvArr[75].Charname := 'CHINESE GB_2312-80 ISO-IR-58 CSISO58GB231280'; IconvArr[76].Charset := ISO_IR_165; IconvArr[76].Charname := 'CN-GB-ISOIR165 ISO-IR-165'; IconvArr[77].Charset := ISO_IR_149; IconvArr[77].Charname := 'ISO-IR-149 KOREAN KSC_5601 KS_C_5601-1987 KS_C_5601-1989 CSKSC56011987'; IconvArr[78].Charset := EUC_JP; IconvArr[78].Charname := 'EUC-JP EUCJP EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE CSEUCPKDFMTJAPANESE'; IconvArr[79].Charset := SHIFT_JIS; IconvArr[79].Charname := 'SHIFT-JIS MS_KANJI SHIFT_JIS SJIS CSSHIFTJIS'; IconvArr[80].Charset := CP932; IconvArr[80].Charname := 'CP932'; IconvArr[81].Charset := ISO_2022_JP; IconvArr[81].Charname := 'ISO-2022-JP CSISO2022JP'; IconvArr[82].Charset := ISO_2022_JP1; IconvArr[82].Charname := 'ISO-2022-JP-1'; IconvArr[83].Charset := ISO_2022_JP2; IconvArr[83].Charname := 'ISO-2022-JP-2 CSISO2022JP2'; IconvArr[84].Charset := GB2312; IconvArr[84].Charname := 'CN-GB EUC-CN EUCCN GB2312 CSGB2312'; IconvArr[85].Charset := CP936; IconvArr[85].Charname := 'CP936 GBK'; IconvArr[86].Charset := GB18030; IconvArr[86].Charname := 'GB18030'; IconvArr[87].Charset := ISO_2022_CN; IconvArr[87].Charname := 'ISO-2022-CN CSISO2022CN'; IconvArr[88].Charset := ISO_2022_CNE; IconvArr[88].Charname := 'ISO-2022-CN-EXT'; IconvArr[89].Charset := HZ; IconvArr[89].Charname := 'HZ HZ-GB-2312'; IconvArr[90].Charset := EUC_TW; IconvArr[90].Charname := 'EUC-TW EUCTW CSEUCTW'; IconvArr[91].Charset := BIG5; IconvArr[91].Charname := 'BIG5 BIG-5 BIG-FIVE BIGFIVE CN-BIG5 CSBIG5'; IconvArr[92].Charset := CP950; IconvArr[92].Charname := 'CP950'; IconvArr[93].Charset := BIG5_HKSCS; IconvArr[93].Charname := 'BIG5-HKSCS BIG5HKSCS'; IconvArr[94].Charset := EUC_KR; IconvArr[94].Charname := 'EUC-KR EUCKR CSEUCKR'; IconvArr[95].Charset := CP949; IconvArr[95].Charname := 'CP949 UHC'; IconvArr[96].Charset := CP1361; IconvArr[96].Charname := 'CP1361 JOHAB'; IconvArr[97].Charset := ISO_2022_KR; IconvArr[97].Charname := 'ISO-2022-KR CSISO2022KR'; IconvArr[98].Charset := ISO_8859_1; IconvArr[98].Charname := '437 CP437 IBM437 CSPC8CODEPAGE437'; IconvArr[99].Charset := CP737; IconvArr[99].Charname := 'CP737'; IconvArr[100].Charset := CP775; IconvArr[100].Charname := 'CP775 IBM775 CSPC775BALTIC'; IconvArr[101].Charset := CP852; IconvArr[101].Charname := '852 CP852 IBM852 CSPCP852'; IconvArr[102].Charset := CP853; IconvArr[102].Charname := 'CP853'; IconvArr[103].Charset := CP855; IconvArr[103].Charname := '855 CP855 IBM855 CSIBM855'; IconvArr[104].Charset := CP857; IconvArr[104].Charname := '857 CP857 IBM857 CSIBM857'; IconvArr[105].Charset := CP858; IconvArr[105].Charname := 'CP858'; IconvArr[106].Charset := CP860; IconvArr[106].Charname := '860 CP860 IBM860 CSIBM860'; IconvArr[107].Charset := CP861; IconvArr[107].Charname := '861 CP-IS CP861 IBM861 CSIBM861'; IconvArr[108].Charset := CP863; IconvArr[108].Charname := '863 CP863 IBM863 CSIBM863'; IconvArr[109].Charset := CP864; IconvArr[109].Charname := 'CP864 IBM864 CSIBM864'; IconvArr[110].Charset := CP865; IconvArr[110].Charname := '865 CP865 IBM865 CSIBM865'; IconvArr[111].Charset := CP869; IconvArr[111].Charname := '869 CP-GR CP869 IBM869 CSIBM869'; IconvArr[112].Charset := CP1125; IconvArr[112].Charname := 'CP1125'; end; end. TransGUI/synapse/source/lib/mimeinln.pas0000644000000000000000000002167411366572451017301 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.011 | |==============================================================================| | Content: Inline MIME support procedures and functions | |==============================================================================| | Copyright (c)1999-2006, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2006. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Utilities for inline MIME) Support for Inline MIME encoding and decoding. Used RFC: RFC-2047, RFC-2231 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit mimeinln; interface uses SysUtils, Classes, synachar, synacode, synautil; {:Decodes mime inline encoding (i.e. in headers) uses target characterset "CP".} function InlineDecode(const Value: string; CP: TMimeChar): string; {:Encodes string to MIME inline encoding. The source characterset is "CP", and the target charset is "MimeP".} function InlineEncode(const Value: string; CP, MimeP: TMimeChar): string; {:Returns @true, if "Value" contains characters needed for inline coding.} function NeedInline(const Value: AnsiString): boolean; {:Inline mime encoding similar to @link(InlineEncode), but you can specify source charset, and the target characterset is automatically assigned.} function InlineCodeEx(const Value: string; FromCP: TMimeChar): string; {:Inline MIME encoding similar to @link(InlineEncode), but the source charset is automatically set to the system default charset, and the target charset is automatically assigned from set of allowed encoding for MIME.} function InlineCode(const Value: string): string; {:Converts e-mail address to canonical mime form. You can specify source charset.} function InlineEmailEx(const Value: string; FromCP: TMimeChar): string; {:Converts e-mail address to canonical mime form. Source charser it system default charset.} function InlineEmail(const Value: string): string; implementation {==============================================================================} function InlineDecode(const Value: string; CP: TMimeChar): string; var s, su, v: string; x, y, z, n: Integer; ichar: TMimeChar; c: Char; function SearchEndInline(const Value: string; be: Integer): Integer; var n, q: Integer; begin q := 0; Result := 0; for n := be + 2 to Length(Value) - 1 do if Value[n] = '?' then begin Inc(q); if (q > 2) and (Value[n + 1] = '=') then begin Result := n; Break; end; end; end; begin Result := ''; v := Value; x := Pos('=?', v); y := SearchEndInline(v, x); //fix for broken coding with begin, but not with end. if (x > 0) and (y <= 0) then y := Length(Result); while (y > x) and (x > 0) do begin s := Copy(v, 1, x - 1); if Trim(s) <> '' then Result := Result + s; s := Copy(v, x, y - x + 2); Delete(v, 1, y + 1); su := Copy(s, 3, Length(s) - 4); z := Pos('?', su); if (Length(su) >= (z + 2)) and (su[z + 2] = '?') then begin ichar := GetCPFromID(SeparateLeft(Copy(su, 1, z - 1), '*')); c := UpperCase(su)[z + 1]; su := Copy(su, z + 3, Length(su) - z - 2); if c = 'B' then begin s := DecodeBase64(su); s := CharsetConversion(s, ichar, CP); end; if c = 'Q' then begin s := ''; for n := 1 to Length(su) do if su[n] = '_' then s := s + ' ' else s := s + su[n]; s := DecodeQuotedPrintable(s); s := CharsetConversion(s, ichar, CP); end; end; Result := Result + s; x := Pos('=?', v); y := SearchEndInline(v, x); end; Result := Result + v; end; {==============================================================================} function InlineEncode(const Value: string; CP, MimeP: TMimeChar): string; var s, s1, e: string; n: Integer; begin s := CharsetConversion(Value, CP, MimeP); s := EncodeSafeQuotedPrintable(s); e := GetIdFromCP(MimeP); s1 := ''; Result := ''; for n := 1 to Length(s) do if s[n] = ' ' then begin // s1 := s1 + '=20'; s1 := s1 + '_'; if Length(s1) > 32 then begin if Result <> '' then Result := Result + ' '; Result := Result + '=?' + e + '?Q?' + s1 + '?='; s1 := ''; end; end else s1 := s1 + s[n]; if s1 <> '' then begin if Result <> '' then Result := Result + ' '; Result := Result + '=?' + e + '?Q?' + s1 + '?='; end; end; {==============================================================================} function NeedInline(const Value: AnsiString): boolean; var n: Integer; begin Result := False; for n := 1 to Length(Value) do if Value[n] in (SpecialChar + NonAsciiChar - ['_']) then begin Result := True; Break; end; end; {==============================================================================} function InlineCodeEx(const Value: string; FromCP: TMimeChar): string; var c: TMimeChar; begin if NeedInline(Value) then begin c := IdealCharsetCoding(Value, FromCP, IdealCharsets); Result := InlineEncode(Value, FromCP, c); end else Result := Value; end; {==============================================================================} function InlineCode(const Value: string): string; begin Result := InlineCodeEx(Value, GetCurCP); end; {==============================================================================} function InlineEmailEx(const Value: string; FromCP: TMimeChar): string; var sd, se: string; begin sd := GetEmailDesc(Value); se := GetEmailAddr(Value); if sd = '' then Result := se else Result := '"' + InlineCodeEx(sd, FromCP) + '" <' + se + '>'; end; {==============================================================================} function InlineEmail(const Value: string): string; begin Result := InlineEmailEx(Value, GetCurCP); end; end. TransGUI/synapse/source/lib/synacode.pas0000644000000000000000000014263211366572451017274 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.002.001 | |==============================================================================| | Content: Coding and decoding support | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Various encoding and decoding support)} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$R-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$WARN SUSPICIOUS_TYPECAST OFF} {$ENDIF} unit synacode; interface uses SysUtils; type TSpecials = set of AnsiChar; const SpecialChar: TSpecials = ['=', '(', ')', '[', ']', '<', '>', ':', ';', ',', '@', '/', '?', '\', '"', '_']; NonAsciiChar: TSpecials = [#0..#31, #127..#255]; URLFullSpecialChar: TSpecials = [';', '/', '?', ':', '@', '=', '&', '#', '+']; URLSpecialChar: TSpecials = [#$00..#$20, '_', '<', '>', '"', '%', '{', '}', '|', '\', '^', '~', '[', ']', '`', #$7F..#$FF]; TableBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; TableBase64mod = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,='; TableUU = '`!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_'; TableXX = '+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; ReTablebase64 = #$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$3E +#$40 +#$40 +#$40 +#$3F +#$34 +#$35 +#$36 +#$37 +#$38 +#$39 +#$3A +#$3B +#$3C +#$3D +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$00 +#$01 +#$02 +#$03 +#$04 +#$05 +#$06 +#$07 +#$08 +#$09 +#$0A +#$0B +#$0C +#$0D +#$0E +#$0F +#$10 +#$11 +#$12 +#$13 +#$14 +#$15 +#$16 +#$17 +#$18 +#$19 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$1A +#$1B +#$1C +#$1D +#$1E +#$1F +#$20 +#$21 +#$22 +#$23 +#$24 +#$25 +#$26 +#$27 +#$28 +#$29 +#$2A +#$2B +#$2C +#$2D +#$2E +#$2F +#$30 +#$31 +#$32 +#$33 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40; ReTableUU = #$01 +#$02 +#$03 +#$04 +#$05 +#$06 +#$07 +#$08 +#$09 +#$0A +#$0B +#$0C +#$0D +#$0E +#$0F +#$10 +#$11 +#$12 +#$13 +#$14 +#$15 +#$16 +#$17 +#$18 +#$19 +#$1A +#$1B +#$1C +#$1D +#$1E +#$1F +#$20 +#$21 +#$22 +#$23 +#$24 +#$25 +#$26 +#$27 +#$28 +#$29 +#$2A +#$2B +#$2C +#$2D +#$2E +#$2F +#$30 +#$31 +#$32 +#$33 +#$34 +#$35 +#$36 +#$37 +#$38 +#$39 +#$3A +#$3B +#$3C +#$3D +#$3E +#$3F +#$00 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40; ReTableXX = #$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$00 +#$40 +#$01 +#$40 +#$40 +#$02 +#$03 +#$04 +#$05 +#$06 +#$07 +#$08 +#$09 +#$0A +#$0B +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$0C +#$0D +#$0E +#$0F +#$10 +#$11 +#$12 +#$13 +#$14 +#$15 +#$16 +#$17 +#$18 +#$19 +#$1A +#$1B +#$1C +#$1D +#$1E +#$1F +#$20 +#$21 +#$22 +#$23 +#$24 +#$25 +#$40 +#$40 +#$40 +#$40 +#$40 +#$40 +#$26 +#$27 +#$28 +#$29 +#$2A +#$2B +#$2C +#$2D +#$2E +#$2F +#$30 +#$31 +#$32 +#$33 +#$34 +#$35 +#$36 +#$37 +#$38 +#$39 +#$3A +#$3B +#$3C +#$3D +#$3E +#$3F +#$40 +#$40 +#$40 +#$40 +#$40 +#$40; {:Decodes triplet encoding with a given character delimiter. It is used for decoding quoted-printable or URL encoding.} function DecodeTriplet(const Value: AnsiString; Delimiter: AnsiChar): AnsiString; {:Decodes a string from quoted printable form. (also decodes triplet sequences like '=7F')} function DecodeQuotedPrintable(const Value: AnsiString): AnsiString; {:Decodes a string of URL encoding. (also decodes triplet sequences like '%7F')} function DecodeURL(const Value: AnsiString): AnsiString; {:Performs triplet encoding with a given character delimiter. Used for encoding quoted-printable or URL encoding.} function EncodeTriplet(const Value: AnsiString; Delimiter: AnsiChar; Specials: TSpecials): AnsiString; {:Encodes a string to triplet quoted printable form. All @link(NonAsciiChar) are encoded.} function EncodeQuotedPrintable(const Value: AnsiString): AnsiString; {:Encodes a string to triplet quoted printable form. All @link(NonAsciiChar) and @link(SpecialChar) are encoded.} function EncodeSafeQuotedPrintable(const Value: AnsiString): AnsiString; {:Encodes a string to URL format. Used for encoding data from a form field in HTTP, etc. (Encodes all critical characters including characters used as URL delimiters ('/',':', etc.)} function EncodeURLElement(const Value: AnsiString): AnsiString; {:Encodes a string to URL format. Used to encode critical characters in all URLs.} function EncodeURL(const Value: AnsiString): AnsiString; {:Decode 4to3 encoding with given table. If some element is not found in table, first item from table is used. This is good for buggy coded items by Microsoft Outlook. This software sometimes using wrong table for UUcode, where is used ' ' instead '`'.} function Decode4to3(const Value, Table: AnsiString): AnsiString; {:Decode 4to3 encoding with given REVERSE table. Using this function with reverse table is much faster then @link(Decode4to3). This function is used internally for Base64, UU or XX decoding.} function Decode4to3Ex(const Value, Table: AnsiString): AnsiString; {:Encode by system 3to4 (used by Base64, UU coding, etc) by given table.} function Encode3to4(const Value, Table: AnsiString): AnsiString; {:Decode string from base64 format.} function DecodeBase64(const Value: AnsiString): AnsiString; {:Encodes a string to base64 format.} function EncodeBase64(const Value: AnsiString): AnsiString; {:Decode string from modified base64 format. (used in IMAP, for example.)} function DecodeBase64mod(const Value: AnsiString): AnsiString; {:Encodes a string to modified base64 format. (used in IMAP, for example.)} function EncodeBase64mod(const Value: AnsiString): AnsiString; {:Decodes a string from UUcode format.} function DecodeUU(const Value: AnsiString): AnsiString; {:encode UUcode. it encode only datas, you must also add header and footer for proper encode.} function EncodeUU(const Value: AnsiString): AnsiString; {:Decodes a string from XXcode format.} function DecodeXX(const Value: AnsiString): AnsiString; {:decode line with Yenc code. This code is sometimes used in newsgroups.} function DecodeYEnc(const Value: AnsiString): AnsiString; {:Returns a new CRC32 value after adding a new byte of data.} function UpdateCrc32(Value: Byte; Crc32: Integer): Integer; {:return CRC32 from a value string.} function Crc32(const Value: AnsiString): Integer; {:Returns a new CRC16 value after adding a new byte of data.} function UpdateCrc16(Value: Byte; Crc16: Word): Word; {:return CRC16 from a value string.} function Crc16(const Value: AnsiString): Word; {:Returns a binary string with a RSA-MD5 hashing of "Value" string.} function MD5(const Value: AnsiString): AnsiString; {:Returns a binary string with HMAC-MD5 hash.} function HMAC_MD5(Text, Key: AnsiString): AnsiString; {:Returns a binary string with a RSA-MD5 hashing of string what is constructed by repeating "value" until length is "Len".} function MD5LongHash(const Value: AnsiString; Len: integer): AnsiString; {:Returns a binary string with a SHA-1 hashing of "Value" string.} function SHA1(const Value: AnsiString): AnsiString; {:Returns a binary string with HMAC-SHA1 hash.} function HMAC_SHA1(Text, Key: AnsiString): AnsiString; {:Returns a binary string with a SHA-1 hashing of string what is constructed by repeating "value" until length is "Len".} function SHA1LongHash(const Value: AnsiString; Len: integer): AnsiString; {:Returns a binary string with a RSA-MD4 hashing of "Value" string.} function MD4(const Value: AnsiString): AnsiString; implementation const Crc32Tab: array[0..255] of Integer = ( Integer($00000000), Integer($77073096), Integer($EE0E612C), Integer($990951BA), Integer($076DC419), Integer($706AF48F), Integer($E963A535), Integer($9E6495A3), Integer($0EDB8832), Integer($79DCB8A4), Integer($E0D5E91E), Integer($97D2D988), Integer($09B64C2B), Integer($7EB17CBD), Integer($E7B82D07), Integer($90BF1D91), Integer($1DB71064), Integer($6AB020F2), Integer($F3B97148), Integer($84BE41DE), Integer($1ADAD47D), Integer($6DDDE4EB), Integer($F4D4B551), Integer($83D385C7), Integer($136C9856), Integer($646BA8C0), Integer($FD62F97A), Integer($8A65C9EC), Integer($14015C4F), Integer($63066CD9), Integer($FA0F3D63), Integer($8D080DF5), Integer($3B6E20C8), Integer($4C69105E), Integer($D56041E4), Integer($A2677172), Integer($3C03E4D1), Integer($4B04D447), Integer($D20D85FD), Integer($A50AB56B), Integer($35B5A8FA), Integer($42B2986C), Integer($DBBBC9D6), Integer($ACBCF940), Integer($32D86CE3), Integer($45DF5C75), Integer($DCD60DCF), Integer($ABD13D59), Integer($26D930AC), Integer($51DE003A), Integer($C8D75180), Integer($BFD06116), Integer($21B4F4B5), Integer($56B3C423), Integer($CFBA9599), Integer($B8BDA50F), Integer($2802B89E), Integer($5F058808), Integer($C60CD9B2), Integer($B10BE924), Integer($2F6F7C87), Integer($58684C11), Integer($C1611DAB), Integer($B6662D3D), Integer($76DC4190), Integer($01DB7106), Integer($98D220BC), Integer($EFD5102A), Integer($71B18589), Integer($06B6B51F), Integer($9FBFE4A5), Integer($E8B8D433), Integer($7807C9A2), Integer($0F00F934), Integer($9609A88E), Integer($E10E9818), Integer($7F6A0DBB), Integer($086D3D2D), Integer($91646C97), Integer($E6635C01), Integer($6B6B51F4), Integer($1C6C6162), Integer($856530D8), Integer($F262004E), Integer($6C0695ED), Integer($1B01A57B), Integer($8208F4C1), Integer($F50FC457), Integer($65B0D9C6), Integer($12B7E950), Integer($8BBEB8EA), Integer($FCB9887C), Integer($62DD1DDF), Integer($15DA2D49), Integer($8CD37CF3), Integer($FBD44C65), Integer($4DB26158), Integer($3AB551CE), Integer($A3BC0074), Integer($D4BB30E2), Integer($4ADFA541), Integer($3DD895D7), Integer($A4D1C46D), Integer($D3D6F4FB), Integer($4369E96A), Integer($346ED9FC), Integer($AD678846), Integer($DA60B8D0), Integer($44042D73), Integer($33031DE5), Integer($AA0A4C5F), Integer($DD0D7CC9), Integer($5005713C), Integer($270241AA), Integer($BE0B1010), Integer($C90C2086), Integer($5768B525), Integer($206F85B3), Integer($B966D409), Integer($CE61E49F), Integer($5EDEF90E), Integer($29D9C998), Integer($B0D09822), Integer($C7D7A8B4), Integer($59B33D17), Integer($2EB40D81), Integer($B7BD5C3B), Integer($C0BA6CAD), Integer($EDB88320), Integer($9ABFB3B6), Integer($03B6E20C), Integer($74B1D29A), Integer($EAD54739), Integer($9DD277AF), Integer($04DB2615), Integer($73DC1683), Integer($E3630B12), Integer($94643B84), Integer($0D6D6A3E), Integer($7A6A5AA8), Integer($E40ECF0B), Integer($9309FF9D), Integer($0A00AE27), Integer($7D079EB1), Integer($F00F9344), Integer($8708A3D2), Integer($1E01F268), Integer($6906C2FE), Integer($F762575D), Integer($806567CB), Integer($196C3671), Integer($6E6B06E7), Integer($FED41B76), Integer($89D32BE0), Integer($10DA7A5A), Integer($67DD4ACC), Integer($F9B9DF6F), Integer($8EBEEFF9), Integer($17B7BE43), Integer($60B08ED5), Integer($D6D6A3E8), Integer($A1D1937E), Integer($38D8C2C4), Integer($4FDFF252), Integer($D1BB67F1), Integer($A6BC5767), Integer($3FB506DD), Integer($48B2364B), Integer($D80D2BDA), Integer($AF0A1B4C), Integer($36034AF6), Integer($41047A60), Integer($DF60EFC3), Integer($A867DF55), Integer($316E8EEF), Integer($4669BE79), Integer($CB61B38C), Integer($BC66831A), Integer($256FD2A0), Integer($5268E236), Integer($CC0C7795), Integer($BB0B4703), Integer($220216B9), Integer($5505262F), Integer($C5BA3BBE), Integer($B2BD0B28), Integer($2BB45A92), Integer($5CB36A04), Integer($C2D7FFA7), Integer($B5D0CF31), Integer($2CD99E8B), Integer($5BDEAE1D), Integer($9B64C2B0), Integer($EC63F226), Integer($756AA39C), Integer($026D930A), Integer($9C0906A9), Integer($EB0E363F), Integer($72076785), Integer($05005713), Integer($95BF4A82), Integer($E2B87A14), Integer($7BB12BAE), Integer($0CB61B38), Integer($92D28E9B), Integer($E5D5BE0D), Integer($7CDCEFB7), Integer($0BDBDF21), Integer($86D3D2D4), Integer($F1D4E242), Integer($68DDB3F8), Integer($1FDA836E), Integer($81BE16CD), Integer($F6B9265B), Integer($6FB077E1), Integer($18B74777), Integer($88085AE6), Integer($FF0F6A70), Integer($66063BCA), Integer($11010B5C), Integer($8F659EFF), Integer($F862AE69), Integer($616BFFD3), Integer($166CCF45), Integer($A00AE278), Integer($D70DD2EE), Integer($4E048354), Integer($3903B3C2), Integer($A7672661), Integer($D06016F7), Integer($4969474D), Integer($3E6E77DB), Integer($AED16A4A), Integer($D9D65ADC), Integer($40DF0B66), Integer($37D83BF0), Integer($A9BCAE53), Integer($DEBB9EC5), Integer($47B2CF7F), Integer($30B5FFE9), Integer($BDBDF21C), Integer($CABAC28A), Integer($53B39330), Integer($24B4A3A6), Integer($BAD03605), Integer($CDD70693), Integer($54DE5729), Integer($23D967BF), Integer($B3667A2E), Integer($C4614AB8), Integer($5D681B02), Integer($2A6F2B94), Integer($B40BBE37), Integer($C30C8EA1), Integer($5A05DF1B), Integer($2D02EF8D) ); Crc16Tab: array[0..255] of Word = ( $0000, $1189, $2312, $329B, $4624, $57AD, $6536, $74BF, $8C48, $9DC1, $AF5A, $BED3, $CA6C, $DBE5, $E97E, $F8F7, $1081, $0108, $3393, $221A, $56A5, $472C, $75B7, $643E, $9CC9, $8D40, $BFDB, $AE52, $DAED, $CB64, $F9FF, $E876, $2102, $308B, $0210, $1399, $6726, $76AF, $4434, $55BD, $AD4A, $BCC3, $8E58, $9FD1, $EB6E, $FAE7, $C87C, $D9F5, $3183, $200A, $1291, $0318, $77A7, $662E, $54B5, $453C, $BDCB, $AC42, $9ED9, $8F50, $FBEF, $EA66, $D8FD, $C974, $4204, $538D, $6116, $709F, $0420, $15A9, $2732, $36BB, $CE4C, $DFC5, $ED5E, $FCD7, $8868, $99E1, $AB7A, $BAF3, $5285, $430C, $7197, $601E, $14A1, $0528, $37B3, $263A, $DECD, $CF44, $FDDF, $EC56, $98E9, $8960, $BBFB, $AA72, $6306, $728F, $4014, $519D, $2522, $34AB, $0630, $17B9, $EF4E, $FEC7, $CC5C, $DDD5, $A96A, $B8E3, $8A78, $9BF1, $7387, $620E, $5095, $411C, $35A3, $242A, $16B1, $0738, $FFCF, $EE46, $DCDD, $CD54, $B9EB, $A862, $9AF9, $8B70, $8408, $9581, $A71A, $B693, $C22C, $D3A5, $E13E, $F0B7, $0840, $19C9, $2B52, $3ADB, $4E64, $5FED, $6D76, $7CFF, $9489, $8500, $B79B, $A612, $D2AD, $C324, $F1BF, $E036, $18C1, $0948, $3BD3, $2A5A, $5EE5, $4F6C, $7DF7, $6C7E, $A50A, $B483, $8618, $9791, $E32E, $F2A7, $C03C, $D1B5, $2942, $38CB, $0A50, $1BD9, $6F66, $7EEF, $4C74, $5DFD, $B58B, $A402, $9699, $8710, $F3AF, $E226, $D0BD, $C134, $39C3, $284A, $1AD1, $0B58, $7FE7, $6E6E, $5CF5, $4D7C, $C60C, $D785, $E51E, $F497, $8028, $91A1, $A33A, $B2B3, $4A44, $5BCD, $6956, $78DF, $0C60, $1DE9, $2F72, $3EFB, $D68D, $C704, $F59F, $E416, $90A9, $8120, $B3BB, $A232, $5AC5, $4B4C, $79D7, $685E, $1CE1, $0D68, $3FF3, $2E7A, $E70E, $F687, $C41C, $D595, $A12A, $B0A3, $8238, $93B1, $6B46, $7ACF, $4854, $59DD, $2D62, $3CEB, $0E70, $1FF9, $F78F, $E606, $D49D, $C514, $B1AB, $A022, $92B9, $8330, $7BC7, $6A4E, $58D5, $495C, $3DE3, $2C6A, $1EF1, $0F78 ); procedure ArrByteToLong(var ArByte: Array of byte; var ArLong: Array of Integer); {$IFDEF CIL} var n: integer; {$ENDIF} begin if (High(ArByte) + 1) > ((High(ArLong) + 1) * 4) then Exit; {$IFDEF CIL} for n := 0 to ((high(ArByte) + 1) div 4) - 1 do ArLong[n] := ArByte[n * 4 + 0] + (ArByte[n * 4 + 1] shl 8) + (ArByte[n * 4 + 2] shl 16) + (ArByte[n * 4 + 3] shl 24); {$ELSE} Move(ArByte[0], ArLong[0], High(ArByte) + 1); {$ENDIF} end; procedure ArrLongToByte(var ArLong: Array of Integer; var ArByte: Array of byte); {$IFDEF CIL} var n: integer; {$ENDIF} begin if (High(ArByte) + 1) < ((High(ArLong) + 1) * 4) then Exit; {$IFDEF CIL} for n := 0 to high(ArLong) do begin ArByte[n * 4 + 0] := ArLong[n] and $000000FF; ArByte[n * 4 + 1] := (ArLong[n] shr 8) and $000000FF; ArByte[n * 4 + 2] := (ArLong[n] shr 16) and $000000FF; ArByte[n * 4 + 3] := (ArLong[n] shr 24) and $000000FF; end; {$ELSE} Move(ArLong[0], ArByte[0], High(ArByte) + 1); {$ENDIF} end; type TMDCtx = record State: array[0..3] of Integer; Count: array[0..1] of Integer; BufAnsiChar: array[0..63] of Byte; BufLong: array[0..15] of Integer; end; TSHA1Ctx= record Hi, Lo: integer; Buffer: array[0..63] of byte; Index: integer; Hash: array[0..4] of Integer; HashByte: array[0..19] of byte; end; TMDTransform = procedure(var Buf: array of LongInt; const Data: array of LongInt); {==============================================================================} function DecodeTriplet(const Value: AnsiString; Delimiter: AnsiChar): AnsiString; var x, l, lv: Integer; c: AnsiChar; b: Byte; bad: Boolean; begin lv := Length(Value); SetLength(Result, lv); x := 1; l := 1; while x <= lv do begin c := Value[x]; Inc(x); if c <> Delimiter then begin Result[l] := c; Inc(l); end else if x < lv then begin Case Value[x] Of #13: if (Value[x + 1] = #10) then Inc(x, 2) else Inc(x); #10: if (Value[x + 1] = #13) then Inc(x, 2) else Inc(x); else begin bad := False; Case Value[x] Of '0'..'9': b := (Byte(Value[x]) - 48) Shl 4; 'a'..'f', 'A'..'F': b := ((Byte(Value[x]) And 7) + 9) shl 4; else begin b := 0; bad := True; end; end; Case Value[x + 1] Of '0'..'9': b := b Or (Byte(Value[x + 1]) - 48); 'a'..'f', 'A'..'F': b := b Or ((Byte(Value[x + 1]) And 7) + 9); else bad := True; end; if bad then begin Result[l] := c; Inc(l); end else begin Inc(x, 2); Result[l] := AnsiChar(b); Inc(l); end; end; end; end else break; end; Dec(l); SetLength(Result, l); end; {==============================================================================} function DecodeQuotedPrintable(const Value: AnsiString): AnsiString; begin Result := DecodeTriplet(Value, '='); end; {==============================================================================} function DecodeURL(const Value: AnsiString): AnsiString; begin Result := DecodeTriplet(Value, '%'); end; {==============================================================================} function EncodeTriplet(const Value: AnsiString; Delimiter: AnsiChar; Specials: TSpecials): AnsiString; var n, l: Integer; s: AnsiString; c: AnsiChar; begin SetLength(Result, Length(Value) * 3); l := 1; for n := 1 to Length(Value) do begin c := Value[n]; if c in Specials then begin Result[l] := Delimiter; Inc(l); s := IntToHex(Ord(c), 2); Result[l] := s[1]; Inc(l); Result[l] := s[2]; Inc(l); end else begin Result[l] := c; Inc(l); end; end; Dec(l); SetLength(Result, l); end; {==============================================================================} function EncodeQuotedPrintable(const Value: AnsiString): AnsiString; begin Result := EncodeTriplet(Value, '=', ['='] + NonAsciiChar); end; {==============================================================================} function EncodeSafeQuotedPrintable(const Value: AnsiString): AnsiString; begin Result := EncodeTriplet(Value, '=', SpecialChar + NonAsciiChar); end; {==============================================================================} function EncodeURLElement(const Value: AnsiString): AnsiString; begin Result := EncodeTriplet(Value, '%', URLSpecialChar + URLFullSpecialChar); end; {==============================================================================} function EncodeURL(const Value: AnsiString): AnsiString; begin Result := EncodeTriplet(Value, '%', URLSpecialChar); end; {==============================================================================} function Decode4to3(const Value, Table: AnsiString): AnsiString; var x, y, n, l: Integer; d: array[0..3] of Byte; begin SetLength(Result, Length(Value)); x := 1; l := 1; while x <= Length(Value) do begin for n := 0 to 3 do begin if x > Length(Value) then d[n] := 64 else begin y := Pos(Value[x], Table); if y < 1 then y := 1; d[n] := y - 1; end; Inc(x); end; Result[l] := AnsiChar((D[0] and $3F) shl 2 + (D[1] and $30) shr 4); Inc(l); if d[2] <> 64 then begin Result[l] := AnsiChar((D[1] and $0F) shl 4 + (D[2] and $3C) shr 2); Inc(l); if d[3] <> 64 then begin Result[l] := AnsiChar((D[2] and $03) shl 6 + (D[3] and $3F)); Inc(l); end; end; end; Dec(l); SetLength(Result, l); end; {==============================================================================} function Decode4to3Ex(const Value, Table: AnsiString): AnsiString; var x, y, lv: Integer; d: integer; dl: integer; c: byte; p: integer; begin lv := Length(Value); SetLength(Result, lv); x := 1; dl := 4; d := 0; p := 1; while x <= lv do begin y := Ord(Value[x]); if y in [33..127] then c := Ord(Table[y - 32]) else c := 64; Inc(x); if c > 63 then continue; d := (d shl 6) or c; dec(dl); if dl <> 0 then continue; Result[p] := AnsiChar((d shr 16) and $ff); inc(p); Result[p] := AnsiChar((d shr 8) and $ff); inc(p); Result[p] := AnsiChar(d and $ff); inc(p); d := 0; dl := 4; end; case dl of 1: begin d := d shr 2; Result[p] := AnsiChar((d shr 8) and $ff); inc(p); Result[p] := AnsiChar(d and $ff); inc(p); end; 2: begin d := d shr 4; Result[p] := AnsiChar(d and $ff); inc(p); end; end; SetLength(Result, p - 1); end; {==============================================================================} function Encode3to4(const Value, Table: AnsiString): AnsiString; var c: Byte; n, l: Integer; Count: Integer; DOut: array[0..3] of Byte; begin setlength(Result, ((Length(Value) + 2) div 3) * 4); l := 1; Count := 1; while Count <= Length(Value) do begin c := Ord(Value[Count]); Inc(Count); DOut[0] := (c and $FC) shr 2; DOut[1] := (c and $03) shl 4; if Count <= Length(Value) then begin c := Ord(Value[Count]); Inc(Count); DOut[1] := DOut[1] + (c and $F0) shr 4; DOut[2] := (c and $0F) shl 2; if Count <= Length(Value) then begin c := Ord(Value[Count]); Inc(Count); DOut[2] := DOut[2] + (c and $C0) shr 6; DOut[3] := (c and $3F); end else begin DOut[3] := $40; end; end else begin DOut[2] := $40; DOut[3] := $40; end; for n := 0 to 3 do begin if (DOut[n] + 1) <= Length(Table) then begin Result[l] := Table[DOut[n] + 1]; Inc(l); end; end; end; SetLength(Result, l - 1); end; {==============================================================================} function DecodeBase64(const Value: AnsiString): AnsiString; begin Result := Decode4to3Ex(Value, ReTableBase64); end; {==============================================================================} function EncodeBase64(const Value: AnsiString): AnsiString; begin Result := Encode3to4(Value, TableBase64); end; {==============================================================================} function DecodeBase64mod(const Value: AnsiString): AnsiString; begin Result := Decode4to3(Value, TableBase64mod); end; {==============================================================================} function EncodeBase64mod(const Value: AnsiString): AnsiString; begin Result := Encode3to4(Value, TableBase64mod); end; {==============================================================================} function DecodeUU(const Value: AnsiString): AnsiString; var s: AnsiString; uut: AnsiString; x: Integer; begin Result := ''; uut := TableUU; s := trim(UpperCase(Value)); if s = '' then Exit; if Pos('BEGIN', s) = 1 then Exit; if Pos('END', s) = 1 then Exit; if Pos('TABLE', s) = 1 then Exit; //ignore Table yet (set custom UUT) //begin decoding x := Pos(Value[1], uut) - 1; case (x mod 3) of 0: x :=(x div 3)* 4; 1: x :=((x div 3) * 4) + 2; 2: x :=((x div 3) * 4) + 3; end; //x - lenght UU line s := Copy(Value, 2, x); if s = '' then Exit; s := s + StringOfChar(' ', x - length(s)); Result := Decode4to3(s, uut); end; {==============================================================================} function EncodeUU(const Value: AnsiString): AnsiString; begin Result := ''; if Length(Value) < Length(TableUU) then Result := TableUU[Length(Value) + 1] + Encode3to4(Value, TableUU); end; {==============================================================================} function DecodeXX(const Value: AnsiString): AnsiString; var s: AnsiString; x: Integer; begin Result := ''; s := trim(UpperCase(Value)); if s = '' then Exit; if Pos('BEGIN', s) = 1 then Exit; if Pos('END', s) = 1 then Exit; //begin decoding x := Pos(Value[1], TableXX) - 1; case (x mod 3) of 0: x :=(x div 3)* 4; 1: x :=((x div 3) * 4) + 2; 2: x :=((x div 3) * 4) + 3; end; //x - lenght XX line s := Copy(Value, 2, x); if s = '' then Exit; s := s + StringOfChar(' ', x - length(s)); Result := Decode4to3(s, TableXX); end; {==============================================================================} function DecodeYEnc(const Value: AnsiString): AnsiString; var C : Byte; i: integer; begin Result := ''; i := 1; while i <= Length(Value) do begin c := Ord(Value[i]); Inc(i); if c = Ord('=') then begin c := Ord(Value[i]); Inc(i); Dec(c, 64); end; Dec(C, 42); Result := Result + AnsiChar(C); end; end; {==============================================================================} function UpdateCrc32(Value: Byte; Crc32: Integer): Integer; begin Result := (Crc32 shr 8) xor crc32tab[Byte(Value xor (Crc32 and Integer($000000FF)))]; end; {==============================================================================} function Crc32(const Value: AnsiString): Integer; var n: Integer; begin Result := Integer($FFFFFFFF); for n := 1 to Length(Value) do Result := UpdateCrc32(Ord(Value[n]), Result); Result := not Result; end; {==============================================================================} function UpdateCrc16(Value: Byte; Crc16: Word): Word; begin Result := ((Crc16 shr 8) and $00FF) xor crc16tab[Byte(Crc16 xor (Word(Value)) and $00FF)]; end; {==============================================================================} function Crc16(const Value: AnsiString): Word; var n: Integer; begin Result := $FFFF; for n := 1 to Length(Value) do Result := UpdateCrc16(Ord(Value[n]), Result); end; {==============================================================================} procedure MDInit(var MDContext: TMDCtx); var n: integer; begin MDContext.Count[0] := 0; MDContext.Count[1] := 0; for n := 0 to high(MDContext.BufAnsiChar) do MDContext.BufAnsiChar[n] := 0; for n := 0 to high(MDContext.BufLong) do MDContext.BufLong[n] := 0; MDContext.State[0] := Integer($67452301); MDContext.State[1] := Integer($EFCDAB89); MDContext.State[2] := Integer($98BADCFE); MDContext.State[3] := Integer($10325476); end; procedure MD5Transform(var Buf: array of LongInt; const Data: array of LongInt); var A, B, C, D: LongInt; procedure Round1(var W: LongInt; X, Y, Z, Data: LongInt; S: Byte); begin Inc(W, (Z xor (X and (Y xor Z))) + Data); W := (W shl S) or (W shr (32 - S)); Inc(W, X); end; procedure Round2(var W: LongInt; X, Y, Z, Data: LongInt; S: Byte); begin Inc(W, (Y xor (Z and (X xor Y))) + Data); W := (W shl S) or (W shr (32 - S)); Inc(W, X); end; procedure Round3(var W: LongInt; X, Y, Z, Data: LongInt; S: Byte); begin Inc(W, (X xor Y xor Z) + Data); W := (W shl S) or (W shr (32 - S)); Inc(W, X); end; procedure Round4(var W: LongInt; X, Y, Z, Data: LongInt; S: Byte); begin Inc(W, (Y xor (X or not Z)) + Data); W := (W shl S) or (W shr (32 - S)); Inc(W, X); end; begin A := Buf[0]; B := Buf[1]; C := Buf[2]; D := Buf[3]; Round1(A, B, C, D, Data[0] + Longint($D76AA478), 7); Round1(D, A, B, C, Data[1] + Longint($E8C7B756), 12); Round1(C, D, A, B, Data[2] + Longint($242070DB), 17); Round1(B, C, D, A, Data[3] + Longint($C1BDCEEE), 22); Round1(A, B, C, D, Data[4] + Longint($F57C0FAF), 7); Round1(D, A, B, C, Data[5] + Longint($4787C62A), 12); Round1(C, D, A, B, Data[6] + Longint($A8304613), 17); Round1(B, C, D, A, Data[7] + Longint($FD469501), 22); Round1(A, B, C, D, Data[8] + Longint($698098D8), 7); Round1(D, A, B, C, Data[9] + Longint($8B44F7AF), 12); Round1(C, D, A, B, Data[10] + Longint($FFFF5BB1), 17); Round1(B, C, D, A, Data[11] + Longint($895CD7BE), 22); Round1(A, B, C, D, Data[12] + Longint($6B901122), 7); Round1(D, A, B, C, Data[13] + Longint($FD987193), 12); Round1(C, D, A, B, Data[14] + Longint($A679438E), 17); Round1(B, C, D, A, Data[15] + Longint($49B40821), 22); Round2(A, B, C, D, Data[1] + Longint($F61E2562), 5); Round2(D, A, B, C, Data[6] + Longint($C040B340), 9); Round2(C, D, A, B, Data[11] + Longint($265E5A51), 14); Round2(B, C, D, A, Data[0] + Longint($E9B6C7AA), 20); Round2(A, B, C, D, Data[5] + Longint($D62F105D), 5); Round2(D, A, B, C, Data[10] + Longint($02441453), 9); Round2(C, D, A, B, Data[15] + Longint($D8A1E681), 14); Round2(B, C, D, A, Data[4] + Longint($E7D3FBC8), 20); Round2(A, B, C, D, Data[9] + Longint($21E1CDE6), 5); Round2(D, A, B, C, Data[14] + Longint($C33707D6), 9); Round2(C, D, A, B, Data[3] + Longint($F4D50D87), 14); Round2(B, C, D, A, Data[8] + Longint($455A14ED), 20); Round2(A, B, C, D, Data[13] + Longint($A9E3E905), 5); Round2(D, A, B, C, Data[2] + Longint($FCEFA3F8), 9); Round2(C, D, A, B, Data[7] + Longint($676F02D9), 14); Round2(B, C, D, A, Data[12] + Longint($8D2A4C8A), 20); Round3(A, B, C, D, Data[5] + Longint($FFFA3942), 4); Round3(D, A, B, C, Data[8] + Longint($8771F681), 11); Round3(C, D, A, B, Data[11] + Longint($6D9D6122), 16); Round3(B, C, D, A, Data[14] + Longint($FDE5380C), 23); Round3(A, B, C, D, Data[1] + Longint($A4BEEA44), 4); Round3(D, A, B, C, Data[4] + Longint($4BDECFA9), 11); Round3(C, D, A, B, Data[7] + Longint($F6BB4B60), 16); Round3(B, C, D, A, Data[10] + Longint($BEBFBC70), 23); Round3(A, B, C, D, Data[13] + Longint($289B7EC6), 4); Round3(D, A, B, C, Data[0] + Longint($EAA127FA), 11); Round3(C, D, A, B, Data[3] + Longint($D4EF3085), 16); Round3(B, C, D, A, Data[6] + Longint($04881D05), 23); Round3(A, B, C, D, Data[9] + Longint($D9D4D039), 4); Round3(D, A, B, C, Data[12] + Longint($E6DB99E5), 11); Round3(C, D, A, B, Data[15] + Longint($1FA27CF8), 16); Round3(B, C, D, A, Data[2] + Longint($C4AC5665), 23); Round4(A, B, C, D, Data[0] + Longint($F4292244), 6); Round4(D, A, B, C, Data[7] + Longint($432AFF97), 10); Round4(C, D, A, B, Data[14] + Longint($AB9423A7), 15); Round4(B, C, D, A, Data[5] + Longint($FC93A039), 21); Round4(A, B, C, D, Data[12] + Longint($655B59C3), 6); Round4(D, A, B, C, Data[3] + Longint($8F0CCC92), 10); Round4(C, D, A, B, Data[10] + Longint($FFEFF47D), 15); Round4(B, C, D, A, Data[1] + Longint($85845DD1), 21); Round4(A, B, C, D, Data[8] + Longint($6FA87E4F), 6); Round4(D, A, B, C, Data[15] + Longint($FE2CE6E0), 10); Round4(C, D, A, B, Data[6] + Longint($A3014314), 15); Round4(B, C, D, A, Data[13] + Longint($4E0811A1), 21); Round4(A, B, C, D, Data[4] + Longint($F7537E82), 6); Round4(D, A, B, C, Data[11] + Longint($BD3AF235), 10); Round4(C, D, A, B, Data[2] + Longint($2AD7D2BB), 15); Round4(B, C, D, A, Data[9] + Longint($EB86D391), 21); Inc(Buf[0], A); Inc(Buf[1], B); Inc(Buf[2], C); Inc(Buf[3], D); end; //fixed by James McAdams procedure MDUpdate(var MDContext: TMDCtx; const Data: AnsiString; transform: TMDTransform); var Index, partLen, InputLen, I: integer; {$IFDEF CIL} n: integer; {$ENDIF} begin InputLen := Length(Data); with MDContext do begin Index := (Count[0] shr 3) and $3F; Inc(Count[0], InputLen shl 3); if Count[0] < (InputLen shl 3) then Inc(Count[1]); Inc(Count[1], InputLen shr 29); partLen := 64 - Index; if InputLen >= partLen then begin ArrLongToByte(BufLong, BufAnsiChar); {$IFDEF CIL} for n := 1 to partLen do BufAnsiChar[index - 1 + n] := Ord(Data[n]); {$ELSE} Move(Data[1], BufAnsiChar[Index], partLen); {$ENDIF} ArrByteToLong(BufAnsiChar, BufLong); Transform(State, Buflong); I := partLen; while I + 63 < InputLen do begin ArrLongToByte(BufLong, BufAnsiChar); {$IFDEF CIL} for n := 1 to 64 do BufAnsiChar[n - 1] := Ord(Data[i + n]); {$ELSE} Move(Data[I+1], BufAnsiChar, 64); {$ENDIF} ArrByteToLong(BufAnsiChar, BufLong); Transform(State, Buflong); inc(I, 64); end; Index := 0; end else I := 0; ArrLongToByte(BufLong, BufAnsiChar); {$IFDEF CIL} for n := 1 to InputLen-I do BufAnsiChar[Index + n - 1] := Ord(Data[i + n]); {$ELSE} Move(Data[I+1], BufAnsiChar[Index], InputLen-I); {$ENDIF} ArrByteToLong(BufAnsiChar, BufLong); end end; function MDFinal(var MDContext: TMDCtx; transform: TMDTransform): AnsiString; var Cnt: Word; P: Byte; digest: array[0..15] of Byte; i: Integer; n: integer; begin for I := 0 to 15 do Digest[I] := I + 1; with MDContext do begin Cnt := (Count[0] shr 3) and $3F; P := Cnt; BufAnsiChar[P] := $80; Inc(P); Cnt := 64 - 1 - Cnt; if Cnt < 8 then begin for n := 0 to cnt - 1 do BufAnsiChar[P + n] := 0; ArrByteToLong(BufAnsiChar, BufLong); // FillChar(BufAnsiChar[P], Cnt, #0); Transform(State, BufLong); ArrLongToByte(BufLong, BufAnsiChar); for n := 0 to 55 do BufAnsiChar[n] := 0; ArrByteToLong(BufAnsiChar, BufLong); // FillChar(BufAnsiChar, 56, #0); end else begin for n := 0 to Cnt - 8 - 1 do BufAnsiChar[p + n] := 0; ArrByteToLong(BufAnsiChar, BufLong); // FillChar(BufAnsiChar[P], Cnt - 8, #0); end; BufLong[14] := Count[0]; BufLong[15] := Count[1]; Transform(State, BufLong); ArrLongToByte(State, Digest); // Move(State, Digest, 16); Result := ''; for i := 0 to 15 do Result := Result + AnsiChar(digest[i]); end; // FillChar(MD5Context, SizeOf(TMD5Ctx), #0) end; {==============================================================================} function MD5(const Value: AnsiString): AnsiString; var MDContext: TMDCtx; begin MDInit(MDContext); MDUpdate(MDContext, Value, @MD5Transform); Result := MDFinal(MDContext, @MD5Transform); end; {==============================================================================} function HMAC_MD5(Text, Key: AnsiString): AnsiString; var ipad, opad, s: AnsiString; n: Integer; MDContext: TMDCtx; begin if Length(Key) > 64 then Key := md5(Key); ipad := StringOfChar(#$36, 64); opad := StringOfChar(#$5C, 64); for n := 1 to Length(Key) do begin ipad[n] := AnsiChar(Byte(ipad[n]) xor Byte(Key[n])); opad[n] := AnsiChar(Byte(opad[n]) xor Byte(Key[n])); end; MDInit(MDContext); MDUpdate(MDContext, ipad, @MD5Transform); MDUpdate(MDContext, Text, @MD5Transform); s := MDFinal(MDContext, @MD5Transform); MDInit(MDContext); MDUpdate(MDContext, opad, @MD5Transform); MDUpdate(MDContext, s, @MD5Transform); Result := MDFinal(MDContext, @MD5Transform); end; {==============================================================================} function MD5LongHash(const Value: AnsiString; Len: integer): AnsiString; var cnt, rest: integer; l: integer; n: integer; MDContext: TMDCtx; begin l := length(Value); cnt := Len div l; rest := Len mod l; MDInit(MDContext); for n := 1 to cnt do MDUpdate(MDContext, Value, @MD5Transform); if rest > 0 then MDUpdate(MDContext, Copy(Value, 1, rest), @MD5Transform); Result := MDFinal(MDContext, @MD5Transform); end; {==============================================================================} // SHA1 is based on sources by Dave Barton (davebarton@bigfoot.com) procedure SHA1init( var SHA1Context: TSHA1Ctx ); var n: integer; begin SHA1Context.Hi := 0; SHA1Context.Lo := 0; SHA1Context.Index := 0; for n := 0 to High(SHA1Context.Buffer) do SHA1Context.Buffer[n] := 0; for n := 0 to High(SHA1Context.HashByte) do SHA1Context.HashByte[n] := 0; // FillChar(SHA1Context, SizeOf(TSHA1Ctx), #0); SHA1Context.Hash[0] := integer($67452301); SHA1Context.Hash[1] := integer($EFCDAB89); SHA1Context.Hash[2] := integer($98BADCFE); SHA1Context.Hash[3] := integer($10325476); SHA1Context.Hash[4] := integer($C3D2E1F0); end; //****************************************************************************** function RB(A: integer): integer; begin Result := (A shr 24) or ((A shr 8) and $FF00) or ((A shl 8) and $FF0000) or (A shl 24); end; procedure SHA1Compress(var Data: TSHA1Ctx); var A, B, C, D, E, T: integer; W: array[0..79] of integer; i: integer; n: integer; function F1(x, y, z: integer): integer; begin Result := z xor (x and (y xor z)); end; function F2(x, y, z: integer): integer; begin Result := x xor y xor z; end; function F3(x, y, z: integer): integer; begin Result := (x and y) or (z and (x or y)); end; function LRot32(X: integer; c: integer): integer; begin result := (x shl c) or (x shr (32 - c)); end; begin ArrByteToLong(Data.Buffer, W); // Move(Data.Buffer, W, Sizeof(Data.Buffer)); for i := 0 to 15 do W[i] := RB(W[i]); for i := 16 to 79 do W[i] := LRot32(W[i-3] xor W[i-8] xor W[i-14] xor W[i-16], 1); A := Data.Hash[0]; B := Data.Hash[1]; C := Data.Hash[2]; D := Data.Hash[3]; E := Data.Hash[4]; for i := 0 to 19 do begin T := LRot32(A, 5) + F1(B, C, D) + E + W[i] + integer($5A827999); E := D; D := C; C := LRot32(B, 30); B := A; A := T; end; for i := 20 to 39 do begin T := LRot32(A, 5) + F2(B, C, D) + E + W[i] + integer($6ED9EBA1); E := D; D := C; C := LRot32(B, 30); B := A; A := T; end; for i := 40 to 59 do begin T := LRot32(A, 5) + F3(B, C, D) + E + W[i] + integer($8F1BBCDC); E := D; D := C; C := LRot32(B, 30); B := A; A := T; end; for i := 60 to 79 do begin T := LRot32(A, 5) + F2(B, C, D) + E + W[i] + integer($CA62C1D6); E := D; D := C; C := LRot32(B, 30); B := A; A := T; end; Data.Hash[0] := Data.Hash[0] + A; Data.Hash[1] := Data.Hash[1] + B; Data.Hash[2] := Data.Hash[2] + C; Data.Hash[3] := Data.Hash[3] + D; Data.Hash[4] := Data.Hash[4] + E; for n := 0 to high(w) do w[n] := 0; // FillChar(W, Sizeof(W), 0); for n := 0 to high(Data.Buffer) do Data.Buffer[n] := 0; // FillChar(Data.Buffer, Sizeof(Data.Buffer), 0); end; //****************************************************************************** procedure SHA1Update(var Context: TSHA1Ctx; const Data: AnsiString); var Len: integer; n: integer; i, k: integer; begin Len := Length(data); for k := 0 to 7 do begin i := Context.Lo; Inc(Context.Lo, Len); if Context.Lo < i then Inc(Context.Hi); end; for n := 1 to len do begin Context.Buffer[Context.Index] := byte(Data[n]); Inc(Context.Index); if Context.Index = 64 then begin Context.Index := 0; SHA1Compress(Context); end; end; end; //****************************************************************************** function SHA1Final(var Context: TSHA1Ctx): AnsiString; type Pinteger = ^integer; var i: integer; procedure ItoArr(var Ar: Array of byte; I, value: Integer); begin Ar[i + 0] := Value and $000000FF; Ar[i + 1] := (Value shr 8) and $000000FF; Ar[i + 2] := (Value shr 16) and $000000FF; Ar[i + 3] := (Value shr 24) and $000000FF; end; begin Context.Buffer[Context.Index] := $80; if Context.Index >= 56 then SHA1Compress(Context); ItoArr(Context.Buffer, 56, RB(Context.Hi)); ItoArr(Context.Buffer, 60, RB(Context.Lo)); // Pinteger(@Context.Buffer[56])^ := RB(Context.Hi); // Pinteger(@Context.Buffer[60])^ := RB(Context.Lo); SHA1Compress(Context); Context.Hash[0] := RB(Context.Hash[0]); Context.Hash[1] := RB(Context.Hash[1]); Context.Hash[2] := RB(Context.Hash[2]); Context.Hash[3] := RB(Context.Hash[3]); Context.Hash[4] := RB(Context.Hash[4]); ArrLongToByte(Context.Hash, Context.HashByte); Result := ''; for i := 0 to 19 do Result := Result + AnsiChar(Context.HashByte[i]); end; function SHA1(const Value: AnsiString): AnsiString; var SHA1Context: TSHA1Ctx; begin SHA1Init(SHA1Context); SHA1Update(SHA1Context, Value); Result := SHA1Final(SHA1Context); end; {==============================================================================} function HMAC_SHA1(Text, Key: AnsiString): AnsiString; var ipad, opad, s: AnsiString; n: Integer; SHA1Context: TSHA1Ctx; begin if Length(Key) > 64 then Key := SHA1(Key); ipad := StringOfChar(#$36, 64); opad := StringOfChar(#$5C, 64); for n := 1 to Length(Key) do begin ipad[n] := AnsiChar(Byte(ipad[n]) xor Byte(Key[n])); opad[n] := AnsiChar(Byte(opad[n]) xor Byte(Key[n])); end; SHA1Init(SHA1Context); SHA1Update(SHA1Context, ipad); SHA1Update(SHA1Context, Text); s := SHA1Final(SHA1Context); SHA1Init(SHA1Context); SHA1Update(SHA1Context, opad); SHA1Update(SHA1Context, s); Result := SHA1Final(SHA1Context); end; {==============================================================================} function SHA1LongHash(const Value: AnsiString; Len: integer): AnsiString; var cnt, rest: integer; l: integer; n: integer; SHA1Context: TSHA1Ctx; begin l := length(Value); cnt := Len div l; rest := Len mod l; SHA1Init(SHA1Context); for n := 1 to cnt do SHA1Update(SHA1Context, Value); if rest > 0 then SHA1Update(SHA1Context, Copy(Value, 1, rest)); Result := SHA1Final(SHA1Context); end; {==============================================================================} procedure MD4Transform(var Buf: array of LongInt; const Data: array of LongInt); var A, B, C, D: LongInt; function LRot32(a, b: longint): longint; begin Result:= (a shl b) or (a shr (32 - b)); end; begin A := Buf[0]; B := Buf[1]; C := Buf[2]; D := Buf[3]; A:= LRot32(A + (D xor (B and (C xor D))) + Data[ 0], 3); D:= LRot32(D + (C xor (A and (B xor C))) + Data[ 1], 7); C:= LRot32(C + (B xor (D and (A xor B))) + Data[ 2], 11); B:= LRot32(B + (A xor (C and (D xor A))) + Data[ 3], 19); A:= LRot32(A + (D xor (B and (C xor D))) + Data[ 4], 3); D:= LRot32(D + (C xor (A and (B xor C))) + Data[ 5], 7); C:= LRot32(C + (B xor (D and (A xor B))) + Data[ 6], 11); B:= LRot32(B + (A xor (C and (D xor A))) + Data[ 7], 19); A:= LRot32(A + (D xor (B and (C xor D))) + Data[ 8], 3); D:= LRot32(D + (C xor (A and (B xor C))) + Data[ 9], 7); C:= LRot32(C + (B xor (D and (A xor B))) + Data[10], 11); B:= LRot32(B + (A xor (C and (D xor A))) + Data[11], 19); A:= LRot32(A + (D xor (B and (C xor D))) + Data[12], 3); D:= LRot32(D + (C xor (A and (B xor C))) + Data[13], 7); C:= LRot32(C + (B xor (D and (A xor B))) + Data[14], 11); B:= LRot32(B + (A xor (C and (D xor A))) + Data[15], 19); A:= LRot32(A + ((B and C) or (B and D) or (C and D)) + Data[ 0] + longint($5a827999), 3); D:= LRot32(D + ((A and B) or (A and C) or (B and C)) + Data[ 4] + longint($5a827999), 5); C:= LRot32(C + ((D and A) or (D and B) or (A and B)) + Data[ 8] + longint($5a827999), 9); B:= LRot32(B + ((C and D) or (C and A) or (D and A)) + Data[12] + longint($5a827999), 13); A:= LRot32(A + ((B and C) or (B and D) or (C and D)) + Data[ 1] + longint($5a827999), 3); D:= LRot32(D + ((A and B) or (A and C) or (B and C)) + Data[ 5] + longint($5a827999), 5); C:= LRot32(C + ((D and A) or (D and B) or (A and B)) + Data[ 9] + longint($5a827999), 9); B:= LRot32(B + ((C and D) or (C and A) or (D and A)) + Data[13] + longint($5a827999), 13); A:= LRot32(A + ((B and C) or (B and D) or (C and D)) + Data[ 2] + longint($5a827999), 3); D:= LRot32(D + ((A and B) or (A and C) or (B and C)) + Data[ 6] + longint($5a827999), 5); C:= LRot32(C + ((D and A) or (D and B) or (A and B)) + Data[10] + longint($5a827999), 9); B:= LRot32(B + ((C and D) or (C and A) or (D and A)) + Data[14] + longint($5a827999), 13); A:= LRot32(A + ((B and C) or (B and D) or (C and D)) + Data[ 3] + longint($5a827999), 3); D:= LRot32(D + ((A and B) or (A and C) or (B and C)) + Data[ 7] + longint($5a827999), 5); C:= LRot32(C + ((D and A) or (D and B) or (A and B)) + Data[11] + longint($5a827999), 9); B:= LRot32(B + ((C and D) or (C and A) or (D and A)) + Data[15] + longint($5a827999), 13); A:= LRot32(A + (B xor C xor D) + Data[ 0] + longint($6ed9eba1), 3); D:= LRot32(D + (A xor B xor C) + Data[ 8] + longint($6ed9eba1), 9); C:= LRot32(C + (D xor A xor B) + Data[ 4] + longint($6ed9eba1), 11); B:= LRot32(B + (C xor D xor A) + Data[12] + longint($6ed9eba1), 15); A:= LRot32(A + (B xor C xor D) + Data[ 2] + longint($6ed9eba1), 3); D:= LRot32(D + (A xor B xor C) + Data[10] + longint($6ed9eba1), 9); C:= LRot32(C + (D xor A xor B) + Data[ 6] + longint($6ed9eba1), 11); B:= LRot32(B + (C xor D xor A) + Data[14] + longint($6ed9eba1), 15); A:= LRot32(A + (B xor C xor D) + Data[ 1] + longint($6ed9eba1), 3); D:= LRot32(D + (A xor B xor C) + Data[ 9] + longint($6ed9eba1), 9); C:= LRot32(C + (D xor A xor B) + Data[ 5] + longint($6ed9eba1), 11); B:= LRot32(B + (C xor D xor A) + Data[13] + longint($6ed9eba1), 15); A:= LRot32(A + (B xor C xor D) + Data[ 3] + longint($6ed9eba1), 3); D:= LRot32(D + (A xor B xor C) + Data[11] + longint($6ed9eba1), 9); C:= LRot32(C + (D xor A xor B) + Data[ 7] + longint($6ed9eba1), 11); B:= LRot32(B + (C xor D xor A) + Data[15] + longint($6ed9eba1), 15); Inc(Buf[0], A); Inc(Buf[1], B); Inc(Buf[2], C); Inc(Buf[3], D); end; {==============================================================================} function MD4(const Value: AnsiString): AnsiString; var MDContext: TMDCtx; begin MDInit(MDContext); MDUpdate(MDContext, Value, @MD4Transform); Result := MDFinal(MDContext, @MD4Transform); end; {==============================================================================} end. TransGUI/synapse/source/lib/synautil.pas0000644000000000000000000013643311366572451017341 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 004.014.000 | |==============================================================================| | Content: support procedures and functions | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. | | Portions created by Hernan Sanchez are Copyright (c) 2000. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Hernan Sanchez (hernan.sanchez@iname.com) | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Support procedures and functions)} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$R-} {$H+} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$WARN SUSPICIOUS_TYPECAST OFF} {$ENDIF} unit synautil; interface uses {$IFDEF MSWINDOWS} Windows, {$ELSE} {$IFDEF FPC} UnixUtil, Unix, BaseUnix, {$ELSE} Libc, {$ENDIF} {$ENDIF} {$IFDEF CIL} System.IO, {$ENDIF} SysUtils, Classes, SynaFpc; {$IFDEF VER100} type int64 = integer; {$ENDIF} {:Return your timezone bias from UTC time in minutes.} function TimeZoneBias: integer; {:Return your timezone bias from UTC time in string representation like "+0200".} function TimeZone: string; {:Returns current time in format defined in RFC-822. Useful for SMTP messages, but other protocols use this time format as well. Results contains the timezone specification. Four digit year is used to break any Y2K concerns. (Example 'Fri, 15 Oct 1999 21:14:56 +0200')} function Rfc822DateTime(t: TDateTime): string; {:Returns date and time in format defined in C compilers in format "mmm dd hh:nn:ss"} function CDateTime(t: TDateTime): string; {:Returns date and time in format defined in format 'yymmdd hhnnss'} function SimpleDateTime(t: TDateTime): string; {:Returns date and time in format defined in ANSI C compilers in format "ddd mmm d hh:nn:ss yyyy" } function AnsiCDateTime(t: TDateTime): string; {:Decode three-letter string with name of month to their month number. If string not match any month name, then is returned 0. For parsing are used predefined names for English, French and German and names from system locale too.} function GetMonthNumber(Value: String): integer; {:Return decoded time from given string. Time must be witch separator ':'. You can use "hh:mm" or "hh:mm:ss".} function GetTimeFromStr(Value: string): TDateTime; {:Decode string in format "m-d-y" to TDateTime type.} function GetDateMDYFromStr(Value: string): TDateTime; {:Decode various string representations of date and time to Tdatetime type. This function do all timezone corrections too! This function can decode lot of formats like: @longcode(# ddd, d mmm yyyy hh:mm:ss ddd, d mmm yy hh:mm:ss ddd, mmm d yyyy hh:mm:ss ddd mmm dd hh:mm:ss yyyy #) and more with lot of modifications, include: @longcode(# Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() Format #) Timezone corrections known lot of symbolic timezone names (like CEST, EDT, etc.) or numeric representation (like +0200). By convention defined in RFC timezone +0000 is GMT and -0000 is current your system timezone.} function DecodeRfcDateTime(Value: string): TDateTime; {:Return current system date and time in UTC timezone.} function GetUTTime: TDateTime; {:Set Newdt as current system date and time in UTC timezone. This function work only if you have administrator rights!} function SetUTTime(Newdt: TDateTime): Boolean; {:Return current value of system timer with precizion 1 millisecond. Good for measure time difference.} function GetTick: LongWord; {:Return difference between two timestamps. It working fine only for differences smaller then maxint. (difference must be smaller then 24 days.)} function TickDelta(TickOld, TickNew: LongWord): LongWord; {:Return two characters, which ordinal values represents the value in byte format. (High-endian)} function CodeInt(Value: Word): Ansistring; {:Decodes two characters located at "Index" offset position of the "Value" string to Word values.} function DecodeInt(const Value: Ansistring; Index: Integer): Word; {:Return four characters, which ordinal values represents the value in byte format. (High-endian)} function CodeLongInt(Value: LongInt): Ansistring; {:Decodes four characters located at "Index" offset position of the "Value" string to LongInt values.} function DecodeLongInt(const Value: Ansistring; Index: Integer): LongInt; {:Dump binary buffer stored in a string to a result string.} function DumpStr(const Buffer: Ansistring): string; {:Dump binary buffer stored in a string to a result string. All bytes with code of character is written as character, not as hexadecimal value.} function DumpExStr(const Buffer: Ansistring): string; {:Dump binary buffer stored in a string to a file with DumpFile filename.} procedure Dump(const Buffer: AnsiString; DumpFile: string); {:Dump binary buffer stored in a string to a file with DumpFile filename. All bytes with code of character is written as character, not as hexadecimal value.} procedure DumpEx(const Buffer: AnsiString; DumpFile: string); {:Like TrimLeft, but remove only spaces, not control characters!} function TrimSPLeft(const S: string): string; {:Like TrimRight, but remove only spaces, not control characters!} function TrimSPRight(const S: string): string; {:Like Trim, but remove only spaces, not control characters!} function TrimSP(const S: string): string; {:Returns a portion of the "Value" string located to the left of the "Delimiter" string. If a delimiter is not found, results is original string.} function SeparateLeft(const Value, Delimiter: string): string; {:Returns the portion of the "Value" string located to the right of the "Delimiter" string. If a delimiter is not found, results is original string.} function SeparateRight(const Value, Delimiter: string): string; {:Returns parameter value from string in format: parameter1="value1"; parameter2=value2} function GetParameter(const Value, Parameter: string): string; {:parse value string with elements differed by Delimiter into stringlist.} procedure ParseParametersEx(Value, Delimiter: string; const Parameters: TStrings); {:parse value string with elements differed by ';' into stringlist.} procedure ParseParameters(Value: string; const Parameters: TStrings); {:Index of string in stringlist with same beginning as Value is returned.} function IndexByBegin(Value: string; const List: TStrings): integer; {:Returns only the e-mail portion of an address from the full address format. i.e. returns 'nobody@@somewhere.com' from '"someone" '} function GetEmailAddr(const Value: string): string; {:Returns only the description part from a full address format. i.e. returns 'someone' from '"someone" '} function GetEmailDesc(Value: string): string; {:Returns a string with hexadecimal digits representing the corresponding values of the bytes found in "Value" string.} function StrToHex(const Value: Ansistring): string; {:Returns a string of binary "Digits" representing "Value".} function IntToBin(Value: Integer; Digits: Byte): string; {:Returns an integer equivalent of the binary string in "Value". (i.e. ('10001010') returns 138)} function BinToInt(const Value: string): Integer; {:Parses a URL to its various components.} function ParseURL(URL: string; var Prot, User, Pass, Host, Port, Path, Para: string): string; {:Replaces all "Search" string values found within "Value" string, with the "Replace" string value.} function ReplaceString(Value, Search, Replace: AnsiString): AnsiString; {:It is like RPos, but search is from specified possition.} function RPosEx(const Sub, Value: string; From: integer): Integer; {:It is like POS function, but from right side of Value string.} function RPos(const Sub, Value: String): Integer; {:Like @link(fetch), but working with binary strings, not with text.} function FetchBin(var Value: string; const Delimiter: string): string; {:Fetch string from left of Value string.} function Fetch(var Value: string; const Delimiter: string): string; {:Fetch string from left of Value string. This function ignore delimitesr inside quotations.} function FetchEx(var Value: string; const Delimiter, Quotation: string): string; {:If string is binary string (contains non-printable characters), then is returned true.} function IsBinaryString(const Value: AnsiString): Boolean; {:return position of string terminator in string. If terminator found, then is returned in terminator parameter. Possible line terminators are: CRLF, LFCR, CR, LF} function PosCRLF(const Value: AnsiString; var Terminator: AnsiString): integer; {:Delete empty strings from end of stringlist.} Procedure StringsTrim(const value: TStrings); {:Like Pos function, buf from given string possition.} function PosFrom(const SubStr, Value: String; From: integer): integer; {$IFNDEF CIL} {:Increase pointer by value.} function IncPoint(const p: pointer; Value: integer): pointer; {$ENDIF} {:Get string between PairBegin and PairEnd. This function respect nesting. For example: @longcode(# Value is: 'Hi! (hello(yes!))' pairbegin is: '(' pairend is: ')' In this case result is: 'hello(yes!)'#)} function GetBetween(const PairBegin, PairEnd, Value: string): string; {:Return count of Chr in Value string.} function CountOfChar(const Value: string; Chr: char): integer; {:Remove quotation from Value string. If Value is not quoted, then return same string without any modification. } function UnquoteStr(const Value: string; Quote: Char): string; {:Quote Value string. If Value contains some Quote chars, then it is doubled.} function QuoteStr(const Value: string; Quote: Char): string; {:Convert lines in stringlist from 'name: value' form to 'name=value' form.} procedure HeadersToList(const Value: TStrings); {:Convert lines in stringlist from 'name=value' form to 'name: value' form.} procedure ListToHeaders(const Value: TStrings); {:swap bytes in integer.} function SwapBytes(Value: integer): integer; {:read string with requested length form stream.} function ReadStrFromStream(const Stream: TStream; len: integer): AnsiString; {:write string to stream.} procedure WriteStrToStream(const Stream: TStream; Value: AnsiString); {:Return filename of new temporary file in Dir (if empty, then default temporary directory is used) and with optional filename prefix.} function GetTempFile(const Dir, prefix: AnsiString): AnsiString; {:Return padded string. If length is greater, string is truncated. If length is smaller, string is padded by Pad character.} function PadString(const Value: AnsiString; len: integer; Pad: AnsiChar): AnsiString; {:Read header from "Value" stringlist beginning at "Index" position. If header is Splitted into multiple lines, then this procedure de-split it into one line.} function NormalizeHeader(Value: TStrings; var Index: Integer): string; var {:can be used for your own months strings for @link(getmonthnumber)} CustomMonthNames: array[1..12] of string; implementation {==============================================================================} const MyDayNames: array[1..7] of AnsiString = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); var MyMonthNames: array[0..6, 1..12] of String = ( ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', //rewrited by system locales 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', //English 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), ('jan', 'fév', 'mar', 'avr', 'mai', 'jun', //French 'jul', 'aoû', 'sep', 'oct', 'nov', 'déc'), ('jan', 'fev', 'mar', 'avr', 'mai', 'jun', //French#2 'jul', 'aou', 'sep', 'oct', 'nov', 'dec'), ('Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', //German 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), ('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', //German#2 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), ('Led', 'Úno', 'Bøe', 'Dub', 'Kvì', 'Èen', //Czech 'Èec', 'Srp', 'Záø', 'Øíj', 'Lis', 'Pro') ); {==============================================================================} function TimeZoneBias: integer; {$IFNDEF MSWINDOWS} {$IFNDEF FPC} var t: TTime_T; UT: TUnixTime; begin __time(@T); localtime_r(@T, UT); Result := ut.__tm_gmtoff div 60; {$ELSE} begin Result := TZSeconds div 60; {$ENDIF} {$ELSE} var zoneinfo: TTimeZoneInformation; bias: Integer; begin case GetTimeZoneInformation(Zoneinfo) of 2: bias := zoneinfo.Bias + zoneinfo.DaylightBias; 1: bias := zoneinfo.Bias + zoneinfo.StandardBias; else bias := zoneinfo.Bias; end; Result := bias * (-1); {$ENDIF} end; {==============================================================================} function TimeZone: string; var bias: Integer; h, m: Integer; begin bias := TimeZoneBias; if bias >= 0 then Result := '+' else Result := '-'; bias := Abs(bias); h := bias div 60; m := bias mod 60; Result := Result + Format('%.2d%.2d', [h, m]); end; {==============================================================================} function Rfc822DateTime(t: TDateTime): string; var wYear, wMonth, wDay: word; begin DecodeDate(t, wYear, wMonth, wDay); Result := Format('%s, %d %s %s %s', [MyDayNames[DayOfWeek(t)], wDay, MyMonthNames[1, wMonth], FormatDateTime('yyyy hh":"nn":"ss', t), TimeZone]); end; {==============================================================================} function CDateTime(t: TDateTime): string; var wYear, wMonth, wDay: word; begin DecodeDate(t, wYear, wMonth, wDay); Result:= Format('%s %2d %s', [MyMonthNames[1, wMonth], wDay, FormatDateTime('hh":"nn":"ss', t)]); end; {==============================================================================} function SimpleDateTime(t: TDateTime): string; begin Result := FormatDateTime('yymmdd hhnnss', t); end; {==============================================================================} function AnsiCDateTime(t: TDateTime): string; var wYear, wMonth, wDay: word; begin DecodeDate(t, wYear, wMonth, wDay); Result := Format('%s %s %d %s', [MyDayNames[DayOfWeek(t)], MyMonthNames[1, wMonth], wDay, FormatDateTime('hh":"nn":"ss yyyy ', t)]); end; {==============================================================================} function DecodeTimeZone(Value: string; var Zone: integer): Boolean; var x: integer; zh, zm: integer; s: string; begin Result := false; s := Value; if (Pos('+', s) = 1) or (Pos('-',s) = 1) then begin if s = '-0000' then Zone := TimeZoneBias else if Length(s) > 4 then begin zh := StrToIntdef(s[2] + s[3], 0); zm := StrToIntdef(s[4] + s[5], 0); zone := zh * 60 + zm; if s[1] = '-' then zone := zone * (-1); end; Result := True; end else begin x := 32767; if s = 'NZDT' then x := 13; if s = 'IDLE' then x := 12; if s = 'NZST' then x := 12; if s = 'NZT' then x := 12; if s = 'EADT' then x := 11; if s = 'GST' then x := 10; if s = 'JST' then x := 9; if s = 'CCT' then x := 8; if s = 'WADT' then x := 8; if s = 'WAST' then x := 7; if s = 'ZP6' then x := 6; if s = 'ZP5' then x := 5; if s = 'ZP4' then x := 4; if s = 'BT' then x := 3; if s = 'EET' then x := 2; if s = 'MEST' then x := 2; if s = 'MESZ' then x := 2; if s = 'SST' then x := 2; if s = 'FST' then x := 2; if s = 'CEST' then x := 2; if s = 'CET' then x := 1; if s = 'FWT' then x := 1; if s = 'MET' then x := 1; if s = 'MEWT' then x := 1; if s = 'SWT' then x := 1; if s = 'UT' then x := 0; if s = 'UTC' then x := 0; if s = 'GMT' then x := 0; if s = 'WET' then x := 0; if s = 'WAT' then x := -1; if s = 'BST' then x := -1; if s = 'AT' then x := -2; if s = 'ADT' then x := -3; if s = 'AST' then x := -4; if s = 'EDT' then x := -4; if s = 'EST' then x := -5; if s = 'CDT' then x := -5; if s = 'CST' then x := -6; if s = 'MDT' then x := -6; if s = 'MST' then x := -7; if s = 'PDT' then x := -7; if s = 'PST' then x := -8; if s = 'YDT' then x := -8; if s = 'YST' then x := -9; if s = 'HDT' then x := -9; if s = 'AHST' then x := -10; if s = 'CAT' then x := -10; if s = 'HST' then x := -10; if s = 'EAST' then x := -10; if s = 'NT' then x := -11; if s = 'IDLW' then x := -12; if x <> 32767 then begin zone := x * 60; Result := True; end; end; end; {==============================================================================} function GetMonthNumber(Value: String): integer; var n: integer; function TestMonth(Value: String; Index: Integer): Boolean; var n: integer; begin Result := False; for n := 0 to 6 do if Value = AnsiUppercase(MyMonthNames[n, Index]) then begin Result := True; Break; end; end; begin Result := 0; Value := AnsiUppercase(Value); for n := 1 to 12 do if TestMonth(Value, n) or (Value = AnsiUppercase(CustomMonthNames[n])) then begin Result := n; Break; end; end; {==============================================================================} function GetTimeFromStr(Value: string): TDateTime; var x: integer; begin x := rpos(':', Value); if (x > 0) and ((Length(Value) - x) > 2) then Value := Copy(Value, 1, x + 2); Value := ReplaceString(Value, ':', TimeSeparator); Result := -1; try Result := StrToTime(Value); except on Exception do ; end; end; {==============================================================================} function GetDateMDYFromStr(Value: string): TDateTime; var wYear, wMonth, wDay: word; s: string; begin Result := 0; s := Fetch(Value, '-'); wMonth := StrToIntDef(s, 12); s := Fetch(Value, '-'); wDay := StrToIntDef(s, 30); wYear := StrToIntDef(Value, 1899); if wYear < 1000 then if (wYear > 99) then wYear := wYear + 1900 else if wYear > 50 then wYear := wYear + 1900 else wYear := wYear + 2000; try Result := EncodeDate(wYear, wMonth, wDay); except on Exception do ; end; end; {==============================================================================} function DecodeRfcDateTime(Value: string): TDateTime; var day, month, year: Word; zone: integer; x, y: integer; s: string; t: TDateTime; begin // ddd, d mmm yyyy hh:mm:ss // ddd, d mmm yy hh:mm:ss // ddd, mmm d yyyy hh:mm:ss // ddd mmm dd hh:mm:ss yyyy // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() Format Result := 0; if Value = '' then Exit; day := 0; month := 0; year := 0; zone := 0; Value := ReplaceString(Value, ' -', ' #'); Value := ReplaceString(Value, '-', ' '); Value := ReplaceString(Value, ' #', ' -'); while Value <> '' do begin s := Fetch(Value, ' '); s := uppercase(s); // timezone if DecodetimeZone(s, x) then begin zone := x; continue; end; x := StrToIntDef(s, 0); // day or year if x > 0 then if (x < 32) and (day = 0) then begin day := x; continue; end else begin if (year = 0) and ((month > 0) or (x > 12)) then begin year := x; if year < 32 then year := year + 2000; if year < 1000 then year := year + 1900; continue; end; end; // time if rpos(':', s) > Pos(':', s) then begin t := GetTimeFromStr(s); if t <> -1 then Result := t; continue; end; //timezone daylight saving time if s = 'DST' then begin zone := zone + 60; continue; end; // month y := GetMonthNumber(s); if (y > 0) and (month = 0) then month := y; end; if year = 0 then year := 1980; if month < 1 then month := 1; if month > 12 then month := 12; if day < 1 then day := 1; x := MonthDays[IsLeapYear(year), month]; if day > x then day := x; Result := Result + Encodedate(year, month, day); zone := zone - TimeZoneBias; x := zone div 1440; Result := Result - x; zone := zone mod 1440; t := EncodeTime(Abs(zone) div 60, Abs(zone) mod 60, 0, 0); if zone < 0 then t := 0 - t; Result := Result - t; end; {==============================================================================} function GetUTTime: TDateTime; {$IFDEF MSWINDOWS} {$IFNDEF FPC} var st: TSystemTime; begin GetSystemTime(st); result := SystemTimeToDateTime(st); {$ELSE} var st: SysUtils.TSystemTime; stw: Windows.TSystemTime; begin GetSystemTime(stw); st.Year := stw.wYear; st.Month := stw.wMonth; st.Day := stw.wDay; st.Hour := stw.wHour; st.Minute := stw.wMinute; st.Second := stw.wSecond; st.Millisecond := stw.wMilliseconds; result := SystemTimeToDateTime(st); {$ENDIF} {$ELSE} {$IFNDEF FPC} var TV: TTimeVal; begin gettimeofday(TV, nil); Result := UnixDateDelta + (TV.tv_sec + TV.tv_usec / 1000000) / 86400; {$ELSE} var TV: TimeVal; begin fpgettimeofday(@TV, nil); Result := UnixDateDelta + (TV.tv_sec + TV.tv_usec / 1000000) / 86400; {$ENDIF} {$ENDIF} end; {==============================================================================} function SetUTTime(Newdt: TDateTime): Boolean; {$IFDEF MSWINDOWS} {$IFNDEF FPC} var st: TSystemTime; begin DateTimeToSystemTime(newdt,st); Result := SetSystemTime(st); {$ELSE} var st: SysUtils.TSystemTime; stw: Windows.TSystemTime; begin DateTimeToSystemTime(newdt,st); stw.wYear := st.Year; stw.wMonth := st.Month; stw.wDay := st.Day; stw.wHour := st.Hour; stw.wMinute := st.Minute; stw.wSecond := st.Second; stw.wMilliseconds := st.Millisecond; Result := SetSystemTime(stw); {$ENDIF} {$ELSE} {$IFNDEF FPC} var TV: TTimeVal; d: double; TZ: Ttimezone; PZ: PTimeZone; begin TZ.tz_minuteswest := 0; TZ.tz_dsttime := 0; PZ := @TZ; gettimeofday(TV, PZ); d := (newdt - UnixDateDelta) * 86400; TV.tv_sec := trunc(d); TV.tv_usec := trunc(frac(d) * 1000000); Result := settimeofday(TV, TZ) <> -1; {$ELSE} var TV: TimeVal; d: double; begin d := (newdt - UnixDateDelta) * 86400; TV.tv_sec := trunc(d); TV.tv_usec := trunc(frac(d) * 1000000); Result := fpsettimeofday(@TV, nil) <> -1; {$ENDIF} {$ENDIF} end; {==============================================================================} {$IFNDEF MSWINDOWS} function GetTick: LongWord; var Stamp: TTimeStamp; begin Stamp := DateTimeToTimeStamp(Now); Result := Stamp.Time; end; {$ELSE} function GetTick: LongWord; var tick, freq: TLargeInteger; {$IFDEF VER100} x: TLargeInteger; {$ENDIF} begin if Windows.QueryPerformanceFrequency(freq) then begin Windows.QueryPerformanceCounter(tick); {$IFDEF VER100} x.QuadPart := (tick.QuadPart / freq.QuadPart) * 1000; Result := x.LowPart; {$ELSE} Result := Trunc((tick / freq) * 1000) and High(LongWord) {$ENDIF} end else Result := Windows.GetTickCount; end; {$ENDIF} {==============================================================================} function TickDelta(TickOld, TickNew: LongWord): LongWord; begin //if DWord is signed type (older Deplhi), // then it not work properly on differencies larger then maxint! Result := 0; if TickOld <> TickNew then begin if TickNew < TickOld then begin TickNew := TickNew + LongWord(MaxInt) + 1; TickOld := TickOld + LongWord(MaxInt) + 1; end; Result := TickNew - TickOld; if TickNew < TickOld then if Result > 0 then Result := 0 - Result; end; end; {==============================================================================} function CodeInt(Value: Word): Ansistring; begin setlength(result, 2); result[1] := AnsiChar(Value div 256); result[2] := AnsiChar(Value mod 256); // Result := AnsiChar(Value div 256) + AnsiChar(Value mod 256) end; {==============================================================================} function DecodeInt(const Value: Ansistring; Index: Integer): Word; var x, y: Byte; begin if Length(Value) > Index then x := Ord(Value[Index]) else x := 0; if Length(Value) >= (Index + 1) then y := Ord(Value[Index + 1]) else y := 0; Result := x * 256 + y; end; {==============================================================================} function CodeLongInt(Value: Longint): Ansistring; var x, y: word; begin // this is fix for negative numbers on systems where longint = integer x := (Value shr 16) and integer($ffff); y := Value and integer($ffff); setlength(result, 4); result[1] := AnsiChar(x div 256); result[2] := AnsiChar(x mod 256); result[3] := AnsiChar(y div 256); result[4] := AnsiChar(y mod 256); end; {==============================================================================} function DecodeLongInt(const Value: Ansistring; Index: Integer): LongInt; var x, y: Byte; xl, yl: Byte; begin if Length(Value) > Index then x := Ord(Value[Index]) else x := 0; if Length(Value) >= (Index + 1) then y := Ord(Value[Index + 1]) else y := 0; if Length(Value) >= (Index + 2) then xl := Ord(Value[Index + 2]) else xl := 0; if Length(Value) >= (Index + 3) then yl := Ord(Value[Index + 3]) else yl := 0; Result := ((x * 256 + y) * 65536) + (xl * 256 + yl); end; {==============================================================================} function DumpStr(const Buffer: Ansistring): string; var n: Integer; begin Result := ''; for n := 1 to Length(Buffer) do Result := Result + ' +#$' + IntToHex(Ord(Buffer[n]), 2); end; {==============================================================================} function DumpExStr(const Buffer: Ansistring): string; var n: Integer; x: Byte; begin Result := ''; for n := 1 to Length(Buffer) do begin x := Ord(Buffer[n]); if x in [65..90, 97..122] then Result := Result + ' +''' + char(x) + '''' else Result := Result + ' +#$' + IntToHex(Ord(Buffer[n]), 2); end; end; {==============================================================================} procedure Dump(const Buffer: AnsiString; DumpFile: string); var f: Text; begin AssignFile(f, DumpFile); if FileExists(DumpFile) then DeleteFile(DumpFile); Rewrite(f); try Writeln(f, DumpStr(Buffer)); finally CloseFile(f); end; end; {==============================================================================} procedure DumpEx(const Buffer: AnsiString; DumpFile: string); var f: Text; begin AssignFile(f, DumpFile); if FileExists(DumpFile) then DeleteFile(DumpFile); Rewrite(f); try Writeln(f, DumpExStr(Buffer)); finally CloseFile(f); end; end; {==============================================================================} function TrimSPLeft(const S: string): string; var I, L: Integer; begin Result := ''; if S = '' then Exit; L := Length(S); I := 1; while (I <= L) and (S[I] = ' ') do Inc(I); Result := Copy(S, I, Maxint); end; {==============================================================================} function TrimSPRight(const S: string): string; var I: Integer; begin Result := ''; if S = '' then Exit; I := Length(S); while (I > 0) and (S[I] = ' ') do Dec(I); Result := Copy(S, 1, I); end; {==============================================================================} function TrimSP(const S: string): string; begin Result := TrimSPLeft(s); Result := TrimSPRight(Result); end; {==============================================================================} function SeparateLeft(const Value, Delimiter: string): string; var x: Integer; begin x := Pos(Delimiter, Value); if x < 1 then Result := Value else Result := Copy(Value, 1, x - 1); end; {==============================================================================} function SeparateRight(const Value, Delimiter: string): string; var x: Integer; begin x := Pos(Delimiter, Value); if x > 0 then x := x + Length(Delimiter) - 1; Result := Copy(Value, x + 1, Length(Value) - x); end; {==============================================================================} function GetParameter(const Value, Parameter: string): string; var s: string; v: string; begin Result := ''; v := Value; while v <> '' do begin s := Trim(FetchEx(v, ';', '"')); if Pos(Uppercase(parameter), Uppercase(s)) = 1 then begin Delete(s, 1, Length(Parameter)); s := Trim(s); if s = '' then Break; if s[1] = '=' then begin Result := Trim(SeparateRight(s, '=')); Result := UnquoteStr(Result, '"'); break; end; end; end; end; {==============================================================================} procedure ParseParametersEx(Value, Delimiter: string; const Parameters: TStrings); var s: string; begin Parameters.Clear; while Value <> '' do begin s := Trim(FetchEx(Value, Delimiter, '"')); Parameters.Add(s); end; end; {==============================================================================} procedure ParseParameters(Value: string; const Parameters: TStrings); begin ParseParametersEx(Value, ';', Parameters); end; {==============================================================================} function IndexByBegin(Value: string; const List: TStrings): integer; var n: integer; s: string; begin Result := -1; Value := uppercase(Value); for n := 0 to List.Count -1 do begin s := UpperCase(List[n]); if Pos(Value, s) = 1 then begin Result := n; Break; end; end; end; {==============================================================================} function GetEmailAddr(const Value: string): string; var s: string; begin s := SeparateRight(Value, '<'); s := SeparateLeft(s, '>'); Result := Trim(s); end; {==============================================================================} function GetEmailDesc(Value: string): string; var s: string; begin Value := Trim(Value); s := SeparateRight(Value, '"'); if s <> Value then s := SeparateLeft(s, '"') else begin s := SeparateLeft(Value, '<'); if s = Value then begin s := SeparateRight(Value, '('); if s <> Value then s := SeparateLeft(s, ')') else s := ''; end; end; Result := Trim(s); end; {==============================================================================} function StrToHex(const Value: Ansistring): string; var n: Integer; begin Result := ''; for n := 1 to Length(Value) do Result := Result + IntToHex(Byte(Value[n]), 2); Result := LowerCase(Result); end; {==============================================================================} function IntToBin(Value: Integer; Digits: Byte): string; var x, y, n: Integer; begin Result := ''; x := Value; repeat y := x mod 2; x := x div 2; if y > 0 then Result := '1' + Result else Result := '0' + Result; until x = 0; x := Length(Result); for n := x to Digits - 1 do Result := '0' + Result; end; {==============================================================================} function BinToInt(const Value: string): Integer; var n: Integer; begin Result := 0; for n := 1 to Length(Value) do begin if Value[n] = '0' then Result := Result * 2 else if Value[n] = '1' then Result := Result * 2 + 1 else Break; end; end; {==============================================================================} function ParseURL(URL: string; var Prot, User, Pass, Host, Port, Path, Para: string): string; var x, y: Integer; sURL: string; s: string; s1, s2: string; begin Prot := 'http'; User := ''; Pass := ''; Port := '80'; Para := ''; x := Pos('://', URL); if x > 0 then begin Prot := SeparateLeft(URL, '://'); sURL := SeparateRight(URL, '://'); end else sURL := URL; if UpperCase(Prot) = 'HTTPS' then Port := '443'; if UpperCase(Prot) = 'FTP' then Port := '21'; x := Pos('@', sURL); y := Pos('/', sURL); if (x > 0) and ((x < y) or (y < 1))then begin s := SeparateLeft(sURL, '@'); sURL := SeparateRight(sURL, '@'); x := Pos(':', s); if x > 0 then begin User := SeparateLeft(s, ':'); Pass := SeparateRight(s, ':'); end else User := s; end; x := Pos('/', sURL); if x > 0 then begin s1 := SeparateLeft(sURL, '/'); s2 := SeparateRight(sURL, '/'); end else begin s1 := sURL; s2 := ''; end; if Pos('[', s1) = 1 then begin Host := Separateleft(s1, ']'); Delete(Host, 1, 1); s1 := SeparateRight(s1, ']'); if Pos(':', s1) = 1 then Port := SeparateRight(s1, ':'); end else begin x := Pos(':', s1); if x > 0 then begin Host := SeparateLeft(s1, ':'); Port := SeparateRight(s1, ':'); end else Host := s1; end; Result := '/' + s2; x := Pos('?', s2); if x > 0 then begin Path := '/' + SeparateLeft(s2, '?'); Para := SeparateRight(s2, '?'); end else Path := '/' + s2; if Host = '' then Host := 'localhost'; end; {==============================================================================} function ReplaceString(Value, Search, Replace: AnsiString): AnsiString; var x, l, ls, lr: Integer; begin if (Value = '') or (Search = '') then begin Result := Value; Exit; end; ls := Length(Search); lr := Length(Replace); Result := ''; x := Pos(Search, Value); while x > 0 do begin {$IFNDEF CIL} l := Length(Result); SetLength(Result, l + x - 1); Move(Pointer(Value)^, Pointer(@Result[l + 1])^, x - 1); {$ELSE} Result:=Result+Copy(Value,1,x-1); {$ENDIF} {$IFNDEF CIL} l := Length(Result); SetLength(Result, l + lr); Move(Pointer(Replace)^, Pointer(@Result[l + 1])^, lr); {$ELSE} Result:=Result+Replace; {$ENDIF} Delete(Value, 1, x - 1 + ls); x := Pos(Search, Value); end; Result := Result + Value; end; {==============================================================================} function RPosEx(const Sub, Value: string; From: integer): Integer; var n: Integer; l: Integer; begin result := 0; l := Length(Sub); for n := From - l + 1 downto 1 do begin if Copy(Value, n, l) = Sub then begin result := n; break; end; end; end; {==============================================================================} function RPos(const Sub, Value: String): Integer; begin Result := RPosEx(Sub, Value, Length(Value)); end; {==============================================================================} function FetchBin(var Value: string; const Delimiter: string): string; var s: string; begin Result := SeparateLeft(Value, Delimiter); s := SeparateRight(Value, Delimiter); if s = Value then Value := '' else Value := s; end; {==============================================================================} function Fetch(var Value: string; const Delimiter: string): string; begin Result := FetchBin(Value, Delimiter); Result := TrimSP(Result); Value := TrimSP(Value); end; {==============================================================================} function FetchEx(var Value: string; const Delimiter, Quotation: string): string; var b: Boolean; begin Result := ''; b := False; while Length(Value) > 0 do begin if b then begin if Pos(Quotation, Value) = 1 then b := False; Result := Result + Value[1]; Delete(Value, 1, 1); end else begin if Pos(Delimiter, Value) = 1 then begin Delete(Value, 1, Length(delimiter)); break; end; b := Pos(Quotation, Value) = 1; Result := Result + Value[1]; Delete(Value, 1, 1); end; end; end; {==============================================================================} function IsBinaryString(const Value: AnsiString): Boolean; var n: integer; begin Result := False; for n := 1 to Length(Value) do if Value[n] in [#0..#8, #10..#31] then //ignore null-terminated strings if not ((n = Length(value)) and (Value[n] = AnsiChar(#0))) then begin Result := True; Break; end; end; {==============================================================================} function PosCRLF(const Value: AnsiString; var Terminator: AnsiString): integer; var n, l: integer; begin Result := -1; Terminator := ''; l := length(value); for n := 1 to l do if value[n] in [#$0d, #$0a] then begin Result := n; Terminator := Value[n]; if n <> l then case value[n] of #$0d: if value[n + 1] = #$0a then Terminator := #$0d + #$0a; #$0a: if value[n + 1] = #$0d then Terminator := #$0a + #$0d; end; Break; end; end; {==============================================================================} Procedure StringsTrim(const Value: TStrings); var n: integer; begin for n := Value.Count - 1 downto 0 do if Value[n] = '' then Value.Delete(n) else Break; end; {==============================================================================} function PosFrom(const SubStr, Value: String; From: integer): integer; var ls,lv: integer; begin Result := 0; ls := Length(SubStr); lv := Length(Value); if (ls = 0) or (lv = 0) then Exit; if From < 1 then From := 1; while (ls + from - 1) <= (lv) do begin {$IFNDEF CIL} if CompareMem(@SubStr[1],@Value[from],ls) then {$ELSE} if SubStr = copy(Value, from, ls) then {$ENDIF} begin result := from; break; end else inc(from); end; end; {==============================================================================} {$IFNDEF CIL} function IncPoint(const p: pointer; Value: integer): pointer; begin Result := PAnsiChar(p) + Value; end; {$ENDIF} {==============================================================================} //improved by 'DoggyDawg' function GetBetween(const PairBegin, PairEnd, Value: string): string; var n: integer; x: integer; s: string; lenBegin: integer; lenEnd: integer; str: string; max: integer; begin lenBegin := Length(PairBegin); lenEnd := Length(PairEnd); n := Length(Value); if (Value = PairBegin + PairEnd) then begin Result := '';//nothing between exit; end; if (n < lenBegin + lenEnd) then begin Result := Value; exit; end; s := SeparateRight(Value, PairBegin); if (s = Value) then begin Result := Value; exit; end; n := Pos(PairEnd, s); if (n = 0) then begin Result := Value; exit; end; Result := ''; x := 1; max := Length(s) - lenEnd + 1; for n := 1 to max do begin str := copy(s, n, lenEnd); if (str = PairEnd) then begin Dec(x); if (x <= 0) then Break; end; str := copy(s, n, lenBegin); if (str = PairBegin) then Inc(x); Result := Result + s[n]; end; end; {==============================================================================} function CountOfChar(const Value: string; Chr: char): integer; var n: integer; begin Result := 0; for n := 1 to Length(Value) do if Value[n] = chr then Inc(Result); end; {==============================================================================} // ! do not use AnsiExtractQuotedStr, it's very buggy and can crash application! function UnquoteStr(const Value: string; Quote: Char): string; var n: integer; inq, dq: Boolean; c, cn: char; begin Result := ''; if Value = '' then Exit; if Value = Quote + Quote then Exit; inq := False; dq := False; for n := 1 to Length(Value) do begin c := Value[n]; if n <> Length(Value) then cn := Value[n + 1] else cn := #0; if c = quote then if dq then dq := False else if not inq then inq := True else if cn = quote then begin Result := Result + Quote; dq := True; end else inq := False else Result := Result + c; end; end; {==============================================================================} function QuoteStr(const Value: string; Quote: Char): string; var n: integer; begin Result := ''; for n := 1 to length(value) do begin Result := result + Value[n]; if value[n] = Quote then Result := Result + Quote; end; Result := Quote + Result + Quote; end; {==============================================================================} procedure HeadersToList(const Value: TStrings); var n, x, y: integer; s: string; begin for n := 0 to Value.Count -1 do begin s := Value[n]; x := Pos(':', s); if x > 0 then begin y:= Pos('=',s); if not ((y > 0) and (y < x)) then begin s[x] := '='; Value[n] := s; end; end; end; end; {==============================================================================} procedure ListToHeaders(const Value: TStrings); var n, x: integer; s: string; begin for n := 0 to Value.Count -1 do begin s := Value[n]; x := Pos('=', s); if x > 0 then begin s[x] := ':'; Value[n] := s; end; end; end; {==============================================================================} function SwapBytes(Value: integer): integer; var s: AnsiString; x, y, xl, yl: Byte; begin s := CodeLongInt(Value); x := Ord(s[4]); y := Ord(s[3]); xl := Ord(s[2]); yl := Ord(s[1]); Result := ((x * 256 + y) * 65536) + (xl * 256 + yl); end; {==============================================================================} function ReadStrFromStream(const Stream: TStream; len: integer): AnsiString; var x: integer; {$IFDEF CIL} buf: Array of Byte; {$ENDIF} begin {$IFDEF CIL} Setlength(buf, Len); x := Stream.read(buf, Len); SetLength(buf, x); Result := StringOf(Buf); {$ELSE} Setlength(Result, Len); x := Stream.read(PAnsiChar(Result)^, Len); SetLength(Result, x); {$ENDIF} end; {==============================================================================} procedure WriteStrToStream(const Stream: TStream; Value: AnsiString); {$IFDEF CIL} var buf: Array of Byte; {$ENDIF} begin {$IFDEF CIL} buf := BytesOf(Value); Stream.Write(buf,length(Value)); {$ELSE} Stream.Write(PAnsiChar(Value)^, Length(Value)); {$ENDIF} end; {==============================================================================} function GetTempFile(const Dir, prefix: AnsiString): AnsiString; {$IFNDEF FPC} {$IFDEF MSWINDOWS} var Path: AnsiString; x: integer; {$ENDIF} {$ENDIF} begin {$IFDEF FPC} Result := GetTempFileName(Dir, Prefix); {$ELSE} {$IFNDEF MSWINDOWS} Result := tempnam(Pointer(Dir), Pointer(prefix)); {$ELSE} {$IFDEF CIL} Result := System.IO.Path.GetTempFileName; {$ELSE} if Dir = '' then begin SetLength(Path, MAX_PATH); x := GetTempPath(Length(Path), PChar(Path)); SetLength(Path, x); end else Path := Dir; x := Length(Path); if Path[x] <> '\' then Path := Path + '\'; SetLength(Result, MAX_PATH + 1); GetTempFileName(PChar(Path), PChar(Prefix), 0, PChar(Result)); Result := PChar(Result); SetFileattributes(PChar(Result), GetFileAttributes(PChar(Result)) or FILE_ATTRIBUTE_TEMPORARY); {$ENDIF} {$ENDIF} {$ENDIF} end; {==============================================================================} function PadString(const Value: AnsiString; len: integer; Pad: AnsiChar): AnsiString; begin if length(value) >= len then Result := Copy(value, 1, len) else Result := Value + StringOfChar(Pad, len - length(value)); end; {==============================================================================} function NormalizeHeader(Value: TStrings; var Index: Integer): string; var s, t: string; n: Integer; begin s := Value[Index]; Inc(Index); if s <> '' then while (Value.Count - 1) > Index do begin t := Value[Index]; if t = '' then Break; for n := 1 to Length(t) do if t[n] = #9 then t[n] := ' '; if not(AnsiChar(t[1]) in [' ', '"', ':', '=']) then Break else begin s := s + ' ' + Trim(t); Inc(Index); end; end; Result := TrimRight(s); end; {==============================================================================} var n: integer; begin for n := 1 to 12 do begin CustomMonthNames[n] := ShortMonthNames[n]; MyMonthNames[0, n] := ShortMonthNames[n]; end; end. TransGUI/synapse/source/lib/ftpsend.pas0000644000000000000000000015305211366572451017130 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 003.005.003 | |==============================================================================| | Content: FTP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Petr Esner | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {: @abstract(FTP client protocol) Used RFC: RFC-959, RFC-2228, RFC-2428 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit ftpsend; interface uses SysUtils, Classes, blcksock, synautil, synaip, synsock; const cFtpProtocol = '21'; cFtpDataProtocol = '20'; {:Terminating value for TLogonActions} FTP_OK = 255; {:Terminating value for TLogonActions} FTP_ERR = 254; type {:Array for holding definition of logon sequence.} TLogonActions = array [0..17] of byte; {:Procedural type for OnStatus event. Sender is calling @link(TFTPSend) object. Value is FTP command or reply to this comand. (if it is reply, Response is @True).} TFTPStatus = procedure(Sender: TObject; Response: Boolean; const Value: string) of object; {: @abstract(Object for holding file information) parsed from directory listing of FTP server.} TFTPListRec = class(TObject) private FFileName: String; FDirectory: Boolean; FReadable: Boolean; FFileSize: Longint; FFileTime: TDateTime; FOriginalLine: string; FMask: string; FPermission: String; public {: You can assign another TFTPListRec to this object.} procedure Assign(Value: TFTPListRec); virtual; {:name of file} property FileName: string read FFileName write FFileName; {:if name is subdirectory not file.} property Directory: Boolean read FDirectory write FDirectory; {:if you have rights to read} property Readable: Boolean read FReadable write FReadable; {:size of file in bytes} property FileSize: Longint read FFileSize write FFileSize; {:date and time of file. Local server timezone is used. Any timezone conversions was not done!} property FileTime: TDateTime read FFileTime write FFileTime; {:original unparsed line} property OriginalLine: string read FOriginalLine write FOriginalLine; {:mask what was used for parsing} property Mask: string read FMask write FMask; {:permission string (depending on used mask!)} property Permission: string read FPermission write FPermission; end; {:@abstract(This is TList of TFTPListRec objects.) This object is used for holding lististing of all files information in listed directory on FTP server.} TFTPList = class(TObject) protected FList: TList; FLines: TStringList; FMasks: TStringList; FUnparsedLines: TStringList; Monthnames: string; BlockSize: string; DirFlagValue: string; FileName: string; VMSFileName: string; Day: string; Month: string; ThreeMonth: string; YearTime: string; Year: string; Hours: string; HoursModif: Ansistring; Minutes: string; Seconds: string; Size: Ansistring; Permissions: Ansistring; DirFlag: string; function GetListItem(Index: integer): TFTPListRec; virtual; function ParseEPLF(Value: string): Boolean; virtual; procedure ClearStore; virtual; function ParseByMask(Value, NextValue, Mask: ansistring): Integer; virtual; function CheckValues: Boolean; virtual; procedure FillRecord(const Value: TFTPListRec); virtual; public {:Constructor. You not need create this object, it is created by TFTPSend class as their property.} constructor Create; destructor Destroy; override; {:Clear list.} procedure Clear; virtual; {:count of holded @link(TFTPListRec) objects} function Count: integer; virtual; {:Assigns one list to another} procedure Assign(Value: TFTPList); virtual; {:try to parse raw directory listing in @link(lines) to list of @link(TFTPListRec).} procedure ParseLines; virtual; {:By this property you have access to list of @link(TFTPListRec). This is for compatibility only. Please, use @link(Items) instead.} property List: TList read FList; {:By this property you have access to list of @link(TFTPListRec).} property Items[Index: Integer]: TFTPListRec read GetListItem; default; {:Set of lines with RAW directory listing for @link(parseLines)} property Lines: TStringList read FLines; {:Set of masks for directory listing parser. It is predefined by default, however you can modify it as you need. (for example, you can add your own definition mask.) Mask is same as mask used in TotalCommander.} property Masks: TStringList read FMasks; {:After @link(ParseLines) it holding lines what was not sucessfully parsed.} property UnparsedLines: TStringList read FUnparsedLines; end; {:@abstract(Implementation of FTP protocol.) Note: Are you missing properties for setting Username and Password? Look to parent @link(TSynaClient) object! (Username and Password have default values for "anonymous" FTP login) Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TFTPSend = class(TSynaClient) protected FOnStatus: TFTPStatus; FSock: TTCPBlockSocket; FDSock: TTCPBlockSocket; FResultCode: Integer; FResultString: string; FFullResult: TStringList; FAccount: string; FFWHost: string; FFWPort: string; FFWUsername: string; FFWPassword: string; FFWMode: integer; FDataStream: TMemoryStream; FDataIP: string; FDataPort: string; FDirectFile: Boolean; FDirectFileName: string; FCanResume: Boolean; FPassiveMode: Boolean; FForceDefaultPort: Boolean; FForceOldPort: Boolean; FFtpList: TFTPList; FBinaryMode: Boolean; FAutoTLS: Boolean; FIsTLS: Boolean; FIsDataTLS: Boolean; FTLSonData: Boolean; FFullSSL: Boolean; function Auth(Mode: integer): Boolean; virtual; function Connect: Boolean; virtual; function InternalStor(const Command: string; RestoreAt: integer): Boolean; virtual; function DataSocket: Boolean; virtual; function AcceptDataSocket: Boolean; virtual; procedure DoStatus(Response: Boolean; const Value: string); virtual; public {:Custom definition of login sequence. You can use this when you set @link(FWMode) to value -1.} CustomLogon: TLogonActions; constructor Create; destructor Destroy; override; {:Waits and read FTP server response. You need this only in special cases!} function ReadResult: Integer; virtual; {:Parse remote side information of data channel from value string (returned by PASV command). This function you need only in special cases!} procedure ParseRemote(Value: string); virtual; {:Parse remote side information of data channel from value string (returned by EPSV command). This function you need only in special cases!} procedure ParseRemoteEPSV(Value: string); virtual; {:Send Value as FTP command to FTP server. Returned result code is result of this function. This command is good for sending site specific command, or non-standard commands.} function FTPCommand(const Value: string): integer; virtual; {:Connect and logon to FTP server. If you specify any FireWall, connect to firewall and throw them connect to FTP server. Login sequence depending on @link(FWMode).} function Login: Boolean; virtual; {:Logoff and disconnect from FTP server.} function Logout: Boolean; virtual; {:Break current transmission of data. (You can call this method from Sock.OnStatus event, or from another thread.)} procedure Abort; virtual; {:Break current transmission of data. It is same as Abort, but it send abort telnet commands prior ABOR FTP command. Some servers need it. (You can call this method from Sock.OnStatus event, or from another thread.)} procedure TelnetAbort; virtual; {:Download directory listing of Directory on FTP server. If Directory is empty string, download listing of current working directory. If NameList is @true, download only names of files in directory. (internally use NLST command instead LIST command) If NameList is @false, returned list is also parsed to @link(FTPList) property.} function List(Directory: string; NameList: Boolean): Boolean; virtual; {:Read data from FileName on FTP server. If Restore is @true and server supports resume dowloads, download is resumed. (received is only rest of file)} function RetrieveFile(const FileName: string; Restore: Boolean): Boolean; virtual; {:Send data to FileName on FTP server. If Restore is @true and server supports resume upload, upload is resumed. (send only rest of file) In this case if remote file is same length as local file, nothing will be done. If remote file is larger then local, resume is disabled and file is transfered from begin!} function StoreFile(const FileName: string; Restore: Boolean): Boolean; virtual; {:Send data to FTP server and assing unique name for this file.} function StoreUniqueFile: Boolean; virtual; {:Append data to FileName on FTP server.} function AppendFile(const FileName: string): Boolean; virtual; {:Rename on FTP server file with OldName to NewName.} function RenameFile(const OldName, NewName: string): Boolean; virtual; {:Delete file FileName on FTP server.} function DeleteFile(const FileName: string): Boolean; virtual; {:Return size of Filename file on FTP server. If command failed (i.e. not implemented), return -1.} function FileSize(const FileName: string): integer; virtual; {:Send NOOP command to FTP server for preserve of disconnect by inactivity timeout.} function NoOp: Boolean; virtual; {:Change currect working directory to Directory on FTP server.} function ChangeWorkingDir(const Directory: string): Boolean; virtual; {:walk to upper directory on FTP server.} function ChangeToParentDir: Boolean; virtual; {:walk to root directory on FTP server. (May not work with all servers properly!)} function ChangeToRootDir: Boolean; virtual; {:Delete Directory on FTP server.} function DeleteDir(const Directory: string): Boolean; virtual; {:Create Directory on FTP server.} function CreateDir(const Directory: string): Boolean; virtual; {:Return current working directory on FTP server.} function GetCurrentDir: String; virtual; {:Establish data channel to FTP server and retrieve data. This function you need only in special cases, i.e. when you need to implement some special unsupported FTP command!} function DataRead(const DestStream: TStream): Boolean; virtual; {:Establish data channel to FTP server and send data. This function you need only in special cases, i.e. when you need to implement some special unsupported FTP command.} function DataWrite(const SourceStream: TStream): Boolean; virtual; published {:After FTP command contains result number of this operation.} property ResultCode: Integer read FResultCode; {:After FTP command contains main line of result.} property ResultString: string read FResultString; {:After any FTP command it contains all lines of FTP server reply.} property FullResult: TStringList read FFullResult; {:Account information used in some cases inside login sequence.} property Account: string read FAccount Write FAccount; {:Address of firewall. If empty string (default), firewall not used.} property FWHost: string read FFWHost Write FFWHost; {:port of firewall. standard value is same port as ftp server used. (21)} property FWPort: string read FFWPort Write FFWPort; {:Username for login to firewall. (if needed)} property FWUsername: string read FFWUsername Write FFWUsername; {:password for login to firewall. (if needed)} property FWPassword: string read FFWPassword Write FFWPassword; {:Type of Firewall. Used only if you set some firewall address. Supported predefined firewall login sequences are described by comments in source file where you can see pseudocode decribing each sequence.} property FWMode: integer read FFWMode Write FFWMode; {:Socket object used for TCP/IP operation on control channel. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; {:Socket object used for TCP/IP operation on data channel. Good for seting OnStatus hook, etc.} property DSock: TTCPBlockSocket read FDSock; {:If you not use @link(DirectFile) mode, all data transfers is made to or from this stream.} property DataStream: TMemoryStream read FDataStream; {:After data connection is established, contains remote side IP of this connection.} property DataIP: string read FDataIP; {:After data connection is established, contains remote side port of this connection.} property DataPort: string read FDataPort; {:Mode of data handling by data connection. If @False, all data operations are made to or from @link(DataStream) TMemoryStream. If @true, data operations is made directly to file in your disk. (filename is specified by @link(DirectFileName) property.) Dafault is @False!} property DirectFile: Boolean read FDirectFile Write FDirectFile; {:Filename for direct disk data operations.} property DirectFileName: string read FDirectFileName Write FDirectFileName; {:Indicate after @link(Login) if remote server support resume downloads and uploads.} property CanResume: Boolean read FCanResume; {:If true (default value), all transfers is made by passive method. It is safer method for various firewalls.} property PassiveMode: Boolean read FPassiveMode Write FPassiveMode; {:Force to listen for dataconnection on standard port (20). Default is @false, dataconnections will be made to any non-standard port reported by PORT FTP command. This setting is not used, if you use passive mode.} property ForceDefaultPort: Boolean read FForceDefaultPort Write FForceDefaultPort; {:When is @true, then is disabled EPSV and EPRT support. However without this commands you cannot use IPv6! (Disabling of this commands is needed only when you are behind some crap firewall/NAT.} property ForceOldPort: Boolean read FForceOldPort Write FForceOldPort; {:You may set this hook for monitoring FTP commands and replies.} property OnStatus: TFTPStatus read FOnStatus write FOnStatus; {:After LIST command is here parsed list of files in given directory.} property FtpList: TFTPList read FFtpList; {:if @true (default), then data transfers is in binary mode. If this is set to @false, then ASCII mode is used.} property BinaryMode: Boolean read FBinaryMode Write FBinaryMode; {:if is true, then if server support upgrade to SSL/TLS mode, then use them.} property AutoTLS: Boolean read FAutoTLS Write FAutoTLS; {:if server listen on SSL/TLS port, then you set this to true.} property FullSSL: Boolean read FFullSSL Write FFullSSL; {:Signalise, if control channel is in SSL/TLS mode.} property IsTLS: Boolean read FIsTLS; {:Signalise, if data transfers is in SSL/TLS mode.} property IsDataTLS: Boolean read FIsDataTLS; {:If @true (default), then try to use SSL/TLS on data transfers too. If @false, then SSL/TLS is used only for control connection.} property TLSonData: Boolean read FTLSonData write FTLSonData; end; {:A very useful function, and example of use can be found in the TFtpSend object. Dowload specified file from FTP server to LocalFile.} function FtpGetFile(const IP, Port, FileName, LocalFile, User, Pass: string): Boolean; {:A very useful function, and example of use can be found in the TFtpSend object. Upload specified LocalFile to FTP server.} function FtpPutFile(const IP, Port, FileName, LocalFile, User, Pass: string): Boolean; {:A very useful function, and example of use can be found in the TFtpSend object. Initiate transfer of file between two FTP servers.} function FtpInterServerTransfer( const FromIP, FromPort, FromFile, FromUser, FromPass: string; const ToIP, ToPort, ToFile, ToUser, ToPass: string): Boolean; implementation constructor TFTPSend.Create; begin inherited Create; FFullResult := TStringList.Create; FDataStream := TMemoryStream.Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.ConvertLineEnd := True; FDSock := TTCPBlockSocket.Create; FDSock.Owner := self; FFtpList := TFTPList.Create; FTimeout := 300000; FTargetPort := cFtpProtocol; FUsername := 'anonymous'; FPassword := 'anonymous@' + FSock.LocalName; FDirectFile := False; FPassiveMode := True; FForceDefaultPort := False; FForceOldPort := false; FAccount := ''; FFWHost := ''; FFWPort := cFtpProtocol; FFWUsername := ''; FFWPassword := ''; FFWMode := 0; FBinaryMode := True; FAutoTLS := False; FFullSSL := False; FIsTLS := False; FIsDataTLS := False; FTLSonData := True; end; destructor TFTPSend.Destroy; begin FDSock.Free; FSock.Free; FFTPList.Free; FDataStream.Free; FFullResult.Free; inherited Destroy; end; procedure TFTPSend.DoStatus(Response: Boolean; const Value: string); begin if assigned(OnStatus) then OnStatus(Self, Response, Value); end; function TFTPSend.ReadResult: Integer; var s, c: AnsiString; begin FFullResult.Clear; c := ''; repeat s := FSock.RecvString(FTimeout); if c = '' then if length(s) > 3 then if s[4] in [' ', '-'] then c :=Copy(s, 1, 3); FResultString := s; FFullResult.Add(s); DoStatus(True, s); if FSock.LastError <> 0 then Break; until (c <> '') and (Pos(c + ' ', s) = 1); Result := StrToIntDef(c, 0); FResultCode := Result; end; function TFTPSend.FTPCommand(const Value: string): integer; begin FSock.Purge; FSock.SendString(Value + CRLF); DoStatus(False, Value); Result := ReadResult; end; // based on idea by Petr Esner function TFTPSend.Auth(Mode: integer): Boolean; const //if not USER then // if not PASS then // if not ACCT then ERROR! //OK! Action0: TLogonActions = (0, FTP_OK, 3, 1, FTP_OK, 6, 2, FTP_OK, FTP_ERR, 0, 0, 0, 0, 0, 0, 0, 0, 0); //if not USER then // if not PASS then ERROR! //if SITE then ERROR! //if not USER then // if not PASS then // if not ACCT then ERROR! //OK! Action1: TLogonActions = (3, 6, 3, 4, 6, FTP_ERR, 5, FTP_ERR, 9, 0, FTP_OK, 12, 1, FTP_OK, 15, 2, FTP_OK, FTP_ERR); //if not USER then // if not PASS then ERROR! //if USER '@' then OK! //if not PASS then // if not ACCT then ERROR! //OK! Action2: TLogonActions = (3, 6, 3, 4, 6, FTP_ERR, 6, FTP_OK, 9, 1, FTP_OK, 12, 2, FTP_OK, FTP_ERR, 0, 0, 0); //if not USER then // if not PASS then ERROR! //if not USER then // if not PASS then // if not ACCT then ERROR! //OK! Action3: TLogonActions = (3, 6, 3, 4, 6, FTP_ERR, 0, FTP_OK, 9, 1, FTP_OK, 12, 2, FTP_OK, FTP_ERR, 0, 0, 0); //OPEN //if not USER then // if not PASS then // if not ACCT then ERROR! //OK! Action4: TLogonActions = (7, 3, 3, 0, FTP_OK, 6, 1, FTP_OK, 9, 2, FTP_OK, FTP_ERR, 0, 0, 0, 0, 0, 0); //if USER '@' then OK! //if not PASS then // if not ACCT then ERROR! //OK! Action5: TLogonActions = (6, FTP_OK, 3, 1, FTP_OK, 6, 2, FTP_OK, FTP_ERR, 0, 0, 0, 0, 0, 0, 0, 0, 0); //if not USER @ then // if not PASS then ERROR! //if not USER then // if not PASS then // if not ACCT then ERROR! //OK! Action6: TLogonActions = (8, 6, 3, 4, 6, FTP_ERR, 0, FTP_OK, 9, 1, FTP_OK, 12, 2, FTP_OK, FTP_ERR, 0, 0, 0); //if USER @ then ERROR! //if not PASS then // if not ACCT then ERROR! //OK! Action7: TLogonActions = (9, FTP_ERR, 3, 1, FTP_OK, 6, 2, FTP_OK, FTP_ERR, 0, 0, 0, 0, 0, 0, 0, 0, 0); //if not USER @@ then // if not PASS @ then // if not ACCT then ERROR! //OK! Action8: TLogonActions = (10, FTP_OK, 3, 11, FTP_OK, 6, 2, FTP_OK, FTP_ERR, 0, 0, 0, 0, 0, 0, 0, 0, 0); var FTPServer: string; LogonActions: TLogonActions; i: integer; s: string; x: integer; begin Result := False; if FFWHost = '' then Mode := 0; if (FTargetPort = cFtpProtocol) or (FTargetPort = '21') then FTPServer := FTargetHost else FTPServer := FTargetHost + ':' + FTargetPort; case Mode of -1: LogonActions := CustomLogon; 1: LogonActions := Action1; 2: LogonActions := Action2; 3: LogonActions := Action3; 4: LogonActions := Action4; 5: LogonActions := Action5; 6: LogonActions := Action6; 7: LogonActions := Action7; 8: LogonActions := Action8; else LogonActions := Action0; end; i := 0; repeat case LogonActions[i] of 0: s := 'USER ' + FUserName; 1: s := 'PASS ' + FPassword; 2: s := 'ACCT ' + FAccount; 3: s := 'USER ' + FFWUserName; 4: s := 'PASS ' + FFWPassword; 5: s := 'SITE ' + FTPServer; 6: s := 'USER ' + FUserName + '@' + FTPServer; 7: s := 'OPEN ' + FTPServer; 8: s := 'USER ' + FFWUserName + '@' + FTPServer; 9: s := 'USER ' + FUserName + '@' + FTPServer + ' ' + FFWUserName; 10: s := 'USER ' + FUserName + '@' + FFWUserName + '@' + FTPServer; 11: s := 'PASS ' + FPassword + '@' + FFWPassword; end; x := FTPCommand(s); x := x div 100; if (x <> 2) and (x <> 3) then Exit; i := LogonActions[i + x - 1]; case i of FTP_ERR: Exit; FTP_OK: begin Result := True; Exit; end; end; until False; end; function TFTPSend.Connect: Boolean; begin FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError = 0 then if FFWHost = '' then FSock.Connect(FTargetHost, FTargetPort) else FSock.Connect(FFWHost, FFWPort); if FSock.LastError = 0 then if FFullSSL then FSock.SSLDoConnect; Result := FSock.LastError = 0; end; function TFTPSend.Login: Boolean; var x: integer; begin Result := False; FCanResume := False; if not Connect then Exit; FIsTLS := FFullSSL; FIsDataTLS := False; repeat x := ReadResult div 100; until x <> 1; if x <> 2 then Exit; if FAutoTLS and not(FIsTLS) then if (FTPCommand('AUTH TLS') div 100) = 2 then begin FSock.SSLDoConnect; FIsTLS := FSock.LastError = 0; if not FIsTLS then begin Result := False; Exit; end; end; if not Auth(FFWMode) then Exit; if FIsTLS then begin FTPCommand('PBSZ 0'); if FTLSonData then FIsDataTLS := (FTPCommand('PROT P') div 100) = 2; if not FIsDataTLS then FTPCommand('PROT C'); end; FTPCommand('TYPE I'); FTPCommand('STRU F'); FTPCommand('MODE S'); if FTPCommand('REST 0') = 350 then if FTPCommand('REST 1') = 350 then begin FTPCommand('REST 0'); FCanResume := True; end; Result := True; end; function TFTPSend.Logout: Boolean; begin Result := (FTPCommand('QUIT') div 100) = 2; FSock.CloseSocket; end; procedure TFTPSend.ParseRemote(Value: string); var n: integer; nb, ne: integer; s: string; x: integer; begin Value := trim(Value); nb := Pos('(',Value); ne := Pos(')',Value); if (nb = 0) or (ne = 0) then begin nb:=RPos(' ',Value); s:=Copy(Value, nb + 1, Length(Value) - nb); end else begin s:=Copy(Value,nb+1,ne-nb-1); end; for n := 1 to 4 do if n = 1 then FDataIP := Fetch(s, ',') else FDataIP := FDataIP + '.' + Fetch(s, ','); x := StrToIntDef(Fetch(s, ','), 0) * 256; x := x + StrToIntDef(Fetch(s, ','), 0); FDataPort := IntToStr(x); end; procedure TFTPSend.ParseRemoteEPSV(Value: string); var n: integer; s, v: AnsiString; begin s := SeparateRight(Value, '('); s := Trim(SeparateLeft(s, ')')); Delete(s, Length(s), 1); v := ''; for n := Length(s) downto 1 do if s[n] in ['0'..'9'] then v := s[n] + v else Break; FDataPort := v; FDataIP := FTargetHost; end; function TFTPSend.DataSocket: boolean; var s: string; begin Result := False; if FIsDataTLS then FPassiveMode := True; if FPassiveMode then begin if FSock.IP6used then s := '2' else s := '1'; if not(FForceOldPort) and ((FTPCommand('EPSV ' + s) div 100) = 2) then begin ParseRemoteEPSV(FResultString); end else if FSock.IP6used then Exit else begin if (FTPCommand('PASV') div 100) <> 2 then Exit; ParseRemote(FResultString); end; FDSock.CloseSocket; FDSock.Bind(FIPInterface, cAnyPort); FDSock.Connect(FDataIP, FDataPort); Result := FDSock.LastError = 0; end else begin FDSock.CloseSocket; if FForceDefaultPort then s := cFtpDataProtocol else s := '0'; //data conection from same interface as command connection FDSock.Bind(FSock.GetLocalSinIP, s); if FDSock.LastError <> 0 then Exit; FDSock.SetLinger(True, 10000); FDSock.Listen; FDSock.GetSins; FDataIP := FDSock.GetLocalSinIP; FDataIP := FDSock.ResolveName(FDataIP); FDataPort := IntToStr(FDSock.GetLocalSinPort); if not FForceOldPort then begin if IsIp6(FDataIP) then s := '2' else s := '1'; s := 'EPRT |' + s +'|' + FDataIP + '|' + FDataPort + '|'; Result := (FTPCommand(s) div 100) = 2; end; if not Result and IsIP(FDataIP) then begin s := ReplaceString(FDataIP, '.', ','); s := 'PORT ' + s + ',' + IntToStr(FDSock.GetLocalSinPort div 256) + ',' + IntToStr(FDSock.GetLocalSinPort mod 256); Result := (FTPCommand(s) div 100) = 2; end; end; end; function TFTPSend.AcceptDataSocket: Boolean; var x: TSocket; begin if FPassiveMode then Result := True else begin Result := False; if FDSock.CanRead(FTimeout) then begin x := FDSock.Accept; if not FDSock.UsingSocks then FDSock.CloseSocket; FDSock.Socket := x; Result := True; end; end; if Result and FIsDataTLS then begin FDSock.SSL.Assign(FSock.SSL); FDSock.SSLDoConnect; Result := FDSock.LastError = 0; end; end; function TFTPSend.DataRead(const DestStream: TStream): Boolean; var x: integer; begin Result := False; try if not AcceptDataSocket then Exit; FDSock.RecvStreamRaw(DestStream, FTimeout); FDSock.CloseSocket; x := ReadResult; Result := (x div 100) = 2; finally FDSock.CloseSocket; end; end; function TFTPSend.DataWrite(const SourceStream: TStream): Boolean; var x: integer; b: Boolean; begin Result := False; try if not AcceptDataSocket then Exit; FDSock.SendStreamRaw(SourceStream); b := FDSock.LastError = 0; FDSock.CloseSocket; x := ReadResult; Result := b and ((x div 100) = 2); finally FDSock.CloseSocket; end; end; function TFTPSend.List(Directory: string; NameList: Boolean): Boolean; var x: integer; begin Result := False; FDataStream.Clear; FFTPList.Clear; if Directory <> '' then Directory := ' ' + Directory; FTPCommand('TYPE A'); if not DataSocket then Exit; if NameList then x := FTPCommand('NLST' + Directory) else x := FTPCommand('LIST' + Directory); if (x div 100) <> 1 then Exit; Result := DataRead(FDataStream); if (not NameList) and Result then begin FDataStream.Position := 0; FFTPList.Lines.LoadFromStream(FDataStream); FFTPList.ParseLines; end; FDataStream.Position := 0; end; function TFTPSend.RetrieveFile(const FileName: string; Restore: Boolean): Boolean; var RetrStream: TStream; begin Result := False; if FileName = '' then Exit; if not DataSocket then Exit; Restore := Restore and FCanResume; if FDirectFile then if Restore and FileExists(FDirectFileName) then RetrStream := TFileStream.Create(FDirectFileName, fmOpenReadWrite or fmShareExclusive) else RetrStream := TFileStream.Create(FDirectFileName, fmCreate or fmShareDenyWrite) else RetrStream := FDataStream; try if FBinaryMode then FTPCommand('TYPE I') else FTPCommand('TYPE A'); if Restore then begin RetrStream.Position := RetrStream.Size; if (FTPCommand('REST ' + IntToStr(RetrStream.Size)) div 100) <> 3 then Exit; end else if RetrStream is TMemoryStream then TMemoryStream(RetrStream).Clear; if (FTPCommand('RETR ' + FileName) div 100) <> 1 then Exit; Result := DataRead(RetrStream); if not FDirectFile then RetrStream.Position := 0; finally if FDirectFile then RetrStream.Free; end; end; function TFTPSend.InternalStor(const Command: string; RestoreAt: integer): Boolean; var SendStream: TStream; StorSize: integer; begin Result := False; if FDirectFile then if not FileExists(FDirectFileName) then Exit else SendStream := TFileStream.Create(FDirectFileName, fmOpenRead or fmShareDenyWrite) else SendStream := FDataStream; try if not DataSocket then Exit; if FBinaryMode then FTPCommand('TYPE I') else FTPCommand('TYPE A'); StorSize := SendStream.Size; if not FCanResume then RestoreAt := 0; if (StorSize > 0) and (RestoreAt = StorSize) then begin Result := True; Exit; end; if RestoreAt > StorSize then RestoreAt := 0; FTPCommand('ALLO ' + IntToStr(StorSize - RestoreAt)); if FCanResume then if (FTPCommand('REST ' + IntToStr(RestoreAt)) div 100) <> 3 then Exit; SendStream.Position := RestoreAt; if (FTPCommand(Command) div 100) <> 1 then Exit; Result := DataWrite(SendStream); finally if FDirectFile then SendStream.Free; end; end; function TFTPSend.StoreFile(const FileName: string; Restore: Boolean): Boolean; var RestoreAt: integer; begin Result := False; if FileName = '' then Exit; RestoreAt := 0; Restore := Restore and FCanResume; if Restore then begin RestoreAt := Self.FileSize(FileName); if RestoreAt < 0 then RestoreAt := 0; end; Result := InternalStor('STOR ' + FileName, RestoreAt); end; function TFTPSend.StoreUniqueFile: Boolean; begin Result := InternalStor('STOU', 0); end; function TFTPSend.AppendFile(const FileName: string): Boolean; begin Result := False; if FileName = '' then Exit; Result := InternalStor('APPE '+FileName, 0); end; function TFTPSend.NoOp: Boolean; begin Result := (FTPCommand('NOOP') div 100) = 2; end; function TFTPSend.RenameFile(const OldName, NewName: string): Boolean; begin Result := False; if (FTPCommand('RNFR ' + OldName) div 100) <> 3 then Exit; Result := (FTPCommand('RNTO ' + NewName) div 100) = 2; end; function TFTPSend.DeleteFile(const FileName: string): Boolean; begin Result := (FTPCommand('DELE ' + FileName) div 100) = 2; end; function TFTPSend.FileSize(const FileName: string): integer; var s: string; begin Result := -1; if (FTPCommand('SIZE ' + FileName) div 100) = 2 then begin s := Trim(SeparateRight(ResultString, ' ')); s := Trim(SeparateLeft(s, ' ')); Result := StrToIntDef(s, -1); end; end; function TFTPSend.ChangeWorkingDir(const Directory: string): Boolean; begin Result := (FTPCommand('CWD ' + Directory) div 100) = 2; end; function TFTPSend.ChangeToParentDir: Boolean; begin Result := (FTPCommand('CDUP') div 100) = 2; end; function TFTPSend.ChangeToRootDir: Boolean; begin Result := ChangeWorkingDir('/'); end; function TFTPSend.DeleteDir(const Directory: string): Boolean; begin Result := (FTPCommand('RMD ' + Directory) div 100) = 2; end; function TFTPSend.CreateDir(const Directory: string): Boolean; begin Result := (FTPCommand('MKD ' + Directory) div 100) = 2; end; function TFTPSend.GetCurrentDir: String; begin Result := ''; if (FTPCommand('PWD') div 100) = 2 then begin Result := SeparateRight(FResultString, '"'); Result := Trim(Separateleft(Result, '"')); end; end; procedure TFTPSend.Abort; begin FSock.SendString('ABOR' + CRLF); FDSock.StopFlag := True; end; procedure TFTPSend.TelnetAbort; begin FSock.SendString(#$FF + #$F4 + #$FF + #$F2); Abort; end; {==============================================================================} procedure TFTPListRec.Assign(Value: TFTPListRec); begin FFileName := Value.FileName; FDirectory := Value.Directory; FReadable := Value.Readable; FFileSize := Value.FileSize; FFileTime := Value.FileTime; FOriginalLine := Value.OriginalLine; FMask := Value.Mask; end; constructor TFTPList.Create; begin inherited Create; FList := TList.Create; FLines := TStringList.Create; FMasks := TStringList.Create; FUnparsedLines := TStringList.Create; //various UNIX FMasks.add('pppppppppp $!!!S*$TTT$DD$hh mm ss$YYYY$n*'); FMasks.add('pppppppppp $!!!S*$DD$TTT$hh mm ss$YYYY$n*'); FMasks.add('pppppppppp $!!!S*$TTT$DD$UUUUU$n*'); //mostly used UNIX format FMasks.add('pppppppppp $!!!S*$DD$TTT$UUUUU$n*'); //MacOS FMasks.add('pppppppppp $!!S*$TTT$DD$UUUUU$n*'); FMasks.add('pppppppppp $!S*$TTT$DD$UUUUU$n*'); //Novell FMasks.add('d $!S*$TTT$DD$UUUUU$n*'); //Windows FMasks.add('MM DD YY hh mmH !S* n*'); FMasks.add('MM DD YY hh mmH $ d!n*'); FMasks.add('MM DD YYYY hh mmH !S* n*'); FMasks.add('MM DD YYYY hh mmH $ d!n*'); FMasks.add('DD MM YYYY hh mmH !S* n*'); FMasks.add('DD MM YYYY hh mmH $ d!n*'); //VMS FMasks.add('v*$ DD TTT YYYY hh mm'); FMasks.add('v*$!DD TTT YYYY hh mm'); FMasks.add('n*$ YYYY MM DD hh mm$S*'); //AS400 FMasks.add('!S*$MM DD YY hh mm ss !n*'); FMasks.add('!S*$DD MM YY hh mm ss !n*'); FMasks.add('n*!S*$MM DD YY hh mm ss d'); FMasks.add('n*!S*$DD MM YY hh mm ss d'); //VxWorks FMasks.add('$S* TTT DD YYYY hh mm ss $n* $ d'); FMasks.add('$S* TTT DD YYYY hh mm ss $n*'); //Distinct FMasks.add('d $S*$TTT DD YYYY hh mm$n*'); FMasks.add('d $S*$TTT DD$hh mm$n*'); //PC-NFSD FMasks.add('nnnnnnnn.nnn dSSSSSSSSSSS MM DD YY hh mmH'); //VOS FMasks.add('- SSSSS YY MM DD hh mm ss n*'); FMasks.add('- d= SSSSS YY MM DD hh mm ss n*'); //Unissys ClearPath FMasks.add('nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn SSSSSSSSS MM DD YYYY hh mm'); FMasks.add('n*\x SSSSSSSSS MM DD YYYY hh mm'); //IBM FMasks.add('- SSSSSSSSSSSS d MM DD YYYY hh mm n*'); //OS9 FMasks.add('- YY MM DD hhmm d SSSSSSSSS n*'); //tandem FMasks.add('nnnnnnnn SSSSSSS DD TTT YY hh mm ss'); //MVS FMasks.add('- YYYY MM DD SSSSS d=O n*'); //BullGCOS8 FMasks.add(' $S* MM DD YY hh mm ss !n*'); FMasks.add('d $S* MM DD YY !n*'); //BullGCOS7 FMasks.add(' TTT DD YYYY n*'); FMasks.add(' d n*'); end; destructor TFTPList.Destroy; begin Clear; FList.Free; FLines.Free; FMasks.Free; FUnparsedLines.Free; inherited Destroy; end; procedure TFTPList.Clear; var n:integer; begin for n := 0 to FList.Count - 1 do if Assigned(FList[n]) then TFTPListRec(FList[n]).Free; FList.Clear; FLines.Clear; FUnparsedLines.Clear; end; function TFTPList.Count: integer; begin Result := FList.Count; end; function TFTPList.GetListItem(Index: integer): TFTPListRec; begin Result := nil; if Index < Count then Result := TFTPListRec(FList[Index]); end; procedure TFTPList.Assign(Value: TFTPList); var flr: TFTPListRec; n: integer; begin Clear; for n := 0 to Value.Count - 1 do begin flr := TFTPListRec.Create; flr.Assign(Value[n]); Flist.Add(flr); end; Lines.Assign(Value.Lines); Masks.Assign(Value.Masks); UnparsedLines.Assign(Value.UnparsedLines); end; procedure TFTPList.ClearStore; begin Monthnames := ''; BlockSize := ''; DirFlagValue := ''; FileName := ''; VMSFileName := ''; Day := ''; Month := ''; ThreeMonth := ''; YearTime := ''; Year := ''; Hours := ''; HoursModif := ''; Minutes := ''; Seconds := ''; Size := ''; Permissions := ''; DirFlag := ''; end; function TFTPList.ParseByMask(Value, NextValue, Mask: AnsiString): Integer; var Ivalue, IMask: integer; MaskC, LastMaskC: AnsiChar; c: AnsiChar; s: string; begin ClearStore; Result := 0; if Value = '' then Exit; if Mask = '' then Exit; Ivalue := 1; IMask := 1; Result := 1; LastMaskC := ' '; while Imask <= Length(mask) do begin if (Mask[Imask] <> '*') and (Ivalue > Length(Value)) then begin Result := 0; Exit; end; MaskC := Mask[Imask]; if Ivalue > Length(Value) then Exit; c := Value[Ivalue]; case MaskC of 'n': FileName := FileName + c; 'v': VMSFileName := VMSFileName + c; '.': begin if c in ['.', ' '] then FileName := TrimSP(FileName) + '.' else begin Result := 0; Exit; end; end; 'D': Day := Day + c; 'M': Month := Month + c; 'T': ThreeMonth := ThreeMonth + c; 'U': YearTime := YearTime + c; 'Y': Year := Year + c; 'h': Hours := Hours + c; 'H': HoursModif := HoursModif + c; 'm': Minutes := Minutes + c; 's': Seconds := Seconds + c; 'S': Size := Size + c; 'p': Permissions := Permissions + c; 'd': DirFlag := DirFlag + c; 'x': if c <> ' ' then begin Result := 0; Exit; end; '*': begin s := ''; if LastMaskC in ['n', 'v'] then begin if Imask = Length(Mask) then s := Copy(Value, IValue, Maxint) else while IValue <= Length(Value) do begin if Value[Ivalue] = ' ' then break; s := s + Value[Ivalue]; Inc(Ivalue); end; if LastMaskC = 'n' then FileName := FileName + s else VMSFileName := VMSFileName + s; end else begin while IValue <= Length(Value) do begin if not(Value[Ivalue] in ['0'..'9']) then break; s := s + Value[Ivalue]; Inc(Ivalue); end; case LastMaskC of 'S': Size := Size + s; end; end; Dec(IValue); end; '!': begin while IValue <= Length(Value) do begin if Value[Ivalue] = ' ' then break; Inc(Ivalue); end; while IValue <= Length(Value) do begin if Value[Ivalue] <> ' ' then break; Inc(Ivalue); end; Dec(IValue); end; '$': begin while IValue <= Length(Value) do begin if not(Value[Ivalue] in [' ', #9]) then break; Inc(Ivalue); end; Dec(IValue); end; '=': begin s := ''; case LastmaskC of 'S': begin while Imask <= Length(Mask) do begin if not(Mask[Imask] in ['0'..'9']) then break; s := s + Mask[Imask]; Inc(Imask); end; Dec(Imask); BlockSize := s; end; 'T': begin Monthnames := Copy(Mask, IMask, 12 * 3); Inc(IMask, 12 * 3); end; 'd': begin Inc(Imask); DirFlagValue := Mask[Imask]; end; end; end; '\': begin Value := NextValue; IValue := 0; Result := 2; end; end; Inc(Ivalue); Inc(Imask); LastMaskC := MaskC; end; end; function TFTPList.CheckValues: Boolean; var x, n: integer; begin Result := false; if FileName <> '' then begin if pos('?', VMSFilename) > 0 then Exit; if pos('*', VMSFilename) > 0 then Exit; end; if VMSFileName <> '' then if pos(';', VMSFilename) <= 0 then Exit; if (FileName = '') and (VMSFileName = '') then Exit; if Permissions <> '' then begin if length(Permissions) <> 10 then Exit; for n := 1 to 10 do if not(Permissions[n] in ['a', 'b', 'c', 'd', 'h', 'l', 'p', 'r', 's', 't', 'w', 'x', 'y', '-']) then Exit; end; if Day <> '' then begin Day := TrimSP(Day); x := StrToIntDef(day, -1); if (x < 1) or (x > 31) then Exit; end; if Month <> '' then begin Month := TrimSP(Month); x := StrToIntDef(Month, -1); if (x < 1) or (x > 12) then Exit; end; if Hours <> '' then begin Hours := TrimSP(Hours); x := StrToIntDef(Hours, -1); if (x < 0) or (x > 24) then Exit; end; if HoursModif <> '' then begin if not (HoursModif[1] in ['a', 'A', 'p', 'P']) then Exit; end; if Minutes <> '' then begin Minutes := TrimSP(Minutes); x := StrToIntDef(Minutes, -1); if (x < 0) or (x > 59) then Exit; end; if Seconds <> '' then begin Seconds := TrimSP(Seconds); x := StrToIntDef(Seconds, -1); if (x < 0) or (x > 59) then Exit; end; if Size <> '' then begin Size := TrimSP(Size); for n := 1 to Length(Size) do if not (Size[n] in ['0'..'9']) then Exit; end; if length(Monthnames) = (12 * 3) then for n := 1 to 12 do CustomMonthNames[n] := Copy(Monthnames, ((n - 1) * 3) + 1, 3); if ThreeMonth <> '' then begin x := GetMonthNumber(ThreeMonth); if (x = 0) then Exit; end; if YearTime <> '' then begin YearTime := ReplaceString(YearTime, '-', ':'); if pos(':', YearTime) > 0 then begin if (GetTimeFromstr(YearTime) = -1) then Exit; end else begin YearTime := TrimSP(YearTime); x := StrToIntDef(YearTime, -1); if (x = -1) then Exit; if (x < 1900) or (x > 2100) then Exit; end; end; if Year <> '' then begin Year := TrimSP(Year); x := StrToIntDef(Year, -1); if (x = -1) then Exit; if Length(Year) = 4 then begin if not((x > 1900) and (x < 2100)) then Exit; end else if Length(Year) = 2 then begin if not((x >= 0) and (x <= 99)) then Exit; end else if Length(Year) = 3 then begin if not((x >= 100) and (x <= 110)) then Exit; end else Exit; end; Result := True; end; procedure TFTPList.FillRecord(const Value: TFTPListRec); var s: string; x: integer; myear: Word; mmonth: Word; mday: Word; mhours, mminutes, mseconds: word; n: integer; begin s := DirFlagValue; if s = '' then s := 'D'; s := Uppercase(s); Value.Directory := s = Uppercase(DirFlag); if FileName <> '' then Value.FileName := SeparateLeft(Filename, ' -> '); if VMSFileName <> '' then begin Value.FileName := VMSFilename; Value.Directory := Pos('.DIR;',VMSFilename) > 0; end; Value.FileName := TrimSPRight(Value.FileName); Value.Readable := not Value.Directory; if BlockSize <> '' then x := StrToIntDef(BlockSize, 1) else x := 1; Value.FileSize := x * StrToIntDef(Size, 0); DecodeDate(Date,myear,mmonth,mday); mhours := 0; mminutes := 0; mseconds := 0; if Day <> '' then mday := StrToIntDef(day, 1); if Month <> '' then mmonth := StrToIntDef(Month, 1); if length(Monthnames) = (12 * 3) then for n := 1 to 12 do CustomMonthNames[n] := Copy(Monthnames, ((n - 1) * 3) + 1, 3); if ThreeMonth <> '' then mmonth := GetMonthNumber(ThreeMonth); if Year <> '' then begin myear := StrToIntDef(Year, 0); if (myear <= 99) and (myear > 50) then myear := myear + 1900; if myear <= 50 then myear := myear + 2000; end; if YearTime <> '' then begin if pos(':', YearTime) > 0 then begin YearTime := TrimSP(YearTime); mhours := StrToIntDef(Separateleft(YearTime, ':'), 0); mminutes := StrToIntDef(SeparateRight(YearTime, ':'), 0); if (Encodedate(myear, mmonth, mday) + EncodeTime(mHours, mminutes, 0, 0)) > now then Dec(mYear); end else myear := StrToIntDef(YearTime, 0); end; if Minutes <> '' then mminutes := StrToIntDef(Minutes, 0); if Seconds <> '' then mseconds := StrToIntDef(Seconds, 0); if Hours <> '' then begin mHours := StrToIntDef(Hours, 0); if HoursModif <> '' then if Uppercase(HoursModif[1]) = 'P' then if mHours <> 12 then mHours := MHours + 12; end; Value.FileTime := Encodedate(myear, mmonth, mday) + EncodeTime(mHours, mminutes, mseconds, 0); if Permissions <> '' then begin Value.Permission := Permissions; Value.Readable := Uppercase(permissions)[2] = 'R'; if Uppercase(permissions)[1] = 'D' then begin Value.Directory := True; Value.Readable := false; end else if Uppercase(permissions)[1] = 'L' then Value.Directory := True; end; end; function TFTPList.ParseEPLF(Value: string): Boolean; var s, os: string; flr: TFTPListRec; begin Result := False; if Value <> '' then if Value[1] = '+' then begin os := Value; Delete(Value, 1, 1); flr := TFTPListRec.create; flr.FileName := SeparateRight(Value, #9); s := Fetch(Value, ','); while s <> '' do begin if s[1] = #9 then Break; case s[1] of '/': flr.Directory := true; 'r': flr.Readable := true; 's': flr.FileSize := StrToIntDef(Copy(s, 2, Length(s) - 1), 0); 'm': flr.FileTime := (StrToIntDef(Copy(s, 2, Length(s) - 1), 0) / 86400) + 25569; end; s := Fetch(Value, ','); end; if flr.FileName <> '' then if (flr.Directory and ((flr.FileName = '.') or (flr.FileName = '..'))) or (flr.FileName = '') then flr.free else begin flr.OriginalLine := os; flr.Mask := 'EPLF'; Flist.Add(flr); Result := True; end; end; end; procedure TFTPList.ParseLines; var flr: TFTPListRec; n, m: Integer; S: string; x: integer; b: Boolean; begin n := 0; while n < Lines.Count do begin if n = Lines.Count - 1 then s := '' else s := Lines[n + 1]; b := False; x := 0; if ParseEPLF(Lines[n]) then begin b := True; x := 1; end else for m := 0 to Masks.Count - 1 do begin x := ParseByMask(Lines[n], s, Masks[m]); if x > 0 then if CheckValues then begin flr := TFTPListRec.create; FillRecord(flr); flr.OriginalLine := Lines[n]; flr.Mask := Masks[m]; if flr.Directory and ((flr.FileName = '.') or (flr.FileName = '..')) then flr.free else Flist.Add(flr); b := True; Break; end; end; if not b then FUnparsedLines.Add(Lines[n]); Inc(n); if x > 1 then Inc(n, x - 1); end; end; {==============================================================================} function FtpGetFile(const IP, Port, FileName, LocalFile, User, Pass: string): Boolean; begin Result := False; with TFTPSend.Create do try if User <> '' then begin Username := User; Password := Pass; end; TargetHost := IP; TargetPort := Port; if not Login then Exit; DirectFileName := LocalFile; DirectFile:=True; Result := RetrieveFile(FileName, False); Logout; finally Free; end; end; function FtpPutFile(const IP, Port, FileName, LocalFile, User, Pass: string): Boolean; begin Result := False; with TFTPSend.Create do try if User <> '' then begin Username := User; Password := Pass; end; TargetHost := IP; TargetPort := Port; if not Login then Exit; DirectFileName := LocalFile; DirectFile:=True; Result := StoreFile(FileName, False); Logout; finally Free; end; end; function FtpInterServerTransfer( const FromIP, FromPort, FromFile, FromUser, FromPass: string; const ToIP, ToPort, ToFile, ToUser, ToPass: string): Boolean; var FromFTP, ToFTP: TFTPSend; s: string; x: integer; begin Result := False; FromFTP := TFTPSend.Create; toFTP := TFTPSend.Create; try if FromUser <> '' then begin FromFTP.Username := FromUser; FromFTP.Password := FromPass; end; if ToUser <> '' then begin ToFTP.Username := ToUser; ToFTP.Password := ToPass; end; FromFTP.TargetHost := FromIP; FromFTP.TargetPort := FromPort; ToFTP.TargetHost := ToIP; ToFTP.TargetPort := ToPort; if not FromFTP.Login then Exit; if not ToFTP.Login then Exit; if (FromFTP.FTPCommand('PASV') div 100) <> 2 then Exit; FromFTP.ParseRemote(FromFTP.ResultString); s := ReplaceString(FromFTP.DataIP, '.', ','); s := 'PORT ' + s + ',' + IntToStr(StrToIntDef(FromFTP.DataPort, 0) div 256) + ',' + IntToStr(StrToIntDef(FromFTP.DataPort, 0) mod 256); if (ToFTP.FTPCommand(s) div 100) <> 2 then Exit; x := ToFTP.FTPCommand('RETR ' + FromFile); if (x div 100) <> 1 then Exit; x := FromFTP.FTPCommand('STOR ' + ToFile); if (x div 100) <> 1 then Exit; FromFTP.Timeout := 21600000; x := FromFTP.ReadResult; if (x div 100) <> 2 then Exit; ToFTP.Timeout := 21600000; x := ToFTP.ReadResult; if (x div 100) <> 2 then Exit; Result := True; finally ToFTP.Free; FromFTP.Free; end; end; end. TransGUI/synapse/source/lib/synaser.pas0000644000000000000000000020117511466757142017154 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 007.005.000 | |==============================================================================| | Content: Serial port support | |==============================================================================| | Copyright (c)2001-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2001-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | (c)2002, Hans-Georg Joepgen (cpom Comport Ownership Manager and bugfixes) | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {: @abstract(Serial port communication library) This unit contains a class that implements serial port communication for Windows, Linux, Unix or MacOSx. This class provides numerous methods with same name and functionality as methods of the Ararat Synapse TCP/IP library. The following is a small example how establish a connection by modem (in this case with my USB modem): @longcode(# ser:=TBlockSerial.Create; try ser.Connect('COM3'); ser.config(460800,8,'N',0,false,true); ser.ATCommand('AT'); if (ser.LastError <> 0) or (not ser.ATResult) then Exit; ser.ATConnect('ATDT+420971200111'); if (ser.LastError <> 0) or (not ser.ATResult) then Exit; // you are now connected to a modem at +420971200111 // you can transmit or receive data now finally ser.free; end; #) } //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} //Kylix does not known UNIX define {$IFDEF LINUX} {$IFNDEF UNIX} {$DEFINE UNIX} {$ENDIF} {$ENDIF} {$IFDEF FPC} {$MODE DELPHI} {$IFDEF MSWINDOWS} {$ASMMODE intel} {$ENDIF} {define working mode w/o LIBC for fpc} {$DEFINE NO_LIBC} {$ENDIF} {$Q-} {$H+} {$M+} unit synaser; interface uses {$IFNDEF MSWINDOWS} {$IFNDEF NO_LIBC} Libc, KernelIoctl, {$ELSE} termio, baseunix, unix, {$ENDIF} {$IFNDEF FPC} Types, {$ENDIF} {$ELSE} Windows, registry, {$IFDEF FPC} winver, {$ENDIF} {$ENDIF} synafpc, Classes, SysUtils, synautil; const CR = #$0d; LF = #$0a; CRLF = CR + LF; cSerialChunk = 8192; LockfileDirectory = '/var/lock'; {HGJ} PortIsClosed = -1; {HGJ} ErrAlreadyOwned = 9991; {HGJ} ErrAlreadyInUse = 9992; {HGJ} ErrWrongParameter = 9993; {HGJ} ErrPortNotOpen = 9994; {HGJ} ErrNoDeviceAnswer = 9995; {HGJ} ErrMaxBuffer = 9996; ErrTimeout = 9997; ErrNotRead = 9998; ErrFrame = 9999; ErrOverrun = 10000; ErrRxOver = 10001; ErrRxParity = 10002; ErrTxFull = 10003; dcb_Binary = $00000001; dcb_ParityCheck = $00000002; dcb_OutxCtsFlow = $00000004; dcb_OutxDsrFlow = $00000008; dcb_DtrControlMask = $00000030; dcb_DtrControlDisable = $00000000; dcb_DtrControlEnable = $00000010; dcb_DtrControlHandshake = $00000020; dcb_DsrSensivity = $00000040; dcb_TXContinueOnXoff = $00000080; dcb_OutX = $00000100; dcb_InX = $00000200; dcb_ErrorChar = $00000400; dcb_NullStrip = $00000800; dcb_RtsControlMask = $00003000; dcb_RtsControlDisable = $00000000; dcb_RtsControlEnable = $00001000; dcb_RtsControlHandshake = $00002000; dcb_RtsControlToggle = $00003000; dcb_AbortOnError = $00004000; dcb_Reserveds = $FFFF8000; {:stopbit value for 1 stopbit} SB1 = 0; {:stopbit value for 1.5 stopbit} SB1andHalf = 1; {:stopbit value for 2 stopbits} SB2 = 2; {$IFNDEF MSWINDOWS} const INVALID_HANDLE_VALUE = THandle(-1); CS7fix = $0000020; type TDCB = record DCBlength: DWORD; BaudRate: DWORD; Flags: Longint; wReserved: Word; XonLim: Word; XoffLim: Word; ByteSize: Byte; Parity: Byte; StopBits: Byte; XonChar: CHAR; XoffChar: CHAR; ErrorChar: CHAR; EofChar: CHAR; EvtChar: CHAR; wReserved1: Word; end; PDCB = ^TDCB; const {$IFDEF UNIX} {$IFDEF DARWIN} MaxRates = 18; //MAC {$ELSE} MaxRates = 30; //UNIX {$ENDIF} {$ELSE} MaxRates = 19; //WIN {$ENDIF} Rates: array[0..MaxRates, 0..1] of cardinal = ( (0, B0), (50, B50), (75, B75), (110, B110), (134, B134), (150, B150), (200, B200), (300, B300), (600, B600), (1200, B1200), (1800, B1800), (2400, B2400), (4800, B4800), (9600, B9600), (19200, B19200), (38400, B38400), (57600, B57600), (115200, B115200), (230400, B230400) {$IFNDEF DARWIN} ,(460800, B460800) {$IFDEF UNIX} ,(500000, B500000), (576000, B576000), (921600, B921600), (1000000, B1000000), (1152000, B1152000), (1500000, B1500000), (2000000, B2000000), (2500000, B2500000), (3000000, B3000000), (3500000, B3500000), (4000000, B4000000) {$ENDIF} {$ENDIF} ); {$ENDIF} {$IFDEF DARWIN} const // From fcntl.h O_SYNC = $0080; { synchronous writes } {$ENDIF} const sOK = 0; sErr = integer(-1); type {:Possible status event types for @link(THookSerialStatus)} THookSerialReason = ( HR_SerialClose, HR_Connect, HR_CanRead, HR_CanWrite, HR_ReadCount, HR_WriteCount, HR_Wait ); {:procedural prototype for status event hooking} THookSerialStatus = procedure(Sender: TObject; Reason: THookSerialReason; const Value: string) of object; {:@abstract(Exception type for SynaSer errors)} ESynaSerError = class(Exception) public ErrorCode: integer; ErrorMessage: string; end; {:@abstract(Main class implementing all communication routines)} TBlockSerial = class(TObject) protected FOnStatus: THookSerialStatus; Fhandle: THandle; FTag: integer; FDevice: string; FLastError: integer; FLastErrorDesc: string; FBuffer: AnsiString; FRaiseExcept: boolean; FRecvBuffer: integer; FSendBuffer: integer; FModemWord: integer; FRTSToggle: Boolean; FDeadlockTimeout: integer; FInstanceActive: boolean; {HGJ} FTestDSR: Boolean; FTestCTS: Boolean; FLastCR: Boolean; FLastLF: Boolean; FMaxLineLength: Integer; FLinuxLock: Boolean; FMaxSendBandwidth: Integer; FNextSend: LongWord; FMaxRecvBandwidth: Integer; FNextRecv: LongWord; FConvertLineEnd: Boolean; FATResult: Boolean; FAtTimeout: integer; FInterPacketTimeout: Boolean; FComNr: integer; {$IFDEF MSWINDOWS} FPortAddr: Word; function CanEvent(Event: dword; Timeout: integer): boolean; procedure DecodeCommError(Error: DWord); virtual; function GetPortAddr: Word; virtual; function ReadTxEmpty(PortAddr: Word): Boolean; virtual; {$ENDIF} procedure SetSizeRecvBuffer(size: integer); virtual; function GetDSR: Boolean; virtual; procedure SetDTRF(Value: Boolean); virtual; function GetCTS: Boolean; virtual; procedure SetRTSF(Value: Boolean); virtual; function GetCarrier: Boolean; virtual; function GetRing: Boolean; virtual; procedure DoStatus(Reason: THookSerialReason; const Value: string); virtual; procedure GetComNr(Value: string); virtual; function PreTestFailing: boolean; virtual;{HGJ} function TestCtrlLine: Boolean; virtual; {$IFDEF UNIX} procedure DcbToTermios(const dcb: TDCB; var term: termios); virtual; procedure TermiosToDcb(const term: termios; var dcb: TDCB); virtual; function ReadLockfile: integer; virtual; function LockfileName: String; virtual; procedure CreateLockfile(PidNr: integer); virtual; {$ENDIF} procedure LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord); virtual; procedure SetBandwidth(Value: Integer); virtual; public {: data Control Block with communication parameters. Usable only when you need to call API directly.} DCB: Tdcb; {$IFDEF UNIX} TermiosStruc: termios; {$ENDIF} {:Object constructor.} constructor Create; {:Object destructor.} destructor Destroy; override; {:Returns a string containing the version number of the library.} class function GetVersion: string; virtual; {:Destroy handle in use. It close connection to serial port.} procedure CloseSocket; virtual; {:Reconfigure communication parameters on the fly. You must be connected to port before! @param(baud Define connection speed. Baud rate can be from 50 to 4000000 bits per second. (it depends on your hardware!)) @param(bits Number of bits in communication.) @param(parity Define communication parity (N - None, O - Odd, E - Even, M - Mark or S - Space).) @param(stop Define number of stopbits. Use constants @link(SB1), @link(SB1andHalf) and @link(SB2).) @param(softflow Enable XON/XOFF handshake.) @param(hardflow Enable CTS/RTS handshake.)} procedure Config(baud, bits: integer; parity: char; stop: integer; softflow, hardflow: boolean); virtual; {:Connects to the port indicated by comport. Comport can be used in Windows style (COM2), or in Linux style (/dev/ttyS1). When you use windows style in Linux, then it will be converted to Linux name. And vice versa! However you can specify any device name! (other device names then standart is not converted!) After successfull connection the DTR signal is set (if you not set hardware handshake, then the RTS signal is set, too!) Connection parameters is predefined by your system configuration. If you need use another parameters, then you can use Config method after. Notes: - Remember, the commonly used serial Laplink cable does not support hardware handshake. - Before setting any handshake you must be sure that it is supported by your hardware. - Some serial devices are slow. In some cases you must wait up to a few seconds after connection for the device to respond. - when you connect to a modem device, then is best to test it by an empty AT command. (call ATCommand('AT'))} procedure Connect(comport: string); virtual; {:Set communication parameters from the DCB structure (the DCB structure is simulated under Linux).} procedure SetCommState; virtual; {:Read communication parameters into the DCB structure (DCB structure is simulated under Linux).} procedure GetCommState; virtual; {:Sends Length bytes of data from Buffer through the connected port.} function SendBuffer(buffer: pointer; length: integer): integer; virtual; {:One data BYTE is sent.} procedure SendByte(data: byte); virtual; {:Send the string in the data parameter. No terminator is appended by this method. If you need to send a string with CR/LF terminator, you must append the CR/LF characters to the data string! Since no terminator is appended, you can use this function for sending binary data too.} procedure SendString(data: AnsiString); virtual; {:send four bytes as integer.} procedure SendInteger(Data: integer); virtual; {:send data as one block. Each block begins with integer value with Length of block.} procedure SendBlock(const Data: AnsiString); virtual; {:send content of stream from current position} procedure SendStreamRaw(const Stream: TStream); virtual; {:send content of stream as block. see @link(SendBlock)} procedure SendStream(const Stream: TStream); virtual; {:send content of stream as block, but this is compatioble with Indy library. (it have swapped lenght of block). See @link(SendStream)} procedure SendStreamIndy(const Stream: TStream); virtual; {:Waits until the allocated buffer is filled by received data. Returns number of data bytes received, which equals to the Length value under normal operation. If it is not equal, the communication channel is possibly broken. This method not using any internal buffering, like all others receiving methods. You cannot freely combine this method with all others receiving methods!} function RecvBuffer(buffer: pointer; length: integer): integer; virtual; {:Method waits until data is received. If no data is received within the Timeout (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout). This method is used to read any amount of data (e. g. 1MB), and may be freely combined with all receviving methods what have Timeout parameter, like the @link(RecvString), @link(RecvByte) or @link(RecvTerminated) methods.} function RecvBufferEx(buffer: pointer; length: integer; timeout: integer): integer; virtual; {:It is like recvBufferEx, but data is readed to dynamicly allocated binary string.} function RecvBufferStr(Length: Integer; Timeout: Integer): AnsiString; virtual; {:Read all available data and return it in the function result string. This function may be combined with @link(RecvString), @link(RecvByte) or related methods.} function RecvPacket(Timeout: Integer): AnsiString; virtual; {:Waits until one data byte is received which is returned as the function result. If no data is received within the Timeout (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout).} function RecvByte(timeout: integer): byte; virtual; {:This method waits until a terminated data string is received. This string is terminated by the Terminator string. The resulting string is returned without this termination string! If no data is received within the Timeout (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout).} function RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString; virtual; {:This method waits until a terminated data string is received. The string is terminated by a CR/LF sequence. The resulting string is returned without the terminator (CR/LF)! If no data is received within the Timeout (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout). If @link(ConvertLineEnd) is used, then the CR/LF sequence may not be exactly CR/LF. See the description of @link(ConvertLineEnd). This method serves for line protocol implementation and uses its own buffers to maximize performance. Therefore do NOT use this method with the @link(RecvBuffer) method to receive data as it may cause data loss.} function Recvstring(timeout: integer): AnsiString; virtual; {:Waits until four data bytes are received which is returned as the function integer result. If no data is received within the Timeout (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout).} function RecvInteger(Timeout: Integer): Integer; virtual; {:Waits until one data block is received. See @link(sendblock). If no data is received within the Timeout (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout).} function RecvBlock(Timeout: Integer): AnsiString; virtual; {:Receive all data to stream, until some error occured. (for example timeout)} procedure RecvStreamRaw(const Stream: TStream; Timeout: Integer); virtual; {:receive requested count of bytes to stream} procedure RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer); virtual; {:receive block of data to stream. (Data can be sended by @link(sendstream)} procedure RecvStream(const Stream: TStream; Timeout: Integer); virtual; {:receive block of data to stream. (Data can be sended by @link(sendstreamIndy)} procedure RecvStreamIndy(const Stream: TStream; Timeout: Integer); virtual; {:Returns the number of received bytes waiting for reading. 0 is returned when there is no data waiting.} function WaitingData: integer; virtual; {:Same as @link(WaitingData), but in respect to data in the internal @link(LineBuffer).} function WaitingDataEx: integer; virtual; {:Returns the number of bytes waiting to be sent in the output buffer. 0 is returned when the output buffer is empty.} function SendingData: integer; virtual; {:Enable or disable RTS driven communication (half-duplex). It can be used to communicate with RS485 converters, or other special equipment. If you enable this feature, the system automatically controls the RTS signal. Notes: - On Windows NT (or higher) ir RTS signal driven by system driver. - On Win9x family is used special code for waiting until last byte is sended from your UART. - On Linux you must have kernel 2.1 or higher!} procedure EnableRTSToggle(value: boolean); virtual; {:Waits until all data to is sent and buffers are emptied. Warning: On Windows systems is this method returns when all buffers are flushed to the serial port controller, before the last byte is sent!} procedure Flush; virtual; {:Unconditionally empty all buffers. It is good when you need to interrupt communication and for cleanups.} procedure Purge; virtual; {:Returns @True, if you can from read any data from the port. Status is tested for a period of time given by the Timeout parameter (in milliseconds). If the value of the Timeout parameter is 0, the status is tested only once and the function returns immediately. If the value of the Timeout parameter is set to -1, the function returns only after it detects data on the port (this may cause the process to hang).} function CanRead(Timeout: integer): boolean; virtual; {:Returns @True, if you can write any data to the port (this function is not sending the contents of the buffer). Status is tested for a period of time given by the Timeout parameter (in milliseconds). If the value of the Timeout parameter is 0, the status is tested only once and the function returns immediately. If the value of the Timeout parameter is set to -1, the function returns only after it detects that it can write data to the port (this may cause the process to hang).} function CanWrite(Timeout: integer): boolean; virtual; {:Same as @link(CanRead), but the test is against data in the internal @link(LineBuffer) too.} function CanReadEx(Timeout: integer): boolean; virtual; {:Returns the status word of the modem. Decoding the status word could yield the status of carrier detect signaland other signals. This method is used internally by the modem status reading properties. You usually do not need to call this method directly.} function ModemStatus: integer; virtual; {:Send a break signal to the communication device for Duration milliseconds.} procedure SetBreak(Duration: integer); virtual; {:This function is designed to send AT commands to the modem. The AT command is sent in the Value parameter and the response is returned in the function return value (may contain multiple lines!). If the AT command is processed successfully (modem returns OK), then the @link(ATResult) property is set to True. This function is designed only for AT commands that return OK or ERROR response! To call connection commands the @link(ATConnect) method. Remember, when you connect to a modem device, it is in AT command mode. Now you can send AT commands to the modem. If you need to transfer data to the modem on the other side of the line, you must first switch to data mode using the @link(ATConnect) method.} function ATCommand(value: AnsiString): AnsiString; virtual; {:This function is used to send connect type AT commands to the modem. It is for commands to switch to connected state. (ATD, ATA, ATO,...) It sends the AT command in the Value parameter and returns the modem's response (may be multiple lines - usually with connection parameters info). If the AT command is processed successfully (the modem returns CONNECT), then the ATResult property is set to @True. This function is designed only for AT commands which respond by CONNECT, BUSY, NO DIALTONE NO CARRIER or ERROR. For other AT commands use the @link(ATCommand) method. The connect timeout is 90*@link(ATTimeout). If this command is successful (@link(ATresult) is @true), then the modem is in data state. When you now send or receive some data, it is not to or from your modem, but from the modem on other side of the line. Now you can transfer your data. If the connection attempt failed (@link(ATResult) is @False), then the modem is still in AT command mode.} function ATConnect(value: AnsiString): AnsiString; virtual; {:If you "manually" call API functions, forward their return code in the SerialResult parameter to this function, which evaluates it and sets @link(LastError) and @link(LastErrorDesc).} function SerialCheck(SerialResult: integer): integer; virtual; {:If @link(Lasterror) is not 0 and exceptions are enabled, then this procedure raises an exception. This method is used internally. You may need it only in special cases.} procedure ExceptCheck; virtual; {:Set Synaser to error state with ErrNumber code. Usually used by internal routines.} procedure SetSynaError(ErrNumber: integer); virtual; {:Raise Synaser error with ErrNumber code. Usually used by internal routines.} procedure RaiseSynaError(ErrNumber: integer); virtual; {$IFDEF UNIX} function cpomComportAccessible: boolean; virtual;{HGJ} procedure cpomReleaseComport; virtual; {HGJ} {$ENDIF} {:True device name of currently used port} property Device: string read FDevice; {:Error code of last operation. Value is defined by the host operating system, but value 0 is always OK.} property LastError: integer read FLastError; {:Human readable description of LastError code.} property LastErrorDesc: string read FLastErrorDesc; {:Indicates if the last @link(ATCommand) or @link(ATConnect) method was successful} property ATResult: Boolean read FATResult; {:Read the value of the RTS signal.} property RTS: Boolean write SetRTSF; {:Indicates the presence of the CTS signal} property CTS: boolean read GetCTS; {:Use this property to set the value of the DTR signal.} property DTR: Boolean write SetDTRF; {:Exposes the status of the DSR signal.} property DSR: boolean read GetDSR; {:Indicates the presence of the Carrier signal} property Carrier: boolean read GetCarrier; {:Reflects the status of the Ring signal.} property Ring: boolean read GetRing; {:indicates if this instance of SynaSer is active. (Connected to some port)} property InstanceActive: boolean read FInstanceActive; {HGJ} {:Defines maximum bandwidth for all sending operations in bytes per second. If this value is set to 0 (default), bandwidth limitation is not used.} property MaxSendBandwidth: Integer read FMaxSendBandwidth Write FMaxSendBandwidth; {:Defines maximum bandwidth for all receiving operations in bytes per second. If this value is set to 0 (default), bandwidth limitation is not used.} property MaxRecvBandwidth: Integer read FMaxRecvBandwidth Write FMaxRecvBandwidth; {:Defines maximum bandwidth for all sending and receiving operations in bytes per second. If this value is set to 0 (default), bandwidth limitation is not used.} property MaxBandwidth: Integer Write SetBandwidth; {:Size of the Windows internal receive buffer. Default value is usually 4096 bytes. Note: Valid only in Windows versions!} property SizeRecvBuffer: integer read FRecvBuffer write SetSizeRecvBuffer; published {:Returns the descriptive text associated with ErrorCode. You need this method only in special cases. Description of LastError is now accessible through the LastErrorDesc property.} class function GetErrorDesc(ErrorCode: integer): string; {:Freely usable property} property Tag: integer read FTag write FTag; {:Contains the handle of the open communication port. You may need this value to directly call communication functions outside SynaSer.} property Handle: THandle read Fhandle write FHandle; {:Internally used read buffer.} property LineBuffer: AnsiString read FBuffer write FBuffer; {:If @true, communication errors raise exceptions. If @false (default), only the @link(LastError) value is set.} property RaiseExcept: boolean read FRaiseExcept write FRaiseExcept; {:This event is triggered when the communication status changes. It can be used to monitor communication status.} property OnStatus: THookSerialStatus read FOnStatus write FOnStatus; {:If you set this property to @true, then the value of the DSR signal is tested before every data transfer. It can be used to detect the presence of a communications device.} property TestDSR: boolean read FTestDSR write FTestDSR; {:If you set this property to @true, then the value of the CTS signal is tested before every data transfer. It can be used to detect the presence of a communications device. Warning: This property cannot be used if you need hardware handshake!} property TestCTS: boolean read FTestCTS write FTestCTS; {:Use this property you to limit the maximum size of LineBuffer (as a protection against unlimited memory allocation for LineBuffer). Default value is 0 - no limit.} property MaxLineLength: Integer read FMaxLineLength Write FMaxLineLength; {:This timeout value is used as deadlock protection when trying to send data to (or receive data from) a device that stopped communicating during data transmission (e.g. by physically disconnecting the device). The timeout value is in milliseconds. The default value is 30,000 (30 seconds).} property DeadlockTimeout: Integer read FDeadlockTimeout Write FDeadlockTimeout; {:If set to @true (default value), port locking is enabled (under Linux only). WARNING: To use this feature, the application must run by a user with full permission to the /var/lock directory!} property LinuxLock: Boolean read FLinuxLock write FLinuxLock; {:Indicates if non-standard line terminators should be converted to a CR/LF pair (standard DOS line terminator). If @TRUE, line terminators CR, single LF or LF/CR are converted to CR/LF. Defaults to @FALSE. This property has effect only on the behavior of the RecvString method.} property ConvertLineEnd: Boolean read FConvertLineEnd Write FConvertLineEnd; {:Timeout for AT modem based operations} property AtTimeout: integer read FAtTimeout Write FAtTimeout; {:If @true (default), then all timeouts is timeout between two characters. If @False, then timeout is overall for whoole reading operation.} property InterPacketTimeout: Boolean read FInterPacketTimeout Write FInterPacketTimeout; end; {:Returns list of existing computer serial ports. Working properly only in Windows!} function GetSerialPortNames: string; implementation constructor TBlockSerial.Create; begin inherited create; FRaiseExcept := false; FHandle := INVALID_HANDLE_VALUE; FDevice := ''; FComNr:= PortIsClosed; {HGJ} FInstanceActive:= false; {HGJ} Fbuffer := ''; FRTSToggle := False; FMaxLineLength := 0; FTestDSR := False; FTestCTS := False; FDeadlockTimeout := 30000; FLinuxLock := True; FMaxSendBandwidth := 0; FNextSend := 0; FMaxRecvBandwidth := 0; FNextRecv := 0; FConvertLineEnd := False; SetSynaError(sOK); FRecvBuffer := 4096; FLastCR := False; FLastLF := False; FAtTimeout := 1000; FInterPacketTimeout := True; end; destructor TBlockSerial.Destroy; begin CloseSocket; inherited destroy; end; class function TBlockSerial.GetVersion: string; begin Result := 'SynaSer 7.5.0'; end; procedure TBlockSerial.CloseSocket; begin if Fhandle <> INVALID_HANDLE_VALUE then begin Purge; RTS := False; DTR := False; FileClose(FHandle); end; if InstanceActive then begin {$IFDEF UNIX} if FLinuxLock then cpomReleaseComport; {$ENDIF} FInstanceActive:= false end; Fhandle := INVALID_HANDLE_VALUE; FComNr:= PortIsClosed; SetSynaError(sOK); DoStatus(HR_SerialClose, FDevice); end; {$IFDEF MSWINDOWS} function TBlockSerial.GetPortAddr: Word; begin Result := 0; if Win32Platform <> VER_PLATFORM_WIN32_NT then begin EscapeCommFunction(FHandle, 10); asm MOV @Result, DX; end; end; end; function TBlockSerial.ReadTxEmpty(PortAddr: Word): Boolean; begin Result := True; if Win32Platform <> VER_PLATFORM_WIN32_NT then begin asm MOV DX, PortAddr; ADD DX, 5; IN AL, DX; AND AL, $40; JZ @K; MOV AL,1; @K: MOV @Result, AL; end; end; end; {$ENDIF} procedure TBlockSerial.GetComNr(Value: string); begin FComNr := PortIsClosed; if pos('COM', uppercase(Value)) = 1 then FComNr := StrToIntdef(copy(Value, 4, Length(Value) - 3), PortIsClosed + 1) - 1; if pos('/DEV/TTYS', uppercase(Value)) = 1 then FComNr := StrToIntdef(copy(Value, 10, Length(Value) - 9), PortIsClosed - 1); end; procedure TBlockSerial.SetBandwidth(Value: Integer); begin MaxSendBandwidth := Value; MaxRecvBandwidth := Value; end; procedure TBlockSerial.LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord); var x: LongWord; y: LongWord; begin if MaxB > 0 then begin y := GetTick; if Next > y then begin x := Next - y; if x > 0 then begin DoStatus(HR_Wait, IntToStr(x)); sleep(x); end; end; Next := GetTick + Trunc((Length / MaxB) * 1000); end; end; procedure TBlockSerial.Config(baud, bits: integer; parity: char; stop: integer; softflow, hardflow: boolean); begin FillChar(dcb, SizeOf(dcb), 0); GetCommState; dcb.DCBlength := SizeOf(dcb); dcb.BaudRate := baud; dcb.ByteSize := bits; case parity of 'N', 'n': dcb.parity := 0; 'O', 'o': dcb.parity := 1; 'E', 'e': dcb.parity := 2; 'M', 'm': dcb.parity := 3; 'S', 's': dcb.parity := 4; end; dcb.StopBits := stop; dcb.XonChar := #17; dcb.XoffChar := #19; dcb.XonLim := FRecvBuffer div 4; dcb.XoffLim := FRecvBuffer div 4; dcb.Flags := dcb_Binary; if softflow then dcb.Flags := dcb.Flags or dcb_OutX or dcb_InX; if hardflow then dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake else dcb.Flags := dcb.Flags or dcb_RtsControlEnable; dcb.Flags := dcb.Flags or dcb_DtrControlEnable; if dcb.Parity > 0 then dcb.Flags := dcb.Flags or dcb_ParityCheck; SetCommState; end; procedure TBlockSerial.Connect(comport: string); {$IFDEF MSWINDOWS} var CommTimeouts: TCommTimeouts; {$ENDIF} begin // Is this TBlockSerial Instance already busy? if InstanceActive then {HGJ} begin {HGJ} RaiseSynaError(ErrAlreadyInUse); Exit; {HGJ} end; {HGJ} FBuffer := ''; FDevice := comport; GetComNr(comport); {$IFDEF MSWINDOWS} SetLastError (sOK); {$ELSE} {$IFNDEF FPC} SetLastError (sOK); {$ELSE} fpSetErrno(sOK); {$ENDIF} {$ENDIF} {$IFNDEF MSWINDOWS} if FComNr <> PortIsClosed then FDevice := '/dev/ttyS' + IntToStr(FComNr); // Comport already owned by another process? {HGJ} if FLinuxLock then if not cpomComportAccessible then begin RaiseSynaError(ErrAlreadyOwned); Exit; end; {$IFNDEF FPC} FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC)); {$ELSE} FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC)); {$ENDIF} if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms! SerialCheck(-1) else SerialCheck(0); {$IFDEF UNIX} if FLastError <> sOK then if FLinuxLock then cpomReleaseComport; {$ENDIF} ExceptCheck; if FLastError <> sOK then Exit; {$ELSE} if FComNr <> PortIsClosed then FDevice := '\\.\COM' + IntToStr(FComNr + 1); FHandle := THandle(CreateFile(PChar(FDevice), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0)); if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms! SerialCheck(-1) else SerialCheck(0); ExceptCheck; if FLastError <> sOK then Exit; SetCommMask(FHandle, 0); SetupComm(Fhandle, FRecvBuffer, 0); CommTimeOuts.ReadIntervalTimeout := MAXWORD; CommTimeOuts.ReadTotalTimeoutMultiplier := 0; CommTimeOuts.ReadTotalTimeoutConstant := 0; CommTimeOuts.WriteTotalTimeoutMultiplier := 0; CommTimeOuts.WriteTotalTimeoutConstant := 0; SetCommTimeOuts(FHandle, CommTimeOuts); FPortAddr := GetPortAddr; {$ENDIF} SetSynaError(sOK); if not TestCtrlLine then {HGJ} begin SetSynaError(ErrNoDeviceAnswer); FileClose(FHandle); {HGJ} {$IFDEF UNIX} if FLinuxLock then cpomReleaseComport; {HGJ} {$ENDIF} {HGJ} Fhandle := INVALID_HANDLE_VALUE; {HGJ} FComNr:= PortIsClosed; {HGJ} end else begin FInstanceActive:= True; RTS := True; DTR := True; Purge; end; ExceptCheck; DoStatus(HR_Connect, FDevice); end; function TBlockSerial.SendBuffer(buffer: pointer; length: integer): integer; {$IFDEF MSWINDOWS} var Overlapped: TOverlapped; x, y, Err: DWord; {$ENDIF} begin Result := 0; if PreTestFailing then {HGJ} Exit; {HGJ} LimitBandwidth(Length, FMaxSendBandwidth, FNextsend); if FRTSToggle then begin Flush; RTS := True; end; {$IFNDEF MSWINDOWS} result := FileWrite(Fhandle, Buffer^, Length); serialcheck(result); {$ELSE} FillChar(Overlapped, Sizeof(Overlapped), 0); SetSynaError(sOK); y := 0; if not WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped) then y := GetLastError; if y = ERROR_IO_PENDING then begin x := WaitForSingleObject(FHandle, FDeadlockTimeout); if x = WAIT_TIMEOUT then begin PurgeComm(FHandle, PURGE_TXABORT); SetSynaError(ErrTimeout); end; GetOverlappedResult(FHandle, Overlapped, Dword(Result), False); end else SetSynaError(y); ClearCommError(FHandle, err, nil); if err <> 0 then DecodeCommError(err); {$ENDIF} if FRTSToggle then begin Flush; CanWrite(255); RTS := False; end; ExceptCheck; DoStatus(HR_WriteCount, IntToStr(Result)); end; procedure TBlockSerial.SendByte(data: byte); begin SendBuffer(@Data, 1); end; procedure TBlockSerial.SendString(data: AnsiString); begin SendBuffer(Pointer(Data), Length(Data)); end; procedure TBlockSerial.SendInteger(Data: integer); begin SendBuffer(@data, SizeOf(Data)); end; procedure TBlockSerial.SendBlock(const Data: AnsiString); begin SendInteger(Length(data)); SendString(Data); end; procedure TBlockSerial.SendStreamRaw(const Stream: TStream); var si: integer; x, y, yr: integer; s: AnsiString; begin si := Stream.Size - Stream.Position; x := 0; while x < si do begin y := si - x; if y > cSerialChunk then y := cSerialChunk; Setlength(s, y); yr := Stream.read(PAnsiChar(s)^, y); if yr > 0 then begin SetLength(s, yr); SendString(s); Inc(x, yr); end else break; end; end; procedure TBlockSerial.SendStreamIndy(const Stream: TStream); var si: integer; begin si := Stream.Size - Stream.Position; si := Swapbytes(si); SendInteger(si); SendStreamRaw(Stream); end; procedure TBlockSerial.SendStream(const Stream: TStream); var si: integer; begin si := Stream.Size - Stream.Position; SendInteger(si); SendStreamRaw(Stream); end; function TBlockSerial.RecvBuffer(buffer: pointer; length: integer): integer; {$IFNDEF MSWINDOWS} begin Result := 0; if PreTestFailing then {HGJ} Exit; {HGJ} LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv); result := FileRead(FHandle, Buffer^, length); serialcheck(result); {$ELSE} var Overlapped: TOverlapped; x, y, Err: DWord; begin Result := 0; if PreTestFailing then {HGJ} Exit; {HGJ} LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv); FillChar(Overlapped, Sizeof(Overlapped), 0); SetSynaError(sOK); y := 0; if not ReadFile(FHandle, Buffer^, length, Dword(Result), @Overlapped) then y := GetLastError; if y = ERROR_IO_PENDING then begin x := WaitForSingleObject(FHandle, FDeadlockTimeout); if x = WAIT_TIMEOUT then begin PurgeComm(FHandle, PURGE_RXABORT); SetSynaError(ErrTimeout); end; GetOverlappedResult(FHandle, Overlapped, Dword(Result), False); end else SetSynaError(y); ClearCommError(FHandle, err, nil); if err <> 0 then DecodeCommError(err); {$ENDIF} ExceptCheck; DoStatus(HR_ReadCount, IntToStr(Result)); end; function TBlockSerial.RecvBufferEx(buffer: pointer; length: integer; timeout: integer): integer; var s: AnsiString; rl, l: integer; ti: LongWord; begin Result := 0; if PreTestFailing then {HGJ} Exit; {HGJ} SetSynaError(sOK); rl := 0; repeat ti := GetTick; s := RecvPacket(Timeout); l := System.Length(s); if (rl + l) > Length then l := Length - rl; Move(Pointer(s)^, IncPoint(Buffer, rl)^, l); rl := rl + l; if FLastError <> sOK then Break; if rl >= Length then Break; if not FInterPacketTimeout then begin Timeout := Timeout - integer(TickDelta(ti, GetTick)); if Timeout <= 0 then begin SetSynaError(ErrTimeout); Break; end; end; until False; delete(s, 1, l); FBuffer := s; Result := rl; end; function TBlockSerial.RecvBufferStr(Length: Integer; Timeout: Integer): AnsiString; var x: integer; begin Result := ''; if PreTestFailing then {HGJ} Exit; {HGJ} SetSynaError(sOK); if Length > 0 then begin Setlength(Result, Length); x := RecvBufferEx(PAnsiChar(Result), Length , Timeout); if FLastError = sOK then SetLength(Result, x) else Result := ''; end; end; function TBlockSerial.RecvPacket(Timeout: Integer): AnsiString; var x: integer; begin Result := ''; if PreTestFailing then {HGJ} Exit; {HGJ} SetSynaError(sOK); if FBuffer <> '' then begin Result := FBuffer; FBuffer := ''; end else begin //not drain CPU on large downloads... Sleep(0); x := WaitingData; if x > 0 then begin SetLength(Result, x); x := RecvBuffer(Pointer(Result), x); if x >= 0 then SetLength(Result, x); end else begin if CanRead(Timeout) then begin x := WaitingData; if x = 0 then SetSynaError(ErrTimeout); if x > 0 then begin SetLength(Result, x); x := RecvBuffer(Pointer(Result), x); if x >= 0 then SetLength(Result, x); end; end else SetSynaError(ErrTimeout); end; end; ExceptCheck; end; function TBlockSerial.RecvByte(timeout: integer): byte; begin Result := 0; if PreTestFailing then {HGJ} Exit; {HGJ} SetSynaError(sOK); if FBuffer = '' then FBuffer := RecvPacket(Timeout); if (FLastError = sOK) and (FBuffer <> '') then begin Result := Ord(FBuffer[1]); System.Delete(FBuffer, 1, 1); end; ExceptCheck; end; function TBlockSerial.RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString; var x: Integer; s: AnsiString; l: Integer; CorCRLF: Boolean; t: ansistring; tl: integer; ti: LongWord; begin Result := ''; if PreTestFailing then {HGJ} Exit; {HGJ} SetSynaError(sOK); l := system.Length(Terminator); if l = 0 then Exit; tl := l; CorCRLF := FConvertLineEnd and (Terminator = CRLF); s := ''; x := 0; repeat ti := GetTick; //get rest of FBuffer or incomming new data... s := s + RecvPacket(Timeout); if FLastError <> sOK then Break; x := 0; if Length(s) > 0 then if CorCRLF then begin if FLastCR and (s[1] = LF) then Delete(s, 1, 1); if FLastLF and (s[1] = CR) then Delete(s, 1, 1); FLastCR := False; FLastLF := False; t := ''; x := PosCRLF(s, t); tl := system.Length(t); if t = CR then FLastCR := True; if t = LF then FLastLF := True; end else begin x := pos(Terminator, s); tl := l; end; if (FMaxLineLength <> 0) and (system.Length(s) > FMaxLineLength) then begin SetSynaError(ErrMaxBuffer); Break; end; if x > 0 then Break; if not FInterPacketTimeout then begin Timeout := Timeout - integer(TickDelta(ti, GetTick)); if Timeout <= 0 then begin SetSynaError(ErrTimeout); Break; end; end; until False; if x > 0 then begin Result := Copy(s, 1, x - 1); System.Delete(s, 1, x + tl - 1); end; FBuffer := s; ExceptCheck; end; function TBlockSerial.RecvString(Timeout: Integer): AnsiString; var s: AnsiString; begin Result := ''; s := RecvTerminated(Timeout, #13 + #10); if FLastError = sOK then Result := s; end; function TBlockSerial.RecvInteger(Timeout: Integer): Integer; var s: AnsiString; begin Result := 0; s := RecvBufferStr(4, Timeout); if FLastError = 0 then Result := (ord(s[1]) + ord(s[2]) * 256) + (ord(s[3]) + ord(s[4]) * 256) * 65536; end; function TBlockSerial.RecvBlock(Timeout: Integer): AnsiString; var x: integer; begin Result := ''; x := RecvInteger(Timeout); if FLastError = 0 then Result := RecvBufferStr(x, Timeout); end; procedure TBlockSerial.RecvStreamRaw(const Stream: TStream; Timeout: Integer); var s: AnsiString; begin repeat s := RecvPacket(Timeout); if FLastError = 0 then WriteStrToStream(Stream, s); until FLastError <> 0; end; procedure TBlockSerial.RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer); var s: AnsiString; n: integer; begin for n := 1 to (Size div cSerialChunk) do begin s := RecvBufferStr(cSerialChunk, Timeout); if FLastError <> 0 then Exit; Stream.Write(PAnsichar(s)^, cSerialChunk); end; n := Size mod cSerialChunk; if n > 0 then begin s := RecvBufferStr(n, Timeout); if FLastError <> 0 then Exit; Stream.Write(PAnsichar(s)^, n); end; end; procedure TBlockSerial.RecvStreamIndy(const Stream: TStream; Timeout: Integer); var x: integer; begin x := RecvInteger(Timeout); x := SwapBytes(x); if FLastError = 0 then RecvStreamSize(Stream, Timeout, x); end; procedure TBlockSerial.RecvStream(const Stream: TStream; Timeout: Integer); var x: integer; begin x := RecvInteger(Timeout); if FLastError = 0 then RecvStreamSize(Stream, Timeout, x); end; {$IFNDEF MSWINDOWS} function TBlockSerial.WaitingData: integer; begin {$IFNDEF FPC} serialcheck(ioctl(FHandle, FIONREAD, @result)); {$ELSE} serialcheck(fpIoctl(FHandle, FIONREAD, @result)); {$ENDIF} if FLastError <> 0 then Result := 0; ExceptCheck; end; {$ELSE} function TBlockSerial.WaitingData: integer; var stat: TComStat; err: DWORD; begin if ClearCommError(FHandle, err, @stat) then begin SetSynaError(sOK); Result := stat.cbInQue; end else begin SerialCheck(sErr); Result := 0; end; ExceptCheck; end; {$ENDIF} function TBlockSerial.WaitingDataEx: integer; begin if FBuffer <> '' then Result := Length(FBuffer) else Result := Waitingdata; end; {$IFNDEF MSWINDOWS} function TBlockSerial.SendingData: integer; begin SetSynaError(sOK); Result := 0; end; {$ELSE} function TBlockSerial.SendingData: integer; var stat: TComStat; err: DWORD; begin SetSynaError(sOK); if not ClearCommError(FHandle, err, @stat) then serialcheck(sErr); ExceptCheck; result := stat.cbOutQue; end; {$ENDIF} {$IFNDEF MSWINDOWS} procedure TBlockSerial.DcbToTermios(const dcb: TDCB; var term: termios); var n: integer; x: cardinal; begin //others cfmakeraw(term); term.c_cflag := term.c_cflag or CREAD; term.c_cflag := term.c_cflag or CLOCAL; term.c_cflag := term.c_cflag or HUPCL; //hardware handshake if (dcb.flags and dcb_RtsControlHandshake) > 0 then term.c_cflag := term.c_cflag or CRTSCTS else term.c_cflag := term.c_cflag and (not CRTSCTS); //software handshake if (dcb.flags and dcb_OutX) > 0 then term.c_iflag := term.c_iflag or IXON or IXOFF or IXANY else term.c_iflag := term.c_iflag and (not (IXON or IXOFF or IXANY)); //size of byte term.c_cflag := term.c_cflag and (not CSIZE); case dcb.bytesize of 5: term.c_cflag := term.c_cflag or CS5; 6: term.c_cflag := term.c_cflag or CS6; 7: {$IFDEF FPC} term.c_cflag := term.c_cflag or CS7; {$ELSE} term.c_cflag := term.c_cflag or CS7fix; {$ENDIF} 8: term.c_cflag := term.c_cflag or CS8; end; //parity if (dcb.flags and dcb_ParityCheck) > 0 then term.c_cflag := term.c_cflag or PARENB else term.c_cflag := term.c_cflag and (not PARENB); case dcb.parity of 1: //'O' term.c_cflag := term.c_cflag or PARODD; 2: //'E' term.c_cflag := term.c_cflag and (not PARODD); end; //stop bits if dcb.stopbits > 0 then term.c_cflag := term.c_cflag or CSTOPB else term.c_cflag := term.c_cflag and (not CSTOPB); //set baudrate; x := 0; for n := 0 to Maxrates do if rates[n, 0] = dcb.BaudRate then begin x := rates[n, 1]; break; end; cfsetospeed(term, x); cfsetispeed(term, x); end; procedure TBlockSerial.TermiosToDcb(const term: termios; var dcb: TDCB); var n: integer; x: cardinal; begin //set baudrate; dcb.baudrate := 0; {$IFDEF FPC} //why FPC not have cfgetospeed??? x := term.c_oflag and $0F; {$ELSE} x := cfgetospeed(term); {$ENDIF} for n := 0 to Maxrates do if rates[n, 1] = x then begin dcb.baudrate := rates[n, 0]; break; end; //hardware handshake if (term.c_cflag and CRTSCTS) > 0 then dcb.flags := dcb.flags or dcb_RtsControlHandshake or dcb_OutxCtsFlow else dcb.flags := dcb.flags and (not (dcb_RtsControlHandshake or dcb_OutxCtsFlow)); //software handshake if (term.c_cflag and IXOFF) > 0 then dcb.flags := dcb.flags or dcb_OutX or dcb_InX else dcb.flags := dcb.flags and (not (dcb_OutX or dcb_InX)); //size of byte case term.c_cflag and CSIZE of CS5: dcb.bytesize := 5; CS6: dcb.bytesize := 6; CS7fix: dcb.bytesize := 7; CS8: dcb.bytesize := 8; end; //parity if (term.c_cflag and PARENB) > 0 then dcb.flags := dcb.flags or dcb_ParityCheck else dcb.flags := dcb.flags and (not dcb_ParityCheck); dcb.parity := 0; if (term.c_cflag and PARODD) > 0 then dcb.parity := 1 else dcb.parity := 2; //stop bits if (term.c_cflag and CSTOPB) > 0 then dcb.stopbits := 2 else dcb.stopbits := 0; end; {$ENDIF} {$IFNDEF MSWINDOWS} procedure TBlockSerial.SetCommState; begin DcbToTermios(dcb, termiosstruc); SerialCheck(tcsetattr(FHandle, TCSANOW, termiosstruc)); ExceptCheck; end; {$ELSE} procedure TBlockSerial.SetCommState; begin SetSynaError(sOK); if not windows.SetCommState(Fhandle, dcb) then SerialCheck(sErr); ExceptCheck; end; {$ENDIF} {$IFNDEF MSWINDOWS} procedure TBlockSerial.GetCommState; begin SerialCheck(tcgetattr(FHandle, termiosstruc)); ExceptCheck; TermiostoDCB(termiosstruc, dcb); end; {$ELSE} procedure TBlockSerial.GetCommState; begin SetSynaError(sOK); if not windows.GetCommState(Fhandle, dcb) then SerialCheck(sErr); ExceptCheck; end; {$ENDIF} procedure TBlockSerial.SetSizeRecvBuffer(size: integer); begin {$IFDEF MSWINDOWS} SetupComm(Fhandle, size, 0); GetCommState; dcb.XonLim := size div 4; dcb.XoffLim := size div 4; SetCommState; {$ENDIF} FRecvBuffer := size; end; function TBlockSerial.GetDSR: Boolean; begin ModemStatus; {$IFNDEF MSWINDOWS} Result := (FModemWord and TIOCM_DSR) > 0; {$ELSE} Result := (FModemWord and MS_DSR_ON) > 0; {$ENDIF} end; procedure TBlockSerial.SetDTRF(Value: Boolean); begin {$IFNDEF MSWINDOWS} ModemStatus; if Value then FModemWord := FModemWord or TIOCM_DTR else FModemWord := FModemWord and not TIOCM_DTR; {$IFNDEF FPC} ioctl(FHandle, TIOCMSET, @FModemWord); {$ELSE} fpioctl(FHandle, TIOCMSET, @FModemWord); {$ENDIF} {$ELSE} if Value then EscapeCommFunction(FHandle, SETDTR) else EscapeCommFunction(FHandle, CLRDTR); {$ENDIF} end; function TBlockSerial.GetCTS: Boolean; begin ModemStatus; {$IFNDEF MSWINDOWS} Result := (FModemWord and TIOCM_CTS) > 0; {$ELSE} Result := (FModemWord and MS_CTS_ON) > 0; {$ENDIF} end; procedure TBlockSerial.SetRTSF(Value: Boolean); begin {$IFNDEF MSWINDOWS} ModemStatus; if Value then FModemWord := FModemWord or TIOCM_RTS else FModemWord := FModemWord and not TIOCM_RTS; {$IFNDEF FPC} ioctl(FHandle, TIOCMSET, @FModemWord); {$ELSE} fpioctl(FHandle, TIOCMSET, @FModemWord); {$ENDIF} {$ELSE} if Value then EscapeCommFunction(FHandle, SETRTS) else EscapeCommFunction(FHandle, CLRRTS); {$ENDIF} end; function TBlockSerial.GetCarrier: Boolean; begin ModemStatus; {$IFNDEF MSWINDOWS} Result := (FModemWord and TIOCM_CAR) > 0; {$ELSE} Result := (FModemWord and MS_RLSD_ON) > 0; {$ENDIF} end; function TBlockSerial.GetRing: Boolean; begin ModemStatus; {$IFNDEF MSWINDOWS} Result := (FModemWord and TIOCM_RNG) > 0; {$ELSE} Result := (FModemWord and MS_RING_ON) > 0; {$ENDIF} end; {$IFDEF MSWINDOWS} function TBlockSerial.CanEvent(Event: dword; Timeout: integer): boolean; var ex: DWord; y: Integer; Overlapped: TOverlapped; begin FillChar(Overlapped, Sizeof(Overlapped), 0); Overlapped.hEvent := CreateEvent(nil, True, False, nil); try SetCommMask(FHandle, Event); SetSynaError(sOK); if (Event = EV_RXCHAR) and (Waitingdata > 0) then Result := True else begin y := 0; if not WaitCommEvent(FHandle, ex, @Overlapped) then y := GetLastError; if y = ERROR_IO_PENDING then begin //timedout WaitForSingleObject(Overlapped.hEvent, Timeout); SetCommMask(FHandle, 0); GetOverlappedResult(FHandle, Overlapped, DWord(y), True); end; Result := (ex and Event) = Event; end; finally SetCommMask(FHandle, 0); CloseHandle(Overlapped.hEvent); end; end; {$ENDIF} {$IFNDEF MSWINDOWS} function TBlockSerial.CanRead(Timeout: integer): boolean; var FDSet: TFDSet; TimeVal: PTimeVal; TimeV: TTimeVal; x: Integer; begin TimeV.tv_usec := (Timeout mod 1000) * 1000; TimeV.tv_sec := Timeout div 1000; TimeVal := @TimeV; if Timeout = -1 then TimeVal := nil; {$IFNDEF FPC} FD_ZERO(FDSet); FD_SET(FHandle, FDSet); x := Select(FHandle + 1, @FDSet, nil, nil, TimeVal); {$ELSE} fpFD_ZERO(FDSet); fpFD_SET(FHandle, FDSet); x := fpSelect(FHandle + 1, @FDSet, nil, nil, TimeVal); {$ENDIF} SerialCheck(x); if FLastError <> sOK then x := 0; Result := x > 0; ExceptCheck; if Result then DoStatus(HR_CanRead, ''); end; {$ELSE} function TBlockSerial.CanRead(Timeout: integer): boolean; begin Result := WaitingData > 0; if not Result then Result := CanEvent(EV_RXCHAR, Timeout) or (WaitingData > 0); //check WaitingData again due some broken virtual ports if Result then DoStatus(HR_CanRead, ''); end; {$ENDIF} {$IFNDEF MSWINDOWS} function TBlockSerial.CanWrite(Timeout: integer): boolean; var FDSet: TFDSet; TimeVal: PTimeVal; TimeV: TTimeVal; x: Integer; begin TimeV.tv_usec := (Timeout mod 1000) * 1000; TimeV.tv_sec := Timeout div 1000; TimeVal := @TimeV; if Timeout = -1 then TimeVal := nil; {$IFNDEF FPC} FD_ZERO(FDSet); FD_SET(FHandle, FDSet); x := Select(FHandle + 1, nil, @FDSet, nil, TimeVal); {$ELSE} fpFD_ZERO(FDSet); fpFD_SET(FHandle, FDSet); x := fpSelect(FHandle + 1, nil, @FDSet, nil, TimeVal); {$ENDIF} SerialCheck(x); if FLastError <> sOK then x := 0; Result := x > 0; ExceptCheck; if Result then DoStatus(HR_CanWrite, ''); end; {$ELSE} function TBlockSerial.CanWrite(Timeout: integer): boolean; var t: LongWord; begin Result := SendingData = 0; if not Result then Result := CanEvent(EV_TXEMPTY, Timeout); if Result and (Win32Platform <> VER_PLATFORM_WIN32_NT) then begin t := GetTick; while not ReadTxEmpty(FPortAddr) do begin if TickDelta(t, GetTick) > 255 then Break; Sleep(0); end; end; if Result then DoStatus(HR_CanWrite, ''); end; {$ENDIF} function TBlockSerial.CanReadEx(Timeout: integer): boolean; begin if Fbuffer <> '' then Result := True else Result := CanRead(Timeout); end; procedure TBlockSerial.EnableRTSToggle(Value: boolean); begin SetSynaError(sOK); {$IFNDEF MSWINDOWS} FRTSToggle := Value; if Value then RTS:=False; {$ELSE} if Win32Platform = VER_PLATFORM_WIN32_NT then begin GetCommState; if value then dcb.Flags := dcb.Flags or dcb_RtsControlToggle else dcb.flags := dcb.flags and (not dcb_RtsControlToggle); SetCommState; end else begin FRTSToggle := Value; if Value then RTS:=False; end; {$ENDIF} end; procedure TBlockSerial.Flush; begin {$IFNDEF MSWINDOWS} SerialCheck(tcdrain(FHandle)); {$ELSE} SetSynaError(sOK); if not Flushfilebuffers(FHandle) then SerialCheck(sErr); {$ENDIF} ExceptCheck; end; {$IFNDEF MSWINDOWS} procedure TBlockSerial.Purge; begin {$IFNDEF FPC} SerialCheck(ioctl(FHandle, TCFLSH, TCIOFLUSH)); {$ELSE} {$IFDEF DARWIN} SerialCheck(fpioctl(FHandle, TCIOflush, TCIOFLUSH)); {$ELSE} SerialCheck(fpioctl(FHandle, TCFLSH, TCIOFLUSH)); {$ENDIF} {$ENDIF} FBuffer := ''; ExceptCheck; end; {$ELSE} procedure TBlockSerial.Purge; var x: integer; begin SetSynaError(sOK); x := PURGE_TXABORT or PURGE_TXCLEAR or PURGE_RXABORT or PURGE_RXCLEAR; if not PurgeComm(FHandle, x) then SerialCheck(sErr); FBuffer := ''; ExceptCheck; end; {$ENDIF} function TBlockSerial.ModemStatus: integer; begin Result := 0; {$IFNDEF MSWINDOWS} {$IFNDEF FPC} SerialCheck(ioctl(FHandle, TIOCMGET, @Result)); {$ELSE} SerialCheck(fpioctl(FHandle, TIOCMGET, @Result)); {$ENDIF} {$ELSE} SetSynaError(sOK); if not GetCommModemStatus(FHandle, dword(Result)) then SerialCheck(sErr); {$ENDIF} ExceptCheck; FModemWord := Result; end; procedure TBlockSerial.SetBreak(Duration: integer); begin {$IFNDEF MSWINDOWS} SerialCheck(tcsendbreak(FHandle, Duration)); {$ELSE} SetCommBreak(FHandle); Sleep(Duration); SetSynaError(sOK); if not ClearCommBreak(FHandle) then SerialCheck(sErr); {$ENDIF} end; {$IFDEF MSWINDOWS} procedure TBlockSerial.DecodeCommError(Error: DWord); begin if (Error and DWord(CE_FRAME)) > 1 then FLastError := ErrFrame; if (Error and DWord(CE_OVERRUN)) > 1 then FLastError := ErrOverrun; if (Error and DWord(CE_RXOVER)) > 1 then FLastError := ErrRxOver; if (Error and DWord(CE_RXPARITY)) > 1 then FLastError := ErrRxParity; if (Error and DWord(CE_TXFULL)) > 1 then FLastError := ErrTxFull; end; {$ENDIF} //HGJ function TBlockSerial.PreTestFailing: Boolean; begin if not FInstanceActive then begin RaiseSynaError(ErrPortNotOpen); result:= true; Exit; end; Result := not TestCtrlLine; if result then RaiseSynaError(ErrNoDeviceAnswer) end; function TBlockSerial.TestCtrlLine: Boolean; begin result := ((not FTestDSR) or DSR) and ((not FTestCTS) or CTS); end; function TBlockSerial.ATCommand(value: AnsiString): AnsiString; var s: AnsiString; ConvSave: Boolean; begin result := ''; FAtResult := False; ConvSave := FConvertLineEnd; try FConvertLineEnd := True; SendString(value + #$0D); repeat s := RecvString(FAtTimeout); if s <> Value then result := result + s + CRLF; if s = 'OK' then begin FAtResult := True; break; end; if s = 'ERROR' then break; until FLastError <> sOK; finally FConvertLineEnd := Convsave; end; end; function TBlockSerial.ATConnect(value: AnsiString): AnsiString; var s: AnsiString; ConvSave: Boolean; begin result := ''; FAtResult := False; ConvSave := FConvertLineEnd; try FConvertLineEnd := True; SendString(value + #$0D); repeat s := RecvString(90 * FAtTimeout); if s <> Value then result := result + s + CRLF; if s = 'NO CARRIER' then break; if s = 'ERROR' then break; if s = 'BUSY' then break; if s = 'NO DIALTONE' then break; if Pos('CONNECT', s) = 1 then begin FAtResult := True; break; end; until FLastError <> sOK; finally FConvertLineEnd := Convsave; end; end; function TBlockSerial.SerialCheck(SerialResult: integer): integer; begin if SerialResult = integer(INVALID_HANDLE_VALUE) then {$IFDEF MSWINDOWS} result := GetLastError {$ELSE} {$IFNDEF FPC} result := GetLastError {$ELSE} result := fpGetErrno {$ENDIF} {$ENDIF} else result := sOK; FLastError := result; FLastErrorDesc := GetErrorDesc(FLastError); end; procedure TBlockSerial.ExceptCheck; var e: ESynaSerError; s: string; begin if FRaiseExcept and (FLastError <> sOK) then begin s := GetErrorDesc(FLastError); e := ESynaSerError.CreateFmt('Communication error %d: %s', [FLastError, s]); e.ErrorCode := FLastError; e.ErrorMessage := s; raise e; end; end; procedure TBlockSerial.SetSynaError(ErrNumber: integer); begin FLastError := ErrNumber; FLastErrorDesc := GetErrorDesc(FLastError); end; procedure TBlockSerial.RaiseSynaError(ErrNumber: integer); begin SetSynaError(ErrNumber); ExceptCheck; end; procedure TBlockSerial.DoStatus(Reason: THookSerialReason; const Value: string); begin if assigned(OnStatus) then OnStatus(Self, Reason, Value); end; {======================================================================} class function TBlockSerial.GetErrorDesc(ErrorCode: integer): string; begin Result:= ''; case ErrorCode of sOK: Result := 'OK'; ErrAlreadyOwned: Result := 'Port owned by other process';{HGJ} ErrAlreadyInUse: Result := 'Instance already in use'; {HGJ} ErrWrongParameter: Result := 'Wrong paramter at call'; {HGJ} ErrPortNotOpen: Result := 'Instance not yet connected'; {HGJ} ErrNoDeviceAnswer: Result := 'No device answer detected'; {HGJ} ErrMaxBuffer: Result := 'Maximal buffer length exceeded'; ErrTimeout: Result := 'Timeout during operation'; ErrNotRead: Result := 'Reading of data failed'; ErrFrame: Result := 'Receive framing error'; ErrOverrun: Result := 'Receive Overrun Error'; ErrRxOver: Result := 'Receive Queue overflow'; ErrRxParity: Result := 'Receive Parity Error'; ErrTxFull: Result := 'Tranceive Queue is full'; end; if Result = '' then begin Result := SysErrorMessage(ErrorCode); end; end; {---------- cpom Comport Ownership Manager Routines ------------- by Hans-Georg Joepgen of Stuttgart, Germany. Copyright (c) 2002, by Hans-Georg Joepgen Stefan Krauss of Stuttgart, Germany, contributed literature and Internet research results, invaluable advice and excellent answers to the Comport Ownership Manager. } {$IFDEF UNIX} function TBlockSerial.LockfileName: String; var s: string; begin s := SeparateRight(FDevice, '/dev/'); result := LockfileDirectory + '/LCK..' + s; end; procedure TBlockSerial.CreateLockfile(PidNr: integer); var f: TextFile; s: string; begin // Create content for file s := IntToStr(PidNr); while length(s) < 10 do s := ' ' + s; // Create file try AssignFile(f, LockfileName); try Rewrite(f); writeln(f, s); finally CloseFile(f); end; // Allow all users to enjoy the benefits of cpom s := 'chmod a+rw ' + LockfileName; {$IFNDEF FPC} FileSetReadOnly( LockfileName, False ) ; // Libc.system(pchar(s)); {$ELSE} fpSystem(s); {$ENDIF} except // not raise exception, if you not have write permission for lock. on Exception do ; end; end; function TBlockSerial.ReadLockfile: integer; {Returns PID from Lockfile. Lockfile must exist.} var f: TextFile; s: string; begin AssignFile(f, LockfileName); Reset(f); try readln(f, s); finally CloseFile(f); end; Result := StrToIntDef(s, -1) end; function TBlockSerial.cpomComportAccessible: boolean; var MyPid: integer; Filename: string; begin Filename := LockfileName; {$IFNDEF FPC} MyPid := Libc.getpid; {$ELSE} MyPid := fpGetPid; {$ENDIF} // Make sure, the Lock Files Directory exists. We need it. if not DirectoryExists(LockfileDirectory) then CreateDir(LockfileDirectory); // Check the Lockfile if not FileExists (Filename) then begin // comport is not locked. Lock it for us. CreateLockfile(MyPid); result := true; exit; // done. end; // Is port owned by orphan? Then it's time for error recovery. //FPC forgot to add getsid.. :-( {$IFNDEF FPC} if Libc.getsid(ReadLockfile) = -1 then begin // Lockfile was left from former desaster DeleteFile(Filename); // error recovery CreateLockfile(MyPid); result := true; exit; end; {$ENDIF} result := false // Sorry, port is owned by living PID and locked end; procedure TBlockSerial.cpomReleaseComport; begin DeleteFile(LockfileName); end; {$ENDIF} {----------------------------------------------------------------} {$IFDEF MSWINDOWS} function GetSerialPortNames: string; var reg: TRegistry; l, v: TStringList; n: integer; begin l := TStringList.Create; v := TStringList.Create; reg := TRegistry.Create; try {$IFNDEF VER100} {$IFNDEF VER120} reg.Access := KEY_READ; {$ENDIF} {$ENDIF} reg.RootKey := HKEY_LOCAL_MACHINE; reg.OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM', false); reg.GetValueNames(l); for n := 0 to l.Count - 1 do v.Add(reg.ReadString(l[n])); Result := v.CommaText; finally reg.Free; l.Free; v.Free; end; end; {$ENDIF} {$IFNDEF MSWINDOWS} function GetSerialPortNames: string; var Index: Integer; Data: string; TmpPorts: String; sr : TSearchRec; begin try TmpPorts := ''; if FindFirst('/dev/ttyS*', $FFFFFFFF, sr) = 0 then begin repeat if (sr.Attr and $FFFFFFFF) = Sr.Attr then begin data := sr.Name; index := length(data); while (index > 1) and (data[index] <> '/') do index := index - 1; TmpPorts := TmpPorts + ' ' + copy(data, 1, index + 1); end; until FindNext(sr) <> 0; end; FindClose(sr); finally Result:=TmpPorts; end; end; {$ENDIF} end. TransGUI/synapse/source/lib/mimepart.pas0000644000000000000000000010127411366572451017302 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.008.000 | |==============================================================================| | Content: MIME support procedures and functions | |==============================================================================| | Copyright (c)1999-2008, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2008. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(MIME part handling) Handling with MIME parts. Used RFC: RFC-2045 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$Q-} {$R-} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit mimepart; interface uses SysUtils, Classes, synafpc, synachar, synacode, synautil, mimeinln; type TMimePart = class; {:@abstract(Procedural type for @link(TMimepart.Walkpart) hook). This hook is used for easy walking through MIME subparts.} THookWalkPart = procedure(const Sender: TMimePart) of object; {:The four types of MIME parts. (textual, multipart, message or any other binary data.)} TMimePrimary = (MP_TEXT, MP_MULTIPART, MP_MESSAGE, MP_BINARY); {:The various types of possible part encodings.} TMimeEncoding = (ME_7BIT, ME_8BIT, ME_QUOTED_PRINTABLE, ME_BASE64, ME_UU, ME_XX); {:@abstract(Object for working with parts of MIME e-mail.) Each TMimePart object can handle any number of nested subparts as new TMimepart objects. It can handle any tree hierarchy structure of nested MIME subparts itself. Basic tasks are: Decoding of MIME message: - store message into Lines property - call DecomposeParts. Now you have decomposed MIME parts in all nested levels! - now you can explore all properties and subparts. (You can use WalkPart method) - if you need decode part, call DecodePart. Encoding of MIME message: - if you need multipart message, you must create subpart by AddSubPart. - set all properties of all parts. - set content of part into DecodedLines stream - encode this stream by EncodePart. - compose full message by ComposeParts. (it build full MIME message from all subparts. Do not call this method for each subpart! It is needed on root part!) - encoded MIME message is stored in Lines property. } TMimePart = class(TObject) private FPrimary: string; FPrimaryCode: TMimePrimary; FSecondary: string; FEncoding: string; FEncodingCode: TMimeEncoding; FDefaultCharset: string; FCharset: string; FCharsetCode: TMimeChar; FTargetCharset: TMimeChar; FDescription: string; FDisposition: string; FContentID: string; FBoundary: string; FFileName: string; FLines: TStringList; FPartBody: TStringList; FHeaders: TStringList; FPrePart: TStringList; FPostPart: TStringList; FDecodedLines: TMemoryStream; FSubParts: TList; FOnWalkPart: THookWalkPart; FMaxLineLength: integer; FSubLevel: integer; FMaxSubLevel: integer; FAttachInside: boolean; FConvertCharset: Boolean; FForcedHTMLConvert: Boolean; procedure SetPrimary(Value: string); procedure SetEncoding(Value: string); procedure SetCharset(Value: string); function IsUUcode(Value: string): boolean; public constructor Create; destructor Destroy; override; {:Assign content of another object to this object. (Only this part, not subparts!)} procedure Assign(Value: TMimePart); {:Assign content of another object to this object. (With all subparts!)} procedure AssignSubParts(Value: TMimePart); {:Clear all data values to default values. It also call @link(ClearSubparts).} procedure Clear; {:Decode Mime part from @link(Lines) to @link(DecodedLines).} procedure DecodePart; {:Parse header lines from Headers property into another properties.} procedure DecodePartHeader; {:Encode mime part from @link(DecodedLines) to @link(Lines) and build mime headers.} procedure EncodePart; {:Build header lines in Headers property from another properties.} procedure EncodePartHeader; {:generate primary and secondary mime type from filename extension in value. If type not recognised, it return 'Application/octet-string' type.} procedure MimeTypeFromExt(Value: string); {:Return number of decomposed subparts. (On this level! Each of this subparts can hold any number of their own nested subparts!)} function GetSubPartCount: integer; {:Get nested subpart object as new TMimePart. For getting maximum possible index you can use @link(GetSubPartCount) method.} function GetSubPart(index: integer): TMimePart; {:delete subpart on given index.} procedure DeleteSubPart(index: integer); {:Clear and destroy all subpart TMimePart objects.} procedure ClearSubParts; {:Add and create new subpart.} function AddSubPart: TMimePart; {:E-mail message in @link(Lines) property is parsed into this object. E-mail headers are stored in @link(Headers) property and is parsed into another properties automaticly. Not need call @link(DecodePartHeader)! Content of message (part) is stored into @link(PartBody) property. This part is in undecoded form! If you need decode it, then you must call @link(DecodePart) method by your hands. Lot of another properties is filled also. Decoding of parts you must call separately due performance reasons. (Not needed to decode all parts in all reasons.) For each MIME subpart is created new TMimepart object (accessible via method @link(GetSubPart)).} procedure DecomposeParts; {:This part and all subparts is composed into one MIME message stored in @link(Lines) property.} procedure ComposeParts; {:By calling this method is called @link(OnWalkPart) event for each part and their subparts. It is very good for calling some code for each part in MIME message} procedure WalkPart; {:Return @true when is possible create next subpart. (@link(maxSublevel) is still not reached)} function CanSubPart: boolean; published {:Primary Mime type of part. (i.e. 'application') Writing to this property automaticly generate value of @link(PrimaryCode).} property Primary: string read FPrimary write SetPrimary; {:String representation of used Mime encoding in part. (i.e. 'base64') Writing to this property automaticly generate value of @link(EncodingCode).} property Encoding: string read FEncoding write SetEncoding; {:String representation of used Mime charset in part. (i.e. 'iso-8859-1') Writing to this property automaticly generate value of @link(CharsetCode). Charset is used only for text parts.} property Charset: string read FCharset write SetCharset; {:Define default charset for decoding text MIME parts without charset specification. Default value is 'ISO-8859-1' by RCF documents. But Microsoft Outlook use windows codings as default. This property allows properly decode textual parts from some broken versions of Microsoft Outlook. (this is bad software!)} property DefaultCharset: string read FDefaultCharset write FDefaultCharset; {:Decoded primary type. Possible values are: MP_TEXT, MP_MULTIPART, MP_MESSAGE and MP_BINARY. If type not recognised, result is MP_BINARY.} property PrimaryCode: TMimePrimary read FPrimaryCode Write FPrimaryCode; {:Decoded encoding type. Possible values are: ME_7BIT, ME_8BIT, ME_QUOTED_PRINTABLE and ME_BASE64. If type not recognised, result is ME_7BIT.} property EncodingCode: TMimeEncoding read FEncodingCode Write FEncodingCode; {:Decoded charset type. Possible values are defined in @link(SynaChar) unit.} property CharsetCode: TMimeChar read FCharsetCode Write FCharsetCode; {:System charset type. Default value is charset used by default in your operating system.} property TargetCharset: TMimeChar read FTargetCharset Write FTargetCharset; {:If @true, then do internal charset translation of part content between @link(CharsetCode) and @link(TargetCharset)} property ConvertCharset: Boolean read FConvertCharset Write FConvertCharset; {:If @true, then allways do internal charset translation of HTML parts by MIME even it have their own charset in META tag. Default is @false.} property ForcedHTMLConvert: Boolean read FForcedHTMLConvert Write FForcedHTMLConvert; {:Secondary Mime type of part. (i.e. 'mixed')} property Secondary: string read FSecondary Write FSecondary; {:Description of Mime part.} property Description: string read FDescription Write FDescription; {:Value of content disposition field. (i.e. 'inline' or 'attachment')} property Disposition: string read FDisposition Write FDisposition; {:Content ID.} property ContentID: string read FContentID Write FContentID; {:Boundary delimiter of multipart Mime part. Used only in multipart part.} property Boundary: string read FBoundary Write FBoundary; {:Filename of file in binary part.} property FileName: string read FFileName Write FFileName; {:String list with lines contains mime part (It can be a full message).} property Lines: TStringList read FLines; {:Encoded form of MIME part data.} property PartBody: TStringList read FPartBody; {:All header lines of MIME part.} property Headers: TStringList read FHeaders; {:On multipart this contains part of message between first line of message and first boundary.} property PrePart: TStringList read FPrePart; {:On multipart this contains part of message between last boundary and end of message.} property PostPart: TStringList read FPostPart; {:Stream with decoded form of budy part.} property DecodedLines: TMemoryStream read FDecodedLines; {:Show nested level in subpart tree. Value 0 means root part. 1 means subpart from this root. etc.} property SubLevel: integer read FSubLevel write FSubLevel; {:Specify maximum sublevel value for decomposing.} property MaxSubLevel: integer read FMaxSubLevel write FMaxSubLevel; {:When is @true, then this part maybe(!) have included some uuencoded binary data.} property AttachInside: boolean read FAttachInside; {:Here you can assign hook procedure for walking through all part and their subparts.} property OnWalkPart: THookWalkPart read FOnWalkPart write FOnWalkPart; {:Here you can specify maximum line length for encoding of MIME part. If line is longer, then is splitted by standard of MIME. Correct MIME mailers can de-split this line into original length.} property MaxLineLength: integer read FMaxLineLength Write FMaxLineLength; end; const MaxMimeType = 25; MimeType: array[0..MaxMimeType, 0..2] of string = ( ('AU', 'audio', 'basic'), ('AVI', 'video', 'x-msvideo'), ('BMP', 'image', 'BMP'), ('DOC', 'application', 'MSWord'), ('EPS', 'application', 'Postscript'), ('GIF', 'image', 'GIF'), ('JPEG', 'image', 'JPEG'), ('JPG', 'image', 'JPEG'), ('MID', 'audio', 'midi'), ('MOV', 'video', 'quicktime'), ('MPEG', 'video', 'MPEG'), ('MPG', 'video', 'MPEG'), ('MP2', 'audio', 'mpeg'), ('MP3', 'audio', 'mpeg'), ('PDF', 'application', 'PDF'), ('PNG', 'image', 'PNG'), ('PS', 'application', 'Postscript'), ('QT', 'video', 'quicktime'), ('RA', 'audio', 'x-realaudio'), ('RTF', 'application', 'RTF'), ('SND', 'audio', 'basic'), ('TIF', 'image', 'TIFF'), ('TIFF', 'image', 'TIFF'), ('WAV', 'audio', 'x-wav'), ('WPD', 'application', 'Wordperfect5.1'), ('ZIP', 'application', 'ZIP') ); {:Generates a unique boundary string.} function GenerateBoundary: string; implementation {==============================================================================} constructor TMIMEPart.Create; begin inherited Create; FOnWalkPart := nil; FLines := TStringList.Create; FPartBody := TStringList.Create; FHeaders := TStringList.Create; FPrePart := TStringList.Create; FPostPart := TStringList.Create; FDecodedLines := TMemoryStream.Create; FSubParts := TList.Create; FTargetCharset := GetCurCP; //was 'US-ASCII' before, but RFC-ignorant Outlook sometimes using default //system charset instead. FDefaultCharset := GetIDFromCP(GetCurCP); FMaxLineLength := 78; FSubLevel := 0; FMaxSubLevel := -1; FAttachInside := false; FConvertCharset := true; FForcedHTMLConvert := false; end; destructor TMIMEPart.Destroy; begin ClearSubParts; FSubParts.Free; FDecodedLines.Free; FPartBody.Free; FLines.Free; FHeaders.Free; FPrePart.Free; FPostPart.Free; inherited Destroy; end; {==============================================================================} procedure TMIMEPart.Clear; begin FPrimary := ''; FEncoding := ''; FCharset := ''; FPrimaryCode := MP_TEXT; FEncodingCode := ME_7BIT; FCharsetCode := ISO_8859_1; FTargetCharset := GetCurCP; FSecondary := ''; FDisposition := ''; FContentID := ''; FDescription := ''; FBoundary := ''; FFileName := ''; FAttachInside := False; FPartBody.Clear; FHeaders.Clear; FPrePart.Clear; FPostPart.Clear; FDecodedLines.Clear; FConvertCharset := true; FForcedHTMLConvert := false; ClearSubParts; end; {==============================================================================} procedure TMIMEPart.Assign(Value: TMimePart); begin Primary := Value.Primary; Encoding := Value.Encoding; Charset := Value.Charset; DefaultCharset := Value.DefaultCharset; PrimaryCode := Value.PrimaryCode; EncodingCode := Value.EncodingCode; CharsetCode := Value.CharsetCode; TargetCharset := Value.TargetCharset; Secondary := Value.Secondary; Description := Value.Description; Disposition := Value.Disposition; ContentID := Value.ContentID; Boundary := Value.Boundary; FileName := Value.FileName; Lines.Assign(Value.Lines); PartBody.Assign(Value.PartBody); Headers.Assign(Value.Headers); PrePart.Assign(Value.PrePart); PostPart.Assign(Value.PostPart); MaxLineLength := Value.MaxLineLength; FAttachInside := Value.AttachInside; FConvertCharset := Value.ConvertCharset; end; {==============================================================================} procedure TMIMEPart.AssignSubParts(Value: TMimePart); var n: integer; p: TMimePart; begin Assign(Value); for n := 0 to Value.GetSubPartCount - 1 do begin p := AddSubPart; p.AssignSubParts(Value.GetSubPart(n)); end; end; {==============================================================================} function TMIMEPart.GetSubPartCount: integer; begin Result := FSubParts.Count; end; {==============================================================================} function TMIMEPart.GetSubPart(index: integer): TMimePart; begin Result := nil; if Index < GetSubPartCount then Result := TMimePart(FSubParts[Index]); end; {==============================================================================} procedure TMIMEPart.DeleteSubPart(index: integer); begin if Index < GetSubPartCount then begin GetSubPart(Index).Free; FSubParts.Delete(Index); end; end; {==============================================================================} procedure TMIMEPart.ClearSubParts; var n: integer; begin for n := 0 to GetSubPartCount - 1 do TMimePart(FSubParts[n]).Free; FSubParts.Clear; end; {==============================================================================} function TMIMEPart.AddSubPart: TMimePart; begin Result := TMimePart.Create; Result.DefaultCharset := FDefaultCharset; FSubParts.Add(Result); Result.SubLevel := FSubLevel + 1; Result.MaxSubLevel := FMaxSubLevel; end; {==============================================================================} procedure TMIMEPart.DecomposeParts; var x: integer; s: string; Mime: TMimePart; procedure SkipEmpty; begin while FLines.Count > x do begin s := TrimRight(FLines[x]); if s <> '' then Break; Inc(x); end; end; begin x := 0; Clear; //extract headers while FLines.Count > x do begin s := NormalizeHeader(FLines, x); if s = '' then Break; FHeaders.Add(s); end; DecodePartHeader; //extract prepart if FPrimaryCode = MP_MULTIPART then begin while FLines.Count > x do begin s := FLines[x]; Inc(x); if TrimRight(s) = '--' + FBoundary then Break; FPrePart.Add(s); if not FAttachInside then FAttachInside := IsUUcode(s); end; end; //extract body part if FPrimaryCode = MP_MULTIPART then begin repeat if CanSubPart then begin Mime := AddSubPart; while FLines.Count > x do begin s := FLines[x]; Inc(x); if Pos('--' + FBoundary, s) = 1 then Break; Mime.Lines.Add(s); end; Mime.DecomposeParts; end else begin s := FLines[x]; Inc(x); FPartBody.Add(s); end; if x >= FLines.Count then break; until s = '--' + FBoundary + '--'; end; if (FPrimaryCode = MP_MESSAGE) and CanSubPart then begin Mime := AddSubPart; SkipEmpty; while FLines.Count > x do begin s := TrimRight(FLines[x]); Inc(x); Mime.Lines.Add(s); end; Mime.DecomposeParts; end else begin while FLines.Count > x do begin s := FLines[x]; Inc(x); FPartBody.Add(s); if not FAttachInside then FAttachInside := IsUUcode(s); end; end; //extract postpart if FPrimaryCode = MP_MULTIPART then begin while FLines.Count > x do begin s := TrimRight(FLines[x]); Inc(x); FPostPart.Add(s); if not FAttachInside then FAttachInside := IsUUcode(s); end; end; end; {==============================================================================} procedure TMIMEPart.ComposeParts; var n: integer; mime: TMimePart; s, t: string; d1, d2, d3: integer; x: integer; begin FLines.Clear; //add headers for n := 0 to FHeaders.Count -1 do begin s := FHeaders[n]; repeat if Length(s) < FMaxLineLength then begin t := s; s := ''; end else begin d1 := RPosEx('; ', s, FMaxLineLength); d2 := RPosEx(' ', s, FMaxLineLength); d3 := RPosEx(', ', s, FMaxLineLength); if (d1 <= 1) and (d2 <= 1) and (d3 <= 1) then begin x := Pos(' ', Copy(s, 2, Length(s) - 1)); if x < 1 then x := Length(s); end else if d1 > 0 then x := d1 else if d3 > 0 then x := d3 else x := d2 - 1; t := Copy(s, 1, x); Delete(s, 1, x); end; Flines.Add(t); until s = ''; end; Flines.Add(''); //add body //if multipart if FPrimaryCode = MP_MULTIPART then begin Flines.AddStrings(FPrePart); for n := 0 to GetSubPartCount - 1 do begin Flines.Add('--' + FBoundary); mime := GetSubPart(n); mime.ComposeParts; FLines.AddStrings(mime.Lines); end; Flines.Add('--' + FBoundary + '--'); Flines.AddStrings(FPostPart); end; //if message if FPrimaryCode = MP_MESSAGE then begin if GetSubPartCount > 0 then begin mime := GetSubPart(0); mime.ComposeParts; FLines.AddStrings(mime.Lines); end; end else //if normal part begin FLines.AddStrings(FPartBody); end; end; {==============================================================================} procedure TMIMEPart.DecodePart; var n: Integer; s, t, t2: string; b: Boolean; begin FDecodedLines.Clear; case FEncodingCode of ME_QUOTED_PRINTABLE: s := DecodeQuotedPrintable(FPartBody.Text); ME_BASE64: s := DecodeBase64(FPartBody.Text); ME_UU, ME_XX: begin s := ''; for n := 0 to FPartBody.Count - 1 do if FEncodingCode = ME_UU then s := s + DecodeUU(FPartBody[n]) else s := s + DecodeXX(FPartBody[n]); end; else s := FPartBody.Text; end; if FConvertCharset and (FPrimaryCode = MP_TEXT) then if (not FForcedHTMLConvert) and (uppercase(FSecondary) = 'HTML') then begin b := false; t2 := uppercase(s); t := SeparateLeft(t2, ''); if length(t) <> length(s) then begin t := SeparateRight(t, ''); t := ReplaceString(t, '"', ''); t := ReplaceString(t, ' ', ''); b := Pos('HTTP-EQUIV=CONTENT-TYPE', t) > 0; end; //workaround for shitty M$ Outlook 11 which is placing this information //outside section if not b then begin t := Copy(t2, 1, 2048); t := ReplaceString(t, '"', ''); t := ReplaceString(t, ' ', ''); b := Pos('HTTP-EQUIV=CONTENT-TYPE', t) > 0; end; if not b then s := CharsetConversion(s, FCharsetCode, FTargetCharset); end else s := CharsetConversion(s, FCharsetCode, FTargetCharset); WriteStrToStream(FDecodedLines, s); FDecodedLines.Seek(0, soFromBeginning); end; {==============================================================================} procedure TMIMEPart.DecodePartHeader; var n: integer; s, su, fn: string; st, st2: string; begin Primary := 'text'; FSecondary := 'plain'; FDescription := ''; Charset := FDefaultCharset; FFileName := ''; //was 7bit before, but this is more compatible with RFC-ignorant outlook Encoding := '8BIT'; FDisposition := ''; FContentID := ''; fn := ''; for n := 0 to FHeaders.Count - 1 do if FHeaders[n] <> '' then begin s := FHeaders[n]; su := UpperCase(s); if Pos('CONTENT-TYPE:', su) = 1 then begin st := Trim(SeparateRight(su, ':')); st2 := Trim(SeparateLeft(st, ';')); Primary := Trim(SeparateLeft(st2, '/')); FSecondary := Trim(SeparateRight(st2, '/')); if (FSecondary = Primary) and (Pos('/', st2) < 1) then FSecondary := ''; case FPrimaryCode of MP_TEXT: begin Charset := UpperCase(GetParameter(s, 'charset')); FFileName := GetParameter(s, 'name'); end; MP_MULTIPART: FBoundary := GetParameter(s, 'Boundary'); MP_MESSAGE: begin end; MP_BINARY: FFileName := GetParameter(s, 'name'); end; end; if Pos('CONTENT-TRANSFER-ENCODING:', su) = 1 then Encoding := Trim(SeparateRight(su, ':')); if Pos('CONTENT-DESCRIPTION:', su) = 1 then FDescription := Trim(SeparateRight(s, ':')); if Pos('CONTENT-DISPOSITION:', su) = 1 then begin FDisposition := SeparateRight(su, ':'); FDisposition := Trim(SeparateLeft(FDisposition, ';')); fn := GetParameter(s, 'FileName'); end; if Pos('CONTENT-ID:', su) = 1 then FContentID := Trim(SeparateRight(s, ':')); end; if fn <> '' then FFileName := fn; FFileName := InlineDecode(FFileName, FTargetCharset); FFileName := ExtractFileName(FFileName); end; {==============================================================================} procedure TMIMEPart.EncodePart; var l: TStringList; s, t: string; n, x: Integer; d1, d2: integer; begin if (FEncodingCode = ME_UU) or (FEncodingCode = ME_XX) then Encoding := 'base64'; l := TStringList.Create; FPartBody.Clear; FDecodedLines.Seek(0, soFromBeginning); try case FPrimaryCode of MP_MULTIPART, MP_MESSAGE: FPartBody.LoadFromStream(FDecodedLines); MP_TEXT, MP_BINARY: begin s := ReadStrFromStream(FDecodedLines, FDecodedLines.Size); if FConvertCharset and (FPrimaryCode = MP_TEXT) and (FEncodingCode <> ME_7BIT) then s := GetBOM(FCharSetCode) + CharsetConversion(s, FTargetCharset, FCharsetCode); if FEncodingCode = ME_BASE64 then begin x := 1; while x <= length(s) do begin t := copy(s, x, 54); x := x + length(t); t := EncodeBase64(t); FPartBody.Add(t); end; end else begin if FPrimaryCode = MP_BINARY then l.Add(s) else l.Text := s; for n := 0 to l.Count - 1 do begin s := l[n]; if FEncodingCode = ME_QUOTED_PRINTABLE then begin s := EncodeQuotedPrintable(s); repeat if Length(s) < FMaxLineLength then begin t := s; s := ''; end else begin d1 := RPosEx('=', s, FMaxLineLength); d2 := RPosEx(' ', s, FMaxLineLength); if (d1 = 0) and (d2 = 0) then x := FMaxLineLength else if d1 > d2 then x := d1 - 1 else x := d2 - 1; if x = 0 then x := FMaxLineLength; t := Copy(s, 1, x); Delete(s, 1, x); if s <> '' then t := t + '='; end; FPartBody.Add(t); until s = ''; end else FPartBody.Add(s); end; if (FPrimaryCode = MP_BINARY) and (FEncodingCode = ME_QUOTED_PRINTABLE) then FPartBody[FPartBody.Count - 1] := FPartBody[FPartBody.Count - 1] + '='; end; end; end; finally l.Free; end; end; {==============================================================================} procedure TMIMEPart.EncodePartHeader; var s: string; begin FHeaders.Clear; if FSecondary = '' then case FPrimaryCode of MP_TEXT: FSecondary := 'plain'; MP_MULTIPART: FSecondary := 'mixed'; MP_MESSAGE: FSecondary := 'rfc822'; MP_BINARY: FSecondary := 'octet-stream'; end; if FDescription <> '' then FHeaders.Insert(0, 'Content-Description: ' + FDescription); if FDisposition <> '' then begin s := ''; if FFileName <> '' then s := '; FileName=' + QuoteStr(InlineCodeEx(FileName, FTargetCharset), '"'); FHeaders.Insert(0, 'Content-Disposition: ' + LowerCase(FDisposition) + s); end; if FContentID <> '' then FHeaders.Insert(0, 'Content-ID: ' + FContentID); case FEncodingCode of ME_7BIT: s := '7bit'; ME_8BIT: s := '8bit'; ME_QUOTED_PRINTABLE: s := 'Quoted-printable'; ME_BASE64: s := 'Base64'; end; case FPrimaryCode of MP_TEXT, MP_BINARY: FHeaders.Insert(0, 'Content-Transfer-Encoding: ' + s); end; case FPrimaryCode of MP_TEXT: s := FPrimary + '/' + FSecondary + '; charset=' + GetIDfromCP(FCharsetCode); MP_MULTIPART: s := FPrimary + '/' + FSecondary + '; boundary="' + FBoundary + '"'; MP_MESSAGE, MP_BINARY: s := FPrimary + '/' + FSecondary; end; if FFileName <> '' then s := s + '; name=' + QuoteStr(InlineCodeEx(FileName, FTargetCharset), '"'); FHeaders.Insert(0, 'Content-type: ' + s); end; {==============================================================================} procedure TMIMEPart.MimeTypeFromExt(Value: string); var s: string; n: Integer; begin Primary := ''; FSecondary := ''; s := UpperCase(ExtractFileExt(Value)); if s = '' then s := UpperCase(Value); s := SeparateRight(s, '.'); for n := 0 to MaxMimeType do if MimeType[n, 0] = s then begin Primary := MimeType[n, 1]; FSecondary := MimeType[n, 2]; Break; end; if Primary = '' then Primary := 'application'; if FSecondary = '' then FSecondary := 'octet-stream'; end; {==============================================================================} procedure TMIMEPart.WalkPart; var n: integer; m: TMimepart; begin if assigned(OnWalkPart) then begin OnWalkPart(self); for n := 0 to GetSubPartCount - 1 do begin m := GetSubPart(n); m.OnWalkPart := OnWalkPart; m.WalkPart; end; end; end; {==============================================================================} procedure TMIMEPart.SetPrimary(Value: string); var s: string; begin FPrimary := Value; s := UpperCase(Value); FPrimaryCode := MP_BINARY; if Pos('TEXT', s) = 1 then FPrimaryCode := MP_TEXT; if Pos('MULTIPART', s) = 1 then FPrimaryCode := MP_MULTIPART; if Pos('MESSAGE', s) = 1 then FPrimaryCode := MP_MESSAGE; end; procedure TMIMEPart.SetEncoding(Value: string); var s: string; begin FEncoding := Value; s := UpperCase(Value); FEncodingCode := ME_7BIT; if Pos('8BIT', s) = 1 then FEncodingCode := ME_8BIT; if Pos('QUOTED-PRINTABLE', s) = 1 then FEncodingCode := ME_QUOTED_PRINTABLE; if Pos('BASE64', s) = 1 then FEncodingCode := ME_BASE64; if Pos('X-UU', s) = 1 then FEncodingCode := ME_UU; if Pos('X-XX', s) = 1 then FEncodingCode := ME_XX; end; procedure TMIMEPart.SetCharset(Value: string); begin if value <> '' then begin FCharset := Value; FCharsetCode := GetCPFromID(Value); end; end; function TMIMEPart.CanSubPart: boolean; begin Result := True; if FMaxSubLevel <> -1 then Result := FMaxSubLevel > FSubLevel; end; function TMIMEPart.IsUUcode(Value: string): boolean; begin Value := UpperCase(Value); Result := (pos('BEGIN ', Value) = 1) and (Trim(SeparateRight(Value, ' ')) <> ''); end; {==============================================================================} function GenerateBoundary: string; var x, y: Integer; begin y := GetTick; x := y; while TickDelta(y, x) = 0 do begin Sleep(1); x := GetTick; end; Randomize; y := Random(MaxInt); Result := IntToHex(x, 8) + '_' + IntToHex(y, 8) + '_Synapse_boundary'; end; end. TransGUI/synapse/source/lib/asn1util.pas0000644000000000000000000003505411366572451017226 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.004.004 | |==============================================================================| | Content: support for ASN.1 BER coding and decoding | |==============================================================================| | Copyright (c)1999-2003, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2003 | | Portions created by Hernan Sanchez are Copyright (c) 2000. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Hernan Sanchez (hernan.sanchez@iname.com) | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {: @abstract(Utilities for handling ASN.1 BER encoding) By this unit you can parse ASN.1 BER encoded data to elements or build back any elements to ASN.1 BER encoded buffer. You can dump ASN.1 BER encoded data to human readable form for easy debugging, too. Supported element types are: ASN1_BOOL, ASN1_INT, ASN1_OCTSTR, ASN1_NULL, ASN1_OBJID, ASN1_ENUM, ASN1_SEQ, ASN1_SETOF, ASN1_IPADDR, ASN1_COUNTER, ASN1_GAUGE, ASN1_TIMETICKS, ASN1_OPAQUE For sample of using, look to @link(TSnmpSend) or @link(TLdapSend)class. } {$Q-} {$H+} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit asn1util; interface uses SysUtils, Classes, synautil; const ASN1_BOOL = $01; ASN1_INT = $02; ASN1_OCTSTR = $04; ASN1_NULL = $05; ASN1_OBJID = $06; ASN1_ENUM = $0a; ASN1_SEQ = $30; ASN1_SETOF = $31; ASN1_IPADDR = $40; ASN1_COUNTER = $41; ASN1_GAUGE = $42; ASN1_TIMETICKS = $43; ASN1_OPAQUE = $44; {:Encodes OID item to binary form.} function ASNEncOIDItem(Value: Integer): AnsiString; {:Decodes an OID item of the next element in the "Buffer" from the "Start" position.} function ASNDecOIDItem(var Start: Integer; const Buffer: AnsiString): Integer; {:Encodes the length of ASN.1 element to binary.} function ASNEncLen(Len: Integer): AnsiString; {:Decodes length of next element in "Buffer" from the "Start" position.} function ASNDecLen(var Start: Integer; const Buffer: AnsiString): Integer; {:Encodes a signed integer to ASN.1 binary} function ASNEncInt(Value: Integer): AnsiString; {:Encodes unsigned integer into ASN.1 binary} function ASNEncUInt(Value: Integer): AnsiString; {:Encodes ASN.1 object to binary form.} function ASNObject(const Data: AnsiString; ASNType: Integer): AnsiString; {:Beginning with the "Start" position, decode the ASN.1 item of the next element in "Buffer". Type of item is stored in "ValueType."} function ASNItem(var Start: Integer; const Buffer: AnsiString; var ValueType: Integer): AnsiString; {:Encodes an MIB OID string to binary form.} function MibToId(Mib: String): AnsiString; {:Decodes MIB OID from binary form to string form.} function IdToMib(const Id: AnsiString): String; {:Encodes an one number from MIB OID to binary form. (used internally from @link(MibToId))} function IntMibToStr(const Value: AnsiString): AnsiString; {:Convert ASN.1 BER encoded buffer to human readable form for debugging.} function ASNdump(const Value: AnsiString): AnsiString; implementation {==============================================================================} function ASNEncOIDItem(Value: Integer): AnsiString; var x, xm: Integer; b: Boolean; begin x := Value; b := False; Result := ''; repeat xm := x mod 128; x := x div 128; if b then xm := xm or $80; if x > 0 then b := True; Result := AnsiChar(xm) + Result; until x = 0; end; {==============================================================================} function ASNDecOIDItem(var Start: Integer; const Buffer: AnsiString): Integer; var x: Integer; b: Boolean; begin Result := 0; repeat Result := Result * 128; x := Ord(Buffer[Start]); Inc(Start); b := x > $7F; x := x and $7F; Result := Result + x; until not b; end; {==============================================================================} function ASNEncLen(Len: Integer): AnsiString; var x, y: Integer; begin if Len < $80 then Result := AnsiChar(Len) else begin x := Len; Result := ''; repeat y := x mod 256; x := x div 256; Result := AnsiChar(y) + Result; until x = 0; y := Length(Result); y := y or $80; Result := AnsiChar(y) + Result; end; end; {==============================================================================} function ASNDecLen(var Start: Integer; const Buffer: AnsiString): Integer; var x, n: Integer; begin x := Ord(Buffer[Start]); Inc(Start); if x < $80 then Result := x else begin Result := 0; x := x and $7F; for n := 1 to x do begin Result := Result * 256; x := Ord(Buffer[Start]); Inc(Start); Result := Result + x; end; end; end; {==============================================================================} function ASNEncInt(Value: Integer): AnsiString; var x, y: Cardinal; neg: Boolean; begin neg := Value < 0; x := Abs(Value); if neg then x := not (x - 1); Result := ''; repeat y := x mod 256; x := x div 256; Result := AnsiChar(y) + Result; until x = 0; if (not neg) and (Result[1] > #$7F) then Result := #0 + Result; end; {==============================================================================} function ASNEncUInt(Value: Integer): AnsiString; var x, y: Integer; neg: Boolean; begin neg := Value < 0; x := Value; if neg then x := x and $7FFFFFFF; Result := ''; repeat y := x mod 256; x := x div 256; Result := AnsiChar(y) + Result; until x = 0; if neg then Result[1] := AnsiChar(Ord(Result[1]) or $80); end; {==============================================================================} function ASNObject(const Data: AnsiString; ASNType: Integer): AnsiString; begin Result := AnsiChar(ASNType) + ASNEncLen(Length(Data)) + Data; end; {==============================================================================} function ASNItem(var Start: Integer; const Buffer: AnsiString; var ValueType: Integer): AnsiString; var ASNType: Integer; ASNSize: Integer; y, n: Integer; x: byte; s: AnsiString; c: AnsiChar; neg: Boolean; l: Integer; begin Result := ''; ValueType := ASN1_NULL; l := Length(Buffer); if l < (Start + 1) then Exit; ASNType := Ord(Buffer[Start]); ValueType := ASNType; Inc(Start); ASNSize := ASNDecLen(Start, Buffer); if (Start + ASNSize - 1) > l then Exit; if (ASNType and $20) > 0 then // Result := '$' + IntToHex(ASNType, 2) Result := Copy(Buffer, Start, ASNSize) else case ASNType of ASN1_INT, ASN1_ENUM, ASN1_BOOL: begin y := 0; neg := False; for n := 1 to ASNSize do begin x := Ord(Buffer[Start]); if (n = 1) and (x > $7F) then neg := True; if neg then x := not x; y := y * 256 + x; Inc(Start); end; if neg then y := -(y + 1); Result := IntToStr(y); end; ASN1_COUNTER, ASN1_GAUGE, ASN1_TIMETICKS: begin y := 0; for n := 1 to ASNSize do begin y := y * 256 + Ord(Buffer[Start]); Inc(Start); end; Result := IntToStr(y); end; ASN1_OCTSTR, ASN1_OPAQUE: begin for n := 1 to ASNSize do begin c := AnsiChar(Buffer[Start]); Inc(Start); s := s + c; end; Result := s; end; ASN1_OBJID: begin for n := 1 to ASNSize do begin c := AnsiChar(Buffer[Start]); Inc(Start); s := s + c; end; Result := IdToMib(s); end; ASN1_IPADDR: begin s := ''; for n := 1 to ASNSize do begin if (n <> 1) then s := s + '.'; y := Ord(Buffer[Start]); Inc(Start); s := s + IntToStr(y); end; Result := s; end; ASN1_NULL: begin Result := ''; Start := Start + ASNSize; end; else // unknown begin for n := 1 to ASNSize do begin c := AnsiChar(Buffer[Start]); Inc(Start); s := s + c; end; Result := s; end; end; end; {==============================================================================} function MibToId(Mib: String): AnsiString; var x: Integer; function WalkInt(var s: String): Integer; var x: Integer; t: AnsiString; begin x := Pos('.', s); if x < 1 then begin t := s; s := ''; end else begin t := Copy(s, 1, x - 1); s := Copy(s, x + 1, Length(s) - x); end; Result := StrToIntDef(t, 0); end; begin Result := ''; x := WalkInt(Mib); x := x * 40 + WalkInt(Mib); Result := ASNEncOIDItem(x); while Mib <> '' do begin x := WalkInt(Mib); Result := Result + ASNEncOIDItem(x); end; end; {==============================================================================} function IdToMib(const Id: AnsiString): String; var x, y, n: Integer; begin Result := ''; n := 1; while Length(Id) + 1 > n do begin x := ASNDecOIDItem(n, Id); if (n - 1) = 1 then begin y := x div 40; x := x mod 40; Result := IntToStr(y); end; Result := Result + '.' + IntToStr(x); end; end; {==============================================================================} function IntMibToStr(const Value: AnsiString): AnsiString; var n, y: Integer; begin y := 0; for n := 1 to Length(Value) - 1 do y := y * 256 + Ord(Value[n]); Result := IntToStr(y); end; {==============================================================================} function ASNdump(const Value: AnsiString): AnsiString; var i, at, x, n: integer; s, indent: AnsiString; il: TStringList; begin il := TStringList.Create; try Result := ''; i := 1; indent := ''; while i < Length(Value) do begin for n := il.Count - 1 downto 0 do begin x := StrToIntDef(il[n], 0); if x <= i then begin il.Delete(n); Delete(indent, 1, 2); end; end; s := ASNItem(i, Value, at); Result := Result + indent + '$' + IntToHex(at, 2); if (at and $20) > 0 then begin x := Length(s); Result := Result + ' constructed: length ' + IntToStr(x); indent := indent + ' '; il.Add(IntToStr(x + i - 1)); end else begin case at of ASN1_BOOL: Result := Result + ' BOOL: '; ASN1_INT: Result := Result + ' INT: '; ASN1_ENUM: Result := Result + ' ENUM: '; ASN1_COUNTER: Result := Result + ' COUNTER: '; ASN1_GAUGE: Result := Result + ' GAUGE: '; ASN1_TIMETICKS: Result := Result + ' TIMETICKS: '; ASN1_OCTSTR: Result := Result + ' OCTSTR: '; ASN1_OPAQUE: Result := Result + ' OPAQUE: '; ASN1_OBJID: Result := Result + ' OBJID: '; ASN1_IPADDR: Result := Result + ' IPADDR: '; ASN1_NULL: Result := Result + ' NULL: '; else // other Result := Result + ' unknown: '; end; if IsBinaryString(s) then s := DumpExStr(s); Result := Result + s; end; Result := Result + #$0d + #$0a; end; finally il.Free; end; end; {==============================================================================} end. TransGUI/synapse/source/lib/blcksock.pas0000644000000000000000000036753411466757142017277 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 009.008.003 | |==============================================================================| | Content: Library base | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)1999-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} { Special thanks to Gregor Ibic (Intelicom d.o.o., http://www.intelicom.si) for good inspiration about SSL programming. } {$DEFINE ONCEWINSOCK} {Note about define ONCEWINSOCK: If you remove this compiler directive, then socket interface is loaded and initialized on constructor of TBlockSocket class for each socket separately. Socket interface is used only if your need it. If you leave this directive here, then socket interface is loaded and initialized only once at start of your program! It boost performace on high count of created and destroyed sockets. It eliminate possible small resource leak on Windows systems too. } //{$DEFINE RAISEEXCEPT} {When you enable this define, then is Raiseexcept property is on by default } {:@abstract(Synapse's library core) Core with implementation basic socket classes. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$IFDEF VER125} {$DEFINE BCB} {$ENDIF} {$IFDEF BCB} {$ObjExportAll On} {$ENDIF} {$Q-} {$H+} {$M+} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit blcksock; interface uses SysUtils, Classes, synafpc, synsock, synautil, synacode, synaip {$IFDEF CIL} ,System.Net ,System.Net.Sockets ,System.Text {$ENDIF} ; const SynapseRelease = '38'; cLocalhost = '127.0.0.1'; cAnyHost = '0.0.0.0'; cBroadcast = '255.255.255.255'; c6Localhost = '::1'; c6AnyHost = '::0'; c6Broadcast = 'ffff::1'; cAnyPort = '0'; CR = #$0d; LF = #$0a; CRLF = CR + LF; c64k = 65536; type {:@abstract(Exception clas used by Synapse) When you enable generating of exceptions, this exception is raised by Synapse's units.} ESynapseError = class(Exception) private FErrorCode: Integer; FErrorMessage: string; published {:Code of error. Value depending on used operating system} property ErrorCode: Integer read FErrorCode Write FErrorCode; {:Human readable description of error.} property ErrorMessage: string read FErrorMessage Write FErrorMessage; end; {:Types of OnStatus events} THookSocketReason = ( {:Resolving is begin. Resolved IP and port is in parameter in format like: 'localhost.somewhere.com:25'.} HR_ResolvingBegin, {:Resolving is done. Resolved IP and port is in parameter in format like: 'localhost.somewhere.com:25'. It is always same as in HR_ResolvingBegin!} HR_ResolvingEnd, {:Socket created by CreateSocket method. It reporting Family of created socket too!} HR_SocketCreate, {:Socket closed by CloseSocket method.} HR_SocketClose, {:Socket binded to IP and Port. Binded IP and Port is in parameter in format like: 'localhost.somewhere.com:25'.} HR_Bind, {:Socket connected to IP and Port. Connected IP and Port is in parameter in format like: 'localhost.somewhere.com:25'.} HR_Connect, {:Called when CanRead method is used with @True result.} HR_CanRead, {:Called when CanWrite method is used with @True result.} HR_CanWrite, {:Socket is swithed to Listen mode. (TCP socket only)} HR_Listen, {:Socket Accepting client connection. (TCP socket only)} HR_Accept, {:report count of bytes readed from socket. Number is in parameter string. If you need is in integer, you must use StrToInt function!} HR_ReadCount, {:report count of bytes writed to socket. Number is in parameter string. If you need is in integer, you must use StrToInt function!} HR_WriteCount, {:If is limiting of bandwidth on, then this reason is called when sending or receiving is stopped for satisfy bandwidth limit. Parameter is count of waiting milliseconds.} HR_Wait, {:report situation where communication error occured. When raiseexcept is @true, then exception is called after this Hook reason.} HR_Error ); {:Procedural type for OnStatus event. Sender is calling TBlockSocket object, Reason is one of set Status events and value is optional data.} THookSocketStatus = procedure(Sender: TObject; Reason: THookSocketReason; const Value: String) of object; {:This procedural type is used for DataFilter hooks.} THookDataFilter = procedure(Sender: TObject; var Value: AnsiString) of object; {:This procedural type is used for hook OnCreateSocket. By this hook you can insert your code after initialisation of socket. (you can set special socket options, etc.)} THookCreateSocket = procedure(Sender: TObject) of object; {:This procedural type is used for monitoring of communication.} THookMonitor = procedure(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer) of object; {:This procedural type is used for hook OnAfterConnect. By this hook you can insert your code after TCP socket has been sucessfully connected.} THookAfterConnect = procedure(Sender: TObject) of object; {:This procedural type is used for hook OnHeartbeat. By this hook you can call your code repeately during long socket operations. You must enable heartbeats by @Link(HeartbeatRate) property!} THookHeartbeat = procedure(Sender: TObject) of object; {:Specify family of socket.} TSocketFamily = ( {:Default mode. Socket family is defined by target address for connection. It allows instant access to IPv4 and IPv6 nodes. When you need IPv6 address as destination, then is used IPv6 mode. othervise is used IPv4 mode. However this mode not working properly with preliminary IPv6 supports!} SF_Any, {:Turn this class to pure IPv4 mode. This mode is totally compatible with previous Synapse releases.} SF_IP4, {:Turn to only IPv6 mode.} SF_IP6 ); {:specify possible values of SOCKS modes.} TSocksType = ( ST_Socks5, ST_Socks4 ); {:Specify requested SSL/TLS version for secure connection.} TSSLType = ( LT_all, LT_SSLv2, LT_SSLv3, LT_TLSv1, LT_TLSv1_1, LT_SSHv2 ); {:Specify type of socket delayed option.} TSynaOptionType = ( SOT_Linger, SOT_RecvBuff, SOT_SendBuff, SOT_NonBlock, SOT_RecvTimeout, SOT_SendTimeout, SOT_Reuse, SOT_TTL, SOT_Broadcast, SOT_MulticastTTL, SOT_MulticastLoop ); {:@abstract(this object is used for remember delayed socket option set.)} TSynaOption = class(TObject) public Option: TSynaOptionType; Enabled: Boolean; Value: Integer; end; TCustomSSL = class; TSSLClass = class of TCustomSSL; {:@abstract(Basic IP object.) This is parent class for other class with protocol implementations. Do not use this class directly! Use @link(TICMPBlockSocket), @link(TRAWBlockSocket), @link(TTCPBlockSocket) or @link(TUDPBlockSocket) instead.} TBlockSocket = class(TObject) private FOnStatus: THookSocketStatus; FOnReadFilter: THookDataFilter; FOnCreateSocket: THookCreateSocket; FOnMonitor: THookMonitor; FOnHeartbeat: THookHeartbeat; FLocalSin: TVarSin; FRemoteSin: TVarSin; FTag: integer; FBuffer: AnsiString; FRaiseExcept: Boolean; FNonBlockMode: Boolean; FMaxLineLength: Integer; FMaxSendBandwidth: Integer; FNextSend: LongWord; FMaxRecvBandwidth: Integer; FNextRecv: LongWord; FConvertLineEnd: Boolean; FLastCR: Boolean; FLastLF: Boolean; FBinded: Boolean; FFamily: TSocketFamily; FFamilySave: TSocketFamily; FIP6used: Boolean; FPreferIP4: Boolean; FDelayedOptions: TList; FInterPacketTimeout: Boolean; {$IFNDEF CIL} FFDSet: TFDSet; {$ENDIF} FRecvCounter: Integer; FSendCounter: Integer; FSendMaxChunk: Integer; FStopFlag: Boolean; FNonblockSendTimeout: Integer; FHeartbeatRate: integer; function GetSizeRecvBuffer: Integer; procedure SetSizeRecvBuffer(Size: Integer); function GetSizeSendBuffer: Integer; procedure SetSizeSendBuffer(Size: Integer); procedure SetNonBlockMode(Value: Boolean); procedure SetTTL(TTL: integer); function GetTTL:integer; procedure SetFamily(Value: TSocketFamily); virtual; procedure SetSocket(Value: TSocket); virtual; function GetWsaData: TWSAData; function FamilyToAF(f: TSocketFamily): TAddrFamily; protected FSocket: TSocket; FLastError: Integer; FLastErrorDesc: string; FOwner: TObject; procedure SetDelayedOption(const Value: TSynaOption); procedure DelayedOption(const Value: TSynaOption); procedure ProcessDelayedOptions; procedure InternalCreateSocket(Sin: TVarSin); procedure SetSin(var Sin: TVarSin; IP, Port: string); function GetSinIP(Sin: TVarSin): string; function GetSinPort(Sin: TVarSin): Integer; procedure DoStatus(Reason: THookSocketReason; const Value: string); procedure DoReadFilter(Buffer: TMemory; var Len: Integer); procedure DoMonitor(Writing: Boolean; const Buffer: TMemory; Len: Integer); procedure DoCreateSocket; procedure DoHeartbeat; procedure LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord); procedure SetBandwidth(Value: Integer); function TestStopFlag: Boolean; procedure InternalSendStream(const Stream: TStream; WithSize, Indy: boolean); virtual; function InternalCanRead(Timeout: Integer): Boolean; virtual; public constructor Create; {:Create object and load all necessary socket library. What library is loaded is described by STUB parameter. If STUB is empty string, then is loaded default libraries.} constructor CreateAlternate(Stub: string); destructor Destroy; override; {:If @link(family) is not SF_Any, then create socket with type defined in @link(Family) property. If family is SF_Any, then do nothing! (socket is created automaticly when you know what type of socket you need to create. (i.e. inside @link(Connect) or @link(Bind) call.) When socket is created, then is aplyed all stored delayed socket options.} procedure CreateSocket; {:It create socket. Address resolving of Value tells what type of socket is created. If Value is resolved as IPv4 IP, then is created IPv4 socket. If value is resolved as IPv6 address, then is created IPv6 socket.} procedure CreateSocketByName(const Value: String); {:Destroy socket in use. This method is also automatically called from object destructor.} procedure CloseSocket; virtual; {:Abort any work on Socket and destroy them.} procedure AbortSocket; virtual; {:Connects socket to local IP address and PORT. IP address may be numeric or symbolic ('192.168.74.50', 'cosi.nekde.cz', 'ff08::1'). The same for PORT - it may be number or mnemonic port ('23', 'telnet'). If port value is '0', system chooses itself and conects unused port in the range 1024 to 4096 (this depending by operating system!). Structure LocalSin is filled after calling this method. Note: If you call this on non-created socket, then socket is created automaticly. Warning: when you call : Bind('0.0.0.0','0'); then is nothing done! In this case is used implicit system bind instead.} procedure Bind(IP, Port: string); {:Connects socket to remote IP address and PORT. The same rules as with @link(BIND) method are valid. The only exception is that PORT with 0 value will not be connected! Structures LocalSin and RemoteSin will be filled with valid values. When you call this on non-created socket, then socket is created automaticly. Type of created socket is by @link(Family) property. If is used SF_IP4, then is created socket for IPv4. If is used SF_IP6, then is created socket for IPv6. When you have family on SF_Any (default!), then type of created socket is determined by address resolving of destination address. (Not work properly on prilimitary winsock IPv6 support!)} procedure Connect(IP, Port: string); virtual; {:Sets socket to receive mode for new incoming connections. It is necessary to use @link(TBlockSocket.BIND) function call before this method to select receiving port!} procedure Listen; virtual; {:Waits until new incoming connection comes. After it comes a new socket is automatically created (socket handler is returned by this function as result).} function Accept: TSocket; virtual; {:Sends data of LENGTH from BUFFER address via connected socket. System automatically splits data to packets.} function SendBuffer(Buffer: Tmemory; Length: Integer): Integer; virtual; {:One data BYTE is sent via connected socket.} procedure SendByte(Data: Byte); virtual; {:Send data string via connected socket. Any terminator is not added! If you need send true string with CR-LF termination, you must add CR-LF characters to sended string! Because any termination is not added automaticly, you can use this function for sending any binary data in binary string.} procedure SendString(Data: AnsiString); virtual; {:Send integer as four bytes to socket.} procedure SendInteger(Data: integer); virtual; {:Send data as one block to socket. Each block begin with 4 bytes with length of data in block. This 4 bytes is added automaticly by this function.} procedure SendBlock(const Data: AnsiString); virtual; {:Send data from stream to socket.} procedure SendStreamRaw(const Stream: TStream); virtual; {:Send content of stream to socket. It using @link(SendBlock) method} procedure SendStream(const Stream: TStream); virtual; {:Send content of stream to socket. It using @link(SendBlock) method and this is compatible with streams in Indy library.} procedure SendStreamIndy(const Stream: TStream); virtual; {:Note: This is low-level receive function. You must be sure if data is waiting for read before call this function for avoid deadlock! Waits until allocated buffer is filled by received data. Returns number of data received, which equals to LENGTH value under normal operation. If it is not equal the communication channel is possibly broken. On stream oriented sockets if is received 0 bytes, it mean 'socket is closed!" On datagram socket is readed first waiting datagram.} function RecvBuffer(Buffer: TMemory; Length: Integer): Integer; virtual; {:Note: This is high-level receive function. It using internal @link(LineBuffer) and you can combine this function freely with other high-level functions! Method waits until data is received. If no data is received within TIMEOUT (in milliseconds) period, @link(LastError) is set to WSAETIMEDOUT. Methods serves for reading any size of data (i.e. one megabyte...). This method is preffered for reading from stream sockets (like TCP).} function RecvBufferEx(Buffer: Tmemory; Len: Integer; Timeout: Integer): Integer; virtual; {:Similar to @link(RecvBufferEx), but readed data is stored in binary string, not in memory buffer.} function RecvBufferStr(Len: Integer; Timeout: Integer): AnsiString; virtual; {:Note: This is high-level receive function. It using internal @link(LineBuffer) and you can combine this function freely with other high-level functions. Waits until one data byte is received which is also returned as function result. If no data is received within TIMEOUT (in milliseconds)period, @link(LastError) is set to WSAETIMEDOUT and result have value 0.} function RecvByte(Timeout: Integer): Byte; virtual; {:Note: This is high-level receive function. It using internal @link(LineBuffer) and you can combine this function freely with other high-level functions. Waits until one four bytes are received and return it as one Ineger Value. If no data is received within TIMEOUT (in milliseconds)period, @link(LastError) is set to WSAETIMEDOUT and result have value 0.} function RecvInteger(Timeout: Integer): Integer; virtual; {:Note: This is high-level receive function. It using internal @link(LineBuffer) and you can combine this function freely with other high-level functions. Method waits until data string is received. This string is terminated by CR-LF characters. The resulting string is returned without this termination (CR-LF)! If @link(ConvertLineEnd) is used, then CR-LF sequence may not be exactly CR-LF. See @link(ConvertLineEnd) description. If no data is received within TIMEOUT (in milliseconds) period, @link(LastError) is set to WSAETIMEDOUT. You may also specify maximum length of reading data by @link(MaxLineLength) property.} function RecvString(Timeout: Integer): AnsiString; virtual; {:Note: This is high-level receive function. It using internal @link(LineBuffer) and you can combine this function freely with other high-level functions. Method waits until data string is received. This string is terminated by Terminator string. The resulting string is returned without this termination. If no data is received within TIMEOUT (in milliseconds) period, @link(LastError) is set to WSAETIMEDOUT. You may also specify maximum length of reading data by @link(MaxLineLength) property.} function RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString; virtual; {:Note: This is high-level receive function. It using internal @link(LineBuffer) and you can combine this function freely with other high-level functions. Method reads all data waiting for read. If no data is received within TIMEOUT (in milliseconds) period, @link(LastError) is set to WSAETIMEDOUT. Methods serves for reading unknown size of data. Because before call this function you don't know size of received data, returned data is stored in dynamic size binary string. This method is preffered for reading from stream sockets (like TCP). It is very goot for receiving datagrams too! (UDP protocol)} function RecvPacket(Timeout: Integer): AnsiString; virtual; {:Read one block of data from socket. Each block begin with 4 bytes with length of data in block. This function read first 4 bytes for get lenght, then it wait for reported count of bytes.} function RecvBlock(Timeout: Integer): AnsiString; virtual; {:Read all data from socket to stream until socket is closed (or any error occured.)} procedure RecvStreamRaw(const Stream: TStream; Timeout: Integer); virtual; {:Read requested count of bytes from socket to stream.} procedure RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer); {:Receive data to stream. It using @link(RecvBlock) method.} procedure RecvStream(const Stream: TStream; Timeout: Integer); virtual; {:Receive data to stream. This function is compatible with similar function in Indy library. It using @link(RecvBlock) method.} procedure RecvStreamIndy(const Stream: TStream; Timeout: Integer); virtual; {:Same as @link(RecvBuffer), but readed data stays in system input buffer. Warning: this function not respect data in @link(LineBuffer)! Is not recommended to use this function!} function PeekBuffer(Buffer: TMemory; Length: Integer): Integer; virtual; {:Same as @link(RecvByte), but readed data stays in input system buffer. Warning: this function not respect data in @link(LineBuffer)! Is not recommended to use this function!} function PeekByte(Timeout: Integer): Byte; virtual; {:On stream sockets it returns number of received bytes waiting for picking. 0 is returned when there is no such data. On datagram socket it returns length of the first waiting datagram. Returns 0 if no datagram is waiting.} function WaitingData: Integer; virtual; {:Same as @link(WaitingData), but if exists some of data in @link(Linebuffer), return their length instead.} function WaitingDataEx: Integer; {:Clear all waiting data for read from buffers.} procedure Purge; {:Sets linger. Enabled linger means that the system waits another LINGER (in milliseconds) time for delivery of sent data. This function is only for stream type of socket! (TCP)} procedure SetLinger(Enable: Boolean; Linger: Integer); {:Actualize values in @link(LocalSin).} procedure GetSinLocal; {:Actualize values in @link(RemoteSin).} procedure GetSinRemote; {:Actualize values in @link(LocalSin) and @link(RemoteSin).} procedure GetSins; {:Reset @link(LastError) and @link(LastErrorDesc) to non-error state.} procedure ResetLastError; {:If you "manually" call Socket API functions, forward their return code as parameter to this function, which evaluates it, eventually calls GetLastError and found error code returns and stores to @link(LastError).} function SockCheck(SockResult: Integer): Integer; virtual; {:If @link(LastError) contains some error code and @link(RaiseExcept) property is @true, raise adequate exception.} procedure ExceptCheck; {:Returns local computer name as numerical or symbolic value. It try get fully qualified domain name. Name is returned in the format acceptable by functions demanding IP as input parameter.} function LocalName: string; {:Try resolve name to all possible IP address. i.e. If you pass as name result of @link(LocalName) method, you get all IP addresses used by local system.} procedure ResolveNameToIP(Name: string; const IPList: TStrings); {:Try resolve name to primary IP address. i.e. If you pass as name result of @link(LocalName) method, you get primary IP addresses used by local system.} function ResolveName(Name: string): string; {:Try resolve IP to their primary domain name. If IP not have domain name, then is returned original IP.} function ResolveIPToName(IP: string): string; {:Try resolve symbolic port name to port number. (i.e. 'Echo' to 8)} function ResolvePort(Port: string): Word; {:Set information about remote side socket. It is good for seting remote side for sending UDP packet, etc.} procedure SetRemoteSin(IP, Port: string); {:Picks IP socket address from @link(LocalSin).} function GetLocalSinIP: string; virtual; {:Picks IP socket address from @link(RemoteSin).} function GetRemoteSinIP: string; virtual; {:Picks socket PORT number from @link(LocalSin).} function GetLocalSinPort: Integer; virtual; {:Picks socket PORT number from @link(RemoteSin).} function GetRemoteSinPort: Integer; virtual; {:Return @TRUE, if you can read any data from socket or is incoming connection on TCP based socket. Status is tested for time Timeout (in milliseconds). If value in Timeout is 0, status is only tested and continue. If value in Timeout is -1, run is breaked and waiting for read data maybe forever. This function is need only on special cases, when you need use @link(RecvBuffer) function directly! read functioms what have timeout as calling parameter, calling this function internally.} function CanRead(Timeout: Integer): Boolean; virtual; {:Same as @link(CanRead), but additionally return @TRUE if is some data in @link(LineBuffer).} function CanReadEx(Timeout: Integer): Boolean; virtual; {:Return @TRUE, if you can to socket write any data (not full sending buffer). Status is tested for time Timeout (in milliseconds). If value in Timeout is 0, status is only tested and continue. If value in Timeout is -1, run is breaked and waiting for write data maybe forever. This function is need only on special cases!} function CanWrite(Timeout: Integer): Boolean; virtual; {:Same as @link(SendBuffer), but send datagram to address from @link(RemoteSin). Usefull for sending reply to datagram received by function @link(RecvBufferFrom).} function SendBufferTo(Buffer: TMemory; Length: Integer): Integer; virtual; {:Note: This is low-lever receive function. You must be sure if data is waiting for read before call this function for avoid deadlock! Receives first waiting datagram to allocated buffer. If there is no waiting one, then waits until one comes. Returns length of datagram stored in BUFFER. If length exceeds buffer datagram is truncated. After this @link(RemoteSin) structure contains information about sender of UDP packet.} function RecvBufferFrom(Buffer: TMemory; Length: Integer): Integer; virtual; {$IFNDEF CIL} {:This function is for check for incoming data on set of sockets. Whitch sockets is checked is decribed by SocketList Tlist with TBlockSocket objects. TList may have maximal number of objects defined by FD_SETSIZE constant. Return @TRUE, if you can from some socket read any data or is incoming connection on TCP based socket. Status is tested for time Timeout (in milliseconds). If value in Timeout is 0, status is only tested and continue. If value in Timeout is -1, run is breaked and waiting for read data maybe forever. If is returned @TRUE, CanReadList TList is filled by all TBlockSocket objects what waiting for read.} function GroupCanRead(const SocketList: TList; Timeout: Integer; const CanReadList: TList): Boolean; {$ENDIF} {:By this method you may turn address reuse mode for local @link(bind). It is good specially for UDP protocol. Using this with TCP protocol is hazardous!} procedure EnableReuse(Value: Boolean); {:Try set timeout for all sending and receiving operations, if socket provider can do it. (It not supported by all socket providers!)} procedure SetTimeout(Timeout: Integer); {:Try set timeout for all sending operations, if socket provider can do it. (It not supported by all socket providers!)} procedure SetSendTimeout(Timeout: Integer); {:Try set timeout for all receiving operations, if socket provider can do it. (It not supported by all socket providers!)} procedure SetRecvTimeout(Timeout: Integer); {:Return value of socket type.} function GetSocketType: integer; Virtual; {:Return value of protocol type for socket creation.} function GetSocketProtocol: integer; Virtual; {:WSA structure with information about socket provider. On non-windows platforms this structure is simulated!} property WSAData: TWSADATA read GetWsaData; {:FDset structure prepared for usage with this socket.} property FDset: TFDSet read FFDset; {:Structure describing local socket side.} property LocalSin: TVarSin read FLocalSin write FLocalSin; {:Structure describing remote socket side.} property RemoteSin: TVarSin read FRemoteSin write FRemoteSin; {:Socket handler. Suitable for "manual" calls to socket API or manual connection of socket to a previously created socket (i.e by Accept method on TCP socket)} property Socket: TSocket read FSocket write SetSocket; {:Last socket operation error code. Error codes are described in socket documentation. Human readable error description is stored in @link(LastErrorDesc) property.} property LastError: Integer read FLastError; {:Human readable error description of @link(LastError) code.} property LastErrorDesc: string read FLastErrorDesc; {:Buffer used by all high-level receiving functions. This buffer is used for optimized reading of data from socket. In normal cases you not need access to this buffer directly!} property LineBuffer: AnsiString read FBuffer write FBuffer; {:Size of Winsock receive buffer. If it is not supported by socket provider, it return as size one kilobyte.} property SizeRecvBuffer: Integer read GetSizeRecvBuffer write SetSizeRecvBuffer; {:Size of Winsock send buffer. If it is not supported by socket provider, it return as size one kilobyte.} property SizeSendBuffer: Integer read GetSizeSendBuffer write SetSizeSendBuffer; {:If @True, turn class to non-blocking mode. Not all functions are working properly in this mode, you must know exactly what you are doing! However when you have big experience with non-blocking programming, then you can optimise your program by non-block mode!} property NonBlockMode: Boolean read FNonBlockMode Write SetNonBlockMode; {:Set Time-to-live value. (if system supporting it!)} property TTL: Integer read GetTTL Write SetTTL; {:If is @true, then class in in IPv6 mode.} property IP6used: Boolean read FIP6used; {:Return count of received bytes on this socket from begin of current connection.} property RecvCounter: Integer read FRecvCounter; {:Return count of sended bytes on this socket from begin of current connection.} property SendCounter: Integer read FSendCounter; published {:Return descriptive string for given error code. This is class function. You may call it without created object!} class function GetErrorDesc(ErrorCode: Integer): string; {:Return descriptive string for @link(LastError).} function GetErrorDescEx: string; virtual; {:this value is for free use.} property Tag: Integer read FTag write FTag; {:If @true, winsock errors raises exception. Otherwise is setted @link(LastError) value only and you must check it from your program! Default value is @false.} property RaiseExcept: Boolean read FRaiseExcept write FRaiseExcept; {:Define maximum length in bytes of @link(LineBuffer) for high-level receiving functions. If this functions try to read more data then this limit, error is returned! If value is 0 (default), no limitation is used. This is very good protection for stupid attacks to your server by sending lot of data without proper terminator... until all your memory is allocated by LineBuffer! Note: This maximum length is checked only in functions, what read unknown number of bytes! (like @link(RecvString) or @link(RecvTerminated))} property MaxLineLength: Integer read FMaxLineLength Write FMaxLineLength; {:Define maximal bandwidth for all sending operations in bytes per second. If value is 0 (default), bandwidth limitation is not used.} property MaxSendBandwidth: Integer read FMaxSendBandwidth Write FMaxSendBandwidth; {:Define maximal bandwidth for all receiving operations in bytes per second. If value is 0 (default), bandwidth limitation is not used.} property MaxRecvBandwidth: Integer read FMaxRecvBandwidth Write FMaxRecvBandwidth; {:Define maximal bandwidth for all sending and receiving operations in bytes per second. If value is 0 (default), bandwidth limitation is not used.} property MaxBandwidth: Integer Write SetBandwidth; {:Do a conversion of non-standard line terminators to CRLF. (Off by default) If @True, then terminators like sigle CR, single LF or LFCR are converted to CRLF internally. This have effect only in @link(RecvString) method!} property ConvertLineEnd: Boolean read FConvertLineEnd Write FConvertLineEnd; {:Specified Family of this socket. When you are using Windows preliminary support for IPv6, then I recommend to set this property!} property Family: TSocketFamily read FFamily Write SetFamily; {:When resolving of domain name return both IPv4 and IPv6 addresses, then specify if is used IPv4 (dafault - @true) or IPv6.} property PreferIP4: Boolean read FPreferIP4 Write FPreferIP4; {:By default (@true) is all timeouts used as timeout between two packets in reading operations. If you set this to @false, then Timeouts is for overall reading operation!} property InterPacketTimeout: Boolean read FInterPacketTimeout Write FInterPacketTimeout; {:All sended datas was splitted by this value.} property SendMaxChunk: Integer read FSendMaxChunk Write FSendMaxChunk; {:By setting this property to @true you can stop any communication. You can use this property for soft abort of communication.} property StopFlag: Boolean read FStopFlag Write FStopFlag; {:Timeout for data sending by non-blocking socket mode.} property NonblockSendTimeout: Integer read FNonblockSendTimeout Write FNonblockSendTimeout; {:This event is called by various reasons. It is good for monitoring socket, create gauges for data transfers, etc.} property OnStatus: THookSocketStatus read FOnStatus write FOnStatus; {:this event is good for some internal thinks about filtering readed datas. It is used by telnet client by example.} property OnReadFilter: THookDataFilter read FOnReadFilter write FOnReadFilter; {:This event is called after real socket creation for setting special socket options, because you not know when socket is created. (it is depended on Ipv4, IPv6 or automatic mode)} property OnCreateSocket: THookCreateSocket read FOnCreateSocket write FOnCreateSocket; {:This event is good for monitoring content of readed or writed datas.} property OnMonitor: THookMonitor read FOnMonitor write FOnMonitor; {:This event is good for calling your code during long socket operations. (Example, for refresing UI if class in not called within the thread.) Rate of heartbeats can be modified by @link(HeartbeatRate) property.} property OnHeartbeat: THookHeartbeat read FOnHeartbeat write FOnHeartbeat; {:Specify typical rate of @link(OnHeartbeat) event and @link(StopFlag) testing. Default value 0 disabling heartbeats! Value is in milliseconds. Real rate can be higher or smaller then this value, because it depending on real socket operations too! Note: Each heartbeat slowing socket processing.} property HeartbeatRate: integer read FHeartbeatRate Write FHeartbeatRate; {:What class own this socket? Used by protocol implementation classes.} property Owner: TObject read FOwner Write FOwner; end; {:@abstract(Support for SOCKS4 and SOCKS5 proxy) Layer with definition all necessary properties and functions for implementation SOCKS proxy client. Do not use this class directly.} TSocksBlockSocket = class(TBlockSocket) protected FSocksIP: string; FSocksPort: string; FSocksTimeout: integer; FSocksUsername: string; FSocksPassword: string; FUsingSocks: Boolean; FSocksResolver: Boolean; FSocksLastError: integer; FSocksResponseIP: string; FSocksResponsePort: string; FSocksLocalIP: string; FSocksLocalPort: string; FSocksRemoteIP: string; FSocksRemotePort: string; FBypassFlag: Boolean; FSocksType: TSocksType; function SocksCode(IP, Port: string): Ansistring; function SocksDecode(Value: Ansistring): integer; public constructor Create; {:Open connection to SOCKS proxy and if @link(SocksUsername) is set, do authorisation to proxy. This is needed only in special cases! (it is called internally!)} function SocksOpen: Boolean; {:Send specified request to SOCKS proxy. This is needed only in special cases! (it is called internally!)} function SocksRequest(Cmd: Byte; const IP, Port: string): Boolean; {:Receive response to previosly sended request. This is needed only in special cases! (it is called internally!)} function SocksResponse: Boolean; {:Is @True when class is using SOCKS proxy.} property UsingSocks: Boolean read FUsingSocks; {:If SOCKS proxy failed, here is error code returned from SOCKS proxy.} property SocksLastError: integer read FSocksLastError; published {:Address of SOCKS server. If value is empty string, SOCKS support is disabled. Assingning any value to this property enable SOCKS mode. Warning: You cannot combine this mode with HTTP-tunneling mode!} property SocksIP: string read FSocksIP write FSocksIP; {:Port of SOCKS server. Default value is '1080'.} property SocksPort: string read FSocksPort write FSocksPort; {:If you need authorisation on SOCKS server, set username here.} property SocksUsername: string read FSocksUsername write FSocksUsername; {:If you need authorisation on SOCKS server, set password here.} property SocksPassword: string read FSocksPassword write FSocksPassword; {:Specify timeout for communicatin with SOCKS server. Default is one minute.} property SocksTimeout: integer read FSocksTimeout write FSocksTimeout; {:If @True, all symbolic names of target hosts is not translated to IP's locally, but resolving is by SOCKS proxy. Default is @True.} property SocksResolver: Boolean read FSocksResolver write FSocksResolver; {:Specify SOCKS type. By default is used SOCKS5, but you can use SOCKS4 too. When you select SOCKS4, then if @link(SOCKSResolver) is enabled, then is used SOCKS4a. Othervise is used pure SOCKS4.} property SocksType: TSocksType read FSocksType write FSocksType; end; {:@abstract(Implementation of TCP socket.) Supported features: IPv4, IPv6, SSL/TLS or SSH (depending on used plugin), SOCKS5 proxy (outgoing connections and limited incomming), SOCKS4/4a proxy (outgoing connections and limited incomming), TCP through HTTP proxy tunnel.} TTCPBlockSocket = class(TSocksBlockSocket) protected FOnAfterConnect: THookAfterConnect; FSSL: TCustomSSL; FHTTPTunnelIP: string; FHTTPTunnelPort: string; FHTTPTunnel: Boolean; FHTTPTunnelRemoteIP: string; FHTTPTunnelRemotePort: string; FHTTPTunnelUser: string; FHTTPTunnelPass: string; FHTTPTunnelTimeout: integer; procedure SocksDoConnect(IP, Port: string); procedure HTTPTunnelDoConnect(IP, Port: string); procedure DoAfterConnect; public {:Create TCP socket class with default plugin for SSL/TSL/SSH implementation (see @link(SSLImplementation))} constructor Create; {:Create TCP socket class with desired plugin for SSL/TSL/SSH implementation} constructor CreateWithSSL(SSLPlugin: TSSLClass); destructor Destroy; override; {:See @link(TBlockSocket.CloseSocket)} procedure CloseSocket; override; {:See @link(TBlockSocket.WaitingData)} function WaitingData: Integer; override; {:Sets socket to receive mode for new incoming connections. It is necessary to use @link(TBlockSocket.BIND) function call before this method to select receiving port! If you use SOCKS, activate incoming TCP connection by this proxy. (By BIND method of SOCKS.)} procedure Listen; override; {:Waits until new incoming connection comes. After it comes a new socket is automatically created (socket handler is returned by this function as result). If you use SOCKS, new socket is not created! In this case is used same socket as socket for listening! So, you can accept only one connection in SOCKS mode.} function Accept: TSocket; override; {:Connects socket to remote IP address and PORT. The same rules as with @link(TBlockSocket.BIND) method are valid. The only exception is that PORT with 0 value will not be connected. After call to this method a communication channel between local and remote socket is created. Local socket is assigned automatically if not controlled by previous call to @link(TBlockSocket.BIND) method. Structures @link(TBlockSocket.LocalSin) and @link(TBlockSocket.RemoteSin) will be filled with valid values. If you use SOCKS, activate outgoing TCP connection by SOCKS proxy specified in @link(TSocksBlockSocket.SocksIP). (By CONNECT method of SOCKS.) If you use HTTP-tunnel mode, activate outgoing TCP connection by HTTP tunnel specified in @link(HTTPTunnelIP). (By CONNECT method of HTTP protocol.) Note: If you call this on non-created socket, then socket is created automaticly.} procedure Connect(IP, Port: string); override; {:If you need upgrade existing TCP connection to SSL/TLS (or SSH2, if plugin allows it) mode, then call this method. This method switch this class to SSL mode and do SSL/TSL handshake.} procedure SSLDoConnect; {:By this method you can downgrade existing SSL/TLS connection to normal TCP connection.} procedure SSLDoShutdown; {:If you need use this component as SSL/TLS TCP server, then after accepting of inbound connection you need start SSL/TLS session by this method. Before call this function, you must have assigned all neeeded certificates and keys!} function SSLAcceptConnection: Boolean; {:See @link(TBlockSocket.GetLocalSinIP)} function GetLocalSinIP: string; override; {:See @link(TBlockSocket.GetRemoteSinIP)} function GetRemoteSinIP: string; override; {:See @link(TBlockSocket.GetLocalSinPort)} function GetLocalSinPort: Integer; override; {:See @link(TBlockSocket.GetRemoteSinPort)} function GetRemoteSinPort: Integer; override; {:See @link(TBlockSocket.SendBuffer)} function SendBuffer(Buffer: TMemory; Length: Integer): Integer; override; {:See @link(TBlockSocket.RecvBuffer)} function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:Return value of socket type. For TCP return SOCK_STREAM.} function GetSocketType: integer; override; {:Return value of protocol type for socket creation. For TCP return IPPROTO_TCP.} function GetSocketProtocol: integer; override; {:Class implementing SSL/TLS support. It is allways some descendant of @link(TCustomSSL) class. When programmer not select some SSL plugin class, then is used @link(TSSLNone)} property SSL: TCustomSSL read FSSL; {:@True if is used HTTP tunnel mode.} property HTTPTunnel: Boolean read FHTTPTunnel; published {:Return descriptive string for @link(LastError). On case of error in SSL/TLS subsystem, it returns right error description.} function GetErrorDescEx: string; override; {:Specify IP address of HTTP proxy. Assingning non-empty value to this property enable HTTP-tunnel mode. This mode is for tunnelling any outgoing TCP connection through HTTP proxy server. (If policy on HTTP proxy server allow this!) Warning: You cannot combine this mode with SOCK5 mode!} property HTTPTunnelIP: string read FHTTPTunnelIP Write FHTTPTunnelIP; {:Specify port of HTTP proxy for HTTP-tunneling.} property HTTPTunnelPort: string read FHTTPTunnelPort Write FHTTPTunnelPort; {:Specify authorisation username for access to HTTP proxy in HTTP-tunnel mode. If you not need authorisation, then let this property empty.} property HTTPTunnelUser: string read FHTTPTunnelUser Write FHTTPTunnelUser; {:Specify authorisation password for access to HTTP proxy in HTTP-tunnel mode.} property HTTPTunnelPass: string read FHTTPTunnelPass Write FHTTPTunnelPass; {:Specify timeout for communication with HTTP proxy in HTTPtunnel mode.} property HTTPTunnelTimeout: integer read FHTTPTunnelTimeout Write FHTTPTunnelTimeout; {:This event is called after sucessful TCP socket connection.} property OnAfterConnect: THookAfterConnect read FOnAfterConnect write FOnAfterConnect; end; {:@abstract(Datagram based communication) This class implementing datagram based communication instead default stream based communication style.} TDgramBlockSocket = class(TSocksBlockSocket) public {:Fill @link(TBlockSocket.RemoteSin) structure. This address is used for sending data.} procedure Connect(IP, Port: string); override; {:Silently redirected to @link(TBlockSocket.SendBufferTo).} function SendBuffer(Buffer: TMemory; Length: Integer): Integer; override; {:Silently redirected to @link(TBlockSocket.RecvBufferFrom).} function RecvBuffer(Buffer: TMemory; Length: Integer): Integer; override; end; {:@abstract(Implementation of UDP socket.) NOTE: in this class is all receiving redirected to RecvBufferFrom. You can use for reading any receive function. Preffered is RecvPacket! Similary all sending is redirected to SendbufferTo. You can use for sending UDP packet any sending function, like SendString. Supported features: IPv4, IPv6, unicasts, broadcasts, multicasts, SOCKS5 proxy (only unicasts! Outgoing and incomming.)} TUDPBlockSocket = class(TDgramBlockSocket) protected FSocksControlSock: TTCPBlockSocket; function UdpAssociation: Boolean; procedure SetMulticastTTL(TTL: integer); function GetMulticastTTL:integer; public destructor Destroy; override; {:Enable or disable sending of broadcasts. If seting OK, result is @true. This method is not supported in SOCKS5 mode! IPv6 does not support broadcasts! In this case you must use Multicasts instead.} procedure EnableBroadcast(Value: Boolean); {:See @link(TBlockSocket.SendBufferTo)} function SendBufferTo(Buffer: TMemory; Length: Integer): Integer; override; {:See @link(TBlockSocket.RecvBufferFrom)} function RecvBufferFrom(Buffer: TMemory; Length: Integer): Integer; override; {$IFNDEF CIL} {:Add this socket to given multicast group. You cannot use Multicasts in SOCKS mode!} procedure AddMulticast(MCastIP:string); {:Remove this socket from given multicast group.} procedure DropMulticast(MCastIP:string); {$ENDIF} {:All sended multicast datagrams is loopbacked to your interface too. (you can read your sended datas.) You can disable this feature by this function. This function not working on some Windows systems!} procedure EnableMulticastLoop(Value: Boolean); {:Return value of socket type. For UDP return SOCK_DGRAM.} function GetSocketType: integer; override; {:Return value of protocol type for socket creation. For UDP return IPPROTO_UDP.} function GetSocketProtocol: integer; override; {:Set Time-to-live value for multicasts packets. It define number of routers for transfer of datas. If you set this to 1 (dafault system value), then multicasts packet goes only to you local network. If you need transport multicast packet to worldwide, then increase this value, but be carefull, lot of routers on internet does not transport multicasts packets!} property MulticastTTL: Integer read GetMulticastTTL Write SetMulticastTTL; end; {:@abstract(Implementation of RAW ICMP socket.) For this object you must have rights for creating RAW sockets!} TICMPBlockSocket = class(TDgramBlockSocket) public {:Return value of socket type. For RAW and ICMP return SOCK_RAW.} function GetSocketType: integer; override; {:Return value of protocol type for socket creation. For ICMP returns IPPROTO_ICMP or IPPROTO_ICMPV6} function GetSocketProtocol: integer; override; end; {:@abstract(Implementation of RAW socket.) For this object you must have rights for creating RAW sockets!} TRAWBlockSocket = class(TBlockSocket) public {:Return value of socket type. For RAW and ICMP return SOCK_RAW.} function GetSocketType: integer; override; {:Return value of protocol type for socket creation. For RAW returns IPPROTO_RAW.} function GetSocketProtocol: integer; override; end; {:@abstract(Implementation of PGM-message socket.) Not all systems supports this protocol!} TPGMMessageBlockSocket = class(TBlockSocket) public {:Return value of socket type. For PGM-message return SOCK_RDM.} function GetSocketType: integer; override; {:Return value of protocol type for socket creation. For PGM-message returns IPPROTO_RM.} function GetSocketProtocol: integer; override; end; {:@abstract(Implementation of PGM-stream socket.) Not all systems supports this protocol!} TPGMStreamBlockSocket = class(TBlockSocket) public {:Return value of socket type. For PGM-stream return SOCK_STREAM.} function GetSocketType: integer; override; {:Return value of protocol type for socket creation. For PGM-stream returns IPPROTO_RM.} function GetSocketProtocol: integer; override; end; {:@abstract(Parent class for all SSL plugins.) This is abstract class defining interface for other SSL plugins. Instance of this class will be created for each @link(TTCPBlockSocket). Warning: not all methods and propertis can work in all existing SSL plugins! Please, read documentation of used SSL plugin.} TCustomSSL = class(TObject) protected FSocket: TTCPBlockSocket; FSSLEnabled: Boolean; FLastError: integer; FLastErrorDesc: string; FSSLType: TSSLType; FKeyPassword: string; FCiphers: string; FCertificateFile: string; FPrivateKeyFile: string; FCertificate: Ansistring; FPrivateKey: Ansistring; FPFX: Ansistring; FPFXfile: string; FCertCA: Ansistring; FCertCAFile: string; FTrustCertificate: Ansistring; FTrustCertificateFile: string; FVerifyCert: Boolean; FUsername: string; FPassword: string; FSSHChannelType: string; FSSHChannelArg1: string; FSSHChannelArg2: string; procedure ReturnError; function CreateSelfSignedCert(Host: string): Boolean; virtual; public {: Create plugin class. it is called internally from @link(TTCPBlockSocket)} constructor Create(const Value: TTCPBlockSocket); virtual; {: Assign settings (certificates and configuration) from another SSL plugin class.} procedure Assign(const Value: TCustomSSL); virtual; {: return description of used plugin. It usually return name and version of used SSL library.} function LibVersion: String; virtual; {: return name of used plugin.} function LibName: String; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for start SSL connection.} function Connect: boolean; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for acept new SSL connection.} function Accept: boolean; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for hard shutdown of SSL connection. (for example, before socket is closed)} function Shutdown: boolean; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for soft shutdown of SSL connection. (for example, when you need to continue with unprotected connection.)} function BiShutdown: boolean; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for sending some datas by SSL connection.} function SendBuffer(Buffer: TMemory; Len: Integer): Integer; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for receiving some datas by SSL connection.} function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; virtual; {: Do not call this directly. It is used internally by @link(TTCPBlockSocket)! Here is needed code for getting count of datas what waiting for read. If SSL plugin not allows this, then it should return 0.} function WaitingData: Integer; virtual; {:Return string with identificator of SSL/TLS version of existing connection.} function GetSSLVersion: string; virtual; {:Return subject of remote SSL peer.} function GetPeerSubject: string; virtual; {:Return issuer certificate of remote SSL peer.} function GetPeerIssuer: string; virtual; {:Return peer name from remote side certificate. This is good for verify, if certificate is generated for remote side IP name.} function GetPeerName: string; virtual; {:Return fingerprint of remote SSL peer.} function GetPeerFingerprint: string; virtual; {:Return all detailed information about certificate from remote side of SSL/TLS connection. Result string can be multilined! Each plugin can return this informations in different format!} function GetCertInfo: string; virtual; {:Return currently used Cipher.} function GetCipherName: string; virtual; {:Return currently used number of bits in current Cipher algorythm.} function GetCipherBits: integer; virtual; {:Return number of bits in current Cipher algorythm.} function GetCipherAlgBits: integer; virtual; {:Return result value of verify remote side certificate. Look to OpenSSL documentation for possible values. For example 0 is successfuly verified certificate, or 18 is self-signed certificate.} function GetVerifyCert: integer; virtual; {: Resurn @true if SSL mode is enabled on existing cvonnection.} property SSLEnabled: Boolean read FSSLEnabled; {:Return error code of last SSL operation. 0 is OK.} property LastError: integer read FLastError; {:Return error description of last SSL operation.} property LastErrorDesc: string read FLastErrorDesc; published {:Here you can specify requested SSL/TLS mode. Default is autodetection, but on some servers autodetection not working properly. In this case you must specify requested SSL/TLS mode by your hand!} property SSLType: TSSLType read FSSLType write FSSLType; {:Password for decrypting of encoded certificate or key.} property KeyPassword: string read FKeyPassword write FKeyPassword; {:Username for possible credentials.} property Username: string read FUsername write FUsername; {:password for possible credentials.} property Password: string read FPassword write FPassword; {:By this property you can modify default set of SSL/TLS ciphers.} property Ciphers: string read FCiphers write FCiphers; {:Used for loading certificate from disk file. See to plugin documentation if this method is supported and how!} property CertificateFile: string read FCertificateFile write FCertificateFile; {:Used for loading private key from disk file. See to plugin documentation if this method is supported and how!} property PrivateKeyFile: string read FPrivateKeyFile write FPrivateKeyFile; {:Used for loading certificate from binary string. See to plugin documentation if this method is supported and how!} property Certificate: Ansistring read FCertificate write FCertificate; {:Used for loading private key from binary string. See to plugin documentation if this method is supported and how!} property PrivateKey: Ansistring read FPrivateKey write FPrivateKey; {:Used for loading PFX from binary string. See to plugin documentation if this method is supported and how!} property PFX: Ansistring read FPFX write FPFX; {:Used for loading PFX from disk file. See to plugin documentation if this method is supported and how!} property PFXfile: string read FPFXfile write FPFXfile; {:Used for loading trusted certificates from disk file. See to plugin documentation if this method is supported and how!} property TrustCertificateFile: string read FTrustCertificateFile write FTrustCertificateFile; {:Used for loading trusted certificates from binary string. See to plugin documentation if this method is supported and how!} property TrustCertificate: Ansistring read FTrustCertificate write FTrustCertificate; {:Used for loading CA certificates from binary string. See to plugin documentation if this method is supported and how!} property CertCA: Ansistring read FCertCA write FCertCA; {:Used for loading CA certificates from disk file. See to plugin documentation if this method is supported and how!} property CertCAFile: string read FCertCAFile write FCertCAFile; {:If @true, then is verified client certificate. (it is good for writing SSL/TLS servers.) When you are not server, but you are client, then if this property is @true, verify servers certificate.} property VerifyCert: Boolean read FVerifyCert write FVerifyCert; {:channel type for possible SSH connections} property SSHChannelType: string read FSSHChannelType write FSSHChannelType; {:First argument of channel type for possible SSH connections} property SSHChannelArg1: string read FSSHChannelArg1 write FSSHChannelArg1; {:Second argument of channel type for possible SSH connections} property SSHChannelArg2: string read FSSHChannelArg2 write FSSHChannelArg2; end; {:@abstract(Default SSL plugin with no SSL support.) Dummy SSL plugin implementation for applications without SSL/TLS support.} TSSLNone = class (TCustomSSL) public {:See @inherited} function LibVersion: String; override; {:See @inherited} function LibName: String; override; end; {:@abstract(Record with definition of IP packet header.) For reading data from ICMP or RAW sockets.} TIPHeader = record VerLen: Byte; TOS: Byte; TotalLen: Word; Identifer: Word; FragOffsets: Word; TTL: Byte; Protocol: Byte; CheckSum: Word; SourceIp: LongWord; DestIp: LongWord; Options: LongWord; end; {:@abstract(Parent class of application protocol implementations.) By this class is defined common properties.} TSynaClient = Class(TObject) protected FTargetHost: string; FTargetPort: string; FIPInterface: string; FTimeout: integer; FUserName: string; FPassword: string; public constructor Create; published {:Specify terget server IP (or symbolic name). Default is 'localhost'.} property TargetHost: string read FTargetHost Write FTargetHost; {:Specify terget server port (or symbolic name).} property TargetPort: string read FTargetPort Write FTargetPort; {:Defined local socket address. (outgoing IP address). By default is used '0.0.0.0' as wildcard for default IP.} property IPInterface: string read FIPInterface Write FIPInterface; {:Specify default timeout for socket operations.} property Timeout: integer read FTimeout Write FTimeout; {:If protocol need user authorization, then fill here username.} property UserName: string read FUserName Write FUserName; {:If protocol need user authorization, then fill here password.} property Password: string read FPassword Write FPassword; end; var {:Selected SSL plugin. Default is @link(TSSLNone). Do not change this value directly!!! Just add your plugin unit to your project uses instead. Each plugin unit have initialization code what modify this variable.} SSLImplementation: TSSLClass = TSSLNone; implementation {$IFDEF ONCEWINSOCK} var WsaDataOnce: TWSADATA; e: ESynapseError; {$ENDIF} constructor TBlockSocket.Create; begin CreateAlternate(''); end; constructor TBlockSocket.CreateAlternate(Stub: string); {$IFNDEF ONCEWINSOCK} var e: ESynapseError; {$ENDIF} begin inherited Create; FDelayedOptions := TList.Create; FRaiseExcept := False; {$IFDEF RAISEEXCEPT} FRaiseExcept := True; {$ENDIF} FSocket := INVALID_SOCKET; FBuffer := ''; FLastCR := False; FLastLF := False; FBinded := False; FNonBlockMode := False; FMaxLineLength := 0; FMaxSendBandwidth := 0; FNextSend := 0; FMaxRecvBandwidth := 0; FNextRecv := 0; FConvertLineEnd := False; FFamily := SF_Any; FFamilySave := SF_Any; FIP6used := False; FPreferIP4 := True; FInterPacketTimeout := True; FRecvCounter := 0; FSendCounter := 0; FSendMaxChunk := c64k; FStopFlag := False; FNonblockSendTimeout := 15000; FHeartbeatRate := 0; FOwner := nil; {$IFNDEF ONCEWINSOCK} if Stub = '' then Stub := DLLStackName; if not InitSocketInterface(Stub) then begin e := ESynapseError.Create('Error loading Socket interface (' + Stub + ')!'); e.ErrorCode := 0; e.ErrorMessage := 'Error loading Socket interface (' + Stub + ')!'; raise e; end; SockCheck(synsock.WSAStartup(WinsockLevel, FWsaDataOnce)); ExceptCheck; {$ENDIF} end; destructor TBlockSocket.Destroy; var n: integer; p: TSynaOption; begin CloseSocket; {$IFNDEF ONCEWINSOCK} synsock.WSACleanup; DestroySocketInterface; {$ENDIF} for n := FDelayedOptions.Count - 1 downto 0 do begin p := TSynaOption(FDelayedOptions[n]); p.Free; end; FDelayedOptions.Free; inherited Destroy; end; function TBlockSocket.FamilyToAF(f: TSocketFamily): TAddrFamily; begin case f of SF_ip4: Result := AF_INET; SF_ip6: Result := AF_INET6; else Result := AF_UNSPEC; end; end; procedure TBlockSocket.SetDelayedOption(const Value: TSynaOption); var li: TLinger; x: integer; buf: TMemory; {$IFNDEF MSWINDOWS} timeval: TTimeval; {$ENDIF} begin case value.Option of SOT_Linger: begin {$IFDEF CIL} li := TLinger.Create(Value.Enabled, Value.Value div 1000); synsock.SetSockOptObj(FSocket, integer(SOL_SOCKET), integer(SO_LINGER), li); {$ELSE} li.l_onoff := Ord(Value.Enabled); li.l_linger := Value.Value div 1000; buf := @li; synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_LINGER), buf, SizeOf(li)); {$ENDIF} end; SOT_RecvBuff: begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(value.Value); {$ELSE} buf := @Value.Value; {$ENDIF} synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_RCVBUF), buf, SizeOf(Value.Value)); end; SOT_SendBuff: begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(value.Value); {$ELSE} buf := @Value.Value; {$ENDIF} synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_SNDBUF), buf, SizeOf(Value.Value)); end; SOT_NonBlock: begin FNonBlockMode := Value.Enabled; x := Ord(FNonBlockMode); synsock.IoctlSocket(FSocket, FIONBIO, x); end; SOT_RecvTimeout: begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(value.Value); synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_RCVTIMEO), buf, SizeOf(Value.Value)); {$ELSE} {$IFDEF MSWINDOWS} buf := @Value.Value; synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_RCVTIMEO), buf, SizeOf(Value.Value)); {$ELSE} timeval.tv_sec:=Value.Value div 1000; timeval.tv_usec:=(Value.Value mod 1000) * 1000; synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_RCVTIMEO), @timeval, SizeOf(timeval)); {$ENDIF} {$ENDIF} end; SOT_SendTimeout: begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(value.Value); {$ELSE} {$IFDEF MSWINDOWS} buf := @Value.Value; synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_SNDTIMEO), buf, SizeOf(Value.Value)); {$ELSE} timeval.tv_sec:=Value.Value div 1000; timeval.tv_usec:=(Value.Value mod 1000) * 1000; synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_SNDTIMEO), @timeval, SizeOf(timeval)); {$ENDIF} {$ENDIF} end; SOT_Reuse: begin x := Ord(Value.Enabled); {$IFDEF CIL} buf := System.BitConverter.GetBytes(x); {$ELSE} buf := @x; {$ENDIF} synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_REUSEADDR), buf, SizeOf(x)); end; SOT_TTL: begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(value.Value); {$ELSE} buf := @Value.Value; {$ENDIF} if FIP6Used then synsock.SetSockOpt(FSocket, integer(IPPROTO_IPV6), integer(IPV6_UNICAST_HOPS), buf, SizeOf(Value.Value)) else synsock.SetSockOpt(FSocket, integer(IPPROTO_IP), integer(IP_TTL), buf, SizeOf(Value.Value)); end; SOT_Broadcast: begin //#todo1 broadcasty na IP6 x := Ord(Value.Enabled); {$IFDEF CIL} buf := System.BitConverter.GetBytes(x); {$ELSE} buf := @x; {$ENDIF} synsock.SetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_BROADCAST), buf, SizeOf(x)); end; SOT_MulticastTTL: begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(value.Value); {$ELSE} buf := @Value.Value; {$ENDIF} if FIP6Used then synsock.SetSockOpt(FSocket, integer(IPPROTO_IPV6), integer(IPV6_MULTICAST_HOPS), buf, SizeOf(Value.Value)) else synsock.SetSockOpt(FSocket, integer(IPPROTO_IP), integer(IP_MULTICAST_TTL), buf, SizeOf(Value.Value)); end; SOT_MulticastLoop: begin x := Ord(Value.Enabled); {$IFDEF CIL} buf := System.BitConverter.GetBytes(x); {$ELSE} buf := @x; {$ENDIF} if FIP6Used then synsock.SetSockOpt(FSocket, integer(IPPROTO_IPV6), integer(IPV6_MULTICAST_LOOP), buf, SizeOf(x)) else synsock.SetSockOpt(FSocket, integer(IPPROTO_IP), integer(IP_MULTICAST_LOOP), buf, SizeOf(x)); end; end; Value.free; end; procedure TBlockSocket.DelayedOption(const Value: TSynaOption); begin if FSocket = INVALID_SOCKET then begin FDelayedOptions.Insert(0, Value); end else SetDelayedOption(Value); end; procedure TBlockSocket.ProcessDelayedOptions; var n: integer; d: TSynaOption; begin for n := FDelayedOptions.Count - 1 downto 0 do begin d := TSynaOption(FDelayedOptions[n]); SetDelayedOption(d); end; FDelayedOptions.Clear; end; procedure TBlockSocket.SetSin(var Sin: TVarSin; IP, Port: string); var f: TSocketFamily; begin DoStatus(HR_ResolvingBegin, IP + ':' + Port); ResetLastError; //if socket exists, then use their type, else use users selection f := SF_Any; if (FSocket = INVALID_SOCKET) and (FFamily = SF_any) then begin if IsIP(IP) then f := SF_IP4 else if IsIP6(IP) then f := SF_IP6; end else f := FFamily; FLastError := synsock.SetVarSin(sin, ip, port, FamilyToAF(f), GetSocketprotocol, GetSocketType, FPreferIP4); DoStatus(HR_ResolvingEnd, GetSinIP(sin) + ':' + IntTostr(GetSinPort(sin))); end; function TBlockSocket.GetSinIP(Sin: TVarSin): string; begin Result := synsock.GetSinIP(sin); end; function TBlockSocket.GetSinPort(Sin: TVarSin): Integer; begin Result := synsock.GetSinPort(sin); end; procedure TBlockSocket.CreateSocket; var sin: TVarSin; begin //dummy for SF_Any Family mode ResetLastError; if (FFamily <> SF_Any) and (FSocket = INVALID_SOCKET) then begin {$IFDEF CIL} if FFamily = SF_IP6 then sin := TVarSin.Create(IPAddress.Parse('::0'), 0) else sin := TVarSin.Create(IPAddress.Parse('0.0.0.0'), 0); {$ELSE} FillChar(Sin, Sizeof(Sin), 0); if FFamily = SF_IP6 then sin.sin_family := AF_INET6 else sin.sin_family := AF_INET; {$ENDIF} InternalCreateSocket(Sin); end; end; procedure TBlockSocket.CreateSocketByName(const Value: String); var sin: TVarSin; begin ResetLastError; if FSocket = INVALID_SOCKET then begin SetSin(sin, value, '0'); if FLastError = 0 then InternalCreateSocket(Sin); end; end; procedure TBlockSocket.InternalCreateSocket(Sin: TVarSin); begin FStopFlag := False; FRecvCounter := 0; FSendCounter := 0; ResetLastError; if FSocket = INVALID_SOCKET then begin FBuffer := ''; FBinded := False; FIP6Used := Sin.AddressFamily = AF_INET6; FSocket := synsock.Socket(integer(Sin.AddressFamily), GetSocketType, GetSocketProtocol); if FSocket = INVALID_SOCKET then FLastError := synsock.WSAGetLastError; {$IFNDEF CIL} FD_ZERO(FFDSet); FD_SET(FSocket, FFDSet); {$ENDIF} ExceptCheck; if FIP6used then DoStatus(HR_SocketCreate, 'IPv6') else DoStatus(HR_SocketCreate, 'IPv4'); ProcessDelayedOptions; DoCreateSocket; end; end; procedure TBlockSocket.CloseSocket; begin AbortSocket; end; procedure TBlockSocket.AbortSocket; var n: integer; p: TSynaOption; begin if FSocket <> INVALID_SOCKET then synsock.CloseSocket(FSocket); FSocket := INVALID_SOCKET; for n := FDelayedOptions.Count - 1 downto 0 do begin p := TSynaOption(FDelayedOptions[n]); p.Free; end; FDelayedOptions.Clear; FFamily := FFamilySave; DoStatus(HR_SocketClose, ''); end; procedure TBlockSocket.Bind(IP, Port: string); var Sin: TVarSin; begin ResetLastError; if (FSocket <> INVALID_SOCKET) or not((FFamily = SF_ANY) and (IP = cAnyHost) and (Port = cAnyPort)) then begin SetSin(Sin, IP, Port); if FLastError = 0 then begin if FSocket = INVALID_SOCKET then InternalCreateSocket(Sin); SockCheck(synsock.Bind(FSocket, Sin)); GetSinLocal; FBuffer := ''; FBinded := True; end; ExceptCheck; DoStatus(HR_Bind, IP + ':' + Port); end; end; procedure TBlockSocket.Connect(IP, Port: string); var Sin: TVarSin; begin SetSin(Sin, IP, Port); if FLastError = 0 then begin if FSocket = INVALID_SOCKET then InternalCreateSocket(Sin); SockCheck(synsock.Connect(FSocket, Sin)); if FLastError = 0 then GetSins; FBuffer := ''; FLastCR := False; FLastLF := False; end; ExceptCheck; DoStatus(HR_Connect, IP + ':' + Port); end; procedure TBlockSocket.Listen; begin SockCheck(synsock.Listen(FSocket, SOMAXCONN)); GetSins; ExceptCheck; DoStatus(HR_Listen, ''); end; function TBlockSocket.Accept: TSocket; begin Result := synsock.Accept(FSocket, FRemoteSin); /// SockCheck(Result); ExceptCheck; DoStatus(HR_Accept, ''); end; procedure TBlockSocket.GetSinLocal; begin synsock.GetSockName(FSocket, FLocalSin); end; procedure TBlockSocket.GetSinRemote; begin synsock.GetPeerName(FSocket, FRemoteSin); end; procedure TBlockSocket.GetSins; begin GetSinLocal; GetSinRemote; end; procedure TBlockSocket.SetBandwidth(Value: Integer); begin MaxSendBandwidth := Value; MaxRecvBandwidth := Value; end; procedure TBlockSocket.LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord); var x: LongWord; y: LongWord; n: integer; begin if FStopFlag then exit; if MaxB > 0 then begin y := GetTick; if Next > y then begin x := Next - y; if x > 0 then begin DoStatus(HR_Wait, IntToStr(x)); sleep(x mod 250); for n := 1 to x div 250 do if FStopFlag then Break else sleep(250); end; end; Next := GetTick + Trunc((Length / MaxB) * 1000); end; end; function TBlockSocket.TestStopFlag: Boolean; begin DoHeartbeat; Result := FStopFlag; if Result then begin FStopFlag := False; FLastError := WSAECONNABORTED; ExceptCheck; end; end; function TBlockSocket.SendBuffer(Buffer: TMemory; Length: Integer): Integer; {$IFNDEF CIL} var x, y: integer; l, r: integer; p: Pointer; {$ENDIF} begin Result := 0; if TestStopFlag then Exit; DoMonitor(True, Buffer, Length); {$IFDEF CIL} Result := synsock.Send(FSocket, Buffer, Length, 0); {$ELSE} l := Length; x := 0; while x < l do begin y := l - x; if y > FSendMaxChunk then y := FSendMaxChunk; if y > 0 then begin LimitBandwidth(y, FMaxSendBandwidth, FNextsend); p := IncPoint(Buffer, x); r := synsock.Send(FSocket, p, y, MSG_NOSIGNAL); SockCheck(r); if FLastError = WSAEWOULDBLOCK then begin if CanWrite(FNonblockSendTimeout) then begin r := synsock.Send(FSocket, p, y, MSG_NOSIGNAL); SockCheck(r); end else FLastError := WSAETIMEDOUT; end; if FLastError <> 0 then Break; Inc(x, r); Inc(Result, r); Inc(FSendCounter, r); DoStatus(HR_WriteCount, IntToStr(r)); end else break; end; {$ENDIF} ExceptCheck; end; procedure TBlockSocket.SendByte(Data: Byte); {$IFDEF CIL} var buf: TMemory; {$ENDIF} begin {$IFDEF CIL} setlength(buf, 1); buf[0] := Data; SendBuffer(buf, 1); {$ELSE} SendBuffer(@Data, 1); {$ENDIF} end; procedure TBlockSocket.SendString(Data: AnsiString); var buf: TMemory; begin {$IFDEF CIL} buf := BytesOf(Data); {$ELSE} buf := Pointer(data); {$ENDIF} SendBuffer(buf, Length(Data)); end; procedure TBlockSocket.SendInteger(Data: integer); var buf: TMemory; begin {$IFDEF CIL} buf := System.BitConverter.GetBytes(Data); {$ELSE} buf := @Data; {$ENDIF} SendBuffer(buf, SizeOf(Data)); end; procedure TBlockSocket.SendBlock(const Data: AnsiString); var i: integer; begin i := SwapBytes(Length(data)); SendString(Codelongint(i) + Data); end; procedure TBlockSocket.InternalSendStream(const Stream: TStream; WithSize, Indy: boolean); var l: integer; yr: integer; s: AnsiString; b: boolean; {$IFDEF CIL} buf: TMemory; {$ENDIF} begin b := true; l := 0; if WithSize then begin l := Stream.Size - Stream.Position;; if not Indy then l := synsock.HToNL(l); end; repeat {$IFDEF CIL} Setlength(buf, FSendMaxChunk); yr := Stream.read(buf, FSendMaxChunk); if yr > 0 then begin if WithSize and b then begin b := false; SendString(CodeLongInt(l)); end; SendBuffer(buf, yr); if FLastError <> 0 then break; end {$ELSE} Setlength(s, FSendMaxChunk); yr := Stream.read(Pointer(s)^, FSendMaxChunk); if yr > 0 then begin SetLength(s, yr); if WithSize and b then begin b := false; SendString(CodeLongInt(l) + s); end else SendString(s); if FLastError <> 0 then break; end {$ENDIF} until yr <= 0; end; procedure TBlockSocket.SendStreamRaw(const Stream: TStream); begin InternalSendStream(Stream, false, false); end; procedure TBlockSocket.SendStreamIndy(const Stream: TStream); begin InternalSendStream(Stream, true, true); end; procedure TBlockSocket.SendStream(const Stream: TStream); begin InternalSendStream(Stream, true, false); end; function TBlockSocket.RecvBuffer(Buffer: TMemory; Length: Integer): Integer; begin Result := 0; if TestStopFlag then Exit; LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv); // Result := synsock.Recv(FSocket, Buffer^, Length, MSG_NOSIGNAL); Result := synsock.Recv(FSocket, Buffer, Length, MSG_NOSIGNAL); if Result = 0 then FLastError := WSAECONNRESET else SockCheck(Result); ExceptCheck; if Result > 0 then begin Inc(FRecvCounter, Result); DoStatus(HR_ReadCount, IntToStr(Result)); DoMonitor(False, Buffer, Result); DoReadFilter(Buffer, Result); end; end; function TBlockSocket.RecvBufferEx(Buffer: TMemory; Len: Integer; Timeout: Integer): Integer; var s: AnsiString; rl, l: integer; ti: LongWord; {$IFDEF CIL} n: integer; b: TMemory; {$ENDIF} begin ResetLastError; Result := 0; if Len > 0 then begin rl := 0; repeat ti := GetTick; s := RecvPacket(Timeout); l := Length(s); if (rl + l) > Len then l := Len - rl; {$IFDEF CIL} b := BytesOf(s); for n := 0 to l do Buffer[rl + n] := b[n]; {$ELSE} Move(Pointer(s)^, IncPoint(Buffer, rl)^, l); {$ENDIF} rl := rl + l; if FLastError <> 0 then Break; if rl >= Len then Break; if not FInterPacketTimeout then begin Timeout := Timeout - integer(TickDelta(ti, GetTick)); if Timeout <= 0 then begin FLastError := WSAETIMEDOUT; Break; end; end; until False; delete(s, 1, l); FBuffer := s; Result := rl; end; end; function TBlockSocket.RecvBufferStr(Len: Integer; Timeout: Integer): AnsiString; var x: integer; {$IFDEF CIL} buf: Tmemory; {$ENDIF} begin Result := ''; if Len > 0 then begin {$IFDEF CIL} Setlength(Buf, Len); x := RecvBufferEx(buf, Len , Timeout); if FLastError = 0 then begin SetLength(Buf, x); Result := StringOf(buf); end else Result := ''; {$ELSE} Setlength(Result, Len); x := RecvBufferEx(Pointer(Result), Len , Timeout); if FLastError = 0 then SetLength(Result, x) else Result := ''; {$ENDIF} end; end; function TBlockSocket.RecvPacket(Timeout: Integer): AnsiString; var x: integer; {$IFDEF CIL} buf: TMemory; {$ENDIF} begin Result := ''; ResetLastError; if FBuffer <> '' then begin Result := FBuffer; FBuffer := ''; end else begin {$IFDEF MSWINDOWS} //not drain CPU on large downloads... Sleep(0); {$ENDIF} x := WaitingData; if x > 0 then begin {$IFDEF CIL} SetLength(Buf, x); x := RecvBuffer(Buf, x); if x >= 0 then begin SetLength(Buf, x); Result := StringOf(Buf); end; {$ELSE} SetLength(Result, x); x := RecvBuffer(Pointer(Result), x); if x >= 0 then SetLength(Result, x); {$ENDIF} end else begin if CanRead(Timeout) then begin x := WaitingData; if x = 0 then FLastError := WSAECONNRESET; if x > 0 then begin {$IFDEF CIL} SetLength(Buf, x); x := RecvBuffer(Buf, x); if x >= 0 then begin SetLength(Buf, x); result := StringOf(Buf); end; {$ELSE} SetLength(Result, x); x := RecvBuffer(Pointer(Result), x); if x >= 0 then SetLength(Result, x); {$ENDIF} end; end else FLastError := WSAETIMEDOUT; end; end; if FConvertLineEnd and (Result <> '') then begin if FLastCR and (Result[1] = LF) then Delete(Result, 1, 1); if FLastLF and (Result[1] = CR) then Delete(Result, 1, 1); FLastCR := False; FLastLF := False; end; ExceptCheck; end; function TBlockSocket.RecvByte(Timeout: Integer): Byte; begin Result := 0; ResetLastError; if FBuffer = '' then FBuffer := RecvPacket(Timeout); if (FLastError = 0) and (FBuffer <> '') then begin Result := Ord(FBuffer[1]); Delete(FBuffer, 1, 1); end; ExceptCheck; end; function TBlockSocket.RecvInteger(Timeout: Integer): Integer; var s: AnsiString; begin Result := 0; s := RecvBufferStr(4, Timeout); if FLastError = 0 then Result := (ord(s[1]) + ord(s[2]) * 256) + (ord(s[3]) + ord(s[4]) * 256) * 65536; end; function TBlockSocket.RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString; var x: Integer; s: AnsiString; l: Integer; CorCRLF: Boolean; t: AnsiString; tl: integer; ti: LongWord; begin ResetLastError; Result := ''; l := Length(Terminator); if l = 0 then Exit; tl := l; CorCRLF := FConvertLineEnd and (Terminator = CRLF); s := ''; x := 0; repeat //get rest of FBuffer or incomming new data... ti := GetTick; s := s + RecvPacket(Timeout); if FLastError <> 0 then Break; x := 0; if Length(s) > 0 then if CorCRLF then begin t := ''; x := PosCRLF(s, t); tl := Length(t); if t = CR then FLastCR := True; if t = LF then FLastLF := True; end else begin x := pos(Terminator, s); tl := l; end; if (FMaxLineLength <> 0) and (Length(s) > FMaxLineLength) then begin FLastError := WSAENOBUFS; Break; end; if x > 0 then Break; if not FInterPacketTimeout then begin Timeout := Timeout - integer(TickDelta(ti, GetTick)); if Timeout <= 0 then begin FLastError := WSAETIMEDOUT; Break; end; end; until False; if x > 0 then begin Result := Copy(s, 1, x - 1); Delete(s, 1, x + tl - 1); end; FBuffer := s; ExceptCheck; end; function TBlockSocket.RecvString(Timeout: Integer): AnsiString; var s: AnsiString; begin Result := ''; s := RecvTerminated(Timeout, CRLF); if FLastError = 0 then Result := s; end; function TBlockSocket.RecvBlock(Timeout: Integer): AnsiString; var x: integer; begin Result := ''; x := RecvInteger(Timeout); if FLastError = 0 then Result := RecvBufferStr(x, Timeout); end; procedure TBlockSocket.RecvStreamRaw(const Stream: TStream; Timeout: Integer); var s: AnsiString; begin repeat s := RecvPacket(Timeout); if FLastError = 0 then WriteStrToStream(Stream, s); until FLastError <> 0; end; procedure TBlockSocket.RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer); var s: AnsiString; n: integer; {$IFDEF CIL} buf: TMemory; {$ENDIF} begin for n := 1 to (Size div FSendMaxChunk) do begin {$IFDEF CIL} SetLength(buf, FSendMaxChunk); RecvBufferEx(buf, FSendMaxChunk, Timeout); if FLastError <> 0 then Exit; Stream.Write(buf, FSendMaxChunk); {$ELSE} s := RecvBufferStr(FSendMaxChunk, Timeout); if FLastError <> 0 then Exit; WriteStrToStream(Stream, s); {$ENDIF} end; n := Size mod FSendMaxChunk; if n > 0 then begin {$IFDEF CIL} SetLength(buf, n); RecvBufferEx(buf, n, Timeout); if FLastError <> 0 then Exit; Stream.Write(buf, n); {$ELSE} s := RecvBufferStr(n, Timeout); if FLastError <> 0 then Exit; WriteStrToStream(Stream, s); {$ENDIF} end; end; procedure TBlockSocket.RecvStreamIndy(const Stream: TStream; Timeout: Integer); var x: integer; begin x := RecvInteger(Timeout); x := synsock.NToHL(x); if FLastError = 0 then RecvStreamSize(Stream, Timeout, x); end; procedure TBlockSocket.RecvStream(const Stream: TStream; Timeout: Integer); var x: integer; begin x := RecvInteger(Timeout); if FLastError = 0 then RecvStreamSize(Stream, Timeout, x); end; function TBlockSocket.PeekBuffer(Buffer: TMemory; Length: Integer): Integer; begin {$IFNDEF CIL} // Result := synsock.Recv(FSocket, Buffer^, Length, MSG_PEEK + MSG_NOSIGNAL); Result := synsock.Recv(FSocket, Buffer, Length, MSG_PEEK + MSG_NOSIGNAL); SockCheck(Result); ExceptCheck; {$ENDIF} end; function TBlockSocket.PeekByte(Timeout: Integer): Byte; var s: string; begin {$IFNDEF CIL} Result := 0; if CanRead(Timeout) then begin SetLength(s, 1); PeekBuffer(Pointer(s), 1); if s <> '' then Result := Ord(s[1]); end else FLastError := WSAETIMEDOUT; ExceptCheck; {$ENDIF} end; procedure TBlockSocket.ResetLastError; begin FLastError := 0; FLastErrorDesc := ''; end; function TBlockSocket.SockCheck(SockResult: Integer): Integer; begin ResetLastError; if SockResult = integer(SOCKET_ERROR) then begin FLastError := synsock.WSAGetLastError; FLastErrorDesc := GetErrorDescEx; end; Result := FLastError; end; procedure TBlockSocket.ExceptCheck; var e: ESynapseError; begin FLastErrorDesc := GetErrorDescEx; if (LastError <> 0) and (LastError <> WSAEINPROGRESS) and (LastError <> WSAEWOULDBLOCK) then begin DoStatus(HR_Error, IntToStr(FLastError) + ',' + FLastErrorDesc); if FRaiseExcept then begin e := ESynapseError.Create(Format('Synapse TCP/IP Socket error %d: %s', [FLastError, FLastErrorDesc])); e.ErrorCode := FLastError; e.ErrorMessage := FLastErrorDesc; raise e; end; end; end; function TBlockSocket.WaitingData: Integer; var x: Integer; begin Result := 0; if synsock.IoctlSocket(FSocket, FIONREAD, x) = 0 then Result := x; if Result > c64k then Result := c64k; end; function TBlockSocket.WaitingDataEx: Integer; begin if FBuffer <> '' then Result := Length(FBuffer) else Result := WaitingData; end; procedure TBlockSocket.Purge; begin Sleep(1); try while (Length(FBuffer) > 0) or (WaitingData > 0) do begin RecvPacket(0); if FLastError <> 0 then break; end; except on exception do; end; ResetLastError; end; procedure TBlockSocket.SetLinger(Enable: Boolean; Linger: Integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_Linger; d.Enabled := Enable; d.Value := Linger; DelayedOption(d); end; function TBlockSocket.LocalName: string; begin Result := synsock.GetHostName; if Result = '' then Result := '127.0.0.1'; end; procedure TBlockSocket.ResolveNameToIP(Name: string; const IPList: TStrings); begin IPList.Clear; synsock.ResolveNameToIP(Name, FamilyToAF(FFamily), GetSocketprotocol, GetSocketType, IPList); if IPList.Count = 0 then IPList.Add(cAnyHost); end; function TBlockSocket.ResolveName(Name: string): string; var l: TStringList; begin l := TStringList.Create; try ResolveNameToIP(Name, l); Result := l[0]; finally l.Free; end; end; function TBlockSocket.ResolvePort(Port: string): Word; begin Result := synsock.ResolvePort(Port, FamilyToAF(FFamily), GetSocketProtocol, GetSocketType); end; function TBlockSocket.ResolveIPToName(IP: string): string; begin if not IsIP(IP) or not IsIp6(IP) then IP := ResolveName(IP); Result := synsock.ResolveIPToName(IP, FamilyToAF(FFamily), GetSocketProtocol, GetSocketType); end; procedure TBlockSocket.SetRemoteSin(IP, Port: string); begin SetSin(FRemoteSin, IP, Port); end; function TBlockSocket.GetLocalSinIP: string; begin Result := GetSinIP(FLocalSin); end; function TBlockSocket.GetRemoteSinIP: string; begin Result := GetSinIP(FRemoteSin); end; function TBlockSocket.GetLocalSinPort: Integer; begin Result := GetSinPort(FLocalSin); end; function TBlockSocket.GetRemoteSinPort: Integer; begin Result := GetSinPort(FRemoteSin); end; function TBlockSocket.InternalCanRead(Timeout: Integer): Boolean; {$IFDEF CIL} begin Result := FSocket.Poll(Timeout * 1000, SelectMode.SelectRead); {$ELSE} var TimeVal: PTimeVal; TimeV: TTimeVal; x: Integer; FDSet: TFDSet; begin TimeV.tv_usec := (Timeout mod 1000) * 1000; TimeV.tv_sec := Timeout div 1000; TimeVal := @TimeV; if Timeout = -1 then TimeVal := nil; FDSet := FFdSet; x := synsock.Select(FSocket + 1, @FDSet, nil, nil, TimeVal); SockCheck(x); if FLastError <> 0 then x := 0; Result := x > 0; {$ENDIF} end; function TBlockSocket.CanRead(Timeout: Integer): Boolean; var ti, tr: Integer; n: integer; begin if (FHeartbeatRate <> 0) and (Timeout <> -1) then begin ti := Timeout div FHeartbeatRate; tr := Timeout mod FHeartbeatRate; end else begin ti := 0; tr := Timeout; end; Result := InternalCanRead(tr); if not Result then for n := 0 to ti do begin DoHeartbeat; if FStopFlag then begin Result := False; FStopFlag := False; Break; end; Result := InternalCanRead(FHeartbeatRate); if Result then break; end; ExceptCheck; if Result then DoStatus(HR_CanRead, ''); end; function TBlockSocket.CanWrite(Timeout: Integer): Boolean; {$IFDEF CIL} begin Result := FSocket.Poll(Timeout * 1000, SelectMode.SelectWrite); {$ELSE} var TimeVal: PTimeVal; TimeV: TTimeVal; x: Integer; FDSet: TFDSet; begin TimeV.tv_usec := (Timeout mod 1000) * 1000; TimeV.tv_sec := Timeout div 1000; TimeVal := @TimeV; if Timeout = -1 then TimeVal := nil; FDSet := FFdSet; x := synsock.Select(FSocket + 1, nil, @FDSet, nil, TimeVal); SockCheck(x); if FLastError <> 0 then x := 0; Result := x > 0; {$ENDIF} ExceptCheck; if Result then DoStatus(HR_CanWrite, ''); end; function TBlockSocket.CanReadEx(Timeout: Integer): Boolean; begin if FBuffer <> '' then Result := True else Result := CanRead(Timeout); end; function TBlockSocket.SendBufferTo(Buffer: TMemory; Length: Integer): Integer; begin Result := 0; if TestStopFlag then Exit; DoMonitor(True, Buffer, Length); LimitBandwidth(Length, FMaxSendBandwidth, FNextsend); Result := synsock.SendTo(FSocket, Buffer, Length, MSG_NOSIGNAL, FRemoteSin); SockCheck(Result); ExceptCheck; Inc(FSendCounter, Result); DoStatus(HR_WriteCount, IntToStr(Result)); end; function TBlockSocket.RecvBufferFrom(Buffer: TMemory; Length: Integer): Integer; begin Result := 0; if TestStopFlag then Exit; LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv); Result := synsock.RecvFrom(FSocket, Buffer, Length, MSG_NOSIGNAL, FRemoteSin); SockCheck(Result); ExceptCheck; Inc(FRecvCounter, Result); DoStatus(HR_ReadCount, IntToStr(Result)); DoMonitor(False, Buffer, Result); end; function TBlockSocket.GetSizeRecvBuffer: Integer; var l: Integer; {$IFDEF CIL} buf: TMemory; {$ENDIF} begin {$IFDEF CIL} setlength(buf, 4); SockCheck(synsock.GetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_RCVBUF), buf, l)); Result := System.BitConverter.ToInt32(buf,0); {$ELSE} l := SizeOf(Result); SockCheck(synsock.GetSockOpt(FSocket, SOL_SOCKET, SO_RCVBUF, @Result, l)); if FLastError <> 0 then Result := 1024; ExceptCheck; {$ENDIF} end; procedure TBlockSocket.SetSizeRecvBuffer(Size: Integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_RecvBuff; d.Value := Size; DelayedOption(d); end; function TBlockSocket.GetSizeSendBuffer: Integer; var l: Integer; {$IFDEF CIL} buf: TMemory; {$ENDIF} begin {$IFDEF CIL} setlength(buf, 4); SockCheck(synsock.GetSockOpt(FSocket, integer(SOL_SOCKET), integer(SO_SNDBUF), buf, l)); Result := System.BitConverter.ToInt32(buf,0); {$ELSE} l := SizeOf(Result); SockCheck(synsock.GetSockOpt(FSocket, SOL_SOCKET, SO_SNDBUF, @Result, l)); if FLastError <> 0 then Result := 1024; ExceptCheck; {$ENDIF} end; procedure TBlockSocket.SetSizeSendBuffer(Size: Integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_SendBuff; d.Value := Size; DelayedOption(d); end; procedure TBlockSocket.SetNonBlockMode(Value: Boolean); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_nonblock; d.Enabled := Value; DelayedOption(d); end; procedure TBlockSocket.SetTimeout(Timeout: Integer); begin SetSendTimeout(Timeout); SetRecvTimeout(Timeout); end; procedure TBlockSocket.SetSendTimeout(Timeout: Integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_sendtimeout; d.Value := Timeout; DelayedOption(d); end; procedure TBlockSocket.SetRecvTimeout(Timeout: Integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_recvtimeout; d.Value := Timeout; DelayedOption(d); end; {$IFNDEF CIL} function TBlockSocket.GroupCanRead(const SocketList: TList; Timeout: Integer; const CanReadList: TList): boolean; var FDSet: TFDSet; TimeVal: PTimeVal; TimeV: TTimeVal; x, n: Integer; Max: Integer; begin TimeV.tv_usec := (Timeout mod 1000) * 1000; TimeV.tv_sec := Timeout div 1000; TimeVal := @TimeV; if Timeout = -1 then TimeVal := nil; FD_ZERO(FDSet); Max := 0; for n := 0 to SocketList.Count - 1 do if TObject(SocketList.Items[n]) is TBlockSocket then begin if TBlockSocket(SocketList.Items[n]).Socket > Max then Max := TBlockSocket(SocketList.Items[n]).Socket; FD_SET(TBlockSocket(SocketList.Items[n]).Socket, FDSet); end; x := synsock.Select(Max + 1, @FDSet, nil, nil, TimeVal); SockCheck(x); ExceptCheck; if FLastError <> 0 then x := 0; Result := x > 0; CanReadList.Clear; if Result then for n := 0 to SocketList.Count - 1 do if TObject(SocketList.Items[n]) is TBlockSocket then if FD_ISSET(TBlockSocket(SocketList.Items[n]).Socket, FDSet) then CanReadList.Add(TBlockSocket(SocketList.Items[n])); end; {$ENDIF} procedure TBlockSocket.EnableReuse(Value: Boolean); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_reuse; d.Enabled := Value; DelayedOption(d); end; procedure TBlockSocket.SetTTL(TTL: integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_TTL; d.Value := TTL; DelayedOption(d); end; function TBlockSocket.GetTTL:integer; var l: Integer; begin {$IFNDEF CIL} l := SizeOf(Result); if FIP6Used then synsock.GetSockOpt(FSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, @Result, l) else synsock.GetSockOpt(FSocket, IPPROTO_IP, IP_TTL, @Result, l); {$ENDIF} end; procedure TBlockSocket.SetFamily(Value: TSocketFamily); begin FFamily := Value; FFamilySave := Value; end; procedure TBlockSocket.SetSocket(Value: TSocket); begin FRecvCounter := 0; FSendCounter := 0; FSocket := Value; {$IFNDEF CIL} FD_ZERO(FFDSet); FD_SET(FSocket, FFDSet); {$ENDIF} GetSins; FIP6Used := FRemoteSin.AddressFamily = AF_INET6; end; function TBlockSocket.GetWsaData: TWSAData; begin Result := WsaDataOnce; end; function TBlockSocket.GetSocketType: integer; begin Result := 0; end; function TBlockSocket.GetSocketProtocol: integer; begin Result := integer(IPPROTO_IP); end; procedure TBlockSocket.DoStatus(Reason: THookSocketReason; const Value: string); begin if assigned(OnStatus) then OnStatus(Self, Reason, Value); end; procedure TBlockSocket.DoReadFilter(Buffer: TMemory; var Len: Integer); var s: AnsiString; begin if assigned(OnReadFilter) then if Len > 0 then begin {$IFDEF CIL} s := StringOf(Buffer); {$ELSE} SetLength(s, Len); Move(Buffer^, Pointer(s)^, Len); {$ENDIF} OnReadFilter(Self, s); if Length(s) > Len then SetLength(s, Len); Len := Length(s); {$IFDEF CIL} Buffer := BytesOf(s); {$ELSE} Move(Pointer(s)^, Buffer^, Len); {$ENDIF} end; end; procedure TBlockSocket.DoCreateSocket; begin if assigned(OnCreateSocket) then OnCreateSocket(Self); end; procedure TBlockSocket.DoMonitor(Writing: Boolean; const Buffer: TMemory; Len: Integer); begin if assigned(OnMonitor) then begin OnMonitor(Self, Writing, Buffer, Len); end; end; procedure TBlockSocket.DoHeartbeat; begin if assigned(OnHeartbeat) and (FHeartbeatRate <> 0) then begin OnHeartbeat(Self); end; end; function TBlockSocket.GetErrorDescEx: string; begin Result := GetErrorDesc(FLastError); end; class function TBlockSocket.GetErrorDesc(ErrorCode: Integer): string; begin {$IFDEF CIL} if ErrorCode = 0 then Result := '' else begin Result := WSAGetLastErrorDesc; if Result = '' then Result := 'Other Winsock error (' + IntToStr(ErrorCode) + ')'; end; {$ELSE} case ErrorCode of 0: Result := ''; WSAEINTR: {10004} Result := 'Interrupted system call'; WSAEBADF: {10009} Result := 'Bad file number'; WSAEACCES: {10013} Result := 'Permission denied'; WSAEFAULT: {10014} Result := 'Bad address'; WSAEINVAL: {10022} Result := 'Invalid argument'; WSAEMFILE: {10024} Result := 'Too many open files'; WSAEWOULDBLOCK: {10035} Result := 'Operation would block'; WSAEINPROGRESS: {10036} Result := 'Operation now in progress'; WSAEALREADY: {10037} Result := 'Operation already in progress'; WSAENOTSOCK: {10038} Result := 'Socket operation on nonsocket'; WSAEDESTADDRREQ: {10039} Result := 'Destination address required'; WSAEMSGSIZE: {10040} Result := 'Message too long'; WSAEPROTOTYPE: {10041} Result := 'Protocol wrong type for Socket'; WSAENOPROTOOPT: {10042} Result := 'Protocol not available'; WSAEPROTONOSUPPORT: {10043} Result := 'Protocol not supported'; WSAESOCKTNOSUPPORT: {10044} Result := 'Socket not supported'; WSAEOPNOTSUPP: {10045} Result := 'Operation not supported on Socket'; WSAEPFNOSUPPORT: {10046} Result := 'Protocol family not supported'; WSAEAFNOSUPPORT: {10047} Result := 'Address family not supported'; WSAEADDRINUSE: {10048} Result := 'Address already in use'; WSAEADDRNOTAVAIL: {10049} Result := 'Can''t assign requested address'; WSAENETDOWN: {10050} Result := 'Network is down'; WSAENETUNREACH: {10051} Result := 'Network is unreachable'; WSAENETRESET: {10052} Result := 'Network dropped connection on reset'; WSAECONNABORTED: {10053} Result := 'Software caused connection abort'; WSAECONNRESET: {10054} Result := 'Connection reset by peer'; WSAENOBUFS: {10055} Result := 'No Buffer space available'; WSAEISCONN: {10056} Result := 'Socket is already connected'; WSAENOTCONN: {10057} Result := 'Socket is not connected'; WSAESHUTDOWN: {10058} Result := 'Can''t send after Socket shutdown'; WSAETOOMANYREFS: {10059} Result := 'Too many references:can''t splice'; WSAETIMEDOUT: {10060} Result := 'Connection timed out'; WSAECONNREFUSED: {10061} Result := 'Connection refused'; WSAELOOP: {10062} Result := 'Too many levels of symbolic links'; WSAENAMETOOLONG: {10063} Result := 'File name is too long'; WSAEHOSTDOWN: {10064} Result := 'Host is down'; WSAEHOSTUNREACH: {10065} Result := 'No route to host'; WSAENOTEMPTY: {10066} Result := 'Directory is not empty'; WSAEPROCLIM: {10067} Result := 'Too many processes'; WSAEUSERS: {10068} Result := 'Too many users'; WSAEDQUOT: {10069} Result := 'Disk quota exceeded'; WSAESTALE: {10070} Result := 'Stale NFS file handle'; WSAEREMOTE: {10071} Result := 'Too many levels of remote in path'; WSASYSNOTREADY: {10091} Result := 'Network subsystem is unusable'; WSAVERNOTSUPPORTED: {10092} Result := 'Winsock DLL cannot support this application'; WSANOTINITIALISED: {10093} Result := 'Winsock not initialized'; WSAEDISCON: {10101} Result := 'Disconnect'; WSAHOST_NOT_FOUND: {11001} Result := 'Host not found'; WSATRY_AGAIN: {11002} Result := 'Non authoritative - host not found'; WSANO_RECOVERY: {11003} Result := 'Non recoverable error'; WSANO_DATA: {11004} Result := 'Valid name, no data record of requested type' else Result := 'Other Winsock error (' + IntToStr(ErrorCode) + ')'; end; {$ENDIF} end; {======================================================================} constructor TSocksBlockSocket.Create; begin inherited Create; FSocksIP:= ''; FSocksPort:= '1080'; FSocksTimeout:= 60000; FSocksUsername:= ''; FSocksPassword:= ''; FUsingSocks := False; FSocksResolver := True; FSocksLastError := 0; FSocksResponseIP := ''; FSocksResponsePort := ''; FSocksLocalIP := ''; FSocksLocalPort := ''; FSocksRemoteIP := ''; FSocksRemotePort := ''; FBypassFlag := False; FSocksType := ST_Socks5; end; function TSocksBlockSocket.SocksOpen: boolean; var Buf: AnsiString; n: integer; begin Result := False; FUsingSocks := False; if FSocksType <> ST_Socks5 then begin FUsingSocks := True; Result := True; end else begin FBypassFlag := True; try if FSocksUsername = '' then Buf := #5 + #1 + #0 else Buf := #5 + #2 + #2 +#0; SendString(Buf); Buf := RecvBufferStr(2, FSocksTimeout); if Length(Buf) < 2 then Exit; if Buf[1] <> #5 then Exit; n := Ord(Buf[2]); case n of 0: //not need authorisation ; 2: begin Buf := #1 + AnsiChar(Length(FSocksUsername)) + FSocksUsername + AnsiChar(Length(FSocksPassword)) + FSocksPassword; SendString(Buf); Buf := RecvBufferStr(2, FSocksTimeout); if Length(Buf) < 2 then Exit; if Buf[2] <> #0 then Exit; end; else //other authorisation is not supported! Exit; end; FUsingSocks := True; Result := True; finally FBypassFlag := False; end; end; end; function TSocksBlockSocket.SocksRequest(Cmd: Byte; const IP, Port: string): Boolean; var Buf: AnsiString; begin FBypassFlag := True; try if FSocksType <> ST_Socks5 then Buf := #4 + AnsiChar(Cmd) + SocksCode(IP, Port) else Buf := #5 + AnsiChar(Cmd) + #0 + SocksCode(IP, Port); SendString(Buf); Result := FLastError = 0; finally FBypassFlag := False; end; end; function TSocksBlockSocket.SocksResponse: Boolean; var Buf, s: AnsiString; x: integer; begin Result := False; FBypassFlag := True; try FSocksResponseIP := ''; FSocksResponsePort := ''; FSocksLastError := -1; if FSocksType <> ST_Socks5 then begin Buf := RecvBufferStr(8, FSocksTimeout); if FLastError <> 0 then Exit; if Buf[1] <> #0 then Exit; FSocksLastError := Ord(Buf[2]); end else begin Buf := RecvBufferStr(4, FSocksTimeout); if FLastError <> 0 then Exit; if Buf[1] <> #5 then Exit; case Ord(Buf[4]) of 1: s := RecvBufferStr(4, FSocksTimeout); 3: begin x := RecvByte(FSocksTimeout); if FLastError <> 0 then Exit; s := AnsiChar(x) + RecvBufferStr(x, FSocksTimeout); end; 4: s := RecvBufferStr(16, FSocksTimeout); else Exit; end; Buf := Buf + s + RecvBufferStr(2, FSocksTimeout); if FLastError <> 0 then Exit; FSocksLastError := Ord(Buf[2]); end; if ((FSocksLastError <> 0) and (FSocksLastError <> 90)) then Exit; SocksDecode(Buf); Result := True; finally FBypassFlag := False; end; end; function TSocksBlockSocket.SocksCode(IP, Port: string): Ansistring; var ip6: TIp6Bytes; n: integer; begin if FSocksType <> ST_Socks5 then begin Result := CodeInt(ResolvePort(Port)); if not FSocksResolver then IP := ResolveName(IP); if IsIP(IP) then begin Result := Result + IPToID(IP); Result := Result + FSocksUsername + #0; end else begin Result := Result + IPToID('0.0.0.1'); Result := Result + FSocksUsername + #0; Result := Result + IP + #0; end; end else begin if not FSocksResolver then IP := ResolveName(IP); if IsIP(IP) then Result := #1 + IPToID(IP) else if IsIP6(IP) then begin ip6 := StrToIP6(IP); Result := #4; for n := 0 to 15 do Result := Result + AnsiChar(ip6[n]); end else Result := #3 + AnsiChar(Length(IP)) + IP; Result := Result + CodeInt(ResolvePort(Port)); end; end; function TSocksBlockSocket.SocksDecode(Value: Ansistring): integer; var Atyp: Byte; y, n: integer; w: Word; ip6: TIp6Bytes; begin FSocksResponsePort := '0'; Result := 0; if FSocksType <> ST_Socks5 then begin if Length(Value) < 8 then Exit; Result := 3; w := DecodeInt(Value, Result); FSocksResponsePort := IntToStr(w); FSocksResponseIP := Format('%d.%d.%d.%d', [Ord(Value[5]), Ord(Value[6]), Ord(Value[7]), Ord(Value[8])]); Result := 9; end else begin if Length(Value) < 4 then Exit; Atyp := Ord(Value[4]); Result := 5; case Atyp of 1: begin if Length(Value) < 10 then Exit; FSocksResponseIP := Format('%d.%d.%d.%d', [Ord(Value[5]), Ord(Value[6]), Ord(Value[7]), Ord(Value[8])]); Result := 9; end; 3: begin y := Ord(Value[5]); if Length(Value) < (5 + y + 2) then Exit; for n := 6 to 6 + y - 1 do FSocksResponseIP := FSocksResponseIP + Value[n]; Result := 5 + y + 1; end; 4: begin if Length(Value) < 22 then Exit; for n := 0 to 15 do ip6[n] := ord(Value[n + 5]); FSocksResponseIP := IP6ToStr(ip6); Result := 21; end; else Exit; end; w := DecodeInt(Value, Result); FSocksResponsePort := IntToStr(w); Result := Result + 2; end; end; {======================================================================} procedure TDgramBlockSocket.Connect(IP, Port: string); begin SetRemoteSin(IP, Port); InternalCreateSocket(FRemoteSin); FBuffer := ''; DoStatus(HR_Connect, IP + ':' + Port); end; function TDgramBlockSocket.RecvBuffer(Buffer: TMemory; Length: Integer): Integer; begin Result := RecvBufferFrom(Buffer, Length); end; function TDgramBlockSocket.SendBuffer(Buffer: TMemory; Length: Integer): Integer; begin Result := SendBufferTo(Buffer, Length); end; {======================================================================} destructor TUDPBlockSocket.Destroy; begin if Assigned(FSocksControlSock) then FSocksControlSock.Free; inherited; end; procedure TUDPBlockSocket.EnableBroadcast(Value: Boolean); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_Broadcast; d.Enabled := Value; DelayedOption(d); end; function TUDPBlockSocket.UdpAssociation: Boolean; var b: Boolean; begin Result := True; FUsingSocks := False; if FSocksIP <> '' then begin Result := False; if not Assigned(FSocksControlSock) then FSocksControlSock := TTCPBlockSocket.Create; FSocksControlSock.CloseSocket; FSocksControlSock.CreateSocketByName(FSocksIP); FSocksControlSock.Connect(FSocksIP, FSocksPort); if FSocksControlSock.LastError <> 0 then Exit; // if not assigned local port, assign it! if not FBinded then Bind(cAnyHost, cAnyPort); //open control TCP connection to SOCKS FSocksControlSock.FSocksUsername := FSocksUsername; FSocksControlSock.FSocksPassword := FSocksPassword; b := FSocksControlSock.SocksOpen; if b then b := FSocksControlSock.SocksRequest(3, GetLocalSinIP, IntToStr(GetLocalSinPort)); if b then b := FSocksControlSock.SocksResponse; if not b and (FLastError = 0) then FLastError := WSANO_RECOVERY; FUsingSocks :=FSocksControlSock.UsingSocks; FSocksRemoteIP := FSocksControlSock.FSocksResponseIP; FSocksRemotePort := FSocksControlSock.FSocksResponsePort; Result := b and (FLastError = 0); end; end; function TUDPBlockSocket.SendBufferTo(Buffer: TMemory; Length: Integer): Integer; var SIp: string; SPort: integer; Buf: Ansistring; begin Result := 0; FUsingSocks := False; if (FSocksIP <> '') and (not UdpAssociation) then FLastError := WSANO_RECOVERY else begin if FUsingSocks then begin {$IFNDEF CIL} Sip := GetRemoteSinIp; SPort := GetRemoteSinPort; SetRemoteSin(FSocksRemoteIP, FSocksRemotePort); SetLength(Buf,Length); Move(Buffer^, Pointer(Buf)^, Length); Buf := #0 + #0 + #0 + SocksCode(Sip, IntToStr(SPort)) + Buf; Result := inherited SendBufferTo(Pointer(Buf), System.Length(buf)); SetRemoteSin(Sip, IntToStr(SPort)); {$ENDIF} end else Result := inherited SendBufferTo(Buffer, Length); end; end; function TUDPBlockSocket.RecvBufferFrom(Buffer: TMemory; Length: Integer): Integer; var Buf: Ansistring; x: integer; begin Result := inherited RecvBufferFrom(Buffer, Length); if FUsingSocks then begin {$IFNDEF CIL} SetLength(Buf, Result); Move(Buffer^, Pointer(Buf)^, Result); x := SocksDecode(Buf); Result := Result - x + 1; Buf := Copy(Buf, x, Result); Move(Pointer(Buf)^, Buffer^, Result); SetRemoteSin(FSocksResponseIP, FSocksResponsePort); {$ENDIF} end; end; {$IFNDEF CIL} procedure TUDPBlockSocket.AddMulticast(MCastIP: string); var Multicast: TIP_mreq; Multicast6: TIPv6_mreq; n: integer; ip6: Tip6bytes; begin if FIP6Used then begin ip6 := StrToIp6(MCastIP); for n := 0 to 15 do Multicast6.ipv6mr_multiaddr.u6_addr8[n] := Ip6[n]; Multicast6.ipv6mr_interface := 0; SockCheck(synsock.SetSockOpt(FSocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, PAnsiChar(@Multicast6), SizeOf(Multicast6))); end else begin Multicast.imr_multiaddr.S_addr := swapbytes(strtoip(MCastIP)); Multicast.imr_interface.S_addr := INADDR_ANY; SockCheck(synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, PAnsiChar(@Multicast), SizeOf(Multicast))); end; ExceptCheck; end; procedure TUDPBlockSocket.DropMulticast(MCastIP: string); var Multicast: TIP_mreq; Multicast6: TIPv6_mreq; n: integer; ip6: Tip6bytes; begin if FIP6Used then begin ip6 := StrToIp6(MCastIP); for n := 0 to 15 do Multicast6.ipv6mr_multiaddr.u6_addr8[n] := Ip6[n]; Multicast6.ipv6mr_interface := 0; SockCheck(synsock.SetSockOpt(FSocket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, PAnsiChar(@Multicast6), SizeOf(Multicast6))); end else begin Multicast.imr_multiaddr.S_addr := swapbytes(strtoip(MCastIP)); Multicast.imr_interface.S_addr := INADDR_ANY; SockCheck(synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_DROP_MEMBERSHIP, PAnsiChar(@Multicast), SizeOf(Multicast))); end; ExceptCheck; end; {$ENDIF} procedure TUDPBlockSocket.SetMulticastTTL(TTL: integer); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_MulticastTTL; d.Value := TTL; DelayedOption(d); end; function TUDPBlockSocket.GetMulticastTTL:integer; var l: Integer; begin {$IFNDEF CIL} l := SizeOf(Result); if FIP6Used then synsock.GetSockOpt(FSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, @Result, l) else synsock.GetSockOpt(FSocket, IPPROTO_IP, IP_MULTICAST_TTL, @Result, l); {$ENDIF} end; procedure TUDPBlockSocket.EnableMulticastLoop(Value: Boolean); var d: TSynaOption; begin d := TSynaOption.Create; d.Option := SOT_MulticastLoop; d.Enabled := Value; DelayedOption(d); end; function TUDPBlockSocket.GetSocketType: integer; begin Result := integer(SOCK_DGRAM); end; function TUDPBlockSocket.GetSocketProtocol: integer; begin Result := integer(IPPROTO_UDP); end; {======================================================================} constructor TTCPBlockSocket.CreateWithSSL(SSLPlugin: TSSLClass); begin inherited Create; FSSL := SSLPlugin.Create(self); FHTTPTunnelIP := ''; FHTTPTunnelPort := ''; FHTTPTunnel := False; FHTTPTunnelRemoteIP := ''; FHTTPTunnelRemotePort := ''; FHTTPTunnelUser := ''; FHTTPTunnelPass := ''; FHTTPTunnelTimeout := 30000; end; constructor TTCPBlockSocket.Create; begin CreateWithSSL(SSLImplementation); end; destructor TTCPBlockSocket.Destroy; begin inherited Destroy; FSSL.Free; end; function TTCPBlockSocket.GetErrorDescEx: string; begin Result := inherited GetErrorDescEx; if (FLastError = WSASYSNOTREADY) and (self.SSL.LastError <> 0) then begin Result := self.SSL.LastErrorDesc; end; end; procedure TTCPBlockSocket.CloseSocket; begin if FSSL.SSLEnabled then FSSL.Shutdown; if (FSocket <> INVALID_SOCKET) and (FLastError = 0) then begin Synsock.Shutdown(FSocket, 1); Purge; end; inherited CloseSocket; end; procedure TTCPBlockSocket.DoAfterConnect; begin if assigned(OnAfterConnect) then begin OnAfterConnect(Self); end; end; function TTCPBlockSocket.WaitingData: Integer; begin Result := 0; if FSSL.SSLEnabled and (FSocket <> INVALID_SOCKET) then Result := FSSL.WaitingData; if Result = 0 then Result := inherited WaitingData; end; procedure TTCPBlockSocket.Listen; var b: Boolean; Sip,SPort: string; begin if FSocksIP = '' then begin inherited Listen; end else begin Sip := GetLocalSinIP; if Sip = cAnyHost then Sip := LocalName; SPort := IntToStr(GetLocalSinPort); inherited Connect(FSocksIP, FSocksPort); b := SocksOpen; if b then b := SocksRequest(2, Sip, SPort); if b then b := SocksResponse; if not b and (FLastError = 0) then FLastError := WSANO_RECOVERY; FSocksLocalIP := FSocksResponseIP; if FSocksLocalIP = cAnyHost then FSocksLocalIP := FSocksIP; FSocksLocalPort := FSocksResponsePort; FSocksRemoteIP := ''; FSocksRemotePort := ''; ExceptCheck; DoStatus(HR_Listen, ''); end; end; function TTCPBlockSocket.Accept: TSocket; begin if FUsingSocks then begin if not SocksResponse and (FLastError = 0) then FLastError := WSANO_RECOVERY; FSocksRemoteIP := FSocksResponseIP; FSocksRemotePort := FSocksResponsePort; Result := FSocket; ExceptCheck; DoStatus(HR_Accept, ''); end else begin result := inherited Accept; end; end; procedure TTCPBlockSocket.Connect(IP, Port: string); begin if FSocksIP <> '' then SocksDoConnect(IP, Port) else if FHTTPTunnelIP <> '' then HTTPTunnelDoConnect(IP, Port) else inherited Connect(IP, Port); if FLasterror = 0 then DoAfterConnect; end; procedure TTCPBlockSocket.SocksDoConnect(IP, Port: string); var b: Boolean; begin inherited Connect(FSocksIP, FSocksPort); if FLastError = 0 then begin b := SocksOpen; if b then b := SocksRequest(1, IP, Port); if b then b := SocksResponse; if not b and (FLastError = 0) then FLastError := WSASYSNOTREADY; FSocksLocalIP := FSocksResponseIP; FSocksLocalPort := FSocksResponsePort; FSocksRemoteIP := IP; FSocksRemotePort := Port; end; ExceptCheck; DoStatus(HR_Connect, IP + ':' + Port); end; procedure TTCPBlockSocket.HTTPTunnelDoConnect(IP, Port: string); //bugfixed by Mike Green (mgreen@emixode.com) var s: string; begin Port := IntToStr(ResolvePort(Port)); inherited Connect(FHTTPTunnelIP, FHTTPTunnelPort); if FLastError <> 0 then Exit; FHTTPTunnel := False; if IsIP6(IP) then IP := '[' + IP + ']'; SendString('CONNECT ' + IP + ':' + Port + ' HTTP/1.0' + CRLF); if FHTTPTunnelUser <> '' then Sendstring('Proxy-Authorization: Basic ' + EncodeBase64(FHTTPTunnelUser + ':' + FHTTPTunnelPass) + CRLF); SendString(CRLF); repeat s := RecvTerminated(FHTTPTunnelTimeout, #$0a); if FLastError <> 0 then Break; if (Pos('HTTP/', s) = 1) and (Length(s) > 11) then FHTTPTunnel := s[10] = '2'; until (s = '') or (s = #$0d); if (FLasterror = 0) and not FHTTPTunnel then FLastError := WSASYSNOTREADY; FHTTPTunnelRemoteIP := IP; FHTTPTunnelRemotePort := Port; ExceptCheck; end; procedure TTCPBlockSocket.SSLDoConnect; begin ResetLastError; if not FSSL.Connect then FLastError := WSASYSNOTREADY; ExceptCheck; end; procedure TTCPBlockSocket.SSLDoShutdown; begin ResetLastError; FSSL.BiShutdown; end; function TTCPBlockSocket.GetLocalSinIP: string; begin if FUsingSocks then Result := FSocksLocalIP else Result := inherited GetLocalSinIP; end; function TTCPBlockSocket.GetRemoteSinIP: string; begin if FUsingSocks then Result := FSocksRemoteIP else if FHTTPTunnel then Result := FHTTPTunnelRemoteIP else Result := inherited GetRemoteSinIP; end; function TTCPBlockSocket.GetLocalSinPort: Integer; begin if FUsingSocks then Result := StrToIntDef(FSocksLocalPort, 0) else Result := inherited GetLocalSinPort; end; function TTCPBlockSocket.GetRemoteSinPort: Integer; begin if FUsingSocks then Result := ResolvePort(FSocksRemotePort) else if FHTTPTunnel then Result := StrToIntDef(FHTTPTunnelRemotePort, 0) else Result := inherited GetRemoteSinPort; end; function TTCPBlockSocket.RecvBuffer(Buffer: TMemory; Len: Integer): Integer; begin if FSSL.SSLEnabled then begin Result := 0; if TestStopFlag then Exit; ResetLastError; LimitBandwidth(Len, FMaxRecvBandwidth, FNextRecv); Result := FSSL.RecvBuffer(Buffer, Len); if FSSL.LastError <> 0 then FLastError := WSASYSNOTREADY; ExceptCheck; Inc(FRecvCounter, Result); DoStatus(HR_ReadCount, IntToStr(Result)); DoMonitor(False, Buffer, Result); DoReadFilter(Buffer, Result); end else Result := inherited RecvBuffer(Buffer, Len); end; function TTCPBlockSocket.SendBuffer(Buffer: TMemory; Length: Integer): Integer; var x, y: integer; l, r: integer; {$IFNDEF CIL} p: Pointer; {$ENDIF} begin if FSSL.SSLEnabled then begin Result := 0; if TestStopFlag then Exit; ResetLastError; DoMonitor(True, Buffer, Length); {$IFDEF CIL} Result := FSSL.SendBuffer(Buffer, Length); if FSSL.LastError <> 0 then FLastError := WSASYSNOTREADY; Inc(FSendCounter, Result); DoStatus(HR_WriteCount, IntToStr(Result)); {$ELSE} l := Length; x := 0; while x < l do begin y := l - x; if y > FSendMaxChunk then y := FSendMaxChunk; if y > 0 then begin LimitBandwidth(y, FMaxSendBandwidth, FNextsend); p := IncPoint(Buffer, x); r := FSSL.SendBuffer(p, y); if FSSL.LastError <> 0 then FLastError := WSASYSNOTREADY; if Flasterror <> 0 then Break; Inc(x, r); Inc(Result, r); Inc(FSendCounter, r); DoStatus(HR_WriteCount, IntToStr(r)); end else break; end; {$ENDIF} ExceptCheck; end else Result := inherited SendBuffer(Buffer, Length); end; function TTCPBlockSocket.SSLAcceptConnection: Boolean; begin ResetLastError; if not FSSL.Accept then FLastError := WSASYSNOTREADY; ExceptCheck; Result := FLastError = 0; end; function TTCPBlockSocket.GetSocketType: integer; begin Result := integer(SOCK_STREAM); end; function TTCPBlockSocket.GetSocketProtocol: integer; begin Result := integer(IPPROTO_TCP); end; {======================================================================} function TICMPBlockSocket.GetSocketType: integer; begin Result := integer(SOCK_RAW); end; function TICMPBlockSocket.GetSocketProtocol: integer; begin if FIP6Used then Result := integer(IPPROTO_ICMPV6) else Result := integer(IPPROTO_ICMP); end; {======================================================================} function TRAWBlockSocket.GetSocketType: integer; begin Result := integer(SOCK_RAW); end; function TRAWBlockSocket.GetSocketProtocol: integer; begin Result := integer(IPPROTO_RAW); end; {======================================================================} function TPGMmessageBlockSocket.GetSocketType: integer; begin Result := integer(SOCK_RDM); end; function TPGMmessageBlockSocket.GetSocketProtocol: integer; begin Result := integer(IPPROTO_RM); end; {======================================================================} function TPGMstreamBlockSocket.GetSocketType: integer; begin Result := integer(SOCK_STREAM); end; function TPGMstreamBlockSocket.GetSocketProtocol: integer; begin Result := integer(IPPROTO_RM); end; {======================================================================} constructor TSynaClient.Create; begin inherited Create; FIPInterface := cAnyHost; FTargetHost := cLocalhost; FTargetPort := cAnyPort; FTimeout := 5000; FUsername := ''; FPassword := ''; end; {======================================================================} constructor TCustomSSL.Create(const Value: TTCPBlockSocket); begin inherited Create; FSocket := Value; FSSLEnabled := False; FUsername := ''; FPassword := ''; FLastError := 0; FLastErrorDesc := ''; FVerifyCert := False; FSSLType := LT_all; FKeyPassword := ''; FCiphers := ''; FCertificateFile := ''; FPrivateKeyFile := ''; FCertCAFile := ''; FCertCA := ''; FTrustCertificate := ''; FTrustCertificateFile := ''; FCertificate := ''; FPrivateKey := ''; FPFX := ''; FPFXfile := ''; FSSHChannelType := ''; FSSHChannelArg1 := ''; FSSHChannelArg2 := ''; end; procedure TCustomSSL.Assign(const Value: TCustomSSL); begin FUsername := Value.Username; FPassword := Value.Password; FVerifyCert := Value.VerifyCert; FSSLType := Value.SSLType; FKeyPassword := Value.KeyPassword; FCiphers := Value.Ciphers; FCertificateFile := Value.CertificateFile; FPrivateKeyFile := Value.PrivateKeyFile; FCertCAFile := Value.CertCAFile; FCertCA := Value.CertCA; FTrustCertificate := Value.TrustCertificate; FTrustCertificateFile := Value.TrustCertificateFile; FCertificate := Value.Certificate; FPrivateKey := Value.PrivateKey; FPFX := Value.PFX; FPFXfile := Value.PFXfile; end; procedure TCustomSSL.ReturnError; begin FLastError := -1; FLastErrorDesc := 'SSL/TLS support is not compiled!'; end; function TCustomSSL.LibVersion: String; begin Result := ''; end; function TCustomSSL.LibName: String; begin Result := ''; end; function TCustomSSL.CreateSelfSignedCert(Host: string): Boolean; begin Result := False; end; function TCustomSSL.Connect: boolean; begin ReturnError; Result := False; end; function TCustomSSL.Accept: boolean; begin ReturnError; Result := False; end; function TCustomSSL.Shutdown: boolean; begin ReturnError; Result := False; end; function TCustomSSL.BiShutdown: boolean; begin ReturnError; Result := False; end; function TCustomSSL.SendBuffer(Buffer: TMemory; Len: Integer): Integer; begin ReturnError; Result := integer(SOCKET_ERROR); end; function TCustomSSL.RecvBuffer(Buffer: TMemory; Len: Integer): Integer; begin ReturnError; Result := integer(SOCKET_ERROR); end; function TCustomSSL.WaitingData: Integer; begin ReturnError; Result := 0; end; function TCustomSSL.GetSSLVersion: string; begin Result := ''; end; function TCustomSSL.GetPeerSubject: string; begin Result := ''; end; function TCustomSSL.GetPeerName: string; begin Result := ''; end; function TCustomSSL.GetPeerIssuer: string; begin Result := ''; end; function TCustomSSL.GetPeerFingerprint: string; begin Result := ''; end; function TCustomSSL.GetCertInfo: string; begin Result := ''; end; function TCustomSSL.GetCipherName: string; begin Result := ''; end; function TCustomSSL.GetCipherBits: integer; begin Result := 0; end; function TCustomSSL.GetCipherAlgBits: integer; begin Result := 0; end; function TCustomSSL.GetVerifyCert: integer; begin Result := 1; end; {======================================================================} function TSSLNone.LibVersion: String; begin Result := 'Without SSL support'; end; function TSSLNone.LibName: String; begin Result := 'ssl_none'; end; {======================================================================} {$IFDEF ONCEWINSOCK} initialization begin if not InitSocketInterface(DLLStackName) then begin e := ESynapseError.Create('Error loading Socket interface (' + DLLStackName + ')!'); e.ErrorCode := 0; e.ErrorMessage := 'Error loading Socket interface (' + DLLStackName + ')!'; raise e; end; synsock.WSAStartup(WinsockLevel, WsaDataOnce); end; {$ENDIF} finalization begin {$IFDEF ONCEWINSOCK} synsock.WSACleanup; DestroySocketInterface; {$ENDIF} end; end. TransGUI/synapse/source/lib/pop3send.pas0000644000000000000000000003542311366572451017221 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.006.002 | |==============================================================================| | Content: POP3 client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2001-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(POP3 protocol client) Used RFC: RFC-1734, RFC-1939, RFC-2195, RFC-2449, RFC-2595 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$M+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit pop3send; interface uses SysUtils, Classes, blcksock, synautil, synacode; const cPop3Protocol = '110'; type {:The three types of possible authorization methods for "logging in" to a POP3 server.} TPOP3AuthType = (POP3AuthAll, POP3AuthLogin, POP3AuthAPOP); {:@abstract(Implementation of POP3 client protocol.) Note: Are you missing properties for setting Username and Password? Look to parent @link(TSynaClient) object! Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TPOP3Send = class(TSynaClient) private FSock: TTCPBlockSocket; FResultCode: Integer; FResultString: string; FFullResult: TStringList; FStatCount: Integer; FStatSize: Integer; FListSize: Integer; FTimeStamp: string; FAuthType: TPOP3AuthType; FPOP3cap: TStringList; FAutoTLS: Boolean; FFullSSL: Boolean; function ReadResult(Full: Boolean): Integer; function Connect: Boolean; function AuthLogin: Boolean; function AuthApop: Boolean; public constructor Create; destructor Destroy; override; {:You can call any custom by this method. Call Command without trailing CRLF. If MultiLine parameter is @true, multilined response are expected. Result is @true on sucess.} function CustomCommand(const Command: string; MultiLine: Boolean): boolean; {:Call CAPA command for get POP3 server capabilites. note: not all servers support this command!} function Capability: Boolean; {:Connect to remote POP3 host. If all OK, result is @true.} function Login: Boolean; {:Disconnects from POP3 server.} function Logout: Boolean; {:Send RSET command. If all OK, result is @true.} function Reset: Boolean; {:Send NOOP command. If all OK, result is @true.} function NoOp: Boolean; {:Send STAT command and fill @link(StatCount) and @link(StatSize) property. If all OK, result is @true.} function Stat: Boolean; {:Send LIST command. If Value is 0, LIST is for all messages. After successful operation is listing in FullResult. If all OK, result is @True.} function List(Value: Integer): Boolean; {:Send RETR command. After successful operation dowloaded message in @link(FullResult). If all OK, result is @true.} function Retr(Value: Integer): Boolean; {:Send RETR command. After successful operation dowloaded message in @link(Stream). If all OK, result is @true.} function RetrStream(Value: Integer; Stream: TStream): Boolean; {:Send DELE command for delete specified message. If all OK, result is @true.} function Dele(Value: Integer): Boolean; {:Send TOP command. After successful operation dowloaded headers of message and maxlines count of message in @link(FullResult). If all OK, result is @true.} function Top(Value, Maxlines: Integer): Boolean; {:Send UIDL command. If Value is 0, UIDL is for all messages. After successful operation is listing in FullResult. If all OK, result is @True.} function Uidl(Value: Integer): Boolean; {:Call STLS command for upgrade connection to SSL/TLS mode.} function StartTLS: Boolean; {:Try to find given capabily in capabilty string returned from POP3 server by CAPA command.} function FindCap(const Value: string): string; published {:Result code of last POP3 operation. 0 - error, 1 - OK.} property ResultCode: Integer read FResultCode; {:Result string of last POP3 operation.} property ResultString: string read FResultString; {:Stringlist with full lines returned as result of POP3 operation. I.e. if operation is LIST, this property is filled by list of messages. If operation is RETR, this property have downloaded message.} property FullResult: TStringList read FFullResult; {:After STAT command is there count of messages in inbox.} property StatCount: Integer read FStatCount; {:After STAT command is there size of all messages in inbox.} property StatSize: Integer read FStatSize; {:After LIST 0 command size of all messages on server, After LIST x size of message x on server} property ListSize: Integer read FListSize; {:If server support this, after comnnect is in this property timestamp of remote server.} property TimeStamp: string read FTimeStamp; {:Type of authorisation for login to POP3 server. Dafault is autodetect one of possible authorisation. Autodetect do this: If remote POP3 server support APOP, try login by APOP method. If APOP is not supported, or if APOP login failed, try classic USER+PASS login method.} property AuthType: TPOP3AuthType read FAuthType Write FAuthType; {:If is set to @true, then upgrade to SSL/TLS mode if remote server support it.} property AutoTLS: Boolean read FAutoTLS Write FAutoTLS; {:SSL/TLS mode is used from first contact to server. Servers with full SSL/TLS mode usualy using non-standard TCP port!} property FullSSL: Boolean read FFullSSL Write FFullSSL; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; end; implementation constructor TPOP3Send.Create; begin inherited Create; FFullResult := TStringList.Create; FPOP3cap := TStringList.Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.ConvertLineEnd := true; FTimeout := 60000; FTargetPort := cPop3Protocol; FStatCount := 0; FStatSize := 0; FListSize := 0; FAuthType := POP3AuthAll; FAutoTLS := False; FFullSSL := False; end; destructor TPOP3Send.Destroy; begin FSock.Free; FPOP3cap.Free; FullResult.Free; inherited Destroy; end; function TPOP3Send.ReadResult(Full: Boolean): Integer; var s: AnsiString; begin Result := 0; FFullResult.Clear; s := FSock.RecvString(FTimeout); if Pos('+OK', s) = 1 then Result := 1; FResultString := s; if Full and (Result = 1) then repeat s := FSock.RecvString(FTimeout); if s = '.' then Break; if s <> '' then if s[1] = '.' then Delete(s, 1, 1); FFullResult.Add(s); until FSock.LastError <> 0; if not Full and (Result = 1) then FFullResult.Add(SeparateRight(FResultString, ' ')); if FSock.LastError <> 0 then Result := 0; FResultCode := Result; end; function TPOP3Send.CustomCommand(const Command: string; MultiLine: Boolean): boolean; begin FSock.SendString(Command + CRLF); Result := ReadResult(MultiLine) <> 0; end; function TPOP3Send.AuthLogin: Boolean; begin Result := False; if not CustomCommand('USER ' + FUserName, False) then exit; Result := CustomCommand('PASS ' + FPassword, False) end; function TPOP3Send.AuthAPOP: Boolean; var s: string; begin s := StrToHex(MD5(FTimeStamp + FPassWord)); Result := CustomCommand('APOP ' + FUserName + ' ' + s, False); end; function TPOP3Send.Connect: Boolean; begin // Do not call this function! It is calling by LOGIN method! FStatCount := 0; FStatSize := 0; FSock.CloseSocket; FSock.LineBuffer := ''; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError = 0 then FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError = 0 then if FFullSSL then FSock.SSLDoConnect; Result := FSock.LastError = 0; end; function TPOP3Send.Capability: Boolean; begin FPOP3cap.Clear; Result := CustomCommand('CAPA', True); if Result then FPOP3cap.AddStrings(FFullResult); end; function TPOP3Send.Login: Boolean; var s, s1: string; begin Result := False; FTimeStamp := ''; if not Connect then Exit; if ReadResult(False) <> 1 then Exit; s := SeparateRight(FResultString, '<'); if s <> FResultString then begin s1 := Trim(SeparateLeft(s, '>')); if s1 <> s then FTimeStamp := '<' + s1 + '>'; end; Result := False; if Capability then if FAutoTLS and (Findcap('STLS') <> '') then if StartTLS then Capability else begin Result := False; Exit; end; if (FTimeStamp <> '') and not (FAuthType = POP3AuthLogin) then begin Result := AuthApop; if not Result then begin if not Connect then Exit; if ReadResult(False) <> 1 then Exit; end; end; if not Result and not (FAuthType = POP3AuthAPOP) then Result := AuthLogin; end; function TPOP3Send.Logout: Boolean; begin Result := CustomCommand('QUIT', False); FSock.CloseSocket; end; function TPOP3Send.Reset: Boolean; begin Result := CustomCommand('RSET', False); end; function TPOP3Send.NoOp: Boolean; begin Result := CustomCommand('NOOP', False); end; function TPOP3Send.Stat: Boolean; var s: string; begin Result := CustomCommand('STAT', False); if Result then begin s := SeparateRight(ResultString, '+OK '); FStatCount := StrToIntDef(Trim(SeparateLeft(s, ' ')), 0); FStatSize := StrToIntDef(Trim(SeparateRight(s, ' ')), 0); end; end; function TPOP3Send.List(Value: Integer): Boolean; var s: string; n: integer; begin if Value = 0 then s := 'LIST' else s := 'LIST ' + IntToStr(Value); Result := CustomCommand(s, Value = 0); FListSize := 0; if Result then if Value <> 0 then begin s := SeparateRight(ResultString, '+OK '); FListSize := StrToIntDef(SeparateLeft(SeparateRight(s, ' '), ' '), 0); end else for n := 0 to FFullResult.Count - 1 do FListSize := FListSize + StrToIntDef(SeparateLeft(SeparateRight(s, ' '), ' '), 0); end; function TPOP3Send.Retr(Value: Integer): Boolean; begin Result := CustomCommand('RETR ' + IntToStr(Value), True); end; //based on code by Miha Vrhovnik function TPOP3Send.RetrStream(Value: Integer; Stream: TStream): Boolean; var s: string; begin Result := False; FFullResult.Clear; Stream.Size := 0; FSock.SendString('RETR ' + IntToStr(Value) + CRLF); s := FSock.RecvString(FTimeout); if Pos('+OK', s) = 1 then Result := True; FResultString := s; if Result then begin repeat s := FSock.RecvString(FTimeout); if s = '.' then Break; if s <> '' then begin if s[1] = '.' then Delete(s, 1, 1); end; WriteStrToStream(Stream, s); WriteStrToStream(Stream, CRLF); until FSock.LastError <> 0; end; if Result then FResultCode := 1 else FResultCode := 0; end; function TPOP3Send.Dele(Value: Integer): Boolean; begin Result := CustomCommand('DELE ' + IntToStr(Value), False); end; function TPOP3Send.Top(Value, Maxlines: Integer): Boolean; begin Result := CustomCommand('TOP ' + IntToStr(Value) + ' ' + IntToStr(Maxlines), True); end; function TPOP3Send.Uidl(Value: Integer): Boolean; var s: string; begin if Value = 0 then s := 'UIDL' else s := 'UIDL ' + IntToStr(Value); Result := CustomCommand(s, Value = 0); end; function TPOP3Send.StartTLS: Boolean; begin Result := False; if CustomCommand('STLS', False) then begin Fsock.SSLDoConnect; Result := FSock.LastError = 0; end; end; function TPOP3Send.FindCap(const Value: string): string; var n: Integer; s: string; begin s := UpperCase(Value); Result := ''; for n := 0 to FPOP3cap.Count - 1 do if Pos(s, UpperCase(FPOP3cap[n])) = 1 then begin Result := FPOP3cap[n]; Break; end; end; end. TransGUI/synapse/source/lib/sntpsend.pas0000644000000000000000000003050311366572451017316 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 003.000.003 | |==============================================================================| | Content: SNTP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Patrick Chevalley | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract( NTP and SNTP client) Used RFC: RFC-1305, RFC-2030 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} unit sntpsend; interface uses SysUtils, synsock, blcksock, synautil; const cNtpProtocol = '123'; type {:@abstract(Record containing the NTP packet.)} TNtp = packed record mode: Byte; stratum: Byte; poll: Byte; Precision: Byte; RootDelay: Longint; RootDisperson: Longint; RefID: Longint; Ref1: Longint; Ref2: Longint; Org1: Longint; Org2: Longint; Rcv1: Longint; Rcv2: Longint; Xmit1: Longint; Xmit2: Longint; end; {:@abstract(Implementation of NTP and SNTP client protocol), include time synchronisation. It can send NTP or SNTP time queries, or it can receive NTP broadcasts too. Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TSNTPSend = class(TSynaClient) private FNTPReply: TNtp; FNTPTime: TDateTime; FNTPOffset: double; FNTPDelay: double; FMaxSyncDiff: double; FSyncTime: Boolean; FSock: TUDPBlockSocket; FBuffer: AnsiString; FLi, FVn, Fmode : byte; function StrToNTP(const Value: AnsiString): TNtp; function NTPtoStr(const Value: Tntp): AnsiString; procedure ClearNTP(var Value: Tntp); public constructor Create; destructor Destroy; override; {:Decode 128 bit timestamp used in NTP packet to TDateTime type.} function DecodeTs(Nsec, Nfrac: Longint): TDateTime; {:Decode TDateTime type to 128 bit timestamp used in NTP packet.} procedure EncodeTs(dt: TDateTime; var Nsec, Nfrac: Longint); {:Send request to @link(TSynaClient.TargetHost) and wait for reply. If all is OK, then result is @true and @link(NTPReply) and @link(NTPTime) are valid.} function GetSNTP: Boolean; {:Send request to @link(TSynaClient.TargetHost) and wait for reply. If all is OK, then result is @true and @link(NTPReply) and @link(NTPTime) are valid. Result time is after all needed corrections.} function GetNTP: Boolean; {:Wait for broadcast NTP packet. If all OK, result is @true and @link(NTPReply) and @link(NTPTime) are valid.} function GetBroadcastNTP: Boolean; {:Holds last received NTP packet.} property NTPReply: TNtp read FNTPReply; published {:Date and time of remote NTP or SNTP server. (UTC time!!!)} property NTPTime: TDateTime read FNTPTime; {:Offset between your computer and remote NTP or SNTP server.} property NTPOffset: Double read FNTPOffset; {:Delay between your computer and remote NTP or SNTP server.} property NTPDelay: Double read FNTPDelay; {:Define allowed maximum difference between your time and remote time for synchronising time. If difference is bigger, your system time is not changed!} property MaxSyncDiff: double read FMaxSyncDiff write FMaxSyncDiff; {:If @true, after successfull getting time is local computer clock synchronised to given time. For synchronising time you must have proper rights! (Usually Administrator)} property SyncTime: Boolean read FSyncTime write FSyncTime; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TUDPBlockSocket read FSock; end; implementation constructor TSNTPSend.Create; begin inherited Create; FSock := TUDPBlockSocket.Create; FSock.Owner := self; FTimeout := 5000; FTargetPort := cNtpProtocol; FMaxSyncDiff := 3600; FSyncTime := False; end; destructor TSNTPSend.Destroy; begin FSock.Free; inherited Destroy; end; function TSNTPSend.StrToNTP(const Value: AnsiString): TNtp; begin if length(FBuffer) >= SizeOf(Result) then begin Result.mode := ord(Value[1]); Result.stratum := ord(Value[2]); Result.poll := ord(Value[3]); Result.Precision := ord(Value[4]); Result.RootDelay := DecodeLongInt(value, 5); Result.RootDisperson := DecodeLongInt(value, 9); Result.RefID := DecodeLongInt(value, 13); Result.Ref1 := DecodeLongInt(value, 17); Result.Ref2 := DecodeLongInt(value, 21); Result.Org1 := DecodeLongInt(value, 25); Result.Org2 := DecodeLongInt(value, 29); Result.Rcv1 := DecodeLongInt(value, 33); Result.Rcv2 := DecodeLongInt(value, 37); Result.Xmit1 := DecodeLongInt(value, 41); Result.Xmit2 := DecodeLongInt(value, 45); end; end; function TSNTPSend.NTPtoStr(const Value: Tntp): AnsiString; begin SetLength(Result, 4); Result[1] := AnsiChar(Value.mode); Result[2] := AnsiChar(Value.stratum); Result[3] := AnsiChar(Value.poll); Result[4] := AnsiChar(Value.precision); Result := Result + CodeLongInt(Value.RootDelay); Result := Result + CodeLongInt(Value.RootDisperson); Result := Result + CodeLongInt(Value.RefID); Result := Result + CodeLongInt(Value.Ref1); Result := Result + CodeLongInt(Value.Ref2); Result := Result + CodeLongInt(Value.Org1); Result := Result + CodeLongInt(Value.Org2); Result := Result + CodeLongInt(Value.Rcv1); Result := Result + CodeLongInt(Value.Rcv2); Result := Result + CodeLongInt(Value.Xmit1); Result := Result + CodeLongInt(Value.Xmit2); end; procedure TSNTPSend.ClearNTP(var Value: Tntp); begin Value.mode := 0; Value.stratum := 0; Value.poll := 0; Value.Precision := 0; Value.RootDelay := 0; Value.RootDisperson := 0; Value.RefID := 0; Value.Ref1 := 0; Value.Ref2 := 0; Value.Org1 := 0; Value.Org2 := 0; Value.Rcv1 := 0; Value.Rcv2 := 0; Value.Xmit1 := 0; Value.Xmit2 := 0; end; function TSNTPSend.DecodeTs(Nsec, Nfrac: Longint): TDateTime; const maxi = 4294967295.0; var d, d1: Double; begin d := Nsec; if d < 0 then d := maxi + d + 1; d1 := Nfrac; if d1 < 0 then d1 := maxi + d1 + 1; d1 := d1 / maxi; d1 := Trunc(d1 * 10000) / 10000; Result := (d + d1) / 86400; Result := Result + 2; end; procedure TSNTPSend.EncodeTs(dt: TDateTime; var Nsec, Nfrac: Longint); const maxi = 4294967295.0; maxilongint = 2147483647; var d, d1: Double; begin d := (dt - 2) * 86400; d1 := frac(d); if d > maxilongint then d := d - maxi - 1; d := trunc(d); d1 := Trunc(d1 * 10000) / 10000; d1 := d1 * maxi; if d1 > maxilongint then d1 := d1 - maxi - 1; Nsec:=trunc(d); Nfrac:=trunc(d1); end; function TSNTPSend.GetBroadcastNTP: Boolean; var x: Integer; begin Result := False; FSock.Bind(FIPInterface, FTargetPort); FBuffer := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then begin x := Length(FBuffer); if (FTargetHost = '0.0.0.0') or (FSock.GetRemoteSinIP = FSock.ResolveName(FTargetHost)) then if x >= SizeOf(NTPReply) then begin FNTPReply := StrToNTP(FBuffer); FNTPTime := DecodeTs(NTPReply.Xmit1, NTPReply.Xmit2); if FSyncTime and ((abs(FNTPTime - GetUTTime) * 86400) <= FMaxSyncDiff) then SetUTTime(FNTPTime); Result := True; end; end; end; function TSNTPSend.GetSNTP: Boolean; var q: TNtp; x: Integer; begin Result := False; FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); ClearNtp(q); q.mode := $1B; FBuffer := NTPtoStr(q); FSock.SendString(FBuffer); FBuffer := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then begin x := Length(FBuffer); if x >= SizeOf(NTPReply) then begin FNTPReply := StrToNTP(FBuffer); FNTPTime := DecodeTs(NTPReply.Xmit1, NTPReply.Xmit2); if FSyncTime and ((abs(FNTPTime - GetUTTime) * 86400) <= FMaxSyncDiff) then SetUTTime(FNTPTime); Result := True; end; end; end; function TSNTPSend.GetNTP: Boolean; var q: TNtp; x: Integer; t1, t2, t3, t4 : TDateTime; begin Result := False; FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); ClearNtp(q); q.mode := $1B; t1 := GetUTTime; EncodeTs(t1, q.org1, q.org2); FBuffer := NTPtoStr(q); FSock.SendString(FBuffer); FBuffer := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then begin x := Length(FBuffer); t4 := GetUTTime; if x >= SizeOf(NTPReply) then begin FNTPReply := StrToNTP(FBuffer); FLi := (NTPReply.mode and $C0) shr 6; FVn := (NTPReply.mode and $38) shr 3; Fmode := NTPReply.mode and $07; if (Fli < 3) and (Fmode = 4) and (NTPReply.stratum >= 1) and (NTPReply.stratum <= 15) and (NTPReply.Rcv1 <> 0) and (NTPReply.Xmit1 <> 0) then begin t2 := DecodeTs(NTPReply.Rcv1, NTPReply.Rcv2); t3 := DecodeTs(NTPReply.Xmit1, NTPReply.Xmit2); FNTPDelay := (T4 - T1) - (T2 - T3); FNTPTime := t3 + FNTPDelay / 2; FNTPOffset := (((T2 - T1) + (T3 - T4)) / 2) * 86400; FNTPDelay := FNTPDelay * 86400; if FSyncTime and ((abs(FNTPTime - t1) * 86400) <= FMaxSyncDiff) then SetUTTime(FNTPTime); Result := True; end else result:=false; end; end; end; end. TransGUI/synapse/source/lib/synamisc.pas0000644000000000000000000003062211466757142017313 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.003.001 | |==============================================================================| | Content: misc. procedures and functions | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 2002-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Misc. network based utilities)} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} //Kylix does not known UNIX define {$IFDEF LINUX} {$IFNDEF UNIX} {$DEFINE UNIX} {$ENDIF} {$ENDIF} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit synamisc; interface {$IFDEF VER125} {$DEFINE BCB} {$ENDIF} {$IFDEF BCB} {$ObjExportAll On} {$HPPEMIT '#pragma comment( lib , "wininet.lib" )'} {$ENDIF} uses synautil, blcksock, SysUtils, Classes {$IFDEF UNIX} {$IFNDEF FPC} , Libc {$ENDIF} {$ELSE} , Windows {$ENDIF} ; Type {:@abstract(This record contains information about proxy setting.)} TProxySetting = record Host: string; Port: string; Bypass: string; end; {:By this function you can turn-on computer on network, if this computer supporting Wake-on-lan feature. You need MAC number (network card indentifier) of computer for turn-on. You can also assign target IP addres. If you not specify it, then is used broadcast for delivery magic wake-on packet. However broadcasts workinh only on your local network. When you need to wake-up computer on another network, you must specify any existing IP addres on same network segment as targeting computer.} procedure WakeOnLan(MAC, IP: string); {:Autodetect current DNS servers used by system. If is defined more then one DNS server, then result is comma-delimited.} function GetDNS: string; {:Autodetect InternetExplorer proxy setting for given protocol. This function working only on windows!} function GetIEProxy(protocol: string): TProxySetting; {:Return all known IP addresses on local system. Addresses are divided by comma.} function GetLocalIPs: string; implementation {==============================================================================} procedure WakeOnLan(MAC, IP: string); var sock: TUDPBlockSocket; HexMac: Ansistring; data: Ansistring; n: integer; b: Byte; begin if MAC <> '' then begin MAC := ReplaceString(MAC, '-', ''); MAC := ReplaceString(MAC, ':', ''); if Length(MAC) < 12 then Exit; HexMac := ''; for n := 0 to 5 do begin b := StrToIntDef('$' + MAC[n * 2 + 1] + MAC[n * 2 + 2], 0); HexMac := HexMac + char(b); end; if IP = '' then IP := cBroadcast; sock := TUDPBlockSocket.Create; try sock.CreateSocket; sock.EnableBroadcast(true); sock.Connect(IP, '9'); data := #$FF + #$FF + #$FF + #$FF + #$FF + #$FF; for n := 1 to 16 do data := data + HexMac; sock.SendString(data); finally sock.Free; end; end; end; {==============================================================================} {$IFNDEF UNIX} function GetDNSbyIpHlp: string; type PTIP_ADDRESS_STRING = ^TIP_ADDRESS_STRING; TIP_ADDRESS_STRING = array[0..15] of Ansichar; PTIP_ADDR_STRING = ^TIP_ADDR_STRING; TIP_ADDR_STRING = packed record Next: PTIP_ADDR_STRING; IpAddress: TIP_ADDRESS_STRING; IpMask: TIP_ADDRESS_STRING; Context: DWORD; end; PTFixedInfo = ^TFixedInfo; TFixedInfo = packed record HostName: array[1..128 + 4] of Ansichar; DomainName: array[1..128 + 4] of Ansichar; CurrentDNSServer: PTIP_ADDR_STRING; DNSServerList: TIP_ADDR_STRING; NodeType: UINT; ScopeID: array[1..256 + 4] of Ansichar; EnableRouting: UINT; EnableProxy: UINT; EnableDNS: UINT; end; const IpHlpDLL = 'IPHLPAPI.DLL'; var IpHlpModule: THandle; FixedInfo: PTFixedInfo; InfoSize: Longint; PDnsServer: PTIP_ADDR_STRING; err: integer; GetNetworkParams: function(FixedInfo: PTFixedInfo; pOutPutLen: PULONG): DWORD; stdcall; begin InfoSize := 0; Result := '...'; IpHlpModule := LoadLibrary(IpHlpDLL); if IpHlpModule = 0 then exit; try GetNetworkParams := GetProcAddress(IpHlpModule,PAnsiChar(AnsiString('GetNetworkParams'))); if @GetNetworkParams = nil then Exit; err := GetNetworkParams(Nil, @InfoSize); if err <> ERROR_BUFFER_OVERFLOW then Exit; Result := ''; GetMem (FixedInfo, InfoSize); try err := GetNetworkParams(FixedInfo, @InfoSize); if err <> ERROR_SUCCESS then exit; with FixedInfo^ do begin Result := DnsServerList.IpAddress; PDnsServer := DnsServerList.Next; while PDnsServer <> Nil do begin if Result <> '' then Result := Result + ','; Result := Result + PDnsServer^.IPAddress; PDnsServer := PDnsServer.Next; end; end; finally FreeMem(FixedInfo); end; finally FreeLibrary(IpHlpModule); end; end; function ReadReg(SubKey, Vn: PChar): string; var OpenKey: HKEY; DataType, DataSize: integer; Temp: array [0..2048] of char; begin Result := ''; if RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, REG_OPTION_NON_VOLATILE, KEY_READ, OpenKey) = ERROR_SUCCESS then begin DataType := REG_SZ; DataSize := SizeOf(Temp); if RegQueryValueEx(OpenKey, Vn, nil, @DataType, @Temp, @DataSize) = ERROR_SUCCESS then SetString(Result, Temp, DataSize div SizeOf(Char) - 1); RegCloseKey(OpenKey); end; end ; {$ENDIF} function GetDNS: string; {$IFDEF UNIX} var l: TStringList; n: integer; begin Result := ''; l := TStringList.Create; try l.LoadFromFile('/etc/resolv.conf'); for n := 0 to l.Count - 1 do if Pos('NAMESERVER', uppercase(l[n])) = 1 then begin if Result <> '' then Result := Result + ','; Result := Result + SeparateRight(l[n], ' '); end; finally l.Free; end; end; {$ELSE} const NTdyn = 'System\CurrentControlSet\Services\Tcpip\Parameters\Temporary'; NTfix = 'System\CurrentControlSet\Services\Tcpip\Parameters'; W9xfix = 'System\CurrentControlSet\Services\MSTCP'; begin Result := GetDNSbyIpHlp; if Result = '...' then begin if Win32Platform = VER_PLATFORM_WIN32_NT then begin Result := ReadReg(NTdyn, 'NameServer'); if result = '' then Result := ReadReg(NTfix, 'NameServer'); if result = '' then Result := ReadReg(NTfix, 'DhcpNameServer'); end else Result := ReadReg(W9xfix, 'NameServer'); Result := ReplaceString(trim(Result), ' ', ','); end; end; {$ENDIF} {==============================================================================} function GetIEProxy(protocol: string): TProxySetting; {$IFDEF UNIX} begin Result.Host := ''; Result.Port := ''; Result.Bypass := ''; end; {$ELSE} type PInternetProxyInfo = ^TInternetProxyInfo; TInternetProxyInfo = packed record dwAccessType: DWORD; lpszProxy: LPCSTR; lpszProxyBypass: LPCSTR; end; const INTERNET_OPTION_PROXY = 38; INTERNET_OPEN_TYPE_PROXY = 3; WininetDLL = 'WININET.DLL'; var WininetModule: THandle; ProxyInfo: PInternetProxyInfo; Err: Boolean; Len: DWORD; Proxy: string; DefProxy: string; ProxyList: TStringList; n: integer; InternetQueryOption: function (hInet: Pointer; dwOption: DWORD; lpBuffer: Pointer; var lpdwBufferLength: DWORD): BOOL; stdcall; begin Result.Host := ''; Result.Port := ''; Result.Bypass := ''; WininetModule := LoadLibrary(WininetDLL); if WininetModule = 0 then exit; try InternetQueryOption := GetProcAddress(WininetModule,PAnsiChar(AnsiString('InternetQueryOptionA'))); if @InternetQueryOption = nil then Exit; if protocol = '' then protocol := 'http'; Len := 4096; GetMem(ProxyInfo, Len); ProxyList := TStringList.Create; try Err := InternetQueryOption(nil, INTERNET_OPTION_PROXY, ProxyInfo, Len); if Err then if ProxyInfo^.dwAccessType = INTERNET_OPEN_TYPE_PROXY then begin ProxyList.CommaText := ReplaceString(ProxyInfo^.lpszProxy, ' ', ','); Proxy := ''; DefProxy := ''; for n := 0 to ProxyList.Count -1 do begin if Pos(lowercase(protocol) + '=', lowercase(ProxyList[n])) = 1 then begin Proxy := SeparateRight(ProxyList[n], '='); break; end; if Pos('=', ProxyList[n]) < 1 then DefProxy := ProxyList[n]; end; if Proxy = '' then Proxy := DefProxy; if Proxy <> '' then begin Result.Host := Trim(SeparateLeft(Proxy, ':')); Result.Port := Trim(SeparateRight(Proxy, ':')); end; Result.Bypass := ReplaceString(ProxyInfo^.lpszProxyBypass, ' ', ','); end; finally ProxyList.Free; FreeMem(ProxyInfo); end; finally FreeLibrary(WininetModule); end; end; {$ENDIF} {==============================================================================} function GetLocalIPs: string; var TcpSock: TTCPBlockSocket; ipList: TStringList; begin Result := ''; ipList := TStringList.Create; try TcpSock := TTCPBlockSocket.create; try TcpSock.ResolveNameToIP(TcpSock.LocalName, ipList); Result := ipList.CommaText; finally TcpSock.Free; end; finally ipList.Free; end; end; {==============================================================================} end. TransGUI/synapse/source/lib/nntpsend.pas0000644000000000000000000003377311366572451017325 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.005.002 | |==============================================================================| | Content: NNTP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(NNTP client) NNTP (network news transfer protocol) Used RFC: RFC-977, RFC-2980 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$WARN SUSPICIOUS_TYPECAST OFF} {$ENDIF} unit nntpsend; interface uses SysUtils, Classes, blcksock, synautil; const cNNTPProtocol = '119'; type {:abstract(Implementation of Network News Transfer Protocol. Note: Are you missing properties for setting Username and Password? Look to parent @link(TSynaClient) object! Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TNNTPSend = class(TSynaClient) private FSock: TTCPBlockSocket; FResultCode: Integer; FResultString: string; FData: TStringList; FDataToSend: TStringList; FAutoTLS: Boolean; FFullSSL: Boolean; FNNTPcap: TStringList; function ReadResult: Integer; function ReadData: boolean; function SendData: boolean; function Connect: Boolean; public constructor Create; destructor Destroy; override; {:Connects to NNTP server and begin session.} function Login: Boolean; {:Logout from NNTP server and terminate session.} function Logout: Boolean; {:By this you can call any NNTP command.} function DoCommand(const Command: string): boolean; {:by this you can call any NNTP command. This variant is used for commands for download information from server.} function DoCommandRead(const Command: string): boolean; {:by this you can call any NNTP command. This variant is used for commands for upload information to server.} function DoCommandWrite(const Command: string): boolean; {:Download full message to @link(data) property. Value can be number of message or message-id (in brackets).} function GetArticle(const Value: string): Boolean; {:Download only body of message to @link(data) property. Value can be number of message or message-id (in brackets).} function GetBody(const Value: string): Boolean; {:Download only headers of message to @link(data) property. Value can be number of message or message-id (in brackets).} function GetHead(const Value: string): Boolean; {:Get message status. Value can be number of message or message-id (in brackets).} function GetStat(const Value: string): Boolean; {:Select given group.} function SelectGroup(const Value: string): Boolean; {:Tell to server 'I have mesage with given message-ID.' If server need this message, message is uploaded to server.} function IHave(const MessID: string): Boolean; {:Move message pointer to last item in group.} function GotoLast: Boolean; {:Move message pointer to next item in group.} function GotoNext: Boolean; {:Download to @link(data) property list of all groups on NNTP server.} function ListGroups: Boolean; {:Download to @link(data) property list of all groups created after given time.} function ListNewGroups(Since: TDateTime): Boolean; {:Download to @link(data) property list of message-ids in given group since given time.} function NewArticles(const Group: string; Since: TDateTime): Boolean; {:Upload new article to server. (for new messages by you)} function PostArticle: Boolean; {:Tells to remote NNTP server 'I am not NNTP client, but I am another NNTP server'.} function SwitchToSlave: Boolean; {:Call NNTP XOVER command.} function Xover(xoStart, xoEnd: string): boolean; {:Call STARTTLS command for upgrade connection to SSL/TLS mode.} function StartTLS: Boolean; {:Try to find given capability in extension list. This list is getted after successful login to NNTP server. If extension capability is not found, then return is empty string.} function FindCap(const Value: string): string; {:Try get list of server extensions. List is returned in @link(data) property.} function ListExtensions: Boolean; published {:Result code number of last operation.} property ResultCode: Integer read FResultCode; {:String description of last result code from NNTP server.} property ResultString: string read FResultString; {:Readed data. (message, etc.)} property Data: TStringList read FData; {:If is set to @true, then upgrade to SSL/TLS mode after login if remote server support it.} property AutoTLS: Boolean read FAutoTLS Write FAutoTLS; {:SSL/TLS mode is used from first contact to server. Servers with full SSL/TLS mode usualy using non-standard TCP port!} property FullSSL: Boolean read FFullSSL Write FFullSSL; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; end; implementation constructor TNNTPSend.Create; begin inherited Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FData := TStringList.Create; FDataToSend := TStringList.Create; FNNTPcap := TStringList.Create; FSock.ConvertLineEnd := True; FTimeout := 60000; FTargetPort := cNNTPProtocol; FAutoTLS := False; FFullSSL := False; end; destructor TNNTPSend.Destroy; begin FSock.Free; FDataToSend.Free; FData.Free; FNNTPcap.Free; inherited Destroy; end; function TNNTPSend.ReadResult: Integer; var s: string; begin Result := 0; FData.Clear; s := FSock.RecvString(FTimeout); FResultString := Copy(s, 5, Length(s) - 4); if FSock.LastError <> 0 then Exit; if Length(s) >= 3 then Result := StrToIntDef(Copy(s, 1, 3), 0); FResultCode := Result; end; function TNNTPSend.ReadData: boolean; var s: string; begin repeat s := FSock.RecvString(FTimeout); if s = '.' then break; if (s <> '') and (s[1] = '.') then s := Copy(s, 2, Length(s) - 1); FData.Add(s); until FSock.LastError <> 0; Result := FSock.LastError = 0; end; function TNNTPSend.SendData: boolean; var s: string; n: integer; begin for n := 0 to FDataToSend.Count - 1 do begin s := FDataToSend[n]; if (s <> '') and (s[1] = '.') then s := s + '.'; FSock.SendString(s + CRLF); if FSock.LastError <> 0 then break; end; if FDataToSend.Count = 0 then FSock.SendString(CRLF); if FSock.LastError = 0 then FSock.SendString('.' + CRLF); FDataToSend.Clear; Result := FSock.LastError = 0; end; function TNNTPSend.Connect: Boolean; begin FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError = 0 then FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError = 0 then if FFullSSL then FSock.SSLDoConnect; Result := FSock.LastError = 0; end; function TNNTPSend.Login: Boolean; begin Result := False; FNNTPcap.Clear; if not Connect then Exit; Result := (ReadResult div 100) = 2; ListExtensions; FNNTPcap.Assign(Fdata); if Result then if (not FullSSL) and FAutoTLS and (FindCap('STARTTLS') <> '') then Result := StartTLS; if (FUsername <> '') and Result then begin FSock.SendString('AUTHINFO USER ' + FUsername + CRLF); if (ReadResult div 100) = 3 then begin FSock.SendString('AUTHINFO PASS ' + FPassword + CRLF); Result := (ReadResult div 100) = 2; end; end; end; function TNNTPSend.Logout: Boolean; begin FSock.SendString('QUIT' + CRLF); Result := (ReadResult div 100) = 2; FSock.CloseSocket; end; function TNNTPSend.DoCommand(const Command: string): Boolean; begin FSock.SendString(Command + CRLF); Result := (ReadResult div 100) = 2; Result := Result and (FSock.LastError = 0); end; function TNNTPSend.DoCommandRead(const Command: string): Boolean; begin Result := DoCommand(Command); if Result then begin Result := ReadData; Result := Result and (FSock.LastError = 0); end; end; function TNNTPSend.DoCommandWrite(const Command: string): Boolean; var x: integer; begin FDataToSend.Assign(FData); FSock.SendString(Command + CRLF); x := (ReadResult div 100); if x = 3 then begin SendData; x := (ReadResult div 100); end; Result := x = 2; Result := Result and (FSock.LastError = 0); end; function TNNTPSend.GetArticle(const Value: string): Boolean; var s: string; begin s := 'ARTICLE'; if Value <> '' then s := s + ' ' + Value; Result := DoCommandRead(s); end; function TNNTPSend.GetBody(const Value: string): Boolean; var s: string; begin s := 'BODY'; if Value <> '' then s := s + ' ' + Value; Result := DoCommandRead(s); end; function TNNTPSend.GetHead(const Value: string): Boolean; var s: string; begin s := 'HEAD'; if Value <> '' then s := s + ' ' + Value; Result := DoCommandRead(s); end; function TNNTPSend.GetStat(const Value: string): Boolean; var s: string; begin s := 'STAT'; if Value <> '' then s := s + ' ' + Value; Result := DoCommand(s); end; function TNNTPSend.SelectGroup(const Value: string): Boolean; begin Result := DoCommand('GROUP ' + Value); end; function TNNTPSend.IHave(const MessID: string): Boolean; begin Result := DoCommandWrite('IHAVE ' + MessID); end; function TNNTPSend.GotoLast: Boolean; begin Result := DoCommand('LAST'); end; function TNNTPSend.GotoNext: Boolean; begin Result := DoCommand('NEXT'); end; function TNNTPSend.ListGroups: Boolean; begin Result := DoCommandRead('LIST'); end; function TNNTPSend.ListNewGroups(Since: TDateTime): Boolean; begin Result := DoCommandRead('NEWGROUPS ' + SimpleDateTime(Since) + ' GMT'); end; function TNNTPSend.NewArticles(const Group: string; Since: TDateTime): Boolean; begin Result := DoCommandRead('NEWNEWS ' + Group + ' ' + SimpleDateTime(Since) + ' GMT'); end; function TNNTPSend.PostArticle: Boolean; begin Result := DoCommandWrite('POST'); end; function TNNTPSend.SwitchToSlave: Boolean; begin Result := DoCommand('SLAVE'); end; function TNNTPSend.Xover(xoStart, xoEnd: string): Boolean; var s: string; begin s := 'XOVER ' + xoStart; if xoEnd <> xoStart then s := s + '-' + xoEnd; Result := DoCommandRead(s); end; function TNNTPSend.StartTLS: Boolean; begin Result := False; if FindCap('STARTTLS') <> '' then begin if DoCommand('STARTTLS') then begin Fsock.SSLDoConnect; Result := FSock.LastError = 0; end; end; end; function TNNTPSend.ListExtensions: Boolean; begin Result := DoCommandRead('LIST EXTENSIONS'); end; function TNNTPSend.FindCap(const Value: string): string; var n: Integer; s: string; begin s := UpperCase(Value); Result := ''; for n := 0 to FNNTPcap.Count - 1 do if Pos(s, UpperCase(FNNTPcap[n])) = 1 then begin Result := FNNTPcap[n]; Break; end; end; {==============================================================================} end. TransGUI/synapse/source/lib/ssl_cryptlib.pas0000644000000000000000000004506211366572451020177 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.000 | |==============================================================================| | Content: SSL/SSH support by Peter Gutmann's CryptLib | |==============================================================================| | Copyright (c)1999-2005, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2005. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(SSL/SSH plugin for CryptLib) This plugin requires cl32.dll at least version 3.2.0! It can be used on Win32 and Linux. This library is staticly linked - when you compile your application with this plugin, you MUST distribute it with Cryptib library, otherwise you cannot run your application! It can work with keys and certificates stored as PKCS#15 only! It must be stored as disk file only, you cannot load them from memory! Each file can hold multiple keys and certificates. You must identify it by 'label' stored in @link(TSSLCryptLib.PrivateKeyLabel). If you need to use secure connection and authorize self by certificate (each SSL/TLS server or client with client authorization), then use @link(TCustomSSL.PrivateKeyFile), @link(TSSLCryptLib.PrivateKeyLabel) and @link(TCustomSSL.KeyPassword) properties. If you need to use server what verifying client certificates, then use @link(TCustomSSL.CertCAFile) as PKCS#15 file with public keyas of allowed clients. Clients with non-matching certificates will be rejected by cryptLib. This plugin is capable to create Ad-Hoc certificates. When you start SSL/TLS server without explicitly assigned key and certificate, then this plugin create Ad-Hoc key and certificate for each incomming connection by self. It slowdown accepting of new connections! You can use this plugin for SSHv2 connections too! You must explicitly set @link(TCustomSSL.SSLType) to value LT_SSHv2 and set @link(TCustomSSL.username) and @link(TCustomSSL.password). You can use special SSH channels too, see @link(TCustomSSL). } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} unit ssl_cryptlib; interface uses SysUtils, blcksock, synsock, synautil, synacode, cryptlib; type {:@abstract(class implementing CryptLib SSL/SSH plugin.) Instance of this class will be created for each @link(TTCPBlockSocket). You not need to create instance of this class, all is done by Synapse itself!} TSSLCryptLib = class(TCustomSSL) protected FCryptSession: CRYPT_SESSION; FPrivateKeyLabel: string; FDelCert: Boolean; FReadBuffer: string; function SSLCheck(Value: integer): Boolean; function Init(server:Boolean): Boolean; function DeInit: Boolean; function Prepare(server:Boolean): Boolean; function GetString(const cryptHandle: CRYPT_HANDLE; const attributeType: CRYPT_ATTRIBUTE_TYPE): string; function CreateSelfSignedCert(Host: string): Boolean; override; function PopAll: string; public {:See @inherited} constructor Create(const Value: TTCPBlockSocket); override; destructor Destroy; override; {:See @inherited} function LibVersion: String; override; {:See @inherited} function LibName: String; override; {:See @inherited} procedure Assign(const Value: TCustomSSL); override; {:See @inherited and @link(ssl_cryptlib) for more details.} function Connect: boolean; override; {:See @inherited and @link(ssl_cryptlib) for more details.} function Accept: boolean; override; {:See @inherited} function Shutdown: boolean; override; {:See @inherited} function BiShutdown: boolean; override; {:See @inherited} function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function WaitingData: Integer; override; {:See @inherited} function GetSSLVersion: string; override; {:See @inherited} function GetPeerSubject: string; override; {:See @inherited} function GetPeerIssuer: string; override; {:See @inherited} function GetPeerName: string; override; {:See @inherited} function GetPeerFingerprint: string; override; published {:name of certificate/key within PKCS#15 file. It can hold more then one certificate/key and each certificate/key must have unique label within one file.} property PrivateKeyLabel: string read FPrivateKeyLabel Write FPrivateKeyLabel; end; implementation {==============================================================================} constructor TSSLCryptLib.Create(const Value: TTCPBlockSocket); begin inherited Create(Value); FcryptSession := CRYPT_SESSION(CRYPT_SESSION_NONE); FPrivateKeyLabel := 'synapse'; FDelCert := false; end; destructor TSSLCryptLib.Destroy; begin DeInit; inherited Destroy; end; procedure TSSLCryptLib.Assign(const Value: TCustomSSL); begin inherited Assign(Value); if Value is TSSLCryptLib then begin FPrivateKeyLabel := TSSLCryptLib(Value).privatekeyLabel; end; end; function TSSLCryptLib.GetString(const cryptHandle: CRYPT_HANDLE; const attributeType: CRYPT_ATTRIBUTE_TYPE): string; var l: integer; begin l := 0; cryptGetAttributeString(cryptHandle, attributeType, nil, l); setlength(Result, l); cryptGetAttributeString(cryptHandle, attributeType, pointer(Result), l); setlength(Result, l); end; function TSSLCryptLib.LibVersion: String; var x: integer; begin Result := GetString(CRYPT_UNUSED, CRYPT_OPTION_INFO_DESCRIPTION); cryptGetAttribute(CRYPT_UNUSED, CRYPT_OPTION_INFO_MAJORVERSION, x); Result := Result + ' v' + IntToStr(x); cryptGetAttribute(CRYPT_UNUSED, CRYPT_OPTION_INFO_MINORVERSION, x); Result := Result + '.' + IntToStr(x); cryptGetAttribute(CRYPT_UNUSED, CRYPT_OPTION_INFO_STEPPING, x); Result := Result + '.' + IntToStr(x); end; function TSSLCryptLib.LibName: String; begin Result := 'ssl_cryptlib'; end; function TSSLCryptLib.SSLCheck(Value: integer): Boolean; begin Result := true; FLastErrorDesc := ''; if Value = CRYPT_ERROR_COMPLETE then Value := 0; FLastError := Value; if FLastError <> 0 then begin Result := False; FLastErrorDesc := GetString(FCryptSession, CRYPT_ATTRIBUTE_INT_ERRORMESSAGE); end; end; function TSSLCryptLib.CreateSelfSignedCert(Host: string): Boolean; var privateKey: CRYPT_CONTEXT; keyset: CRYPT_KEYSET; cert: CRYPT_CERTIFICATE; publicKey: CRYPT_CONTEXT; begin Result := False; if FPrivatekeyFile = '' then FPrivatekeyFile := GetTempFile('', 'key'); cryptCreateContext(privateKey, CRYPT_UNUSED, CRYPT_ALGO_RSA); cryptSetAttributeString(privateKey, CRYPT_CTXINFO_LABEL, Pointer(FPrivatekeyLabel), Length(FPrivatekeyLabel)); cryptSetAttribute(privateKey, CRYPT_CTXINFO_KEYSIZE, 1024); cryptGenerateKey(privateKey); cryptKeysetOpen(keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, PChar(FPrivatekeyFile), CRYPT_KEYOPT_CREATE); FDelCert := True; cryptAddPrivateKey(keyset, privateKey, PChar(FKeyPassword)); cryptCreateCert(cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE); cryptSetAttribute(cert, CRYPT_CERTINFO_XYZZY, 1); cryptGetPublicKey(keyset, publicKey, CRYPT_KEYID_NAME, PChar(FPrivatekeyLabel)); cryptSetAttribute(cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, publicKey); cryptSetAttributeString(cert, CRYPT_CERTINFO_COMMONNAME, Pointer(host), Length(host)); cryptSignCert(cert, privateKey); cryptAddPublicKey(keyset, cert); cryptKeysetClose(keyset); cryptDestroyCert(cert); cryptDestroyContext(privateKey); cryptDestroyContext(publicKey); Result := True; end; function TSSLCryptLib.PopAll: string; const BufferMaxSize = 32768; var Outbuffer: string; WriteLen: integer; begin Result := ''; repeat setlength(outbuffer, BufferMaxSize); Writelen := 0; SSLCheck(CryptPopData(FCryptSession, @OutBuffer[1], BufferMaxSize, Writelen)); if FLastError <> 0 then Break; if WriteLen > 0 then begin setlength(outbuffer, WriteLen); Result := Result + outbuffer; end; until WriteLen = 0; end; function TSSLCryptLib.Init(server:Boolean): Boolean; var st: CRYPT_SESSION_TYPE; keysetobj: CRYPT_KEYSET; cryptContext: CRYPT_CONTEXT; x: integer; begin Result := False; FLastErrorDesc := ''; FLastError := 0; FDelCert := false; FcryptSession := CRYPT_SESSION(CRYPT_SESSION_NONE); if server then case FSSLType of LT_all, LT_SSLv3, LT_TLSv1, LT_TLSv1_1: st := CRYPT_SESSION_SSL_SERVER; LT_SSHv2: st := CRYPT_SESSION_SSH_SERVER; else Exit; end else case FSSLType of LT_all, LT_SSLv3, LT_TLSv1, LT_TLSv1_1: st := CRYPT_SESSION_SSL; LT_SSHv2: st := CRYPT_SESSION_SSH; else Exit; end; if not SSLCheck(cryptCreateSession(FcryptSession, CRYPT_UNUSED, st)) then Exit; x := -1; case FSSLType of LT_SSLv3: x := 0; LT_TLSv1: x := 1; LT_TLSv1_1: x := 2; end; if x >= 0 then if not SSLCheck(cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_VERSION, x)) then Exit; if FUsername <> '' then begin cryptSetAttributeString(FcryptSession, CRYPT_SESSINFO_USERNAME, Pointer(FUsername), Length(FUsername)); cryptSetAttributeString(FcryptSession, CRYPT_SESSINFO_PASSWORD, Pointer(FPassword), Length(FPassword)); end; if FSSLType = LT_SSHv2 then if FSSHChannelType <> '' then begin cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_SSH_CHANNEL, CRYPT_UNUSED); cryptSetAttributeString(FCryptSession, CRYPT_SESSINFO_SSH_CHANNEL_TYPE, Pointer(FSSHChannelType), Length(FSSHChannelType)); if FSSHChannelArg1 <> '' then cryptSetAttributeString(FCryptSession, CRYPT_SESSINFO_SSH_CHANNEL_ARG1, Pointer(FSSHChannelArg1), Length(FSSHChannelArg1)); if FSSHChannelArg2 <> '' then cryptSetAttributeString(FCryptSession, CRYPT_SESSINFO_SSH_CHANNEL_ARG2, Pointer(FSSHChannelArg2), Length(FSSHChannelArg2)); end; if server and (FPrivatekeyFile = '') then begin if FPrivatekeyLabel = '' then FPrivatekeyLabel := 'synapse'; if FkeyPassword = '' then FkeyPassword := 'synapse'; CreateSelfSignedcert(FSocket.ResolveIPToName(FSocket.GetRemoteSinIP)); end; if (FPrivatekeyLabel <> '') and (FPrivatekeyFile <> '') then begin if not SSLCheck(cryptKeysetOpen(KeySetObj, CRYPT_UNUSED, CRYPT_KEYSET_FILE, PChar(FPrivatekeyFile), CRYPT_KEYOPT_READONLY)) then Exit; try if not SSLCheck(cryptGetPrivateKey(KeySetObj, cryptcontext, CRYPT_KEYID_NAME, PChar(FPrivatekeyLabel), PChar(FKeyPassword))) then Exit; if not SSLCheck(cryptSetAttribute(FcryptSession, CRYPT_SESSINFO_PRIVATEKEY, cryptcontext)) then Exit; finally cryptKeysetClose(keySetObj); cryptDestroyContext(cryptcontext); end; end; if server and FVerifyCert then begin if not SSLCheck(cryptKeysetOpen(KeySetObj, CRYPT_UNUSED, CRYPT_KEYSET_FILE, PChar(FCertCAFile), CRYPT_KEYOPT_READONLY)) then Exit; try if not SSLCheck(cryptSetAttribute(FcryptSession, CRYPT_SESSINFO_KEYSET, keySetObj)) then Exit; finally cryptKeysetClose(keySetObj); end; end; Result := true; end; function TSSLCryptLib.DeInit: Boolean; begin Result := True; if FcryptSession <> CRYPT_SESSION(CRYPT_SESSION_NONE) then CryptDestroySession(FcryptSession); FcryptSession := CRYPT_SESSION(CRYPT_SESSION_NONE); FSSLEnabled := False; if FDelCert then Deletefile(FPrivatekeyFile); end; function TSSLCryptLib.Prepare(server:Boolean): Boolean; begin Result := false; DeInit; if Init(server) then Result := true else DeInit; end; function TSSLCryptLib.Connect: boolean; begin Result := False; if FSocket.Socket = INVALID_SOCKET then Exit; if Prepare(false) then begin if not SSLCheck(cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_NETWORKSOCKET, FSocket.Socket)) then Exit; if not SSLCheck(cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_ACTIVE, 1)) then Exit; FSSLEnabled := True; Result := True; FReadBuffer := ''; end; end; function TSSLCryptLib.Accept: boolean; begin Result := False; if FSocket.Socket = INVALID_SOCKET then Exit; if Prepare(true) then begin if not SSLCheck(cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_NETWORKSOCKET, FSocket.Socket)) then Exit; if not SSLCheck(cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_ACTIVE, 1)) then Exit; FSSLEnabled := True; Result := True; FReadBuffer := ''; end; end; function TSSLCryptLib.Shutdown: boolean; begin Result := BiShutdown; end; function TSSLCryptLib.BiShutdown: boolean; begin if FcryptSession <> CRYPT_SESSION(CRYPT_SESSION_NONE) then cryptSetAttribute(FCryptSession, CRYPT_SESSINFO_ACTIVE, 0); DeInit; FReadBuffer := ''; Result := True; end; function TSSLCryptLib.SendBuffer(Buffer: TMemory; Len: Integer): Integer; var l: integer; begin FLastError := 0; FLastErrorDesc := ''; SSLCheck(cryptPushData(FCryptSession, Buffer, Len, L)); cryptFlushData(FcryptSession); Result := l; end; function TSSLCryptLib.RecvBuffer(Buffer: TMemory; Len: Integer): Integer; var l: integer; begin FLastError := 0; FLastErrorDesc := ''; if Length(FReadBuffer) = 0 then FReadBuffer := PopAll; if Len > Length(FReadBuffer) then Len := Length(FReadBuffer); Move(Pointer(FReadBuffer)^, buffer^, Len); Delete(FReadBuffer, 1, Len); Result := Len; end; function TSSLCryptLib.WaitingData: Integer; begin Result := Length(FReadBuffer); end; function TSSLCryptLib.GetSSLVersion: string; var x: integer; begin Result := ''; if FcryptSession = CRYPT_SESSION(CRYPT_SESSION_NONE) then Exit; cryptGetAttribute(FCryptSession, CRYPT_SESSINFO_VERSION, x); if FSSLType in [LT_SSLv3, LT_TLSv1, LT_TLSv1_1, LT_all] then case x of 0: Result := 'SSLv3'; 1: Result := 'TLSv1'; 2: Result := 'TLSv1.1'; end; if FSSLType in [LT_SSHv2] then case x of 0: Result := 'SSHv1'; 1: Result := 'SSHv2'; end; end; function TSSLCryptLib.GetPeerSubject: string; var cert: CRYPT_CERTIFICATE; begin Result := ''; if FcryptSession = CRYPT_SESSION(CRYPT_SESSION_NONE) then Exit; cryptGetAttribute(FCryptSession, CRYPT_SESSINFO_RESPONSE, cert); cryptSetAttribute(cert, CRYPT_CERTINFO_SUBJECTNAME, CRYPT_UNUSED); Result := GetString(cert, CRYPT_CERTINFO_DN); cryptDestroyCert(cert); end; function TSSLCryptLib.GetPeerName: string; var cert: CRYPT_CERTIFICATE; begin Result := ''; if FcryptSession = CRYPT_SESSION(CRYPT_SESSION_NONE) then Exit; cryptGetAttribute(FCryptSession, CRYPT_SESSINFO_RESPONSE, cert); cryptSetAttribute(cert, CRYPT_CERTINFO_ISSUERNAME, CRYPT_UNUSED); Result := GetString(cert, CRYPT_CERTINFO_COMMONNAME); cryptDestroyCert(cert); end; function TSSLCryptLib.GetPeerIssuer: string; var cert: CRYPT_CERTIFICATE; begin Result := ''; if FcryptSession = CRYPT_SESSION(CRYPT_SESSION_NONE) then Exit; cryptGetAttribute(FCryptSession, CRYPT_SESSINFO_RESPONSE, cert); cryptSetAttribute(cert, CRYPT_CERTINFO_ISSUERNAME, CRYPT_UNUSED); Result := GetString(cert, CRYPT_CERTINFO_DN); cryptDestroyCert(cert); end; function TSSLCryptLib.GetPeerFingerprint: string; var cert: CRYPT_CERTIFICATE; begin Result := ''; if FcryptSession = CRYPT_SESSION(CRYPT_SESSION_NONE) then Exit; cryptGetAttribute(FCryptSession, CRYPT_SESSINFO_RESPONSE, cert); Result := GetString(cert, CRYPT_CERTINFO_FINGERPRINT); Result := MD5(Result); cryptDestroyCert(cert); end; {==============================================================================} initialization if cryptInit = CRYPT_OK then SSLImplementation := TSSLCryptLib; cryptAddRandom(nil, CRYPT_RANDOM_SLOWPOLL); finalization cryptEnd; end. TransGUI/synapse/source/lib/snmpsend.pas0000644000000000000000000010377611366572451017324 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 003.000.010 | |==============================================================================| | Content: SNMP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Jean-Fabien Connault (cycocrew@worldnet.fr) | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(SNMP client) Supports SNMPv1 include traps, SNMPv2c and SNMPv3 include authorization (encryption not yet supported!) Used RFC: RFC-1157, RFC-1901, RFC-3412, RFC-3414, RFC-3416 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit snmpsend; interface uses Classes, SysUtils, blcksock, synautil, asn1util, synaip, synacode; const cSnmpProtocol = '161'; cSnmpTrapProtocol = '162'; SNMP_V1 = 0; SNMP_V2C = 1; SNMP_V3 = 3; //PDU type PDUGetRequest = $A0; PDUGetNextRequest = $A1; PDUGetResponse = $A2; PDUSetRequest = $A3; PDUTrap = $A4; //Obsolete //for SNMPv2 PDUGetBulkRequest = $A5; PDUInformRequest = $A6; PDUTrapV2 = $A7; PDUReport = $A8; //errors ENoError = 0; ETooBig = 1; ENoSuchName = 2; EBadValue = 3; EReadOnly = 4; EGenErr = 5; //errors SNMPv2 ENoAccess = 6; EWrongType = 7; EWrongLength = 8; EWrongEncoding = 9; EWrongValue = 10; ENoCreation = 11; EInconsistentValue = 12; EResourceUnavailable = 13; ECommitFailed = 14; EUndoFailed = 15; EAuthorizationError = 16; ENotWritable = 17; EInconsistentName = 18; type {:@abstract(Possible values for SNMPv3 flags.) This flags specify level of authorization and encryption.} TV3Flags = ( NoAuthNoPriv, AuthNoPriv, AuthPriv); {:@abstract(Type of SNMPv3 authorization)} TV3Auth = ( AuthMD5, AuthSHA1); {:@abstract(Data object with one record of MIB OID and corresponding values.)} TSNMPMib = class(TObject) protected FOID: AnsiString; FValue: AnsiString; FValueType: Integer; published {:OID number in string format.} property OID: AnsiString read FOID write FOID; {:Value of OID object in string format.} property Value: AnsiString read FValue write FValue; {:Define type of Value. Supported values are defined in @link(asn1util). For queries use ASN1_NULL, becouse you don't know type in response!} property ValueType: Integer read FValueType write FValueType; end; {:@abstract(It holding all information for SNMPv3 agent synchronization) Used internally.} TV3Sync = record EngineID: AnsiString; EngineBoots: integer; EngineTime: integer; EngineStamp: Cardinal; end; {:@abstract(Data object abstracts SNMP data packet)} TSNMPRec = class(TObject) protected FVersion: Integer; FPDUType: Integer; FID: Integer; FErrorStatus: Integer; FErrorIndex: Integer; FCommunity: AnsiString; FSNMPMibList: TList; FMaxSize: Integer; FFlags: TV3Flags; FFlagReportable: Boolean; FContextEngineID: AnsiString; FContextName: AnsiString; FAuthMode: TV3Auth; FAuthEngineID: AnsiString; FAuthEngineBoots: integer; FAuthEngineTime: integer; FAuthEngineTimeStamp: cardinal; FUserName: AnsiString; FPassword: AnsiString; FAuthKey: AnsiString; FPrivKey: AnsiString; FOldTrapEnterprise: AnsiString; FOldTrapHost: AnsiString; FOldTrapGen: Integer; FOldTrapSpec: Integer; FOldTrapTimeTicks: Integer; function Pass2Key(const Value: AnsiString): AnsiString; public constructor Create; destructor Destroy; override; {:Decode SNMP packet in buffer to object properties.} function DecodeBuf(const Buffer: AnsiString): Boolean; {:Encode obeject properties to SNMP packet.} function EncodeBuf: AnsiString; {:Clears all object properties to default values.} procedure Clear; {:Add entry to @link(SNMPMibList). For queries use value as empty string, and ValueType as ASN1_NULL.} procedure MIBAdd(const MIB, Value: AnsiString; ValueType: Integer); {:Delete entry from @link(SNMPMibList).} procedure MIBDelete(Index: Integer); {:Search @link(SNMPMibList) list for MIB and return correspond value.} function MIBGet(const MIB: AnsiString): AnsiString; {:return number of entries in MIB array.} function MIBCount: integer; {:Return MIB information from given row of MIB array.} function MIBByIndex(Index: Integer): TSNMPMib; {:List of @link(TSNMPMib) objects.} property SNMPMibList: TList read FSNMPMibList; published {:Version of SNMP packet. Default value is 0 (SNMP ver. 1). You can use value 1 for SNMPv2c or value 3 for SNMPv3.} property Version: Integer read FVersion write FVersion; {:Community string for autorize access to SNMP server. (Case sensitive!) Community string is not used in SNMPv3! Use @link(Username) and @link(password) instead!} property Community: AnsiString read FCommunity write FCommunity; {:Define type of SNMP operation.} property PDUType: Integer read FPDUType write FPDUType; {:Contains ID number. Not need to use.} property ID: Integer read FID write FID; {:When packet is reply, contains error code. Supported values are defined by E* constants.} property ErrorStatus: Integer read FErrorStatus write FErrorStatus; {:Point to error position in reply packet. Not usefull for users. It only good for debugging!} property ErrorIndex: Integer read FErrorIndex write FErrorIndex; {:special value for GetBulkRequest of SNMPv2 and v3.} property NonRepeaters: Integer read FErrorStatus write FErrorStatus; {:special value for GetBulkRequest of SNMPv2 and v3.} property MaxRepetitions: Integer read FErrorIndex write FErrorIndex; {:Maximum message size in bytes for SNMPv3. For sending is default 1472 bytes.} property MaxSize: Integer read FMaxSize write FMaxSize; {:Specify if message is authorised or encrypted. Used only in SNMPv3, and encryption is not yet supported!} property Flags: TV3Flags read FFlags write FFlags; {:For SNMPv3.... If is @true, SNMP agent must send reply (at least with some error).} property FlagReportable: Boolean read FFlagReportable write FFlagReportable; {:For SNMPv3. If not specified, is used value from @link(AuthEngineID)} property ContextEngineID: AnsiString read FContextEngineID write FContextEngineID; {:For SNMPv3.} property ContextName: AnsiString read FContextName write FContextName; {:For SNMPv3. Specify Authorization mode. (specify used hash for authorization)} property AuthMode: TV3Auth read FAuthMode write FAuthMode; {:value used by SNMPv3 authorisation for synchronization with SNMP agent.} property AuthEngineID: AnsiString read FAuthEngineID write FAuthEngineID; {:value used by SNMPv3 authorisation for synchronization with SNMP agent.} property AuthEngineBoots: Integer read FAuthEngineBoots write FAuthEngineBoots; {:value used by SNMPv3 authorisation for synchronization with SNMP agent.} property AuthEngineTime: Integer read FAuthEngineTime write FAuthEngineTime; {:value used by SNMPv3 authorisation for synchronization with SNMP agent.} property AuthEngineTimeStamp: Cardinal read FAuthEngineTimeStamp Write FAuthEngineTimeStamp; {:SNMPv3 authorization username} property UserName: AnsiString read FUserName write FUserName; {:SNMPv3 authorization password} property Password: AnsiString read FPassword write FPassword; {:For SNMPv3. Computed Athorization key from @link(password).} property AuthKey: AnsiString read FAuthKey write FAuthKey; {:For SNMPv3. Encryption key for message encryption. Not yet used!} property PrivKey: AnsiString read FPrivKey write FPrivKey; {:MIB value to identify the object that sent the TRAPv1.} property OldTrapEnterprise: AnsiString read FOldTrapEnterprise write FOldTrapEnterprise; {:Address of TRAPv1 sender (IP address).} property OldTrapHost: AnsiString read FOldTrapHost write FOldTrapHost; {:Generic TRAPv1 identification.} property OldTrapGen: Integer read FOldTrapGen write FOldTrapGen; {:Specific TRAPv1 identification.} property OldTrapSpec: Integer read FOldTrapSpec write FOldTrapSpec; {:Number of 1/100th of seconds since last reboot or power up. (for TRAPv1)} property OldTrapTimeTicks: Integer read FOldTrapTimeTicks write FOldTrapTimeTicks; end; {:@abstract(Implementation of SNMP protocol.) Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TSNMPSend = class(TSynaClient) protected FSock: TUDPBlockSocket; FBuffer: AnsiString; FHostIP: AnsiString; FQuery: TSNMPRec; FReply: TSNMPRec; function InternalSendSnmp(const Value: TSNMPRec): Boolean; function InternalRecvSnmp(const Value: TSNMPRec): Boolean; function InternalSendRequest(const QValue, RValue: TSNMPRec): Boolean; function GetV3EngineID: AnsiString; function GetV3Sync: TV3Sync; public constructor Create; destructor Destroy; override; {:Connects to a Host and send there query. If in timeout SNMP server send back query, result is @true. If is used SNMPv3, then it synchronize self with SNMPv3 agent first. (It is needed for SNMPv3 auhorization!)} function SendRequest: Boolean; {:Send SNMP packet only, but not waits for reply. Good for sending traps.} function SendTrap: Boolean; {:Receive SNMP packet only. Good for receiving traps.} function RecvTrap: Boolean; {:Mapped to @link(SendRequest) internally. This function is only for backward compatibility.} function DoIt: Boolean; published {:contains raw binary form of SNMP packet. Good for debugging.} property Buffer: AnsiString read FBuffer write FBuffer; {:After SNMP operation hold IP address of remote side.} property HostIP: AnsiString read FHostIP; {:Data object contains SNMP query.} property Query: TSNMPRec read FQuery; {:Data object contains SNMP reply.} property Reply: TSNMPRec read FReply; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TUDPBlockSocket read FSock; end; {:A very useful function and example of its use would be found in the TSNMPSend object. It implements basic GET method of the SNMP protocol. The MIB value is located in the "OID" variable, and is sent to the requested "SNMPHost" with the proper "Community" access identifier. Upon a successful retrieval, "Value" will contain the information requested. If the SNMP operation is successful, the result returns @true.} function SNMPGet(const OID, Community, SNMPHost: AnsiString; var Value: AnsiString): Boolean; {:This is useful function and example of use TSNMPSend object. It implements the basic SET method of the SNMP protocol. If the SNMP operation is successful, the result is @true. "Value" is value of MIB Oid for "SNMPHost" with "Community" access identifier. You must specify "ValueType" too.} function SNMPSet(const OID, Community, SNMPHost, Value: AnsiString; ValueType: Integer): Boolean; {:A very useful function and example of its use would be found in the TSNMPSend object. It implements basic GETNEXT method of the SNMP protocol. The MIB value is located in the "OID" variable, and is sent to the requested "SNMPHost" with the proper "Community" access identifier. Upon a successful retrieval, "Value" will contain the information requested. If the SNMP operation is successful, the result returns @true.} function SNMPGetNext(var OID: AnsiString; const Community, SNMPHost: AnsiString; var Value: AnsiString): Boolean; {:A very useful function and example of its use would be found in the TSNMPSend object. It implements basic read of SNMP MIB tables. As BaseOID you must specify basic MIB OID of requested table (base IOD is OID without row and column specificator!) Table is readed into stringlist, where each string is comma delimited string. Warning: this function is not have best performance. For better performance you must write your own function. best performace you can get by knowledge of structuture of table and by more then one MIB on one query. } function SNMPGetTable(const BaseOID, Community, SNMPHost: AnsiString; const Value: TStrings): Boolean; {:A very useful function and example of its use would be found in the TSNMPSend object. It implements basic read of SNMP MIB table element. As BaseOID you must specify basic MIB OID of requested table (base IOD is OID without row and column specificator!) As next you must specify identificator of row and column for specify of needed field of table.} function SNMPGetTableElement(const BaseOID, RowID, ColID, Community, SNMPHost: AnsiString; var Value: AnsiString): Boolean; {:A very useful function and example of its use would be found in the TSNMPSend object. It implements a TRAPv1 to send with all data in the parameters.} function SendTrap(const Dest, Source, Enterprise, Community: AnsiString; Generic, Specific, Seconds: Integer; const MIBName, MIBValue: AnsiString; MIBtype: Integer): Integer; {:A very useful function and example of its use would be found in the TSNMPSend object. It receives a TRAPv1 and returns all the data that comes with it.} function RecvTrap(var Dest, Source, Enterprise, Community: AnsiString; var Generic, Specific, Seconds: Integer; const MIBName, MIBValue: TStringList): Integer; implementation {==============================================================================} constructor TSNMPRec.Create; begin inherited Create; FSNMPMibList := TList.Create; Clear; FID := 1; FMaxSize := 1472; end; destructor TSNMPRec.Destroy; var i: Integer; begin for i := 0 to FSNMPMibList.Count - 1 do TSNMPMib(FSNMPMibList[i]).Free; FSNMPMibList.Clear; FSNMPMibList.Free; inherited Destroy; end; function TSNMPRec.Pass2Key(const Value: AnsiString): AnsiString; var key: AnsiString; begin case FAuthMode of AuthMD5: begin key := MD5LongHash(Value, 1048576); Result := MD5(key + FAuthEngineID + key); end; AuthSHA1: begin key := SHA1LongHash(Value, 1048576); Result := SHA1(key + FAuthEngineID + key); end; else Result := ''; end; end; function TSNMPRec.DecodeBuf(const Buffer: AnsiString): Boolean; var Pos: Integer; EndPos: Integer; sm, sv: AnsiString; Svt: Integer; s: AnsiString; Spos: integer; x: Byte; begin Clear; Result := False; if Length(Buffer) < 2 then Exit; if (Ord(Buffer[1]) and $20) = 0 then Exit; Pos := 2; EndPos := ASNDecLen(Pos, Buffer); if Length(Buffer) < (EndPos + 2) then Exit; Self.FVersion := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); if FVersion = 3 then begin ASNItem(Pos, Buffer, Svt); //header data seq ASNItem(Pos, Buffer, Svt); //ID FMaxSize := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); s := ASNItem(Pos, Buffer, Svt); x := 0; if s <> '' then x := Ord(s[1]); FFlagReportable := (x and 4) > 0; x := x and 3; case x of 1: FFlags := AuthNoPriv; 3: FFlags := AuthPriv; else FFlags := NoAuthNoPriv; end; x := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); s := ASNItem(Pos, Buffer, Svt); //SecurityParameters //if SecurityModel is USM, then try to decode SecurityParameters if (x = 3) and (s <> '') then begin spos := 1; ASNItem(SPos, s, Svt); FAuthEngineID := ASNItem(SPos, s, Svt); FAuthEngineBoots := StrToIntDef(ASNItem(SPos, s, Svt), 0); FAuthEngineTime := StrToIntDef(ASNItem(SPos, s, Svt), 0); FAuthEngineTimeStamp := GetTick; FUserName := ASNItem(SPos, s, Svt); FAuthKey := ASNItem(SPos, s, Svt); FPrivKey := ASNItem(SPos, s, Svt); end; //scopedPDU s := ASNItem(Pos, Buffer, Svt); if Svt = ASN1_OCTSTR then begin //decrypt! end; FContextEngineID := ASNItem(Pos, Buffer, Svt); FContextName := ASNItem(Pos, Buffer, Svt); end else begin //old packet Self.FCommunity := ASNItem(Pos, Buffer, Svt); end; ASNItem(Pos, Buffer, Svt); Self.FPDUType := Svt; if Self.FPDUType = PDUTrap then begin FOldTrapEnterprise := ASNItem(Pos, Buffer, Svt); FOldTrapHost := ASNItem(Pos, Buffer, Svt); FOldTrapGen := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); FOldTrapSpec := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); FOldTrapTimeTicks := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); end else begin Self.FID := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); Self.FErrorStatus := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); Self.FErrorIndex := StrToIntDef(ASNItem(Pos, Buffer, Svt), 0); end; ASNItem(Pos, Buffer, Svt); while Pos < EndPos do begin ASNItem(Pos, Buffer, Svt); Sm := ASNItem(Pos, Buffer, Svt); Sv := ASNItem(Pos, Buffer, Svt); Self.MIBAdd(sm, sv, Svt); end; Result := True; end; function TSNMPRec.EncodeBuf: AnsiString; var s: AnsiString; SNMPMib: TSNMPMib; n: Integer; pdu, head, auth, authbeg: AnsiString; x: Byte; begin pdu := ''; for n := 0 to FSNMPMibList.Count - 1 do begin SNMPMib := TSNMPMib(FSNMPMibList[n]); case SNMPMib.ValueType of ASN1_INT: s := ASNObject(MibToID(SNMPMib.OID), ASN1_OBJID) + ASNObject(ASNEncInt(StrToIntDef(SNMPMib.Value, 0)), SNMPMib.ValueType); ASN1_COUNTER, ASN1_GAUGE, ASN1_TIMETICKS: s := ASNObject(MibToID(SNMPMib.OID), ASN1_OBJID) + ASNObject(ASNEncUInt(StrToIntDef(SNMPMib.Value, 0)), SNMPMib.ValueType); ASN1_OBJID: s := ASNObject(MibToID(SNMPMib.OID), ASN1_OBJID) + ASNObject(MibToID(SNMPMib.Value), SNMPMib.ValueType); ASN1_IPADDR: s := ASNObject(MibToID(SNMPMib.OID), ASN1_OBJID) + ASNObject(IPToID(SNMPMib.Value), SNMPMib.ValueType); ASN1_NULL: s := ASNObject(MibToID(SNMPMib.OID), ASN1_OBJID) + ASNObject('', ASN1_NULL); else s := ASNObject(MibToID(SNMPMib.OID), ASN1_OBJID) + ASNObject(SNMPMib.Value, SNMPMib.ValueType); end; pdu := pdu + ASNObject(s, ASN1_SEQ); end; pdu := ASNObject(pdu, ASN1_SEQ); if Self.FPDUType = PDUTrap then pdu := ASNObject(MibToID(FOldTrapEnterprise), ASN1_OBJID) + ASNObject(IPToID(FOldTrapHost), ASN1_IPADDR) + ASNObject(ASNEncInt(FOldTrapGen), ASN1_INT) + ASNObject(ASNEncInt(FOldTrapSpec), ASN1_INT) + ASNObject(ASNEncUInt(FOldTrapTimeTicks), ASN1_TIMETICKS) + pdu else pdu := ASNObject(ASNEncInt(Self.FID), ASN1_INT) + ASNObject(ASNEncInt(Self.FErrorStatus), ASN1_INT) + ASNObject(ASNEncInt(Self.FErrorIndex), ASN1_INT) + pdu; pdu := ASNObject(pdu, Self.FPDUType); if FVersion = 3 then begin if FContextEngineID = '' then FContextEngineID := FAuthEngineID; //complete PDUv3... pdu := ASNObject(FContextEngineID, ASN1_OCTSTR) + ASNObject(FContextName, ASN1_OCTSTR) + pdu; //maybe encrypt pdu... in future pdu := ASNObject(pdu, ASN1_SEQ); //prepare flags case FFlags of AuthNoPriv: x := 1; AuthPriv: x := 3; else x := 0; end; if FFlagReportable then x := x or 4; head := ASNObject(ASNEncInt(Self.FVersion), ASN1_INT); s := ASNObject(ASNEncInt(FID), ASN1_INT) + ASNObject(ASNEncInt(FMaxSize), ASN1_INT) + ASNObject(AnsiChar(x), ASN1_OCTSTR) //encode security model USM + ASNObject(ASNEncInt(3), ASN1_INT); head := head + ASNObject(s, ASN1_SEQ); //compute engine time difference x := TickDelta(FAuthEngineTimeStamp, GetTick) div 1000; authbeg := ASNObject(FAuthEngineID, ASN1_OCTSTR) + ASNObject(ASNEncInt(FAuthEngineBoots), ASN1_INT) + ASNObject(ASNEncInt(FAuthEngineTime + x), ASN1_INT) + ASNObject(FUserName, ASN1_OCTSTR); case FFlags of AuthNoPriv, AuthPriv: begin s := authbeg + ASNObject(StringOfChar(#0, 12), ASN1_OCTSTR) + ASNObject(FPrivKey, ASN1_OCTSTR); s := ASNObject(s, ASN1_SEQ); s := head + ASNObject(s, ASN1_OCTSTR); s := ASNObject(s + pdu, ASN1_SEQ); //in s is entire packet without auth info... case FAuthMode of AuthMD5: begin s := HMAC_MD5(s, Pass2Key(FPassword) + StringOfChar(#0, 48)); //strip to HMAC-MD5-96 delete(s, 13, 4); end; AuthSHA1: begin s := HMAC_SHA1(s, Pass2Key(FPassword) + StringOfChar(#0, 44)); //strip to HMAC-SHA-96 delete(s, 13, 8); end; else s := ''; end; FAuthKey := s; end; end; auth := authbeg + ASNObject(FAuthKey, ASN1_OCTSTR) + ASNObject(FPrivKey, ASN1_OCTSTR); auth := ASNObject(auth, ASN1_SEQ); head := head + ASNObject(auth, ASN1_OCTSTR); Result := ASNObject(head + pdu, ASN1_SEQ); end else begin head := ASNObject(ASNEncInt(Self.FVersion), ASN1_INT) + ASNObject(Self.FCommunity, ASN1_OCTSTR); Result := ASNObject(head + pdu, ASN1_SEQ); end; inc(self.FID); end; procedure TSNMPRec.Clear; var i: Integer; begin FVersion := SNMP_V1; FCommunity := 'public'; FUserName := ''; FPassword := ''; FPDUType := 0; FErrorStatus := 0; FErrorIndex := 0; for i := 0 to FSNMPMibList.Count - 1 do TSNMPMib(FSNMPMibList[i]).Free; FSNMPMibList.Clear; FOldTrapEnterprise := ''; FOldTrapHost := ''; FOldTrapGen := 0; FOldTrapSpec := 0; FOldTrapTimeTicks := 0; FFlags := NoAuthNoPriv; FFlagReportable := false; FContextEngineID := ''; FContextName := ''; FAuthMode := AuthMD5; FAuthEngineID := ''; FAuthEngineBoots := 0; FAuthEngineTime := 0; FAuthEngineTimeStamp := 0; FAuthKey := ''; FPrivKey := ''; end; procedure TSNMPRec.MIBAdd(const MIB, Value: AnsiString; ValueType: Integer); var SNMPMib: TSNMPMib; begin SNMPMib := TSNMPMib.Create; SNMPMib.OID := MIB; SNMPMib.Value := Value; SNMPMib.ValueType := ValueType; FSNMPMibList.Add(SNMPMib); end; procedure TSNMPRec.MIBDelete(Index: Integer); begin if (Index >= 0) and (Index < MIBCount) then begin TSNMPMib(FSNMPMibList[Index]).Free; FSNMPMibList.Delete(Index); end; end; function TSNMPRec.MIBCount: integer; begin Result := FSNMPMibList.Count; end; function TSNMPRec.MIBByIndex(Index: Integer): TSNMPMib; begin Result := nil; if (Index >= 0) and (Index < MIBCount) then Result := TSNMPMib(FSNMPMibList[Index]); end; function TSNMPRec.MIBGet(const MIB: AnsiString): AnsiString; var i: Integer; begin Result := ''; for i := 0 to MIBCount - 1 do begin if ((TSNMPMib(FSNMPMibList[i])).OID = MIB) then begin Result := (TSNMPMib(FSNMPMibList[i])).Value; Break; end; end; end; {==============================================================================} constructor TSNMPSend.Create; begin inherited Create; FQuery := TSNMPRec.Create; FReply := TSNMPRec.Create; FQuery.Clear; FReply.Clear; FSock := TUDPBlockSocket.Create; FSock.Owner := self; FTimeout := 5000; FTargetPort := cSnmpProtocol; FHostIP := ''; end; destructor TSNMPSend.Destroy; begin FSock.Free; FReply.Free; FQuery.Free; inherited Destroy; end; function TSNMPSend.InternalSendSnmp(const Value: TSNMPRec): Boolean; begin FBuffer := Value.EncodeBuf; FSock.SendString(FBuffer); Result := FSock.LastError = 0; end; function TSNMPSend.InternalRecvSnmp(const Value: TSNMPRec): Boolean; begin Result := False; FReply.Clear; FHostIP := cAnyHost; FBuffer := FSock.RecvPacket(FTimeout); if FSock.LastError = 0 then begin FHostIP := FSock.GetRemoteSinIP; Result := Value.DecodeBuf(FBuffer); end; end; function TSNMPSend.InternalSendRequest(const QValue, RValue: TSNMPRec): Boolean; begin Result := False; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); if InternalSendSnmp(QValue) then Result := InternalRecvSnmp(RValue); end; function TSNMPSend.SendRequest: Boolean; var sync: TV3Sync; begin Result := False; if FQuery.FVersion = 3 then begin sync := GetV3Sync; FQuery.AuthEngineBoots := Sync.EngineBoots; FQuery.AuthEngineTime := Sync.EngineTime; FQuery.AuthEngineTimeStamp := Sync.EngineStamp; FQuery.AuthEngineID := Sync.EngineID; end; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); if InternalSendSnmp(FQuery) then Result := InternalRecvSnmp(FReply); end; function TSNMPSend.SendTrap: Boolean; begin FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); Result := InternalSendSnmp(FQuery); end; function TSNMPSend.RecvTrap: Boolean; begin FSock.Bind(FIPInterface, FTargetPort); Result := InternalRecvSnmp(FReply); end; function TSNMPSend.DoIt: Boolean; begin Result := SendRequest; end; function TSNMPSend.GetV3EngineID: AnsiString; var DisQuery: TSNMPRec; begin Result := ''; DisQuery := TSNMPRec.Create; try DisQuery.Version := 3; DisQuery.UserName := ''; DisQuery.FlagReportable := True; DisQuery.PDUType := PDUGetRequest; if InternalSendRequest(DisQuery, FReply) then Result := FReply.FAuthEngineID; finally DisQuery.Free; end; end; function TSNMPSend.GetV3Sync: TV3Sync; var SyncQuery: TSNMPRec; begin Result.EngineID := GetV3EngineID; Result.EngineBoots := FReply.AuthEngineBoots; Result.EngineTime := FReply.AuthEngineTime; Result.EngineStamp := FReply.AuthEngineTimeStamp; if Result.EngineTime = 0 then begin //still not have sync... SyncQuery := TSNMPRec.Create; try SyncQuery.Version := 3; SyncQuery.UserName := FQuery.UserName; SyncQuery.Password := FQuery.Password; SyncQuery.FlagReportable := True; SyncQuery.Flags := FQuery.Flags; SyncQuery.PDUType := PDUGetRequest; SyncQuery.AuthEngineID := FReply.FAuthEngineID; if InternalSendRequest(SyncQuery, FReply) then begin Result.EngineBoots := FReply.AuthEngineBoots; Result.EngineTime := FReply.AuthEngineTime; Result.EngineStamp := FReply.AuthEngineTimeStamp; end; finally SyncQuery.Free; end; end; end; {==============================================================================} function SNMPGet(const OID, Community, SNMPHost: AnsiString; var Value: AnsiString): Boolean; var SNMPSend: TSNMPSend; begin SNMPSend := TSNMPSend.Create; try SNMPSend.Query.Clear; SNMPSend.Query.Community := Community; SNMPSend.Query.PDUType := PDUGetRequest; SNMPSend.Query.MIBAdd(OID, '', ASN1_NULL); SNMPSend.TargetHost := SNMPHost; Result := SNMPSend.SendRequest; Value := ''; if Result then Value := SNMPSend.Reply.MIBGet(OID); finally SNMPSend.Free; end; end; function SNMPSet(const OID, Community, SNMPHost, Value: AnsiString; ValueType: Integer): Boolean; var SNMPSend: TSNMPSend; begin SNMPSend := TSNMPSend.Create; try SNMPSend.Query.Clear; SNMPSend.Query.Community := Community; SNMPSend.Query.PDUType := PDUSetRequest; SNMPSend.Query.MIBAdd(OID, Value, ValueType); SNMPSend.TargetHost := SNMPHost; Result := SNMPSend.Sendrequest = True; finally SNMPSend.Free; end; end; function InternalGetNext(const SNMPSend: TSNMPSend; var OID: AnsiString; const Community: AnsiString; var Value: AnsiString): Boolean; begin SNMPSend.Query.Clear; SNMPSend.Query.ID := SNMPSend.Query.ID + 1; SNMPSend.Query.Community := Community; SNMPSend.Query.PDUType := PDUGetNextRequest; SNMPSend.Query.MIBAdd(OID, '', ASN1_NULL); Result := SNMPSend.Sendrequest; Value := ''; if Result then if SNMPSend.Reply.SNMPMibList.Count > 0 then begin OID := TSNMPMib(SNMPSend.Reply.SNMPMibList[0]).OID; Value := TSNMPMib(SNMPSend.Reply.SNMPMibList[0]).Value; end; end; function SNMPGetNext(var OID: AnsiString; const Community, SNMPHost: AnsiString; var Value: AnsiString): Boolean; var SNMPSend: TSNMPSend; begin SNMPSend := TSNMPSend.Create; try SNMPSend.TargetHost := SNMPHost; Result := InternalGetNext(SNMPSend, OID, Community, Value); finally SNMPSend.Free; end; end; function SNMPGetTable(const BaseOID, Community, SNMPHost: AnsiString; const Value: TStrings): Boolean; var OID: AnsiString; s: AnsiString; col,row: String; x: integer; SNMPSend: TSNMPSend; RowList: TStringList; begin Value.Clear; SNMPSend := TSNMPSend.Create; RowList := TStringList.Create; try SNMPSend.TargetHost := SNMPHost; OID := BaseOID; repeat Result := InternalGetNext(SNMPSend, OID, Community, s); if Pos(BaseOID, OID) <> 1 then break; row := separateright(oid, baseoid + '.'); col := fetch(row, '.'); if IsBinaryString(s) then s := StrToHex(s); x := RowList.indexOf(Row); if x < 0 then begin x := RowList.add(Row); Value.Add(''); end; if (Value[x] <> '') then Value[x] := Value[x] + ','; Value[x] := Value[x] + AnsiQuotedStr(s, '"'); until not result; finally SNMPSend.Free; RowList.Free; end; end; function SNMPGetTableElement(const BaseOID, RowID, ColID, Community, SNMPHost: AnsiString; var Value: AnsiString): Boolean; var s: AnsiString; begin s := BaseOID + '.' + ColID + '.' + RowID; Result := SnmpGet(s, Community, SNMPHost, Value); end; function SendTrap(const Dest, Source, Enterprise, Community: AnsiString; Generic, Specific, Seconds: Integer; const MIBName, MIBValue: AnsiString; MIBtype: Integer): Integer; var SNMPSend: TSNMPSend; begin SNMPSend := TSNMPSend.Create; try SNMPSend.TargetHost := Dest; SNMPSend.TargetPort := cSnmpTrapProtocol; SNMPSend.Query.Community := Community; SNMPSend.Query.Version := SNMP_V1; SNMPSend.Query.PDUType := PDUTrap; SNMPSend.Query.OldTrapHost := Source; SNMPSend.Query.OldTrapEnterprise := Enterprise; SNMPSend.Query.OldTrapGen := Generic; SNMPSend.Query.OldTrapSpec := Specific; SNMPSend.Query.OldTrapTimeTicks := Seconds; SNMPSend.Query.MIBAdd(MIBName, MIBValue, MIBType); Result := Ord(SNMPSend.SendTrap); finally SNMPSend.Free; end; end; function RecvTrap(var Dest, Source, Enterprise, Community: AnsiString; var Generic, Specific, Seconds: Integer; const MIBName, MIBValue: TStringList): Integer; var SNMPSend: TSNMPSend; i: Integer; begin SNMPSend := TSNMPSend.Create; try Result := 0; SNMPSend.TargetPort := cSnmpTrapProtocol; if SNMPSend.RecvTrap then begin Result := 1; Dest := SNMPSend.HostIP; Community := SNMPSend.Reply.Community; Source := SNMPSend.Reply.OldTrapHost; Enterprise := SNMPSend.Reply.OldTrapEnterprise; Generic := SNMPSend.Reply.OldTrapGen; Specific := SNMPSend.Reply.OldTrapSpec; Seconds := SNMPSend.Reply.OldTrapTimeTicks; MIBName.Clear; MIBValue.Clear; for i := 0 to SNMPSend.Reply.SNMPMibList.Count - 1 do begin MIBName.Add(TSNMPMib(SNMPSend.Reply.SNMPMibList[i]).OID); MIBValue.Add(TSNMPMib(SNMPSend.Reply.SNMPMibList[i]).Value); end; end; finally SNMPSend.Free; end; end; end. TransGUI/synapse/source/lib/clamsend.pas0000644000000000000000000002153611366572451017254 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.001 | |==============================================================================| | Content: ClamAV-daemon client | |==============================================================================| | Copyright (c)2005-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2005-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract( ClamAV-daemon client) This unit is capable to do antivirus scan of your data by TCP channel to ClamD daemon from ClamAV. See more about ClamAV on @LINK(http://www.clamav.net) } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit clamsend; interface uses SysUtils, Classes, synsock, blcksock, synautil; const cClamProtocol = '3310'; type {:@abstract(Implementation of ClamAV-daemon client protocol) By this class you can scan any your data by ClamAV opensource antivirus. This class can connect to ClamD by TCP channel, send your data to ClamD and read result.} TClamSend = class(TSynaClient) private FSock: TTCPBlockSocket; FDSock: TTCPBlockSocket; FSession: boolean; function Login: boolean; virtual; function Logout: Boolean; virtual; function OpenStream: Boolean; virtual; public constructor Create; destructor Destroy; override; {:Call any command to ClamD. Used internally by other methods.} function DoCommand(const Value: AnsiString): AnsiString; virtual; {:Return ClamAV version and version of loaded databases.} function GetVersion: AnsiString; virtual; {:Scan content of TStrings.} function ScanStrings(const Value: TStrings): AnsiString; virtual; {:Scan content of TStream.} function ScanStream(const Value: TStream): AnsiString; virtual; {:Scan content of TStrings by new 0.95 API.} function ScanStrings2(const Value: TStrings): AnsiString; virtual; {:Scan content of TStream by new 0.95 API.} function ScanStream2(const Value: TStream): AnsiString; virtual; published {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; {:Socket object used for TCP data transfer operation. Good for seting OnStatus hook, etc.} property DSock: TTCPBlockSocket read FDSock; {:Can turn-on session mode of communication with ClamD. Default is @false, because ClamAV developers design their TCP code very badly and session mode is broken now (CVS-20051031). Maybe ClamAV developers fix their bugs and this mode will be possible in future.} property Session: boolean read FSession write FSession; end; implementation constructor TClamSend.Create; begin inherited Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FDSock := TTCPBlockSocket.Create; FDSock.Owner := self; FTimeout := 60000; FTargetPort := cClamProtocol; FSession := false; end; destructor TClamSend.Destroy; begin Logout; FDSock.Free; FSock.Free; inherited Destroy; end; function TClamSend.DoCommand(const Value: AnsiString): AnsiString; begin Result := ''; if not FSession then FSock.CloseSocket else FSock.SendString(Value + LF); if not FSession or (FSock.LastError <> 0) then begin if Login then FSock.SendString(Value + LF) else Exit; end; Result := FSock.RecvTerminated(FTimeout, LF); end; function TClamSend.Login: boolean; begin Result := False; Sock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError <> 0 then Exit; FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError <> 0 then Exit; if FSession then FSock.SendString('SESSION' + LF); Result := FSock.LastError = 0; end; function TClamSend.Logout: Boolean; begin FSock.SendString('END' + LF); Result := FSock.LastError = 0; FSock.CloseSocket; end; function TClamSend.GetVersion: AnsiString; begin Result := DoCommand('nVERSION'); end; function TClamSend.OpenStream: Boolean; var S: AnsiString; begin Result := False; s := DoCommand('nSTREAM'); if (s <> '') and (Copy(s, 1, 4) = 'PORT') then begin s := SeparateRight(s, ' '); FDSock.CloseSocket; FDSock.Bind(FIPInterface, cAnyPort); if FDSock.LastError <> 0 then Exit; FDSock.Connect(FTargetHost, s); if FDSock.LastError <> 0 then Exit; Result := True; end; end; function TClamSend.ScanStrings(const Value: TStrings): AnsiString; begin Result := ''; if OpenStream then begin DSock.SendString(Value.Text); DSock.CloseSocket; Result := FSock.RecvTerminated(FTimeout, LF); end; end; function TClamSend.ScanStream(const Value: TStream): AnsiString; begin Result := ''; if OpenStream then begin DSock.SendStreamRaw(Value); DSock.CloseSocket; Result := FSock.RecvTerminated(FTimeout, LF); end; end; function TClamSend.ScanStrings2(const Value: TStrings): AnsiString; var i: integer; s: AnsiString; begin Result := ''; if not FSession then FSock.CloseSocket else FSock.sendstring('nINSTREAM' + LF); if not FSession or (FSock.LastError <> 0) then begin if Login then FSock.sendstring('nINSTREAM' + LF) else Exit; end; s := Value.text; i := length(s); FSock.SendString(CodeLongint(i) + s + #0#0#0#0); Result := FSock.RecvTerminated(FTimeout, LF); end; function TClamSend.ScanStream2(const Value: TStream): AnsiString; var i: integer; begin Result := ''; if not FSession then FSock.CloseSocket else FSock.sendstring('nINSTREAM' + LF); if not FSession or (FSock.LastError <> 0) then begin if Login then FSock.sendstring('nINSTREAM' + LF) else Exit; end; i := value.Size; FSock.SendString(CodeLongint(i)); FSock.SendStreamRaw(Value); FSock.SendString(#0#0#0#0); Result := FSock.RecvTerminated(FTimeout, LF); end; end. TransGUI/synapse/source/lib/sslinux.pas0000644000000000000000000011601711366572451017172 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.000.009 | |==============================================================================| | Content: Socket Independent Platform Layer - Linux definition include | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2003-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@exclude} {$IFDEF LINUX} //{$DEFINE FORCEOLDAPI} {Note about define FORCEOLDAPI: If you activate this compiler directive, then is allways used old socket API for name resolution. If you leave this directive inactive, then the new API is used, when running system allows it. For IPv6 support you must have new API! } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} interface uses SyncObjs, SysUtils, Classes, synafpc, Libc; function InitSocketInterface(stack: string): Boolean; function DestroySocketInterface: Boolean; const WinsockLevel = $0202; type u_char = Char; u_short = Word; u_int = Integer; u_long = Longint; pu_long = ^u_long; pu_short = ^u_short; TSocket = u_int; TAddrFamily = integer; TMemory = pointer; const DLLStackName = 'libc.so.6'; cLocalhost = '127.0.0.1'; cAnyHost = '0.0.0.0'; cBroadcast = '255.255.255.255'; c6Localhost = '::1'; c6AnyHost = '::0'; c6Broadcast = 'ffff::1'; cAnyPort = '0'; type DWORD = Integer; __fd_mask = LongWord; const __FD_SETSIZE = 1024; __NFDBITS = 8 * sizeof(__fd_mask); type __fd_set = {packed} record fds_bits: packed array[0..(__FD_SETSIZE div __NFDBITS)-1] of __fd_mask; end; TFDSet = __fd_set; PFDSet = ^TFDSet; const FIONREAD = $541B; FIONBIO = $5421; FIOASYNC = $5452; type PTimeVal = ^TTimeVal; TTimeVal = packed record tv_sec: Longint; tv_usec: Longint; end; const IPPROTO_IP = 0; { Dummy } IPPROTO_ICMP = 1; { Internet Control Message Protocol } IPPROTO_IGMP = 2; { Internet Group Management Protocol} IPPROTO_TCP = 6; { TCP } IPPROTO_UDP = 17; { User Datagram Protocol } IPPROTO_IPV6 = 41; IPPROTO_ICMPV6 = 58; IPPROTO_RM = 113; IPPROTO_RAW = 255; IPPROTO_MAX = 256; type PInAddr = ^TInAddr; TInAddr = packed record case integer of 0: (S_bytes: packed array [0..3] of byte); 1: (S_addr: u_long); end; PSockAddrIn = ^TSockAddrIn; TSockAddrIn = packed record case Integer of 0: (sin_family: u_short; sin_port: u_short; sin_addr: TInAddr; sin_zero: array[0..7] of Char); 1: (sa_family: u_short; sa_data: array[0..13] of Char) end; TIP_mreq = record imr_multiaddr: TInAddr; { IP multicast address of group } imr_interface: TInAddr; { local IP address of interface } end; PInAddr6 = ^TInAddr6; TInAddr6 = packed record case integer of 0: (S6_addr: packed array [0..15] of byte); 1: (u6_addr8: packed array [0..15] of byte); 2: (u6_addr16: packed array [0..7] of word); 3: (u6_addr32: packed array [0..3] of integer); end; PSockAddrIn6 = ^TSockAddrIn6; TSockAddrIn6 = packed record sin6_family: u_short; // AF_INET6 sin6_port: u_short; // Transport level port number sin6_flowinfo: u_long; // IPv6 flow information sin6_addr: TInAddr6; // IPv6 address sin6_scope_id: u_long; // Scope Id: IF number for link-local // SITE id for site-local end; TIPv6_mreq = record ipv6mr_multiaddr: TInAddr6; // IPv6 multicast address. ipv6mr_interface: integer; // Interface index. padding: u_long; end; PHostEnt = ^THostEnt; THostent = record h_name: PChar; h_aliases: PPChar; h_addrtype: Integer; h_length: Cardinal; case Byte of 0: (h_addr_list: PPChar); 1: (h_addr: PPChar); end; PNetEnt = ^TNetEnt; TNetEnt = record n_name: PChar; n_aliases: PPChar; n_addrtype: Integer; n_net: uint32_t; end; PServEnt = ^TServEnt; TServEnt = record s_name: PChar; s_aliases: PPChar; s_port: Integer; s_proto: PChar; end; PProtoEnt = ^TProtoEnt; TProtoEnt = record p_name: PChar; p_aliases: ^PChar; p_proto: u_short; end; const INADDR_ANY = $00000000; INADDR_LOOPBACK = $7F000001; INADDR_BROADCAST = $FFFFFFFF; INADDR_NONE = $FFFFFFFF; ADDR_ANY = INADDR_ANY; INVALID_SOCKET = TSocket(NOT(0)); SOCKET_ERROR = -1; Const IP_TOS = 1; { int; IP type of service and precedence. } IP_TTL = 2; { int; IP time to live. } IP_HDRINCL = 3; { int; Header is included with data. } IP_OPTIONS = 4; { ip_opts; IP per-packet options. } IP_ROUTER_ALERT = 5; { bool } IP_RECVOPTS = 6; { bool } IP_RETOPTS = 7; { bool } IP_PKTINFO = 8; { bool } IP_PKTOPTIONS = 9; IP_PMTUDISC = 10; { obsolete name? } IP_MTU_DISCOVER = 10; { int; see below } IP_RECVERR = 11; { bool } IP_RECVTTL = 12; { bool } IP_RECVTOS = 13; { bool } IP_MULTICAST_IF = 32; { in_addr; set/get IP multicast i/f } IP_MULTICAST_TTL = 33; { u_char; set/get IP multicast ttl } IP_MULTICAST_LOOP = 34; { i_char; set/get IP multicast loopback } IP_ADD_MEMBERSHIP = 35; { ip_mreq; add an IP group membership } IP_DROP_MEMBERSHIP = 36; { ip_mreq; drop an IP group membership } SOL_SOCKET = 1; SO_DEBUG = 1; SO_REUSEADDR = 2; SO_TYPE = 3; SO_ERROR = 4; SO_DONTROUTE = 5; SO_BROADCAST = 6; SO_SNDBUF = 7; SO_RCVBUF = 8; SO_KEEPALIVE = 9; SO_OOBINLINE = 10; SO_NO_CHECK = 11; SO_PRIORITY = 12; SO_LINGER = 13; SO_BSDCOMPAT = 14; SO_REUSEPORT = 15; SO_PASSCRED = 16; SO_PEERCRED = 17; SO_RCVLOWAT = 18; SO_SNDLOWAT = 19; SO_RCVTIMEO = 20; SO_SNDTIMEO = 21; { Security levels - as per NRL IPv6 - don't actually do anything } SO_SECURITY_AUTHENTICATION = 22; SO_SECURITY_ENCRYPTION_TRANSPORT = 23; SO_SECURITY_ENCRYPTION_NETWORK = 24; SO_BINDTODEVICE = 25; { Socket filtering } SO_ATTACH_FILTER = 26; SO_DETACH_FILTER = 27; SOMAXCONN = 128; IPV6_UNICAST_HOPS = 16; IPV6_MULTICAST_IF = 17; IPV6_MULTICAST_HOPS = 18; IPV6_MULTICAST_LOOP = 19; IPV6_JOIN_GROUP = 20; IPV6_LEAVE_GROUP = 21; MSG_NOSIGNAL = $4000; // Do not generate SIGPIPE. // getnameinfo constants NI_MAXHOST = 1025; NI_MAXSERV = 32; NI_NOFQDN = $4; NI_NUMERICHOST = $1; NI_NAMEREQD = $8; NI_NUMERICSERV = $2; NI_DGRAM = $10; const SOCK_STREAM = 1; { stream socket } SOCK_DGRAM = 2; { datagram socket } SOCK_RAW = 3; { raw-protocol interface } SOCK_RDM = 4; { reliably-delivered message } SOCK_SEQPACKET = 5; { sequenced packet stream } { TCP options. } TCP_NODELAY = $0001; { Address families. } AF_UNSPEC = 0; { unspecified } AF_INET = 2; { internetwork: UDP, TCP, etc. } AF_INET6 = 10; { Internetwork Version 6 } AF_MAX = 24; { Protocol families, same as address families for now. } PF_UNSPEC = AF_UNSPEC; PF_INET = AF_INET; PF_INET6 = AF_INET6; PF_MAX = AF_MAX; type { Structure used by kernel to store most addresses. } PSockAddr = ^TSockAddr; TSockAddr = TSockAddrIn; { Structure used by kernel to pass protocol information in raw sockets. } PSockProto = ^TSockProto; TSockProto = packed record sp_family: u_short; sp_protocol: u_short; end; type PAddrInfo = ^TAddrInfo; TAddrInfo = record ai_flags: integer; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST. ai_family: integer; // PF_xxx. ai_socktype: integer; // SOCK_xxx. ai_protocol: integer; // 0 or IPPROTO_xxx for IPv4 and IPv6. ai_addrlen: u_int; // Length of ai_addr. ai_addr: PSockAddr; // Binary address. ai_canonname: PChar; // Canonical name for nodename. ai_next: PAddrInfo; // Next structure in linked list. end; const // Flags used in "hints" argument to getaddrinfo(). AI_PASSIVE = $1; // Socket address will be used in bind() call. AI_CANONNAME = $2; // Return canonical name in first ai_canonname. AI_NUMERICHOST = $4; // Nodename must be a numeric address string. type { Structure used for manipulating linger option. } PLinger = ^TLinger; TLinger = packed record l_onoff: integer; l_linger: integer; end; const MSG_OOB = $01; // Process out-of-band data. MSG_PEEK = $02; // Peek at incoming messages. const WSAEINTR = EINTR; WSAEBADF = EBADF; WSAEACCES = EACCES; WSAEFAULT = EFAULT; WSAEINVAL = EINVAL; WSAEMFILE = EMFILE; WSAEWOULDBLOCK = EWOULDBLOCK; WSAEINPROGRESS = EINPROGRESS; WSAEALREADY = EALREADY; WSAENOTSOCK = ENOTSOCK; WSAEDESTADDRREQ = EDESTADDRREQ; WSAEMSGSIZE = EMSGSIZE; WSAEPROTOTYPE = EPROTOTYPE; WSAENOPROTOOPT = ENOPROTOOPT; WSAEPROTONOSUPPORT = EPROTONOSUPPORT; WSAESOCKTNOSUPPORT = ESOCKTNOSUPPORT; WSAEOPNOTSUPP = EOPNOTSUPP; WSAEPFNOSUPPORT = EPFNOSUPPORT; WSAEAFNOSUPPORT = EAFNOSUPPORT; WSAEADDRINUSE = EADDRINUSE; WSAEADDRNOTAVAIL = EADDRNOTAVAIL; WSAENETDOWN = ENETDOWN; WSAENETUNREACH = ENETUNREACH; WSAENETRESET = ENETRESET; WSAECONNABORTED = ECONNABORTED; WSAECONNRESET = ECONNRESET; WSAENOBUFS = ENOBUFS; WSAEISCONN = EISCONN; WSAENOTCONN = ENOTCONN; WSAESHUTDOWN = ESHUTDOWN; WSAETOOMANYREFS = ETOOMANYREFS; WSAETIMEDOUT = ETIMEDOUT; WSAECONNREFUSED = ECONNREFUSED; WSAELOOP = ELOOP; WSAENAMETOOLONG = ENAMETOOLONG; WSAEHOSTDOWN = EHOSTDOWN; WSAEHOSTUNREACH = EHOSTUNREACH; WSAENOTEMPTY = ENOTEMPTY; WSAEPROCLIM = -1; WSAEUSERS = EUSERS; WSAEDQUOT = EDQUOT; WSAESTALE = ESTALE; WSAEREMOTE = EREMOTE; WSASYSNOTREADY = -2; WSAVERNOTSUPPORTED = -3; WSANOTINITIALISED = -4; WSAEDISCON = -5; WSAHOST_NOT_FOUND = HOST_NOT_FOUND; WSATRY_AGAIN = TRY_AGAIN; WSANO_RECOVERY = NO_RECOVERY; WSANO_DATA = -6; EAI_BADFLAGS = -1; { Invalid value for `ai_flags' field. } EAI_NONAME = -2; { NAME or SERVICE is unknown. } EAI_AGAIN = -3; { Temporary failure in name resolution. } EAI_FAIL = -4; { Non-recoverable failure in name res. } EAI_NODATA = -5; { No address associated with NAME. } EAI_FAMILY = -6; { `ai_family' not supported. } EAI_SOCKTYPE = -7; { `ai_socktype' not supported. } EAI_SERVICE = -8; { SERVICE not supported for `ai_socktype'. } EAI_ADDRFAMILY = -9; { Address family for NAME not supported. } EAI_MEMORY = -10; { Memory allocation failure. } EAI_SYSTEM = -11; { System error returned in `errno'. } const WSADESCRIPTION_LEN = 256; WSASYS_STATUS_LEN = 128; type PWSAData = ^TWSAData; TWSAData = packed record wVersion: Word; wHighVersion: Word; szDescription: array[0..WSADESCRIPTION_LEN] of Char; szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char; iMaxSockets: Word; iMaxUdpDg: Word; lpVendorInfo: PChar; end; function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6):boolean; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); var in6addr_any, in6addr_loopback : TInAddr6; procedure FD_CLR(Socket: TSocket; var FDSet: TFDSet); function FD_ISSET(Socket: TSocket; var FDSet: TFDSet): Boolean; procedure FD_SET(Socket: TSocket; var FDSet: TFDSet); procedure FD_ZERO(var FDSet: TFDSet); {=============================================================================} type TWSAStartup = function(wVersionRequired: Word; var WSData: TWSAData): Integer; cdecl; TWSACleanup = function: Integer; cdecl; TWSAGetLastError = function: Integer; cdecl; TGetServByName = function(name, proto: PChar): PServEnt; cdecl; TGetServByPort = function(port: Integer; proto: PChar): PServEnt; cdecl; TGetProtoByName = function(name: PChar): PProtoEnt; cdecl; TGetProtoByNumber = function(proto: Integer): PProtoEnt; cdecl; TGetHostByName = function(name: PChar): PHostEnt; cdecl; TGetHostByAddr = function(addr: Pointer; len, Struc: Integer): PHostEnt; cdecl; TGetHostName = function(name: PChar; len: Integer): Integer; cdecl; TShutdown = function(s: TSocket; how: Integer): Integer; cdecl; TSetSockOpt = function(s: TSocket; level, optname: Integer; optval: PChar; optlen: Integer): Integer; cdecl; TGetSockOpt = function(s: TSocket; level, optname: Integer; optval: PChar; var optlen: Integer): Integer; cdecl; TSendTo = function(s: TSocket; const Buf; len, flags: Integer; addrto: PSockAddr; tolen: Integer): Integer; cdecl; TSend = function(s: TSocket; const Buf; len, flags: Integer): Integer; cdecl; TRecv = function(s: TSocket; var Buf; len, flags: Integer): Integer; cdecl; TRecvFrom = function(s: TSocket; var Buf; len, flags: Integer; from: PSockAddr; var fromlen: Integer): Integer; cdecl; Tntohs = function(netshort: u_short): u_short; cdecl; Tntohl = function(netlong: u_long): u_long; cdecl; TListen = function(s: TSocket; backlog: Integer): Integer; cdecl; TIoctlSocket = function(s: TSocket; cmd: DWORD; var arg: integer): Integer; cdecl; TInet_ntoa = function(inaddr: TInAddr): PChar; cdecl; TInet_addr = function(cp: PChar): u_long; cdecl; Thtons = function(hostshort: u_short): u_short; cdecl; Thtonl = function(hostlong: u_long): u_long; cdecl; TGetSockName = function(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; cdecl; TGetPeerName = function(s: TSocket; name: PSockAddr; var namelen: Integer): Integer; cdecl; TConnect = function(s: TSocket; name: PSockAddr; namelen: Integer): Integer; cdecl; TCloseSocket = function(s: TSocket): Integer; cdecl; TBind = function(s: TSocket; addr: PSockAddr; namelen: Integer): Integer; cdecl; TAccept = function(s: TSocket; addr: PSockAddr; var addrlen: Integer): TSocket; cdecl; TTSocket = function(af, Struc, Protocol: Integer): TSocket; cdecl; TSelect = function(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Longint; cdecl; TGetAddrInfo = function(NodeName: PChar; ServName: PChar; Hints: PAddrInfo; var Addrinfo: PAddrInfo): integer; cdecl; TFreeAddrInfo = procedure(ai: PAddrInfo); cdecl; TGetNameInfo = function( addr: PSockAddr; namelen: Integer; host: PChar; hostlen: DWORD; serv: PChar; servlen: DWORD; flags: integer): integer; cdecl; var WSAStartup: TWSAStartup = nil; WSACleanup: TWSACleanup = nil; WSAGetLastError: TWSAGetLastError = nil; GetServByName: TGetServByName = nil; GetServByPort: TGetServByPort = nil; GetProtoByName: TGetProtoByName = nil; GetProtoByNumber: TGetProtoByNumber = nil; GetHostByName: TGetHostByName = nil; GetHostByAddr: TGetHostByAddr = nil; ssGetHostName: TGetHostName = nil; Shutdown: TShutdown = nil; SetSockOpt: TSetSockOpt = nil; GetSockOpt: TGetSockOpt = nil; ssSendTo: TSendTo = nil; ssSend: TSend = nil; ssRecv: TRecv = nil; ssRecvFrom: TRecvFrom = nil; ntohs: Tntohs = nil; ntohl: Tntohl = nil; Listen: TListen = nil; IoctlSocket: TIoctlSocket = nil; Inet_ntoa: TInet_ntoa = nil; Inet_addr: TInet_addr = nil; htons: Thtons = nil; htonl: Thtonl = nil; ssGetSockName: TGetSockName = nil; ssGetPeerName: TGetPeerName = nil; ssConnect: TConnect = nil; CloseSocket: TCloseSocket = nil; ssBind: TBind = nil; ssAccept: TAccept = nil; Socket: TTSocket = nil; Select: TSelect = nil; GetAddrInfo: TGetAddrInfo = nil; FreeAddrInfo: TFreeAddrInfo = nil; GetNameInfo: TGetNameInfo = nil; function LSWSAStartup(wVersionRequired: Word; var WSData: TWSAData): Integer; cdecl; function LSWSACleanup: Integer; cdecl; function LSWSAGetLastError: Integer; cdecl; var SynSockCS: SyncObjs.TCriticalSection; SockEnhancedApi: Boolean; SockWship6Api: Boolean; type TVarSin = packed record case integer of 0: (AddressFamily: u_short); 1: ( case sin_family: u_short of AF_INET: (sin_port: u_short; sin_addr: TInAddr; sin_zero: array[0..7] of Char); AF_INET6: (sin6_port: u_short; sin6_flowinfo: u_long; sin6_addr: TInAddr6; sin6_scope_id: u_long); ); end; function SizeOfVarSin(sin: TVarSin): integer; function Bind(s: TSocket; const addr: TVarSin): Integer; function Connect(s: TSocket; const name: TVarSin): Integer; function GetSockName(s: TSocket; var name: TVarSin): Integer; function GetPeerName(s: TSocket; var name: TVarSin): Integer; function GetHostName: string; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; function Accept(s: TSocket; var addr: TVarSin): TSocket; function IsNewApi(Family: integer): Boolean; function SetVarSin(var Sin: TVarSin; IP, Port: string; Family, SockProtocol, SockType: integer; PreferIP4: Boolean): integer; function GetSinIP(Sin: TVarSin): string; function GetSinPort(Sin: TVarSin): Integer; procedure ResolveNameToIP(Name: string; Family, SockProtocol, SockType: integer; const IPList: TStrings); function ResolveIPToName(IP: string; Family, SockProtocol, SockType: integer): string; function ResolvePort(Port: string; Family, SockProtocol, SockType: integer): Word; {==============================================================================} implementation var SynSockCount: Integer = 0; LibHandle: TLibHandle = 0; Libwship6Handle: TLibHandle = 0; function IN6_IS_ADDR_UNSPECIFIED(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr32[0] = 0) and (a^.u6_addr32[1] = 0) and (a^.u6_addr32[2] = 0) and (a^.u6_addr32[3] = 0)); end; function IN6_IS_ADDR_LOOPBACK(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr32[0] = 0) and (a^.u6_addr32[1] = 0) and (a^.u6_addr32[2] = 0) and (a^.u6_addr8[12] = 0) and (a^.u6_addr8[13] = 0) and (a^.u6_addr8[14] = 0) and (a^.u6_addr8[15] = 1)); end; function IN6_IS_ADDR_LINKLOCAL(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr8[0] = $FE) and (a^.u6_addr8[1] = $80)); end; function IN6_IS_ADDR_SITELOCAL(const a: PInAddr6): boolean; begin Result := ((a^.u6_addr8[0] = $FE) and (a^.u6_addr8[1] = $C0)); end; function IN6_IS_ADDR_MULTICAST(const a: PInAddr6): boolean; begin Result := (a^.u6_addr8[0] = $FF); end; function IN6_ADDR_EQUAL(const a: PInAddr6; const b: PInAddr6): boolean; begin Result := (CompareMem( a, b, sizeof(TInAddr6))); end; procedure SET_IN6_IF_ADDR_ANY (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); end; procedure SET_LOOPBACK_ADDR6 (const a: PInAddr6); begin FillChar(a^, sizeof(TInAddr6), 0); a^.u6_addr8[15] := 1; end; {=============================================================================} var {$IFNDEF VER1_0} //FTP version 1.0.x errno_loc: function: PInteger cdecl = nil; {$ELSE} errno_loc: function: PInteger = nil; cdecl; {$ENDIF} function LSWSAStartup(wVersionRequired: Word; var WSData: TWSAData): Integer; begin with WSData do begin wVersion := wVersionRequired; wHighVersion := $202; szDescription := 'Synsock - Synapse Platform Independent Socket Layer'; szSystemStatus := 'Running on Linux'; iMaxSockets := 32768; iMaxUdpDg := 8192; end; Result := 0; end; function LSWSACleanup: Integer; begin Result := 0; end; function LSWSAGetLastError: Integer; var p: PInteger; begin p := errno_loc; Result := p^; end; function __FDELT(Socket: TSocket): Integer; begin Result := Socket div __NFDBITS; end; function __FDMASK(Socket: TSocket): __fd_mask; begin Result := LongWord(1) shl (Socket mod __NFDBITS); end; function FD_ISSET(Socket: TSocket; var fdset: TFDSet): Boolean; begin Result := (fdset.fds_bits[__FDELT(Socket)] and __FDMASK(Socket)) <> 0; end; procedure FD_SET(Socket: TSocket; var fdset: TFDSet); begin fdset.fds_bits[__FDELT(Socket)] := fdset.fds_bits[__FDELT(Socket)] or __FDMASK(Socket); end; procedure FD_CLR(Socket: TSocket; var fdset: TFDSet); begin fdset.fds_bits[__FDELT(Socket)] := fdset.fds_bits[__FDELT(Socket)] and (not __FDMASK(Socket)); end; procedure FD_ZERO(var fdset: TFDSet); var I: Integer; begin with fdset do for I := Low(fds_bits) to High(fds_bits) do fds_bits[I] := 0; end; {=============================================================================} function SizeOfVarSin(sin: TVarSin): integer; begin case sin.sin_family of AF_INET: Result := SizeOf(TSockAddrIn); AF_INET6: Result := SizeOf(TSockAddrIn6); else Result := 0; end; end; {=============================================================================} function Bind(s: TSocket; const addr: TVarSin): Integer; begin Result := ssBind(s, @addr, SizeOfVarSin(addr)); end; function Connect(s: TSocket; const name: TVarSin): Integer; begin Result := ssConnect(s, @name, SizeOfVarSin(name)); end; function GetSockName(s: TSocket; var name: TVarSin): Integer; var len: integer; begin len := SizeOf(name); FillChar(name, len, 0); Result := ssGetSockName(s, @name, Len); end; function GetPeerName(s: TSocket; var name: TVarSin): Integer; var len: integer; begin len := SizeOf(name); FillChar(name, len, 0); Result := ssGetPeerName(s, @name, Len); end; function GetHostName: string; var s: string; begin Result := ''; setlength(s, 255); ssGetHostName(pchar(s), Length(s) - 1); Result := Pchar(s); end; function Send(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; begin Result := ssSend(s, Buf^, len, flags); end; function Recv(s: TSocket; Buf: TMemory; len, flags: Integer): Integer; begin Result := ssRecv(s, Buf^, len, flags); end; function SendTo(s: TSocket; Buf: TMemory; len, flags: Integer; addrto: TVarSin): Integer; begin Result := ssSendTo(s, Buf^, len, flags, @addrto, SizeOfVarSin(addrto)); end; function RecvFrom(s: TSocket; Buf: TMemory; len, flags: Integer; var from: TVarSin): Integer; var x: integer; begin x := SizeOf(from); Result := ssRecvFrom(s, Buf^, len, flags, @from, x); end; function Accept(s: TSocket; var addr: TVarSin): TSocket; var x: integer; begin x := SizeOf(addr); Result := ssAccept(s, @addr, x); end; {=============================================================================} function IsNewApi(Family: integer): Boolean; begin Result := SockEnhancedApi; if not Result then Result := (Family = AF_INET6) and SockWship6Api; end; function SetVarSin(var Sin: TVarSin; IP, Port: string; Family, SockProtocol, SockType: integer; PreferIP4: Boolean): integer; type pu_long = ^u_long; var ProtoEnt: PProtoEnt; ServEnt: PServEnt; HostEnt: PHostEnt; r: integer; Hints1, Hints2: TAddrInfo; Sin1, Sin2: TVarSin; TwoPass: boolean; function GetAddr(const IP, port: string; Hints: TAddrInfo; var Sin: TVarSin): integer; var Addr: PAddrInfo; begin Addr := nil; try FillChar(Sin, Sizeof(Sin), 0); if Hints.ai_socktype = SOCK_RAW then begin Hints.ai_socktype := 0; Hints.ai_protocol := 0; Result := synsock.GetAddrInfo(PChar(IP), nil, @Hints, Addr); end else begin if (IP = cAnyHost) or (IP = c6AnyHost) then begin Hints.ai_flags := AI_PASSIVE; Result := synsock.GetAddrInfo(nil, PChar(Port), @Hints, Addr); end else if (IP = cLocalhost) or (IP = c6Localhost) then begin Result := synsock.GetAddrInfo(nil, PChar(Port), @Hints, Addr); end else begin Result := synsock.GetAddrInfo(PChar(IP), PChar(Port), @Hints, Addr); end; end; if Result = 0 then if (Addr <> nil) then Move(Addr^.ai_addr^, Sin, Addr^.ai_addrlen); finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; begin Result := 0; FillChar(Sin, Sizeof(Sin), 0); if not IsNewApi(family) then begin SynSockCS.Enter; try Sin.sin_family := AF_INET; ProtoEnt := synsock.GetProtoByNumber(SockProtocol); ServEnt := nil; if ProtoEnt <> nil then ServEnt := synsock.GetServByName(PChar(Port), ProtoEnt^.p_name); if ServEnt = nil then Sin.sin_port := synsock.htons(StrToIntDef(Port, 0)) else Sin.sin_port := ServEnt^.s_port; if IP = cBroadcast then Sin.sin_addr.s_addr := u_long(INADDR_BROADCAST) else begin Sin.sin_addr.s_addr := synsock.inet_addr(PChar(IP)); if Sin.sin_addr.s_addr = u_long(INADDR_NONE) then begin HostEnt := synsock.GetHostByName(PChar(IP)); Result := synsock.WSAGetLastError; if HostEnt <> nil then Sin.sin_addr.S_addr := u_long(Pu_long(HostEnt^.h_addr_list^)^); end; end; finally SynSockCS.Leave; end; end else begin FillChar(Hints1, Sizeof(Hints1), 0); FillChar(Hints2, Sizeof(Hints2), 0); TwoPass := False; if Family = AF_UNSPEC then begin if PreferIP4 then begin Hints1.ai_family := AF_INET; Hints2.ai_family := AF_INET6; TwoPass := True; end else begin Hints2.ai_family := AF_INET; Hints1.ai_family := AF_INET6; TwoPass := True; end; end else Hints1.ai_family := Family; Hints1.ai_socktype := SockType; Hints1.ai_protocol := SockProtocol; Hints2.ai_socktype := Hints1.ai_socktype; Hints2.ai_protocol := Hints1.ai_protocol; r := GetAddr(IP, Port, Hints1, Sin1); Result := r; sin := sin1; if r <> 0 then if TwoPass then begin r := GetAddr(IP, Port, Hints2, Sin2); Result := r; if r = 0 then sin := sin2; end; end; end; function GetSinIP(Sin: TVarSin): string; var p: PChar; host, serv: string; hostlen, servlen: integer; r: integer; begin Result := ''; if not IsNewApi(Sin.AddressFamily) then begin p := synsock.inet_ntoa(Sin.sin_addr); if p <> nil then Result := p; end else begin hostlen := NI_MAXHOST; servlen := NI_MAXSERV; setlength(host, hostlen); setlength(serv, servlen); r := getnameinfo(@sin, SizeOfVarSin(sin), PChar(host), hostlen, PChar(serv), servlen, NI_NUMERICHOST + NI_NUMERICSERV); if r = 0 then Result := PChar(host); end; end; function GetSinPort(Sin: TVarSin): Integer; begin if (Sin.sin_family = AF_INET6) then Result := synsock.ntohs(Sin.sin6_port) else Result := synsock.ntohs(Sin.sin_port); end; procedure ResolveNameToIP(Name: string; Family, SockProtocol, SockType: integer; const IPList: TStrings); type TaPInAddr = array[0..250] of PInAddr; PaPInAddr = ^TaPInAddr; var Hints: TAddrInfo; Addr: PAddrInfo; AddrNext: PAddrInfo; r: integer; host, serv: string; hostlen, servlen: integer; RemoteHost: PHostEnt; IP: u_long; PAdrPtr: PaPInAddr; i: Integer; s: string; InAddr: TInAddr; begin IPList.Clear; if not IsNewApi(Family) then begin IP := synsock.inet_addr(PChar(Name)); if IP = u_long(INADDR_NONE) then begin SynSockCS.Enter; try RemoteHost := synsock.GetHostByName(PChar(Name)); if RemoteHost <> nil then begin PAdrPtr := PAPInAddr(RemoteHost^.h_addr_list); i := 0; while PAdrPtr^[i] <> nil do begin InAddr := PAdrPtr^[i]^; s := Format('%d.%d.%d.%d', [InAddr.S_bytes[0], InAddr.S_bytes[1], InAddr.S_bytes[2], InAddr.S_bytes[3]]); IPList.Add(s); Inc(i); end; end; finally SynSockCS.Leave; end; end else IPList.Add(Name); end else begin Addr := nil; try FillChar(Hints, Sizeof(Hints), 0); Hints.ai_family := AF_UNSPEC; Hints.ai_socktype := SockType; Hints.ai_protocol := SockProtocol; Hints.ai_flags := 0; r := synsock.GetAddrInfo(PChar(Name), nil, @Hints, Addr); if r = 0 then begin AddrNext := Addr; while not(AddrNext = nil) do begin if not(((Family = AF_INET6) and (AddrNext^.ai_family = AF_INET)) or ((Family = AF_INET) and (AddrNext^.ai_family = AF_INET6))) then begin hostlen := NI_MAXHOST; servlen := NI_MAXSERV; setlength(host, hostlen); setlength(serv, servlen); r := getnameinfo(AddrNext^.ai_addr, AddrNext^.ai_addrlen, PChar(host), hostlen, PChar(serv), servlen, NI_NUMERICHOST + NI_NUMERICSERV); if r = 0 then begin host := PChar(host); IPList.Add(host); end; end; AddrNext := AddrNext^.ai_next; end; end; finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; if IPList.Count = 0 then IPList.Add(cAnyHost); end; function ResolvePort(Port: string; Family, SockProtocol, SockType: integer): Word; var ProtoEnt: PProtoEnt; ServEnt: PServEnt; Hints: TAddrInfo; Addr: PAddrInfo; r: integer; begin Result := 0; if not IsNewApi(Family) then begin SynSockCS.Enter; try ProtoEnt := synsock.GetProtoByNumber(SockProtocol); ServEnt := nil; if ProtoEnt <> nil then ServEnt := synsock.GetServByName(PChar(Port), ProtoEnt^.p_name); if ServEnt = nil then Result := StrToIntDef(Port, 0) else Result := synsock.htons(ServEnt^.s_port); finally SynSockCS.Leave; end; end else begin Addr := nil; try FillChar(Hints, Sizeof(Hints), 0); Hints.ai_family := AF_UNSPEC; Hints.ai_socktype := SockType; Hints.ai_protocol := Sockprotocol; Hints.ai_flags := AI_PASSIVE; r := synsock.GetAddrInfo(nil, PChar(Port), @Hints, Addr); if (r = 0) and Assigned(Addr) then begin if Addr^.ai_family = AF_INET then Result := synsock.htons(Addr^.ai_addr^.sin_port); if Addr^.ai_family = AF_INET6 then Result := synsock.htons(PSockAddrIn6(Addr^.ai_addr)^.sin6_port); end; finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; end; function ResolveIPToName(IP: string; Family, SockProtocol, SockType: integer): string; var Hints: TAddrInfo; Addr: PAddrInfo; r: integer; host, serv: string; hostlen, servlen: integer; RemoteHost: PHostEnt; IPn: u_long; begin Result := IP; if not IsNewApi(Family) then begin IPn := synsock.inet_addr(PChar(IP)); if IPn <> u_long(INADDR_NONE) then begin SynSockCS.Enter; try RemoteHost := GetHostByAddr(@IPn, SizeOf(IPn), AF_INET); if RemoteHost <> nil then Result := RemoteHost^.h_name; finally SynSockCS.Leave; end; end; end else begin Addr := nil; try FillChar(Hints, Sizeof(Hints), 0); Hints.ai_family := AF_UNSPEC; Hints.ai_socktype := SockType; Hints.ai_protocol := SockProtocol; Hints.ai_flags := 0; r := synsock.GetAddrInfo(PChar(IP), nil, @Hints, Addr); if (r = 0) and Assigned(Addr)then begin hostlen := NI_MAXHOST; servlen := NI_MAXSERV; setlength(host, hostlen); setlength(serv, servlen); r := getnameinfo(Addr^.ai_addr, Addr^.ai_addrlen, PChar(host), hostlen, PChar(serv), servlen, NI_NUMERICSERV); if r = 0 then Result := PChar(host); end; finally if Assigned(Addr) then synsock.FreeAddrInfo(Addr); end; end; end; {=============================================================================} function InitSocketInterface(stack: string): Boolean; begin Result := False; SockEnhancedApi := False; if stack = '' then stack := DLLStackName; SynSockCS.Enter; try if SynSockCount = 0 then begin SockEnhancedApi := False; SockWship6Api := False; Libc.Signal(Libc.SIGPIPE, TSignalHandler(Libc.SIG_IGN)); LibHandle := LoadLibrary(PChar(Stack)); if LibHandle <> 0 then begin errno_loc := GetProcAddress(LibHandle, PChar('__errno_location')); CloseSocket := GetProcAddress(LibHandle, PChar('close')); IoctlSocket := GetProcAddress(LibHandle, PChar('ioctl')); WSAGetLastError := LSWSAGetLastError; WSAStartup := LSWSAStartup; WSACleanup := LSWSACleanup; ssAccept := GetProcAddress(LibHandle, PChar('accept')); ssBind := GetProcAddress(LibHandle, PChar('bind')); ssConnect := GetProcAddress(LibHandle, PChar('connect')); ssGetPeerName := GetProcAddress(LibHandle, PChar('getpeername')); ssGetSockName := GetProcAddress(LibHandle, PChar('getsockname')); GetSockOpt := GetProcAddress(LibHandle, PChar('getsockopt')); Htonl := GetProcAddress(LibHandle, PChar('htonl')); Htons := GetProcAddress(LibHandle, PChar('htons')); Inet_Addr := GetProcAddress(LibHandle, PChar('inet_addr')); Inet_Ntoa := GetProcAddress(LibHandle, PChar('inet_ntoa')); Listen := GetProcAddress(LibHandle, PChar('listen')); Ntohl := GetProcAddress(LibHandle, PChar('ntohl')); Ntohs := GetProcAddress(LibHandle, PChar('ntohs')); ssRecv := GetProcAddress(LibHandle, PChar('recv')); ssRecvFrom := GetProcAddress(LibHandle, PChar('recvfrom')); Select := GetProcAddress(LibHandle, PChar('select')); ssSend := GetProcAddress(LibHandle, PChar('send')); ssSendTo := GetProcAddress(LibHandle, PChar('sendto')); SetSockOpt := GetProcAddress(LibHandle, PChar('setsockopt')); ShutDown := GetProcAddress(LibHandle, PChar('shutdown')); Socket := GetProcAddress(LibHandle, PChar('socket')); GetHostByAddr := GetProcAddress(LibHandle, PChar('gethostbyaddr')); GetHostByName := GetProcAddress(LibHandle, PChar('gethostbyname')); GetProtoByName := GetProcAddress(LibHandle, PChar('getprotobyname')); GetProtoByNumber := GetProcAddress(LibHandle, PChar('getprotobynumber')); GetServByName := GetProcAddress(LibHandle, PChar('getservbyname')); GetServByPort := GetProcAddress(LibHandle, PChar('getservbyport')); ssGetHostName := GetProcAddress(LibHandle, PChar('gethostname')); {$IFNDEF FORCEOLDAPI} GetAddrInfo := GetProcAddress(LibHandle, PChar('getaddrinfo')); FreeAddrInfo := GetProcAddress(LibHandle, PChar('freeaddrinfo')); GetNameInfo := GetProcAddress(LibHandle, PChar('getnameinfo')); SockEnhancedApi := Assigned(GetAddrInfo) and Assigned(FreeAddrInfo) and Assigned(GetNameInfo); {$ENDIF} Result := True; end; end else Result := True; if Result then Inc(SynSockCount); finally SynSockCS.Leave; end; end; function DestroySocketInterface: Boolean; begin SynSockCS.Enter; try Dec(SynSockCount); if SynSockCount < 0 then SynSockCount := 0; if SynSockCount = 0 then begin if LibHandle <> 0 then begin FreeLibrary(libHandle); LibHandle := 0; end; if LibWship6Handle <> 0 then begin FreeLibrary(LibWship6Handle); LibWship6Handle := 0; end; end; finally SynSockCS.Leave; end; Result := True; end; initialization begin SynSockCS := SyncObjs.TCriticalSection.Create; SET_IN6_IF_ADDR_ANY (@in6addr_any); SET_LOOPBACK_ADDR6 (@in6addr_loopback); end; finalization begin SynSockCS.Free; end; {$ENDIF} TransGUI/synapse/source/lib/mimemess.pas0000644000000000000000000006446311366572451017313 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.005.002 | |==============================================================================| | Content: MIME message object | |==============================================================================| | Copyright (c)1999-2006, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2000-2006. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM From distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(MIME message handling) Classes for easy handling with e-mail message. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} unit mimemess; interface uses Classes, SysUtils, mimepart, synachar, synautil, mimeinln; type {:Possible values for message priority} TMessPriority = (MP_unknown, MP_low, MP_normal, MP_high); {:@abstract(Object for basic e-mail header fields.)} TMessHeader = class(TObject) private FFrom: string; FToList: TStringList; FCCList: TStringList; FSubject: string; FOrganization: string; FCustomHeaders: TStringList; FDate: TDateTime; FXMailer: string; FCharsetCode: TMimeChar; FReplyTo: string; FMessageID: string; FPriority: TMessPriority; Fpri: TMessPriority; Fxpri: TMessPriority; Fxmspri: TMessPriority; protected function ParsePriority(value: string): TMessPriority; function DecodeHeader(value: string): boolean; virtual; public constructor Create; virtual; destructor Destroy; override; {:Clears all data fields.} procedure Clear; virtual; {Add headers from from this object to Value.} procedure EncodeHeaders(const Value: TStrings); virtual; {:Parse header from Value to this object.} procedure DecodeHeaders(const Value: TStrings); {:Try find specific header in CustomHeader. Search is case insensitive. This is good for reading any non-parsed header.} function FindHeader(Value: string): string; {:Try find specific headers in CustomHeader. This metod is for repeatly used headers like 'received' header, etc. Search is case insensitive. This is good for reading ano non-parsed header.} procedure FindHeaderList(Value: string; const HeaderList: TStrings); published {:Sender of message.} property From: string read FFrom Write FFrom; {:Stringlist with receivers of message. (one per line)} property ToList: TStringList read FToList; {:Stringlist with Carbon Copy receivers of message. (one per line)} property CCList: TStringList read FCCList; {:Subject of message.} property Subject: string read FSubject Write FSubject; {:Organization string.} property Organization: string read FOrganization Write FOrganization; {:After decoding contains all headers lines witch not have parsed to any other structures in this object. It mean: this conatins all other headers except: X-MAILER, FROM, SUBJECT, ORGANIZATION, TO, CC, DATE, MIME-VERSION, CONTENT-TYPE, CONTENT-DESCRIPTION, CONTENT-DISPOSITION, CONTENT-ID, CONTENT-TRANSFER-ENCODING, REPLY-TO, MESSAGE-ID, X-MSMAIL-PRIORITY, X-PRIORITY, PRIORITY When you encode headers, all this lines is added as headers. Be carefull for duplicites!} property CustomHeaders: TStringList read FCustomHeaders; {:Date and time of message.} property Date: TDateTime read FDate Write FDate; {:Mailer identification.} property XMailer: string read FXMailer Write FXMailer; {:Address for replies} property ReplyTo: string read FReplyTo Write FReplyTo; {:message indetifier} property MessageID: string read FMessageID Write FMessageID; {:message priority} property Priority: TMessPriority read FPriority Write FPriority; {:Specify base charset. By default is used system charset.} property CharsetCode: TMimeChar read FCharsetCode Write FCharsetCode; end; TMessHeaderClass = class of TMessHeader; {:@abstract(Object for handling of e-mail message.)} TMimeMess = class(TObject) private FMessagePart: TMimePart; FLines: TStringList; FHeader: TMessHeader; public constructor Create; {:create this object and assign your own descendant of @link(TMessHeader) object to @link(header) property. So, you can create your own message headers parser and use it by this object.} constructor CreateAltHeaders(HeadClass: TMessHeaderClass); destructor Destroy; override; {:Reset component to default state.} procedure Clear; virtual; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then one subpart, you must have PartParent of multipart type!} function AddPart(const PartParent: TMimePart): TMimePart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! This part is marked as multipart with secondary MIME type specified by MultipartType parameter. (typical value is 'mixed') This part can be used as PartParent for another parts (include next multipart). If you need only one part, then you not need Multipart part.} function AddPartMultipart(const MultipartType: String; const PartParent: TMimePart): TMimePart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! After creation of part set type to text part and set all necessary properties. Content of part is readed from value stringlist.} function AddPartText(const Value: TStrings; const PartParent: TMimePart): TMimepart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! After creation of part set type to text part and set all necessary properties. Content of part is readed from value stringlist. You can select your charset and your encoding type. If Raw is @true, then it not doing charset conversion!} function AddPartTextEx(const Value: TStrings; const PartParent: TMimePart; PartCharset: TMimeChar; Raw: Boolean; PartEncoding: TMimeEncoding): TMimepart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! After creation of part set type to text part to HTML type and set all necessary properties. Content of HTML part is readed from Value stringlist.} function AddPartHTML(const Value: TStrings; const PartParent: TMimePart): TMimepart; {:Same as @link(AddPartText), but content is readed from file} function AddPartTextFromFile(const FileName: String; const PartParent: TMimePart): TMimepart; {:Same as @link(AddPartHTML), but content is readed from file} function AddPartHTMLFromFile(const FileName: String; const PartParent: TMimePart): TMimepart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! After creation of part set type to binary and set all necessary properties. MIME primary and secondary types defined automaticly by filename extension. Content of binary part is readed from Stream. This binary part is encoded as file attachment.} function AddPartBinary(const Stream: TStream; const FileName: string; const PartParent: TMimePart): TMimepart; {:Same as @link(AddPartBinary), but content is readed from file} function AddPartBinaryFromFile(const FileName: string; const PartParent: TMimePart): TMimepart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! After creation of part set type to binary and set all necessary properties. MIME primary and secondary types defined automaticly by filename extension. Content of binary part is readed from Stream. This binary part is encoded as inline data with given Conten ID (cid). Content ID can be used as reference ID in HTML source in HTML part.} function AddPartHTMLBinary(const Stream: TStream; const FileName, Cid: string; const PartParent: TMimePart): TMimepart; {:Same as @link(AddPartHTMLBinary), but content is readed from file} function AddPartHTMLBinaryFromFile(const FileName, Cid: string; const PartParent: TMimePart): TMimepart; {:Add MIME part as subpart of PartParent. If you need set root MIME part, then set as PartParent @NIL value. If you need set more then 1 subpart, you must have PartParent of multipart type! After creation of part set type to message and set all necessary properties. MIME primary and secondary types are setted to 'message/rfc822'. Content of raw RFC-822 message is readed from Stream.} function AddPartMess(const Value: TStrings; const PartParent: TMimePart): TMimepart; {:Same as @link(AddPartMess), but content is readed from file} function AddPartMessFromFile(const FileName: string; const PartParent: TMimePart): TMimepart; {:Compose message from @link(MessagePart) to @link(Lines). Headers from @link(Header) object is added also.} procedure EncodeMessage; {:Decode message from @link(Lines) to @link(MessagePart). Massage headers are parsed into @link(Header) object.} procedure DecodeMessage; published {:@link(TMimePart) object with decoded MIME message. This object can handle any number of nested @link(TMimePart) objects itself. It is used for handle any tree of MIME subparts.} property MessagePart: TMimePart read FMessagePart; {:Raw MIME encoded message.} property Lines: TStringList read FLines; {:Object for e-mail header fields. This object is created automaticly. Do not free this object!} property Header: TMessHeader read FHeader; end; implementation {==============================================================================} constructor TMessHeader.Create; begin inherited Create; FToList := TStringList.Create; FCCList := TStringList.Create; FCustomHeaders := TStringList.Create; FCharsetCode := GetCurCP; end; destructor TMessHeader.Destroy; begin FCustomHeaders.Free; FCCList.Free; FToList.Free; inherited Destroy; end; {==============================================================================} procedure TMessHeader.Clear; begin FFrom := ''; FToList.Clear; FCCList.Clear; FSubject := ''; FOrganization := ''; FCustomHeaders.Clear; FDate := 0; FXMailer := ''; FReplyTo := ''; FMessageID := ''; FPriority := MP_unknown; end; procedure TMessHeader.EncodeHeaders(const Value: TStrings); var n: Integer; s: string; begin if FDate = 0 then FDate := Now; for n := FCustomHeaders.Count - 1 downto 0 do if FCustomHeaders[n] <> '' then Value.Insert(0, FCustomHeaders[n]); if FPriority <> MP_unknown then case FPriority of MP_high: begin Value.Insert(0, 'X-MSMAIL-Priority: High'); Value.Insert(0, 'X-Priority: 1'); Value.Insert(0, 'Priority: urgent'); end; MP_low: begin Value.Insert(0, 'X-MSMAIL-Priority: low'); Value.Insert(0, 'X-Priority: 5'); Value.Insert(0, 'Priority: non-urgent'); end; end; if FReplyTo <> '' then Value.Insert(0, 'Reply-To: ' + GetEmailAddr(FReplyTo)); if FMessageID <> '' then Value.Insert(0, 'Message-ID: <' + trim(FMessageID) + '>'); if FXMailer = '' then Value.Insert(0, 'X-mailer: Synapse - Pascal TCP/IP library by Lukas Gebauer') else Value.Insert(0, 'X-mailer: ' + FXMailer); Value.Insert(0, 'MIME-Version: 1.0 (produced by Synapse)'); if FOrganization <> '' then Value.Insert(0, 'Organization: ' + InlineCodeEx(FOrganization, FCharsetCode)); s := ''; for n := 0 to FCCList.Count - 1 do if s = '' then s := InlineEmailEx(FCCList[n], FCharsetCode) else s := s + ', ' + InlineEmailEx(FCCList[n], FCharsetCode); if s <> '' then Value.Insert(0, 'CC: ' + s); Value.Insert(0, 'Date: ' + Rfc822DateTime(FDate)); if FSubject <> '' then Value.Insert(0, 'Subject: ' + InlineCodeEx(FSubject, FCharsetCode)); s := ''; for n := 0 to FToList.Count - 1 do if s = '' then s := InlineEmailEx(FToList[n], FCharsetCode) else s := s + ', ' + InlineEmailEx(FToList[n], FCharsetCode); if s <> '' then Value.Insert(0, 'To: ' + s); Value.Insert(0, 'From: ' + InlineEmailEx(FFrom, FCharsetCode)); end; function TMessHeader.ParsePriority(value: string): TMessPriority; var s: string; x: integer; begin Result := MP_unknown; s := Trim(separateright(value, ':')); s := Separateleft(s, ' '); x := StrToIntDef(s, -1); if x >= 0 then case x of 1, 2: Result := MP_High; 3: Result := MP_Normal; 4, 5: Result := MP_Low; end else begin s := lowercase(s); if (s = 'urgent') or (s = 'high') or (s = 'highest') then Result := MP_High; if (s = 'normal') or (s = 'medium') then Result := MP_Normal; if (s = 'low') or (s = 'lowest') or (s = 'no-priority') or (s = 'non-urgent') then Result := MP_Low; end; end; function TMessHeader.DecodeHeader(value: string): boolean; var s, t: string; cp: TMimeChar; begin Result := True; cp := FCharsetCode; s := uppercase(value); if Pos('X-MAILER:', s) = 1 then begin FXMailer := Trim(SeparateRight(Value, ':')); Exit; end; if Pos('FROM:', s) = 1 then begin FFrom := InlineDecode(Trim(SeparateRight(Value, ':')), cp); Exit; end; if Pos('SUBJECT:', s) = 1 then begin FSubject := InlineDecode(Trim(SeparateRight(Value, ':')), cp); Exit; end; if Pos('ORGANIZATION:', s) = 1 then begin FOrganization := InlineDecode(Trim(SeparateRight(Value, ':')), cp); Exit; end; if Pos('TO:', s) = 1 then begin s := Trim(SeparateRight(Value, ':')); repeat t := InlineDecode(Trim(FetchEx(s, ',', '"')), cp); if t <> '' then FToList.Add(t); until s = ''; Exit; end; if Pos('CC:', s) = 1 then begin s := Trim(SeparateRight(Value, ':')); repeat t := InlineDecode(Trim(FetchEx(s, ',', '"')), cp); if t <> '' then FCCList.Add(t); until s = ''; Exit; end; if Pos('DATE:', s) = 1 then begin FDate := DecodeRfcDateTime(Trim(SeparateRight(Value, ':'))); Exit; end; if Pos('REPLY-TO:', s) = 1 then begin FReplyTo := InlineDecode(Trim(SeparateRight(Value, ':')), cp); Exit; end; if Pos('MESSAGE-ID:', s) = 1 then begin FMessageID := GetEmailAddr(Trim(SeparateRight(Value, ':'))); Exit; end; if Pos('PRIORITY:', s) = 1 then begin FPri := ParsePriority(value); Exit; end; if Pos('X-PRIORITY:', s) = 1 then begin FXPri := ParsePriority(value); Exit; end; if Pos('X-MSMAIL-PRIORITY:', s) = 1 then begin FXmsPri := ParsePriority(value); Exit; end; if Pos('MIME-VERSION:', s) = 1 then Exit; if Pos('CONTENT-TYPE:', s) = 1 then Exit; if Pos('CONTENT-DESCRIPTION:', s) = 1 then Exit; if Pos('CONTENT-DISPOSITION:', s) = 1 then Exit; if Pos('CONTENT-ID:', s) = 1 then Exit; if Pos('CONTENT-TRANSFER-ENCODING:', s) = 1 then Exit; Result := False; end; procedure TMessHeader.DecodeHeaders(const Value: TStrings); var s: string; x: Integer; begin Clear; Fpri := MP_unknown; Fxpri := MP_unknown; Fxmspri := MP_unknown; x := 0; while Value.Count > x do begin s := NormalizeHeader(Value, x); if s = '' then Break; if not DecodeHeader(s) then FCustomHeaders.Add(s); end; if Fpri <> MP_unknown then FPriority := Fpri else if Fxpri <> MP_unknown then FPriority := Fxpri else if Fxmspri <> MP_unknown then FPriority := Fxmspri end; function TMessHeader.FindHeader(Value: string): string; var n: integer; begin Result := ''; for n := 0 to FCustomHeaders.Count - 1 do if Pos(UpperCase(Value), UpperCase(FCustomHeaders[n])) = 1 then begin Result := Trim(SeparateRight(FCustomHeaders[n], ':')); break; end; end; procedure TMessHeader.FindHeaderList(Value: string; const HeaderList: TStrings); var n: integer; begin HeaderList.Clear; for n := 0 to FCustomHeaders.Count - 1 do if Pos(UpperCase(Value), UpperCase(FCustomHeaders[n])) = 1 then begin HeaderList.Add(Trim(SeparateRight(FCustomHeaders[n], ':'))); end; end; {==============================================================================} constructor TMimeMess.Create; begin CreateAltHeaders(TMessHeader); end; constructor TMimeMess.CreateAltHeaders(HeadClass: TMessHeaderClass); begin inherited Create; FMessagePart := TMimePart.Create; FLines := TStringList.Create; FHeader := HeadClass.Create; end; destructor TMimeMess.Destroy; begin FMessagePart.Free; FHeader.Free; FLines.Free; inherited Destroy; end; {==============================================================================} procedure TMimeMess.Clear; begin FMessagePart.Clear; FLines.Clear; FHeader.Clear; end; {==============================================================================} function TMimeMess.AddPart(const PartParent: TMimePart): TMimePart; begin if PartParent = nil then Result := FMessagePart else Result := PartParent.AddSubPart; Result.Clear; end; {==============================================================================} function TMimeMess.AddPartMultipart(const MultipartType: String; const PartParent: TMimePart): TMimePart; begin Result := AddPart(PartParent); with Result do begin Primary := 'Multipart'; Secondary := MultipartType; Description := 'Multipart message'; Boundary := GenerateBoundary; EncodePartHeader; end; end; function TMimeMess.AddPartText(const Value: TStrings; const PartParent: TMimePart): TMimepart; begin Result := AddPart(PartParent); with Result do begin Value.SaveToStream(DecodedLines); Primary := 'text'; Secondary := 'plain'; Description := 'Message text'; Disposition := 'inline'; CharsetCode := IdealCharsetCoding(Value.Text, TargetCharset, IdealCharsets); EncodingCode := ME_QUOTED_PRINTABLE; EncodePart; EncodePartHeader; end; end; function TMimeMess.AddPartTextEx(const Value: TStrings; const PartParent: TMimePart; PartCharset: TMimeChar; Raw: Boolean; PartEncoding: TMimeEncoding): TMimepart; begin Result := AddPart(PartParent); with Result do begin Value.SaveToStream(DecodedLines); Primary := 'text'; Secondary := 'plain'; Description := 'Message text'; Disposition := 'inline'; CharsetCode := PartCharset; EncodingCode := PartEncoding; ConvertCharset := not Raw; EncodePart; EncodePartHeader; end; end; function TMimeMess.AddPartHTML(const Value: TStrings; const PartParent: TMimePart): TMimepart; begin Result := AddPart(PartParent); with Result do begin Value.SaveToStream(DecodedLines); Primary := 'text'; Secondary := 'html'; Description := 'HTML text'; Disposition := 'inline'; CharsetCode := UTF_8; EncodingCode := ME_QUOTED_PRINTABLE; EncodePart; EncodePartHeader; end; end; function TMimeMess.AddPartTextFromFile(const FileName: String; const PartParent: TMimePart): TMimepart; var tmp: TStrings; begin tmp := TStringList.Create; try tmp.LoadFromFile(FileName); Result := AddPartText(tmp, PartParent); Finally tmp.Free; end; end; function TMimeMess.AddPartHTMLFromFile(const FileName: String; const PartParent: TMimePart): TMimepart; var tmp: TStrings; begin tmp := TStringList.Create; try tmp.LoadFromFile(FileName); Result := AddPartHTML(tmp, PartParent); Finally tmp.Free; end; end; function TMimeMess.AddPartBinary(const Stream: TStream; const FileName: string; const PartParent: TMimePart): TMimepart; begin Result := AddPart(PartParent); Result.DecodedLines.LoadFromStream(Stream); Result.MimeTypeFromExt(FileName); Result.Description := 'Attached file: ' + FileName; Result.Disposition := 'attachment'; Result.FileName := FileName; Result.EncodingCode := ME_BASE64; Result.EncodePart; Result.EncodePartHeader; end; function TMimeMess.AddPartBinaryFromFile(const FileName: string; const PartParent: TMimePart): TMimepart; var tmp: TMemoryStream; begin tmp := TMemoryStream.Create; try tmp.LoadFromFile(FileName); Result := AddPartBinary(tmp, ExtractFileName(FileName), PartParent); finally tmp.Free; end; end; function TMimeMess.AddPartHTMLBinary(const Stream: TStream; const FileName, Cid: string; const PartParent: TMimePart): TMimepart; begin Result := AddPart(PartParent); Result.DecodedLines.LoadFromStream(Stream); Result.MimeTypeFromExt(FileName); Result.Description := 'Included file: ' + FileName; Result.Disposition := 'inline'; Result.ContentID := Cid; Result.FileName := FileName; Result.EncodingCode := ME_BASE64; Result.EncodePart; Result.EncodePartHeader; end; function TMimeMess.AddPartHTMLBinaryFromFile(const FileName, Cid: string; const PartParent: TMimePart): TMimepart; var tmp: TMemoryStream; begin tmp := TMemoryStream.Create; try tmp.LoadFromFile(FileName); Result :=AddPartHTMLBinary(tmp, ExtractFileName(FileName), Cid, PartParent); finally tmp.Free; end; end; function TMimeMess.AddPartMess(const Value: TStrings; const PartParent: TMimePart): TMimepart; var part: Tmimepart; begin Result := AddPart(PartParent); part := AddPart(result); part.lines.addstrings(Value); part.DecomposeParts; with Result do begin Primary := 'message'; Secondary := 'rfc822'; Description := 'E-mail Message'; EncodePart; EncodePartHeader; end; end; function TMimeMess.AddPartMessFromFile(const FileName: String; const PartParent: TMimePart): TMimepart; var tmp: TStrings; begin tmp := TStringList.Create; try tmp.LoadFromFile(FileName); Result := AddPartMess(tmp, PartParent); Finally tmp.Free; end; end; {==============================================================================} procedure TMimeMess.EncodeMessage; var l: TStringList; x: integer; begin //merge headers from THeaders and header field from MessagePart l := TStringList.Create; try FHeader.EncodeHeaders(l); x := IndexByBegin('CONTENT-TYPE', FMessagePart.Headers); if x >= 0 then l.add(FMessagePart.Headers[x]); x := IndexByBegin('CONTENT-DESCRIPTION', FMessagePart.Headers); if x >= 0 then l.add(FMessagePart.Headers[x]); x := IndexByBegin('CONTENT-DISPOSITION', FMessagePart.Headers); if x >= 0 then l.add(FMessagePart.Headers[x]); x := IndexByBegin('CONTENT-ID', FMessagePart.Headers); if x >= 0 then l.add(FMessagePart.Headers[x]); x := IndexByBegin('CONTENT-TRANSFER-ENCODING', FMessagePart.Headers); if x >= 0 then l.add(FMessagePart.Headers[x]); FMessagePart.Headers.Assign(l); finally l.Free; end; FMessagePart.ComposeParts; FLines.Assign(FMessagePart.Lines); end; {==============================================================================} procedure TMimeMess.DecodeMessage; begin FHeader.Clear; FHeader.DecodeHeaders(FLines); FMessagePart.Lines.Assign(FLines); FMessagePart.DecomposeParts; end; end. TransGUI/synapse/source/lib/synacrypt.pas0000644000000000000000000014224411366572451017522 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.000.001 | |==============================================================================| | Content: Encryption support | |==============================================================================| | Copyright (c)2007-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2007-2010. | | All Rights Reserved. | | Based on work of David Barton and Eric Young | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Encryption support) Implemented are DES and 3DES encryption/decryption by ECB, CBC, CFB-8bit, CFB-block, OFB and CTR methods. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$R-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit synacrypt; interface uses SysUtils, Classes, synautil; type {:@abstract(Implementation of common routines for 64-bit block ciphers) Do not use this class directly, use descendants only!} TSynaBlockCipher= class(TObject) protected procedure InitKey(Key: AnsiString); virtual; private IV, CV: AnsiString; procedure IncCounter; public {:Sets the IV to Value and performs a reset} procedure SetIV(const Value: AnsiString); virtual; {:Returns the current chaining information, not the actual IV} function GetIV: AnsiString; virtual; {:Reset any stored chaining information} procedure Reset; virtual; {:Encrypt a 64-bit block of data using the ECB method of encryption} function EncryptECB(const InData: AnsiString): AnsiString; virtual; {:Decrypt a 64-bit block of data using the ECB method of decryption} function DecryptECB(const InData: AnsiString): AnsiString; virtual; {:Encrypt data using the CBC method of encryption} function EncryptCBC(const Indata: AnsiString): AnsiString; virtual; {:Decrypt data using the CBC method of decryption} function DecryptCBC(const Indata: AnsiString): AnsiString; virtual; {:Encrypt data using the CFB (8 bit) method of encryption} function EncryptCFB8bit(const Indata: AnsiString): AnsiString; virtual; {:Decrypt data using the CFB (8 bit) method of decryption} function DecryptCFB8bit(const Indata: AnsiString): AnsiString; virtual; {:Encrypt data using the CFB (block) method of encryption} function EncryptCFBblock(const Indata: AnsiString): AnsiString; virtual; {:Decrypt data using the CFB (block) method of decryption} function DecryptCFBblock(const Indata: AnsiString): AnsiString; virtual; {:Encrypt data using the OFB method of encryption} function EncryptOFB(const Indata: AnsiString): AnsiString; virtual; {:Decrypt data using the OFB method of decryption} function DecryptOFB(const Indata: AnsiString): AnsiString; virtual; {:Encrypt data using the CTR method of encryption} function EncryptCTR(const Indata: AnsiString): AnsiString; virtual; {:Decrypt data using the CTR method of decryption} function DecryptCTR(const Indata: AnsiString): AnsiString; virtual; {:Create a encryptor/decryptor instance and initialize it by the Key.} constructor Create(Key: AnsiString); end; {:@abstract(Datatype for holding one DES key data) This data type is used internally.} TDesKeyData = array[0..31] of integer; {:@abstract(Implementation of common routines for DES encryption) Do not use this class directly, use descendants only!} TSynaCustomDes = class(TSynaBlockcipher) protected procedure DoInit(KeyB: AnsiString; var KeyData: TDesKeyData); function EncryptBlock(const InData: AnsiString; var KeyData: TDesKeyData): AnsiString; function DecryptBlock(const InData: AnsiString; var KeyData: TDesKeyData): AnsiString; end; {:@abstract(Implementation of DES encryption)} TSynaDes= class(TSynaCustomDes) protected KeyData: TDesKeyData; procedure InitKey(Key: AnsiString); override; public {:Encrypt a 64-bit block of data using the ECB method of encryption} function EncryptECB(const InData: AnsiString): AnsiString; override; {:Decrypt a 64-bit block of data using the ECB method of decryption} function DecryptECB(const InData: AnsiString): AnsiString; override; end; {:@abstract(Implementation of 3DES encryption)} TSyna3Des= class(TSynaCustomDes) protected KeyData: array[0..2] of TDesKeyData; procedure InitKey(Key: AnsiString); override; public {:Encrypt a 64-bit block of data using the ECB method of encryption} function EncryptECB(const InData: AnsiString): AnsiString; override; {:Decrypt a 64-bit block of data using the ECB method of decryption} function DecryptECB(const InData: AnsiString): AnsiString; override; end; {:Call internal test of all DES encryptions. Returns @true if all is OK.} function TestDes: boolean; {:Call internal test of all 3DES encryptions. Returns @true if all is OK.} function Test3Des: boolean; {==============================================================================} implementation const shifts2: array[0..15]of byte= (0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0); des_skb: array[0..7,0..63]of integer=( ( (* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 *) integer($00000000),integer($00000010),integer($20000000),integer($20000010), integer($00010000),integer($00010010),integer($20010000),integer($20010010), integer($00000800),integer($00000810),integer($20000800),integer($20000810), integer($00010800),integer($00010810),integer($20010800),integer($20010810), integer($00000020),integer($00000030),integer($20000020),integer($20000030), integer($00010020),integer($00010030),integer($20010020),integer($20010030), integer($00000820),integer($00000830),integer($20000820),integer($20000830), integer($00010820),integer($00010830),integer($20010820),integer($20010830), integer($00080000),integer($00080010),integer($20080000),integer($20080010), integer($00090000),integer($00090010),integer($20090000),integer($20090010), integer($00080800),integer($00080810),integer($20080800),integer($20080810), integer($00090800),integer($00090810),integer($20090800),integer($20090810), integer($00080020),integer($00080030),integer($20080020),integer($20080030), integer($00090020),integer($00090030),integer($20090020),integer($20090030), integer($00080820),integer($00080830),integer($20080820),integer($20080830), integer($00090820),integer($00090830),integer($20090820),integer($20090830) ),( (* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 *) integer($00000000),integer($02000000),integer($00002000),integer($02002000), integer($00200000),integer($02200000),integer($00202000),integer($02202000), integer($00000004),integer($02000004),integer($00002004),integer($02002004), integer($00200004),integer($02200004),integer($00202004),integer($02202004), integer($00000400),integer($02000400),integer($00002400),integer($02002400), integer($00200400),integer($02200400),integer($00202400),integer($02202400), integer($00000404),integer($02000404),integer($00002404),integer($02002404), integer($00200404),integer($02200404),integer($00202404),integer($02202404), integer($10000000),integer($12000000),integer($10002000),integer($12002000), integer($10200000),integer($12200000),integer($10202000),integer($12202000), integer($10000004),integer($12000004),integer($10002004),integer($12002004), integer($10200004),integer($12200004),integer($10202004),integer($12202004), integer($10000400),integer($12000400),integer($10002400),integer($12002400), integer($10200400),integer($12200400),integer($10202400),integer($12202400), integer($10000404),integer($12000404),integer($10002404),integer($12002404), integer($10200404),integer($12200404),integer($10202404),integer($12202404) ),( (* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 *) integer($00000000),integer($00000001),integer($00040000),integer($00040001), integer($01000000),integer($01000001),integer($01040000),integer($01040001), integer($00000002),integer($00000003),integer($00040002),integer($00040003), integer($01000002),integer($01000003),integer($01040002),integer($01040003), integer($00000200),integer($00000201),integer($00040200),integer($00040201), integer($01000200),integer($01000201),integer($01040200),integer($01040201), integer($00000202),integer($00000203),integer($00040202),integer($00040203), integer($01000202),integer($01000203),integer($01040202),integer($01040203), integer($08000000),integer($08000001),integer($08040000),integer($08040001), integer($09000000),integer($09000001),integer($09040000),integer($09040001), integer($08000002),integer($08000003),integer($08040002),integer($08040003), integer($09000002),integer($09000003),integer($09040002),integer($09040003), integer($08000200),integer($08000201),integer($08040200),integer($08040201), integer($09000200),integer($09000201),integer($09040200),integer($09040201), integer($08000202),integer($08000203),integer($08040202),integer($08040203), integer($09000202),integer($09000203),integer($09040202),integer($09040203) ),( (* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 *) integer($00000000),integer($00100000),integer($00000100),integer($00100100), integer($00000008),integer($00100008),integer($00000108),integer($00100108), integer($00001000),integer($00101000),integer($00001100),integer($00101100), integer($00001008),integer($00101008),integer($00001108),integer($00101108), integer($04000000),integer($04100000),integer($04000100),integer($04100100), integer($04000008),integer($04100008),integer($04000108),integer($04100108), integer($04001000),integer($04101000),integer($04001100),integer($04101100), integer($04001008),integer($04101008),integer($04001108),integer($04101108), integer($00020000),integer($00120000),integer($00020100),integer($00120100), integer($00020008),integer($00120008),integer($00020108),integer($00120108), integer($00021000),integer($00121000),integer($00021100),integer($00121100), integer($00021008),integer($00121008),integer($00021108),integer($00121108), integer($04020000),integer($04120000),integer($04020100),integer($04120100), integer($04020008),integer($04120008),integer($04020108),integer($04120108), integer($04021000),integer($04121000),integer($04021100),integer($04121100), integer($04021008),integer($04121008),integer($04021108),integer($04121108) ),( (* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 *) integer($00000000),integer($10000000),integer($00010000),integer($10010000), integer($00000004),integer($10000004),integer($00010004),integer($10010004), integer($20000000),integer($30000000),integer($20010000),integer($30010000), integer($20000004),integer($30000004),integer($20010004),integer($30010004), integer($00100000),integer($10100000),integer($00110000),integer($10110000), integer($00100004),integer($10100004),integer($00110004),integer($10110004), integer($20100000),integer($30100000),integer($20110000),integer($30110000), integer($20100004),integer($30100004),integer($20110004),integer($30110004), integer($00001000),integer($10001000),integer($00011000),integer($10011000), integer($00001004),integer($10001004),integer($00011004),integer($10011004), integer($20001000),integer($30001000),integer($20011000),integer($30011000), integer($20001004),integer($30001004),integer($20011004),integer($30011004), integer($00101000),integer($10101000),integer($00111000),integer($10111000), integer($00101004),integer($10101004),integer($00111004),integer($10111004), integer($20101000),integer($30101000),integer($20111000),integer($30111000), integer($20101004),integer($30101004),integer($20111004),integer($30111004) ),( (* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 *) integer($00000000),integer($08000000),integer($00000008),integer($08000008), integer($00000400),integer($08000400),integer($00000408),integer($08000408), integer($00020000),integer($08020000),integer($00020008),integer($08020008), integer($00020400),integer($08020400),integer($00020408),integer($08020408), integer($00000001),integer($08000001),integer($00000009),integer($08000009), integer($00000401),integer($08000401),integer($00000409),integer($08000409), integer($00020001),integer($08020001),integer($00020009),integer($08020009), integer($00020401),integer($08020401),integer($00020409),integer($08020409), integer($02000000),integer($0A000000),integer($02000008),integer($0A000008), integer($02000400),integer($0A000400),integer($02000408),integer($0A000408), integer($02020000),integer($0A020000),integer($02020008),integer($0A020008), integer($02020400),integer($0A020400),integer($02020408),integer($0A020408), integer($02000001),integer($0A000001),integer($02000009),integer($0A000009), integer($02000401),integer($0A000401),integer($02000409),integer($0A000409), integer($02020001),integer($0A020001),integer($02020009),integer($0A020009), integer($02020401),integer($0A020401),integer($02020409),integer($0A020409) ),( (* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 *) integer($00000000),integer($00000100),integer($00080000),integer($00080100), integer($01000000),integer($01000100),integer($01080000),integer($01080100), integer($00000010),integer($00000110),integer($00080010),integer($00080110), integer($01000010),integer($01000110),integer($01080010),integer($01080110), integer($00200000),integer($00200100),integer($00280000),integer($00280100), integer($01200000),integer($01200100),integer($01280000),integer($01280100), integer($00200010),integer($00200110),integer($00280010),integer($00280110), integer($01200010),integer($01200110),integer($01280010),integer($01280110), integer($00000200),integer($00000300),integer($00080200),integer($00080300), integer($01000200),integer($01000300),integer($01080200),integer($01080300), integer($00000210),integer($00000310),integer($00080210),integer($00080310), integer($01000210),integer($01000310),integer($01080210),integer($01080310), integer($00200200),integer($00200300),integer($00280200),integer($00280300), integer($01200200),integer($01200300),integer($01280200),integer($01280300), integer($00200210),integer($00200310),integer($00280210),integer($00280310), integer($01200210),integer($01200310),integer($01280210),integer($01280310) ),( (* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 *) integer($00000000),integer($04000000),integer($00040000),integer($04040000), integer($00000002),integer($04000002),integer($00040002),integer($04040002), integer($00002000),integer($04002000),integer($00042000),integer($04042000), integer($00002002),integer($04002002),integer($00042002),integer($04042002), integer($00000020),integer($04000020),integer($00040020),integer($04040020), integer($00000022),integer($04000022),integer($00040022),integer($04040022), integer($00002020),integer($04002020),integer($00042020),integer($04042020), integer($00002022),integer($04002022),integer($00042022),integer($04042022), integer($00000800),integer($04000800),integer($00040800),integer($04040800), integer($00000802),integer($04000802),integer($00040802),integer($04040802), integer($00002800),integer($04002800),integer($00042800),integer($04042800), integer($00002802),integer($04002802),integer($00042802),integer($04042802), integer($00000820),integer($04000820),integer($00040820),integer($04040820), integer($00000822),integer($04000822),integer($00040822),integer($04040822), integer($00002820),integer($04002820),integer($00042820),integer($04042820), integer($00002822),integer($04002822),integer($00042822),integer($04042822) )); des_sptrans: array[0..7,0..63] of integer=( ( (* nibble 0 *) integer($02080800), integer($00080000), integer($02000002), integer($02080802), integer($02000000), integer($00080802), integer($00080002), integer($02000002), integer($00080802), integer($02080800), integer($02080000), integer($00000802), integer($02000802), integer($02000000), integer($00000000), integer($00080002), integer($00080000), integer($00000002), integer($02000800), integer($00080800), integer($02080802), integer($02080000), integer($00000802), integer($02000800), integer($00000002), integer($00000800), integer($00080800), integer($02080002), integer($00000800), integer($02000802), integer($02080002), integer($00000000), integer($00000000), integer($02080802), integer($02000800), integer($00080002), integer($02080800), integer($00080000), integer($00000802), integer($02000800), integer($02080002), integer($00000800), integer($00080800), integer($02000002), integer($00080802), integer($00000002), integer($02000002), integer($02080000), integer($02080802), integer($00080800), integer($02080000), integer($02000802), integer($02000000), integer($00000802), integer($00080002), integer($00000000), integer($00080000), integer($02000000), integer($02000802), integer($02080800), integer($00000002), integer($02080002), integer($00000800), integer($00080802) ),( (* nibble 1 *) integer($40108010), integer($00000000), integer($00108000), integer($40100000), integer($40000010), integer($00008010), integer($40008000), integer($00108000), integer($00008000), integer($40100010), integer($00000010), integer($40008000), integer($00100010), integer($40108000), integer($40100000), integer($00000010), integer($00100000), integer($40008010), integer($40100010), integer($00008000), integer($00108010), integer($40000000), integer($00000000), integer($00100010), integer($40008010), integer($00108010), integer($40108000), integer($40000010), integer($40000000), integer($00100000), integer($00008010), integer($40108010), integer($00100010), integer($40108000), integer($40008000), integer($00108010), integer($40108010), integer($00100010), integer($40000010), integer($00000000), integer($40000000), integer($00008010), integer($00100000), integer($40100010), integer($00008000), integer($40000000), integer($00108010), integer($40008010), integer($40108000), integer($00008000), integer($00000000), integer($40000010), integer($00000010), integer($40108010), integer($00108000), integer($40100000), integer($40100010), integer($00100000), integer($00008010), integer($40008000), integer($40008010), integer($00000010), integer($40100000), integer($00108000) ),( (* nibble 2 *) integer($04000001), integer($04040100), integer($00000100), integer($04000101), integer($00040001), integer($04000000), integer($04000101), integer($00040100), integer($04000100), integer($00040000), integer($04040000), integer($00000001), integer($04040101), integer($00000101), integer($00000001), integer($04040001), integer($00000000), integer($00040001), integer($04040100), integer($00000100), integer($00000101), integer($04040101), integer($00040000), integer($04000001), integer($04040001), integer($04000100), integer($00040101), integer($04040000), integer($00040100), integer($00000000), integer($04000000), integer($00040101), integer($04040100), integer($00000100), integer($00000001), integer($00040000), integer($00000101), integer($00040001), integer($04040000), integer($04000101), integer($00000000), integer($04040100), integer($00040100), integer($04040001), integer($00040001), integer($04000000), integer($04040101), integer($00000001), integer($00040101), integer($04000001), integer($04000000), integer($04040101), integer($00040000), integer($04000100), integer($04000101), integer($00040100), integer($04000100), integer($00000000), integer($04040001), integer($00000101), integer($04000001), integer($00040101), integer($00000100), integer($04040000) ),( (* nibble 3 *) integer($00401008), integer($10001000), integer($00000008), integer($10401008), integer($00000000), integer($10400000), integer($10001008), integer($00400008), integer($10401000), integer($10000008), integer($10000000), integer($00001008), integer($10000008), integer($00401008), integer($00400000), integer($10000000), integer($10400008), integer($00401000), integer($00001000), integer($00000008), integer($00401000), integer($10001008), integer($10400000), integer($00001000), integer($00001008), integer($00000000), integer($00400008), integer($10401000), integer($10001000), integer($10400008), integer($10401008), integer($00400000), integer($10400008), integer($00001008), integer($00400000), integer($10000008), integer($00401000), integer($10001000), integer($00000008), integer($10400000), integer($10001008), integer($00000000), integer($00001000), integer($00400008), integer($00000000), integer($10400008), integer($10401000), integer($00001000), integer($10000000), integer($10401008), integer($00401008), integer($00400000), integer($10401008), integer($00000008), integer($10001000), integer($00401008), integer($00400008), integer($00401000), integer($10400000), integer($10001008), integer($00001008), integer($10000000), integer($10000008), integer($10401000) ),( (* nibble 4 *) integer($08000000), integer($00010000), integer($00000400), integer($08010420), integer($08010020), integer($08000400), integer($00010420), integer($08010000), integer($00010000), integer($00000020), integer($08000020), integer($00010400), integer($08000420), integer($08010020), integer($08010400), integer($00000000), integer($00010400), integer($08000000), integer($00010020), integer($00000420), integer($08000400), integer($00010420), integer($00000000), integer($08000020), integer($00000020), integer($08000420), integer($08010420), integer($00010020), integer($08010000), integer($00000400), integer($00000420), integer($08010400), integer($08010400), integer($08000420), integer($00010020), integer($08010000), integer($00010000), integer($00000020), integer($08000020), integer($08000400), integer($08000000), integer($00010400), integer($08010420), integer($00000000), integer($00010420), integer($08000000), integer($00000400), integer($00010020), integer($08000420), integer($00000400), integer($00000000), integer($08010420), integer($08010020), integer($08010400), integer($00000420), integer($00010000), integer($00010400), integer($08010020), integer($08000400), integer($00000420), integer($00000020), integer($00010420), integer($08010000), integer($08000020) ),( (* nibble 5 *) integer($80000040), integer($00200040), integer($00000000), integer($80202000), integer($00200040), integer($00002000), integer($80002040), integer($00200000), integer($00002040), integer($80202040), integer($00202000), integer($80000000), integer($80002000), integer($80000040), integer($80200000), integer($00202040), integer($00200000), integer($80002040), integer($80200040), integer($00000000), integer($00002000), integer($00000040), integer($80202000), integer($80200040), integer($80202040), integer($80200000), integer($80000000), integer($00002040), integer($00000040), integer($00202000), integer($00202040), integer($80002000), integer($00002040), integer($80000000), integer($80002000), integer($00202040), integer($80202000), integer($00200040), integer($00000000), integer($80002000), integer($80000000), integer($00002000), integer($80200040), integer($00200000), integer($00200040), integer($80202040), integer($00202000), integer($00000040), integer($80202040), integer($00202000), integer($00200000), integer($80002040), integer($80000040), integer($80200000), integer($00202040), integer($00000000), integer($00002000), integer($80000040), integer($80002040), integer($80202000), integer($80200000), integer($00002040), integer($00000040), integer($80200040) ),( (* nibble 6 *) integer($00004000), integer($00000200), integer($01000200), integer($01000004), integer($01004204), integer($00004004), integer($00004200), integer($00000000), integer($01000000), integer($01000204), integer($00000204), integer($01004000), integer($00000004), integer($01004200), integer($01004000), integer($00000204), integer($01000204), integer($00004000), integer($00004004), integer($01004204), integer($00000000), integer($01000200), integer($01000004), integer($00004200), integer($01004004), integer($00004204), integer($01004200), integer($00000004), integer($00004204), integer($01004004), integer($00000200), integer($01000000), integer($00004204), integer($01004000), integer($01004004), integer($00000204), integer($00004000), integer($00000200), integer($01000000), integer($01004004), integer($01000204), integer($00004204), integer($00004200), integer($00000000), integer($00000200), integer($01000004), integer($00000004), integer($01000200), integer($00000000), integer($01000204), integer($01000200), integer($00004200), integer($00000204), integer($00004000), integer($01004204), integer($01000000), integer($01004200), integer($00000004), integer($00004004), integer($01004204), integer($01000004), integer($01004200), integer($01004000), integer($00004004) ),( (* nibble 7 *) integer($20800080), integer($20820000), integer($00020080), integer($00000000), integer($20020000), integer($00800080), integer($20800000), integer($20820080), integer($00000080), integer($20000000), integer($00820000), integer($00020080), integer($00820080), integer($20020080), integer($20000080), integer($20800000), integer($00020000), integer($00820080), integer($00800080), integer($20020000), integer($20820080), integer($20000080), integer($00000000), integer($00820000), integer($20000000), integer($00800000), integer($20020080), integer($20800080), integer($00800000), integer($00020000), integer($20820000), integer($00000080), integer($00800000), integer($00020000), integer($20000080), integer($20820080), integer($00020080), integer($20000000), integer($00000000), integer($00820000), integer($20800080), integer($20020080), integer($20020000), integer($00800080), integer($20820000), integer($00000080), integer($00800080), integer($20020000), integer($20820080), integer($00800000), integer($20800000), integer($20000080), integer($00820000), integer($00020080), integer($20020080), integer($20800000), integer($00000080), integer($20820000), integer($00820080), integer($00000000), integer($20000000), integer($20800080), integer($00020000), integer($00820080) )); {==============================================================================} function XorString(Indata1, Indata2: AnsiString): AnsiString; var i: integer; begin Indata2 := PadString(Indata2, length(Indata1), #0); Result := ''; for i := 1 to length(Indata1) do Result := Result + AnsiChar(ord(Indata1[i]) xor ord(Indata2[i])); end; procedure hperm_op(var a, t: integer; n, m: integer); begin t:= ((a shl (16 - n)) xor a) and m; a:= a xor t xor (t shr (16 - n)); end; procedure perm_op(var a, b, t: integer; n, m: integer); begin t:= ((a shr n) xor b) and m; b:= b xor t; a:= a xor (t shl n); end; {==============================================================================} procedure TSynaBlockCipher.IncCounter; var i: integer; begin Inc(CV[8]); i:= 7; while (i> 0) and (CV[i + 1] = #0) do begin Inc(CV[i]); Dec(i); end; end; procedure TSynaBlockCipher.Reset; begin CV := IV; end; procedure TSynaBlockCipher.InitKey(Key: AnsiString); begin end; procedure TSynaBlockCipher.SetIV(const Value: AnsiString); begin IV := PadString(Value, 8, #0); Reset; end; function TSynaBlockCipher.GetIV: AnsiString; begin Result := CV; end; function TSynaBlockCipher.EncryptECB(const InData: AnsiString): AnsiString; begin Result := InData; end; function TSynaBlockCipher.DecryptECB(const InData: AnsiString): AnsiString; begin Result := InData; end; function TSynaBlockCipher.EncryptCBC(const Indata: AnsiString): AnsiString; var i: integer; s: ansistring; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin s := copy(Indata, (i - 1) * 8 + 1, 8); s := XorString(s, CV); s := EncryptECB(s); CV := s; Result := Result + s; end; if (l mod 8)<> 0 then begin CV := EncryptECB(CV); s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, CV); Result := Result + s; end; end; function TSynaBlockCipher.DecryptCBC(const Indata: AnsiString): AnsiString; var i: integer; s, temp: ansistring; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin s := copy(Indata, (i - 1) * 8 + 1, 8); temp := s; s := DecryptECB(s); s := XorString(s, CV); Result := Result + s; CV := Temp; end; if (l mod 8)<> 0 then begin CV := EncryptECB(CV); s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, CV); Result := Result + s; end; end; function TSynaBlockCipher.EncryptCFB8bit(const Indata: AnsiString): AnsiString; var i: integer; Temp: AnsiString; c: AnsiChar; begin Result := ''; for i:= 1 to Length(Indata) do begin Temp := EncryptECB(CV); c := AnsiChar(ord(InData[i]) xor ord(temp[1])); Result := Result + c; Delete(CV, 1, 1); CV := CV + c; end; end; function TSynaBlockCipher.DecryptCFB8bit(const Indata: AnsiString): AnsiString; var i: integer; Temp: AnsiString; c: AnsiChar; begin Result := ''; for i:= 1 to length(Indata) do begin c:= Indata[i]; Temp := EncryptECB(CV); Result := Result + AnsiChar(ord(InData[i]) xor ord(temp[1])); Delete(CV, 1, 1); CV := CV + c; end; end; function TSynaBlockCipher.EncryptCFBblock(const Indata: AnsiString): AnsiString; var i: integer; s: AnsiString; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin CV := EncryptECB(CV); s := copy(Indata, (i - 1) * 8 + 1, 8); s := XorString(s, CV); Result := Result + s; CV := s; end; if (l mod 8)<> 0 then begin CV := EncryptECB(CV); s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, CV); Result := Result + s; end; end; function TSynaBlockCipher.DecryptCFBblock(const Indata: AnsiString): AnsiString; var i: integer; S, Temp: AnsiString; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin s := copy(Indata, (i - 1) * 8 + 1, 8); Temp := s; CV := EncryptECB(CV); s := XorString(s, CV); Result := result + s; CV := temp; end; if (l mod 8)<> 0 then begin CV := EncryptECB(CV); s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, CV); Result := Result + s; end; end; function TSynaBlockCipher.EncryptOFB(const Indata: AnsiString): AnsiString; var i: integer; s: AnsiString; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin CV := EncryptECB(CV); s := copy(Indata, (i - 1) * 8 + 1, 8); s := XorString(s, CV); Result := Result + s; end; if (l mod 8)<> 0 then begin CV := EncryptECB(CV); s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, CV); Result := Result + s; end; end; function TSynaBlockCipher.DecryptOFB(const Indata: AnsiString): AnsiString; var i: integer; s: AnsiString; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin Cv := EncryptECB(CV); s := copy(Indata, (i - 1) * 8 + 1, 8); s := XorString(s, CV); Result := Result + s; end; if (l mod 8)<> 0 then begin CV := EncryptECB(CV); s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, CV); Result := Result + s; end; end; function TSynaBlockCipher.EncryptCTR(const Indata: AnsiString): AnsiString; var temp: AnsiString; i: integer; s: AnsiString; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin temp := EncryptECB(CV); IncCounter; s := copy(Indata, (i - 1) * 8 + 1, 8); s := XorString(s, temp); Result := Result + s; end; if (l mod 8)<> 0 then begin temp := EncryptECB(CV); IncCounter; s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, temp); Result := Result + s; end; end; function TSynaBlockCipher.DecryptCTR(const Indata: AnsiString): AnsiString; var temp: AnsiString; s: AnsiString; i: integer; l: integer; begin Result := ''; l := Length(InData); for i:= 1 to (l div 8) do begin temp := EncryptECB(CV); IncCounter; s := copy(Indata, (i - 1) * 8 + 1, 8); s := XorString(s, temp); Result := Result + s; end; if (l mod 8)<> 0 then begin temp := EncryptECB(CV); IncCounter; s := copy(Indata, (l div 8) * 8 + 1, l mod 8); s := XorString(s, temp); Result := Result + s; end; end; constructor TSynaBlockCipher.Create(Key: AnsiString); begin inherited Create; InitKey(Key); IV := StringOfChar(#0, 8); IV := EncryptECB(IV); Reset; end; {==============================================================================} procedure TSynaCustomDes.DoInit(KeyB: AnsiString; var KeyData: TDesKeyData); var c, d, t, s, t2, i: integer; begin KeyB := PadString(KeyB, 8, #0); c:= ord(KeyB[1]) or (ord(KeyB[2]) shl 8) or (ord(KeyB[3]) shl 16) or (ord(KeyB[4]) shl 24); d:= ord(KeyB[5]) or (ord(KeyB[6]) shl 8) or (ord(KeyB[7]) shl 16) or (ord(KeyB[8]) shl 24); perm_op(d,c,t,4,integer($0f0f0f0f)); hperm_op(c,t,integer(-2),integer($cccc0000)); hperm_op(d,t,integer(-2),integer($cccc0000)); perm_op(d,c,t,1,integer($55555555)); perm_op(c,d,t,8,integer($00ff00ff)); perm_op(d,c,t,1,integer($55555555)); d:= ((d and $ff) shl 16) or (d and $ff00) or ((d and $ff0000) shr 16) or ((c and integer($f0000000)) shr 4); c:= c and $fffffff; for i:= 0 to 15 do begin if shifts2[i]<> 0 then begin c:= ((c shr 2) or (c shl 26)); d:= ((d shr 2) or (d shl 26)); end else begin c:= ((c shr 1) or (c shl 27)); d:= ((d shr 1) or (d shl 27)); end; c:= c and $fffffff; d:= d and $fffffff; s:= des_skb[0,c and $3f] or des_skb[1,((c shr 6) and $03) or ((c shr 7) and $3c)] or des_skb[2,((c shr 13) and $0f) or ((c shr 14) and $30)] or des_skb[3,((c shr 20) and $01) or ((c shr 21) and $06) or ((c shr 22) and $38)]; t:= des_skb[4,d and $3f] or des_skb[5,((d shr 7) and $03) or ((d shr 8) and $3c)] or des_skb[6, (d shr 15) and $3f ] or des_skb[7,((d shr 21) and $0f) or ((d shr 22) and $30)]; t2:= ((t shl 16) or (s and $ffff)); KeyData[(i shl 1)]:= ((t2 shl 2) or (t2 shr 30)); t2:= ((s shr 16) or (t and integer($ffff0000))); KeyData[(i shl 1)+1]:= ((t2 shl 6) or (t2 shr 26)); end; end; function TSynaCustomDes.EncryptBlock(const InData: AnsiString; var KeyData: TDesKeyData): AnsiString; var l, r, t, u: integer; i: longint; begin r := Swapbytes(DecodeLongint(Indata, 1)); l := swapbytes(DecodeLongint(Indata, 5)); t:= ((l shr 4) xor r) and $0f0f0f0f; r:= r xor t; l:= l xor (t shl 4); t:= ((r shr 16) xor l) and $0000ffff; l:= l xor t; r:= r xor (t shl 16); t:= ((l shr 2) xor r) and $33333333; r:= r xor t; l:= l xor (t shl 2); t:= ((r shr 8) xor l) and $00ff00ff; l:= l xor t; r:= r xor (t shl 8); t:= ((l shr 1) xor r) and $55555555; r:= r xor t; l:= l xor (t shl 1); r:= (r shr 29) or (r shl 3); l:= (l shr 29) or (l shl 3); i:= 0; while i< 32 do begin u:= r xor KeyData[i ]; t:= r xor KeyData[i+1]; t:= (t shr 4) or (t shl 28); l:= l xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; u:= l xor KeyData[i+2]; t:= l xor KeyData[i+3]; t:= (t shr 4) or (t shl 28); r:= r xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; u:= r xor KeyData[i+4]; t:= r xor KeyData[i+5]; t:= (t shr 4) or (t shl 28); l:= l xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; u:= l xor KeyData[i+6]; t:= l xor KeyData[i+7]; t:= (t shr 4) or (t shl 28); r:= r xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; Inc(i,8); end; r:= (r shr 3) or (r shl 29); l:= (l shr 3) or (l shl 29); t:= ((r shr 1) xor l) and $55555555; l:= l xor t; r:= r xor (t shl 1); t:= ((l shr 8) xor r) and $00ff00ff; r:= r xor t; l:= l xor (t shl 8); t:= ((r shr 2) xor l) and $33333333; l:= l xor t; r:= r xor (t shl 2); t:= ((l shr 16) xor r) and $0000ffff; r:= r xor t; l:= l xor (t shl 16); t:= ((r shr 4) xor l) and $0f0f0f0f; l:= l xor t; r:= r xor (t shl 4); Result := CodeLongInt(Swapbytes(l)) + CodeLongInt(Swapbytes(r)); end; function TSynaCustomDes.DecryptBlock(const InData: AnsiString; var KeyData: TDesKeyData): AnsiString; var l, r, t, u: integer; i: longint; begin r := Swapbytes(DecodeLongint(Indata, 1)); l := Swapbytes(DecodeLongint(Indata, 5)); t:= ((l shr 4) xor r) and $0f0f0f0f; r:= r xor t; l:= l xor (t shl 4); t:= ((r shr 16) xor l) and $0000ffff; l:= l xor t; r:= r xor (t shl 16); t:= ((l shr 2) xor r) and $33333333; r:= r xor t; l:= l xor (t shl 2); t:= ((r shr 8) xor l) and $00ff00ff; l:= l xor t; r:= r xor (t shl 8); t:= ((l shr 1) xor r) and $55555555; r:= r xor t; l:= l xor (t shl 1); r:= (r shr 29) or (r shl 3); l:= (l shr 29) or (l shl 3); i:= 30; while i> 0 do begin u:= r xor KeyData[i ]; t:= r xor KeyData[i+1]; t:= (t shr 4) or (t shl 28); l:= l xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; u:= l xor KeyData[i-2]; t:= l xor KeyData[i-1]; t:= (t shr 4) or (t shl 28); r:= r xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; u:= r xor KeyData[i-4]; t:= r xor KeyData[i-3]; t:= (t shr 4) or (t shl 28); l:= l xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; u:= l xor KeyData[i-6]; t:= l xor KeyData[i-5]; t:= (t shr 4) or (t shl 28); r:= r xor des_SPtrans[0,(u shr 2) and $3f] xor des_SPtrans[2,(u shr 10) and $3f] xor des_SPtrans[4,(u shr 18) and $3f] xor des_SPtrans[6,(u shr 26) and $3f] xor des_SPtrans[1,(t shr 2) and $3f] xor des_SPtrans[3,(t shr 10) and $3f] xor des_SPtrans[5,(t shr 18) and $3f] xor des_SPtrans[7,(t shr 26) and $3f]; Dec(i,8); end; r:= (r shr 3) or (r shl 29); l:= (l shr 3) or (l shl 29); t:= ((r shr 1) xor l) and $55555555; l:= l xor t; r:= r xor (t shl 1); t:= ((l shr 8) xor r) and $00ff00ff; r:= r xor t; l:= l xor (t shl 8); t:= ((r shr 2) xor l) and $33333333; l:= l xor t; r:= r xor (t shl 2); t:= ((l shr 16) xor r) and $0000ffff; r:= r xor t; l:= l xor (t shl 16); t:= ((r shr 4) xor l) and $0f0f0f0f; l:= l xor t; r:= r xor (t shl 4); Result := CodeLongInt(Swapbytes(l)) + CodeLongInt(Swapbytes(r)); end; {==============================================================================} procedure TSynaDes.InitKey(Key: AnsiString); begin Key := PadString(Key, 8, #0); DoInit(Key,KeyData); end; function TSynaDes.EncryptECB(const InData: AnsiString): AnsiString; begin Result := EncryptBlock(InData,KeyData); end; function TSynaDes.DecryptECB(const InData: AnsiString): AnsiString; begin Result := DecryptBlock(Indata,KeyData); end; {==============================================================================} procedure TSyna3Des.InitKey(Key: AnsiString); var Size: integer; n: integer; begin Size := length(Key); key := PadString(key, 3 * 8, #0); DoInit(Copy(key, 1, 8),KeyData[0]); DoInit(Copy(key, 9, 8),KeyData[1]); if Size > 16 then DoInit(Copy(key, 17, 8),KeyData[2]) else for n := 0 to high(KeyData[0]) do KeyData[2][n] := Keydata[0][n]; end; function TSyna3Des.EncryptECB(const InData: AnsiString): AnsiString; begin Result := EncryptBlock(Indata,KeyData[0]); Result := DecryptBlock(Result,KeyData[1]); Result := EncryptBlock(Result,KeyData[2]); end; function TSyna3Des.DecryptECB(const InData: AnsiString): AnsiString; begin Result := DecryptBlock(InData,KeyData[2]); Result := EncryptBlock(Result,KeyData[1]); Result := DecryptBlock(Result,KeyData[0]); end; {==============================================================================} function TestDes: boolean; var des: TSynaDes; s, t: string; const key = '01234567'; data1= '01234567'; data2= '0123456789abcdefghij'; begin //ECB des := TSynaDes.Create(key); try s := des.EncryptECB(data1); t := strtohex(s); result := t = 'c50ad028c6da9800'; s := des.DecryptECB(s); result := result and (data1 = s); finally des.free; end; //CBC des := TSynaDes.Create(key); try s := des.EncryptCBC(data2); t := strtohex(s); result := result and (t = 'eec50f6353115ad6dee90a22ed1b6a88a0926e35'); des.Reset; s := des.DecryptCBC(s); result := result and (data2 = s); finally des.free; end; //CFB-8bit des := TSynaDes.Create(key); try s := des.EncryptCFB8bit(data2); t := strtohex(s); result := result and (t = 'eb6aa12c2f0ff634b4dfb6da6cb2af8f9c5c1452'); des.Reset; s := des.DecryptCFB8bit(s); result := result and (data2 = s); finally des.free; end; //CFB-block des := TSynaDes.Create(key); try s := des.EncryptCFBblock(data2); t := strtohex(s); result := result and (t = 'ebdbbaa7f9286cdec28605e07f9b7f3be1053257'); des.Reset; s := des.DecryptCFBblock(s); result := result and (data2 = s); finally des.free; end; //OFB des := TSynaDes.Create(key); try s := des.EncryptOFB(data2); t := strtohex(s); result := result and (t = 'ebdbbaa7f9286cdee0b8b3798c4c34baac87dbdc'); des.Reset; s := des.DecryptOFB(s); result := result and (data2 = s); finally des.free; end; //CTR des := TSynaDes.Create(key); try s := des.EncryptCTR(data2); t := strtohex(s); result := result and (t = 'ebdbbaa7f9286cde0dd20b45f3afd9aa1b91b87e'); des.Reset; s := des.DecryptCTR(s); result := result and (data2 = s); finally des.free; end; end; function Test3Des: boolean; var des: TSyna3Des; s, t: string; const key = '0123456789abcdefghijklmn'; data1= '01234567'; data2= '0123456789abcdefghij'; begin //ECB des := TSyna3Des.Create(key); try s := des.EncryptECB(data1); t := strtohex(s); result := t = 'e0dee91008dc460c'; s := des.DecryptECB(s); result := result and (data1 = s); finally des.free; end; //CBC des := TSyna3Des.Create(key); try s := des.EncryptCBC(data2); t := strtohex(s); result := result and (t = 'ee844a2a4f49c01b91a1599b8eba29128c1ad87a'); des.Reset; s := des.DecryptCBC(s); result := result and (data2 = s); finally des.free; end; //CFB-8bit des := TSyna3Des.Create(key); try s := des.EncryptCFB8bit(data2); t := strtohex(s); result := result and (t = '935bbf5210c32cfa1faf61f91e8dc02dfa0ff1e8'); des.Reset; s := des.DecryptCFB8bit(s); result := result and (data2 = s); finally des.free; end; //CFB-block des := TSyna3Des.Create(key); try s := des.EncryptCFBblock(data2); t := strtohex(s); result := result and (t = '93754e3d54828fbf4bd81f1739419e8d2cfe1671'); des.Reset; s := des.DecryptCFBblock(s); result := result and (data2 = s); finally des.free; end; //OFB des := TSyna3Des.Create(key); try s := des.EncryptOFB(data2); t := strtohex(s); result := result and (t = '93754e3d54828fbf04ef0a5efc926ebdf2d95f20'); des.Reset; s := des.DecryptOFB(s); result := result and (data2 = s); finally des.free; end; //CTR des := TSyna3Des.Create(key); try s := des.EncryptCTR(data2); t := strtohex(s); result := result and (t = '93754e3d54828fbf1c51a121d2c93f989e70b3ad'); des.Reset; s := des.DecryptCTR(s); result := result and (data2 = s); finally des.free; end; end; {==============================================================================} end. TransGUI/synapse/source/lib/ssl_streamsec.pas0000644000000000000000000004046611366572451020340 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.000.006 | |==============================================================================| | Content: SSL support by StreamSecII | |==============================================================================| | Copyright (c)1999-2005, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2005. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Henrick Hellström | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(SSL plugin for StreamSecII or OpenStreamSecII) StreamSecII is native pascal library, you not need any external libraries! You can tune lot of StreamSecII properties by using your GlobalServer. If you not using your GlobalServer, then this plugin create own TSimpleTLSInternalServer instance for each TCP connection. Formore information about GlobalServer usage refer StreamSecII documentation. If you are not using key and certificate by GlobalServer, then you can use properties of this plugin instead, but this have limited features and @link(TCustomSSL.KeyPassword) not working properly yet! For handling keys and certificates you can use this properties: @link(TCustomSSL.CertCAFile), @link(TCustomSSL.CertCA), @link(TCustomSSL.TrustCertificateFile), @link(TCustomSSL.TrustCertificate), @link(TCustomSSL.PrivateKeyFile), @link(TCustomSSL.PrivateKey), @link(TCustomSSL.CertificateFile), @link(TCustomSSL.Certificate), @link(TCustomSSL.PFXFile). For usage of this properties and for possible formats of keys and certificates refer to StreamSecII documentation. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} unit ssl_streamsec; interface uses SysUtils, Classes, blcksock, synsock, synautil, synacode, TlsInternalServer, TlsSynaSock, TlsConst, StreamSecII, Asn1, X509Base, SecUtils; type {:@exclude} TMyTLSSynSockSlave = class(TTLSSynSockSlave) protected procedure SetMyTLSServer(const Value: TCustomTLSInternalServer); function GetMyTLSServer: TCustomTLSInternalServer; published property MyTLSServer: TCustomTLSInternalServer read GetMyTLSServer write SetMyTLSServer; end; {:@abstract(class implementing StreamSecII SSL plugin.) Instance of this class will be created for each @link(TTCPBlockSocket). You not need to create instance of this class, all is done by Synapse itself!} TSSLStreamSec = class(TCustomSSL) protected FSlave: TMyTLSSynSockSlave; FIsServer: Boolean; FTLSServer: TCustomTLSInternalServer; FServerCreated: Boolean; function SSLCheck: Boolean; function Init(server:Boolean): Boolean; function DeInit: Boolean; function Prepare(server:Boolean): Boolean; procedure NotTrustEvent(Sender: TObject; Cert: TASN1Struct; var ExplicitTrust: Boolean); function X500StrToStr(const Prefix: string; const Value: TX500String): string; function X501NameToStr(const Value: TX501Name): string; function GetCert: PASN1Struct; public constructor Create(const Value: TTCPBlockSocket); override; destructor Destroy; override; {:See @inherited} function LibVersion: String; override; {:See @inherited} function LibName: String; override; {:See @inherited and @link(ssl_streamsec) for more details.} function Connect: boolean; override; {:See @inherited and @link(ssl_streamsec) for more details.} function Accept: boolean; override; {:See @inherited} function Shutdown: boolean; override; {:See @inherited} function BiShutdown: boolean; override; {:See @inherited} function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function WaitingData: Integer; override; {:See @inherited} function GetSSLVersion: string; override; {:See @inherited} function GetPeerSubject: string; override; {:See @inherited} function GetPeerIssuer: string; override; {:See @inherited} function GetPeerName: string; override; {:See @inherited} function GetPeerFingerprint: string; override; {:See @inherited} function GetCertInfo: string; override; published {:TLS server for tuning of StreamSecII.} property TLSServer: TCustomTLSInternalServer read FTLSServer write FTLSServer; end; implementation {==============================================================================} procedure TMyTLSSynSockSlave.SetMyTLSServer(const Value: TCustomTLSInternalServer); begin TLSServer := Value; end; function TMyTLSSynSockSlave.GetMyTLSServer: TCustomTLSInternalServer; begin Result := TLSServer; end; {==============================================================================} constructor TSSLStreamSec.Create(const Value: TTCPBlockSocket); begin inherited Create(Value); FSlave := nil; FIsServer := False; FTLSServer := nil; end; destructor TSSLStreamSec.Destroy; begin DeInit; inherited Destroy; end; function TSSLStreamSec.LibVersion: String; begin Result := 'StreamSecII'; end; function TSSLStreamSec.LibName: String; begin Result := 'ssl_streamsec'; end; function TSSLStreamSec.SSLCheck: Boolean; begin Result := true; FLastErrorDesc := ''; if not Assigned(FSlave) then Exit; FLastError := FSlave.ErrorCode; if FLastError <> 0 then begin FLastErrorDesc := TlsConst.AlertMsg(FLastError); end; end; procedure TSSLStreamSec.NotTrustEvent(Sender: TObject; Cert: TASN1Struct; var ExplicitTrust: Boolean); begin ExplicitTrust := true; end; function TSSLStreamSec.Init(server:Boolean): Boolean; var st: TMemoryStream; pass: ISecretKey; ws: WideString; begin Result := False; ws := FKeyPassword; pass := TSecretKey.CreateBmpStr(PWideChar(ws), length(ws)); try FIsServer := Server; FSlave := TMyTLSSynSockSlave.CreateSocket(FSocket.Socket); if Assigned(FTLSServer) then FSlave.MyTLSServer := FTLSServer else if Assigned(TLSInternalServer.GlobalServer) then FSlave.MyTLSServer := TLSInternalServer.GlobalServer else begin FSlave.MyTLSServer := TSimpleTLSInternalServer.Create(nil); FServerCreated := True; end; if server then FSlave.MyTLSServer.ClientOrServer := cosServerSide else FSlave.MyTLSServer.ClientOrServer := cosClientSide; if not FVerifyCert then begin FSlave.MyTLSServer.OnCertNotTrusted := NotTrustEvent; end; FSlave.MyTLSServer.Options.VerifyServerName := []; FSlave.MyTLSServer.Options.Export40Bit := prAllowed; FSlave.MyTLSServer.Options.Export56Bit := prAllowed; FSlave.MyTLSServer.Options.RequestClientCertificate := False; FSlave.MyTLSServer.Options.RequireClientCertificate := False; if server and FVerifyCert then begin FSlave.MyTLSServer.Options.RequestClientCertificate := True; FSlave.MyTLSServer.Options.RequireClientCertificate := True; end; if FCertCAFile <> '' then FSlave.MyTLSServer.LoadRootCertsFromFile(CertCAFile); if FCertCA <> '' then begin st := TMemoryStream.Create; try WriteStrToStream(st, FCertCA); st.Seek(0, soFromBeginning); FSlave.MyTLSServer.LoadRootCertsFromStream(st); finally st.free; end; end; if FTrustCertificateFile <> '' then FSlave.MyTLSServer.LoadTrustedCertsFromFile(FTrustCertificateFile); if FTrustCertificate <> '' then begin st := TMemoryStream.Create; try WriteStrToStream(st, FTrustCertificate); st.Seek(0, soFromBeginning); FSlave.MyTLSServer.LoadTrustedCertsFromStream(st); finally st.free; end; end; if FPrivateKeyFile <> '' then FSlave.MyTLSServer.LoadPrivateKeyRingFromFile(FPrivateKeyFile, pass); // FSlave.MyTLSServer.PrivateKeyRing.LoadPrivateKeyFromFile(FPrivateKeyFile, pass); if FPrivateKey <> '' then begin st := TMemoryStream.Create; try WriteStrToStream(st, FPrivateKey); st.Seek(0, soFromBeginning); FSlave.MyTLSServer.LoadPrivateKeyRingFromStream(st, pass); finally st.free; end; end; if FCertificateFile <> '' then FSlave.MyTLSServer.LoadMyCertsFromFile(FCertificateFile); if FCertificate <> '' then begin st := TMemoryStream.Create; try WriteStrToStream(st, FCertificate); st.Seek(0, soFromBeginning); FSlave.MyTLSServer.LoadMyCertsFromStream(st); finally st.free; end; end; if FPFXfile <> '' then FSlave.MyTLSServer.ImportFromPFX(FPFXfile, pass); if server and FServerCreated then begin FSlave.MyTLSServer.Options.BulkCipherAES128 := prPrefer; FSlave.MyTLSServer.Options.BulkCipherAES256 := prAllowed; FSlave.MyTLSServer.Options.EphemeralECDHKeySize := ecs256; FSlave.MyTLSServer.Options.SignatureRSA := prPrefer; FSlave.MyTLSServer.Options.KeyAgreementRSA := prAllowed; FSlave.MyTLSServer.Options.KeyAgreementECDHE := prAllowed; FSlave.MyTLSServer.Options.KeyAgreementDHE := prPrefer; FSlave.MyTLSServer.TLSSetupServer; end; Result := true; finally pass := nil; end; end; function TSSLStreamSec.DeInit: Boolean; var obj: TObject; begin Result := True; if assigned(FSlave) then begin FSlave.Close; if FServerCreated then obj := FSlave.TLSServer else obj := nil; FSlave.Free; obj.Free; FSlave := nil; end; FSSLEnabled := false; end; function TSSLStreamSec.Prepare(server:Boolean): Boolean; begin Result := false; DeInit; if Init(server) then Result := true else DeInit; end; function TSSLStreamSec.Connect: boolean; begin Result := False; if FSocket.Socket = INVALID_SOCKET then Exit; if Prepare(false) then begin FSlave.Open; SSLCheck; if FLastError <> 0 then Exit; FSSLEnabled := True; Result := True; end; end; function TSSLStreamSec.Accept: boolean; begin Result := False; if FSocket.Socket = INVALID_SOCKET then Exit; if Prepare(true) then begin FSlave.DoConnect; SSLCheck; if FLastError <> 0 then Exit; FSSLEnabled := True; Result := True; end; end; function TSSLStreamSec.Shutdown: boolean; begin Result := BiShutdown; end; function TSSLStreamSec.BiShutdown: boolean; begin DeInit; Result := True; end; function TSSLStreamSec.SendBuffer(Buffer: TMemory; Len: Integer): Integer; var l: integer; begin l := len; FSlave.SendBuf(Buffer^, l, true); Result := l; SSLCheck; end; function TSSLStreamSec.RecvBuffer(Buffer: TMemory; Len: Integer): Integer; var l: integer; begin l := Len; Result := FSlave.ReceiveBuf(Buffer^, l); SSLCheck; end; function TSSLStreamSec.WaitingData: Integer; begin Result := 0; while FSlave.Connected do begin Result := FSlave.ReceiveLength; if Result > 0 then Break; Sleep(1); end; end; function TSSLStreamSec.GetSSLVersion: string; begin Result := 'SSLv3 or TLSv1'; end; function TSSLStreamSec.GetCert: PASN1Struct; begin if FIsServer then Result := FSlave.GetClientCert else Result := FSlave.GetServerCert; end; function TSSLStreamSec.GetPeerSubject: string; var XName: TX501Name; Cert: PASN1Struct; begin Result := ''; Cert := GetCert; if Assigned(cert) then begin ExtractSubject(Cert^,XName, false); Result := X501NameToStr(XName); end; end; function TSSLStreamSec.GetPeerName: string; var XName: TX501Name; Cert: PASN1Struct; begin Result := ''; Cert := GetCert; if Assigned(cert) then begin ExtractSubject(Cert^,XName, false); Result := XName.commonName.Str; end; end; function TSSLStreamSec.GetPeerIssuer: string; var XName: TX501Name; Cert: PASN1Struct; begin Result := ''; Cert := GetCert; if Assigned(cert) then begin ExtractIssuer(Cert^, XName, false); Result := X501NameToStr(XName); end; end; function TSSLStreamSec.GetPeerFingerprint: string; var Cert: PASN1Struct; begin Result := ''; Cert := GetCert; if Assigned(cert) then Result := MD5(Cert.ContentAsOctetString); end; function TSSLStreamSec.GetCertInfo: string; var Cert: PASN1Struct; l: Tstringlist; begin Result := ''; Cert := GetCert; if Assigned(cert) then begin l := TStringList.Create; try Asn1.RenderAsText(cert^, l, true, true, true, 2); Result := l.Text; finally l.free; end; end; end; function TSSLStreamSec.X500StrToStr(const Prefix: string; const Value: TX500String): string; begin if Value.Str = '' then Result := '' else Result := '/' + Prefix + '=' + Value.Str; end; function TSSLStreamSec.X501NameToStr(const Value: TX501Name): string; begin Result := X500StrToStr('CN',Value.commonName) + X500StrToStr('C',Value.countryName) + X500StrToStr('L',Value.localityName) + X500StrToStr('ST',Value.stateOrProvinceName) + X500StrToStr('O',Value.organizationName) + X500StrToStr('OU',Value.organizationalUnitName) + X500StrToStr('T',Value.title) + X500StrToStr('N',Value.name) + X500StrToStr('G',Value.givenName) + X500StrToStr('I',Value.initials) + X500StrToStr('SN',Value.surname) + X500StrToStr('GQ',Value.generationQualifier) + X500StrToStr('DNQ',Value.dnQualifier) + X500StrToStr('E',Value.emailAddress); end; {==============================================================================} initialization SSLImplementation := TSSLStreamSec; finalization end. TransGUI/synapse/source/lib/tlntsend.pas0000644000000000000000000002536611366572451017326 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.003.001 | |==============================================================================| | Content: TELNET and SSH2 client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2002-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Telnet script client) Used RFC: RFC-854 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit tlntsend; interface uses SysUtils, Classes, blcksock, synautil; const cTelnetProtocol = '23'; cSSHProtocol = '22'; TLNT_EOR = #239; TLNT_SE = #240; TLNT_NOP = #241; TLNT_DATA_MARK = #242; TLNT_BREAK = #243; TLNT_IP = #244; TLNT_AO = #245; TLNT_AYT = #246; TLNT_EC = #247; TLNT_EL = #248; TLNT_GA = #249; TLNT_SB = #250; TLNT_WILL = #251; TLNT_WONT = #252; TLNT_DO = #253; TLNT_DONT = #254; TLNT_IAC = #255; type {:@abstract(State of telnet protocol). Used internaly by TTelnetSend.} TTelnetState =(tsDATA, tsIAC, tsIAC_SB, tsIAC_WILL, tsIAC_DO, tsIAC_WONT, tsIAC_DONT, tsIAC_SBIAC, tsIAC_SBDATA, tsSBDATA_IAC); {:@abstract(Class with implementation of Telnet/SSH script client.) Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TTelnetSend = class(TSynaClient) private FSock: TTCPBlockSocket; FBuffer: Ansistring; FState: TTelnetState; FSessionLog: Ansistring; FSubNeg: Ansistring; FSubType: Ansichar; FTermType: Ansistring; function Connect: Boolean; function Negotiate(const Buf: Ansistring): Ansistring; procedure FilterHook(Sender: TObject; var Value: AnsiString); public constructor Create; destructor Destroy; override; {:Connects to Telnet server.} function Login: Boolean; {:Connects to SSH2 server and login by Username and Password properties. You must use some of SSL plugins with SSH support. For exammple CryptLib.} function SSHLogin: Boolean; {:Logout from telnet server.} procedure Logout; {:Send this data to telnet server.} procedure Send(const Value: string); {:Reading data from telnet server until Value is readed. If it is not readed until timeout, result is @false. Otherwise result is @true.} function WaitFor(const Value: string): Boolean; {:Read data terminated by terminator from telnet server.} function RecvTerminated(const Terminator: string): string; {:Read string from telnet server.} function RecvString: string; published {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; {:all readed datas in this session (from connect) is stored in this large string.} property SessionLog: Ansistring read FSessionLog write FSessionLog; {:Terminal type indentification. By default is 'SYNAPSE'.} property TermType: Ansistring read FTermType write FTermType; end; implementation constructor TTelnetSend.Create; begin inherited Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.OnReadFilter := FilterHook; FTimeout := 60000; FTargetPort := cTelnetProtocol; FSubNeg := ''; FSubType := #0; FTermType := 'SYNAPSE'; end; destructor TTelnetSend.Destroy; begin FSock.Free; inherited Destroy; end; function TTelnetSend.Connect: Boolean; begin // Do not call this function! It is calling by LOGIN method! FBuffer := ''; FSessionLog := ''; FState := tsDATA; FSock.CloseSocket; FSock.LineBuffer := ''; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); Result := FSock.LastError = 0; end; function TTelnetSend.RecvTerminated(const Terminator: string): string; begin Result := FSock.RecvTerminated(FTimeout, Terminator); end; function TTelnetSend.RecvString: string; begin Result := FSock.RecvTerminated(FTimeout, CRLF); end; function TTelnetSend.WaitFor(const Value: string): Boolean; begin Result := FSock.RecvTerminated(FTimeout, Value) <> ''; end; procedure TTelnetSend.FilterHook(Sender: TObject; var Value: AnsiString); begin Value := Negotiate(Value); FSessionLog := FSessionLog + Value; end; function TTelnetSend.Negotiate(const Buf: Ansistring): Ansistring; var n: integer; c: Ansichar; Reply: Ansistring; SubReply: Ansistring; begin Result := ''; for n := 1 to Length(Buf) do begin c := Buf[n]; Reply := ''; case FState of tsData: if c = TLNT_IAC then FState := tsIAC else Result := Result + c; tsIAC: case c of TLNT_IAC: begin FState := tsData; Result := Result + TLNT_IAC; end; TLNT_WILL: FState := tsIAC_WILL; TLNT_WONT: FState := tsIAC_WONT; TLNT_DONT: FState := tsIAC_DONT; TLNT_DO: FState := tsIAC_DO; TLNT_EOR: FState := tsDATA; TLNT_SB: begin FState := tsIAC_SB; FSubType := #0; FSubNeg := ''; end; else FState := tsData; end; tsIAC_WILL: begin case c of #3: //suppress GA Reply := TLNT_DO; else Reply := TLNT_DONT; end; FState := tsData; end; tsIAC_WONT: begin Reply := TLNT_DONT; FState := tsData; end; tsIAC_DO: begin case c of #24: //termtype Reply := TLNT_WILL; else Reply := TLNT_WONT; end; FState := tsData; end; tsIAC_DONT: begin Reply := TLNT_WONT; FState := tsData; end; tsIAC_SB: begin FSubType := c; FState := tsIAC_SBDATA; end; tsIAC_SBDATA: begin if c = TLNT_IAC then FState := tsSBDATA_IAC else FSubNeg := FSubNeg + c; end; tsSBDATA_IAC: case c of TLNT_IAC: begin FState := tsIAC_SBDATA; FSubNeg := FSubNeg + c; end; TLNT_SE: begin SubReply := ''; case FSubType of #24: //termtype begin if (FSubNeg <> '') and (FSubNeg[1] = #1) then SubReply := #0 + FTermType; end; end; Sock.SendString(TLNT_IAC + TLNT_SB + FSubType + SubReply + TLNT_IAC + TLNT_SE); FState := tsDATA; end; else FState := tsDATA; end; else FState := tsData; end; if Reply <> '' then Sock.SendString(TLNT_IAC + Reply + c); end; end; procedure TTelnetSend.Send(const Value: string); begin Sock.SendString(ReplaceString(Value, TLNT_IAC, TLNT_IAC + TLNT_IAC)); end; function TTelnetSend.Login: Boolean; begin Result := False; if not Connect then Exit; Result := True; end; function TTelnetSend.SSHLogin: Boolean; begin Result := False; if Connect then begin FSock.SSL.SSLType := LT_SSHv2; FSock.SSL.Username := FUsername; FSock.SSL.Password := FPassword; FSock.SSLDoConnect; Result := FSock.LastError = 0; end; end; procedure TTelnetSend.Logout; begin FSock.CloseSocket; end; end. TransGUI/synapse/source/lib/ssl_openssl.pas0000644000000000000000000005134311520020531020004 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.001 | |==============================================================================| | Content: SSL support by OpenSSL | |==============================================================================| | Copyright (c)1999-2008, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2005-2008. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} //requires OpenSSL libraries! {:@abstract(SSL plugin for OpenSSL) You need OpenSSL libraries version 0.9.7. It can work with 0.9.6 too, but application mysteriously crashing when you are using freePascal on Linux. Use Kylix on Linux is OK! If you have version 0.9.7 on Linux, then I not see any problems with FreePascal. OpenSSL libraries are loaded dynamicly - you not need OpenSSl librares even you compile your application with this unit. SSL just not working when you not have OpenSSL libraries. This plugin have limited support for .NET too! Because is not possible to use callbacks with CDECL calling convention under .NET, is not supported key/certificate passwords and multithread locking. :-( For handling keys and certificates you can use this properties: @link(TCustomSSL.CertificateFile) for PEM or ASN1 DER (cer) format. @br @link(TCustomSSL.Certificate) for ASN1 DER format only. @br @link(TCustomSSL.PrivateKeyFile) for PEM or ASN1 DER (key) format. @br @link(TCustomSSL.PrivateKey) for ASN1 DER format only. @br @link(TCustomSSL.CertCAFile) for PEM CA certificate bundle. @br @link(TCustomSSL.PFXFile) for PFX format. @br @link(TCustomSSL.PFX) for PFX format from binary string. @br This plugin is capable to create Ad-Hoc certificates. When you start SSL/TLS server without explicitly assigned key and certificate, then this plugin create Ad-Hoc key and certificate for each incomming connection by self. It slowdown accepting of new connections! } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit ssl_openssl; interface uses SysUtils, Classes, blcksock, synsock, synautil, {$IFDEF CIL} System.Text, {$ENDIF} ssl_openssl_lib; type {:@abstract(class implementing OpenSSL SSL plugin.) Instance of this class will be created for each @link(TTCPBlockSocket). You not need to create instance of this class, all is done by Synapse itself!} TSSLOpenSSL = class(TCustomSSL) protected FSsl: PSSL; Fctx: PSSL_CTX; function SSLCheck: Boolean; function SetSslKeys: boolean; function Init(server:Boolean): Boolean; function DeInit: Boolean; function Prepare(server:Boolean): Boolean; function LoadPFX(pfxdata: ansistring): Boolean; function CreateSelfSignedCert(Host: string): Boolean; override; public {:See @inherited} constructor Create(const Value: TTCPBlockSocket); override; destructor Destroy; override; {:See @inherited} function LibVersion: String; override; {:See @inherited} function LibName: String; override; {:See @inherited and @link(ssl_cryptlib) for more details.} function Connect: boolean; override; {:See @inherited and @link(ssl_cryptlib) for more details.} function Accept: boolean; override; {:See @inherited} function Shutdown: boolean; override; {:See @inherited} function BiShutdown: boolean; override; {:See @inherited} function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function WaitingData: Integer; override; {:See @inherited} function GetSSLVersion: string; override; {:See @inherited} function GetPeerSubject: string; override; {:See @inherited} function GetPeerIssuer: string; override; {:See @inherited} function GetPeerName: string; override; {:See @inherited} function GetPeerFingerprint: string; override; {:See @inherited} function GetCertInfo: string; override; {:See @inherited} function GetCipherName: string; override; {:See @inherited} function GetCipherBits: integer; override; {:See @inherited} function GetCipherAlgBits: integer; override; {:See @inherited} function GetVerifyCert: integer; override; end; implementation {==============================================================================} {$IFNDEF CIL} function PasswordCallback(buf:PAnsiChar; size:Integer; rwflag:Integer; userdata: Pointer):Integer; cdecl; var Password: AnsiString; begin Password := ''; if TCustomSSL(userdata) is TCustomSSL then Password := TCustomSSL(userdata).KeyPassword; if Length(Password) > (Size - 1) then SetLength(Password, Size - 1); Result := Length(Password); StrLCopy(buf, PAnsiChar(Password + #0), Result + 1); end; {$ENDIF} {==============================================================================} constructor TSSLOpenSSL.Create(const Value: TTCPBlockSocket); begin inherited Create(Value); FCiphers := 'DEFAULT'; FSsl := nil; Fctx := nil; end; destructor TSSLOpenSSL.Destroy; begin DeInit; inherited Destroy; end; function TSSLOpenSSL.LibVersion: String; begin Result := SSLeayversion(0); end; function TSSLOpenSSL.LibName: String; begin Result := 'ssl_openssl'; end; function TSSLOpenSSL.SSLCheck: Boolean; var {$IFDEF CIL} sb: StringBuilder; {$ENDIF} s : AnsiString; begin Result := true; FLastErrorDesc := ''; FLastError := ErrGetError; ErrClearError; if FLastError <> 0 then begin Result := False; {$IFDEF CIL} sb := StringBuilder.Create(256); ErrErrorString(FLastError, sb, 256); FLastErrorDesc := Trim(sb.ToString); {$ELSE} s := StringOfChar(#0, 256); ErrErrorString(FLastError, s, Length(s)); FLastErrorDesc := s; {$ENDIF} end; end; function TSSLOpenSSL.CreateSelfSignedCert(Host: string): Boolean; var pk: EVP_PKEY; x: PX509; rsa: PRSA; t: PASN1_UTCTIME; name: PX509_NAME; b: PBIO; xn, y: integer; s: AnsiString; {$IFDEF CIL} sb: StringBuilder; {$ENDIF} begin Result := True; pk := EvpPkeynew; x := X509New; try rsa := RsaGenerateKey(1024, $10001, nil, nil); EvpPkeyAssign(pk, EVP_PKEY_RSA, rsa); X509SetVersion(x, 2); Asn1IntegerSet(X509getSerialNumber(x), 0); t := Asn1UtctimeNew; try X509GmtimeAdj(t, -60 * 60 *24); X509SetNotBefore(x, t); X509GmtimeAdj(t, 60 * 60 * 60 *24); X509SetNotAfter(x, t); finally Asn1UtctimeFree(t); end; X509SetPubkey(x, pk); Name := X509GetSubjectName(x); X509NameAddEntryByTxt(Name, 'C', $1001, 'CZ', -1, -1, 0); X509NameAddEntryByTxt(Name, 'CN', $1001, host, -1, -1, 0); x509SetIssuerName(x, Name); x509Sign(x, pk, EvpGetDigestByName('SHA1')); b := BioNew(BioSMem); try i2dX509Bio(b, x); xn := bioctrlpending(b); {$IFDEF CIL} sb := StringBuilder.Create(xn); y := bioread(b, sb, xn); if y > 0 then begin sb.Length := y; s := sb.ToString; end; {$ELSE} setlength(s, xn); y := bioread(b, s, xn); if y > 0 then setlength(s, y); {$ENDIF} finally BioFreeAll(b); end; FCertificate := s; b := BioNew(BioSMem); try i2dPrivatekeyBio(b, pk); xn := bioctrlpending(b); {$IFDEF CIL} sb := StringBuilder.Create(xn); y := bioread(b, sb, xn); if y > 0 then begin sb.Length := y; s := sb.ToString; end; {$ELSE} setlength(s, xn); y := bioread(b, s, xn); if y > 0 then setlength(s, y); {$ENDIF} finally BioFreeAll(b); end; FPrivatekey := s; finally X509free(x); EvpPkeyFree(pk); end; end; function TSSLOpenSSL.LoadPFX(pfxdata: Ansistring): Boolean; var cert, pkey, ca: SslPtr; b: PBIO; p12: SslPtr; begin Result := False; b := BioNew(BioSMem); try BioWrite(b, pfxdata, Length(PfxData)); p12 := d2iPKCS12bio(b, nil); if not Assigned(p12) then Exit; try cert := nil; pkey := nil; ca := nil; if PKCS12parse(p12, FKeyPassword, pkey, cert, ca) > 0 then if SSLCTXusecertificate(Fctx, cert) > 0 then if SSLCTXusePrivateKey(Fctx, pkey) > 0 then Result := True; finally PKCS12free(p12); end; finally BioFreeAll(b); end; end; function TSSLOpenSSL.SetSslKeys: boolean; var st: TFileStream; s: string; begin Result := False; if not assigned(FCtx) then Exit; try if FCertificateFile <> '' then if SslCtxUseCertificateChainFile(FCtx, FCertificateFile) <> 1 then if SslCtxUseCertificateFile(FCtx, FCertificateFile, SSL_FILETYPE_PEM) <> 1 then if SslCtxUseCertificateFile(FCtx, FCertificateFile, SSL_FILETYPE_ASN1) <> 1 then Exit; if FCertificate <> '' then if SslCtxUseCertificateASN1(FCtx, length(FCertificate), FCertificate) <> 1 then Exit; SSLCheck; if FPrivateKeyFile <> '' then if SslCtxUsePrivateKeyFile(FCtx, FPrivateKeyFile, SSL_FILETYPE_PEM) <> 1 then if SslCtxUsePrivateKeyFile(FCtx, FPrivateKeyFile, SSL_FILETYPE_ASN1) <> 1 then Exit; if FPrivateKey <> '' then if SslCtxUsePrivateKeyASN1(EVP_PKEY_RSA, FCtx, FPrivateKey, length(FPrivateKey)) <> 1 then Exit; SSLCheck; if FCertCAFile <> '' then if SslCtxLoadVerifyLocations(FCtx, FCertCAFile, '') <> 1 then Exit; if FPFXfile <> '' then begin try st := TFileStream.Create(FPFXfile, fmOpenRead or fmShareDenyNone); try s := ReadStrFromStream(st, st.Size); finally st.Free; end; if not LoadPFX(s) then Exit; except on Exception do Exit; end; end; if FPFX <> '' then if not LoadPFX(FPfx) then Exit; SSLCheck; Result := True; finally SSLCheck; end; end; function TSSLOpenSSL.Init(server:Boolean): Boolean; var s: AnsiString; begin Result := False; FLastErrorDesc := ''; FLastError := 0; Fctx := nil; case FSSLType of LT_SSLv2: Fctx := SslCtxNew(SslMethodV2); LT_SSLv3: Fctx := SslCtxNew(SslMethodV3); LT_TLSv1: Fctx := SslCtxNew(SslMethodTLSV1); LT_all: Fctx := SslCtxNew(SslMethodV23); else Exit; end; if Fctx = nil then begin SSLCheck; Exit; end else begin s := FCiphers; SslCtxSetCipherList(Fctx, s); if FVerifyCert then SslCtxSetVerify(FCtx, SSL_VERIFY_PEER, nil) else SslCtxSetVerify(FCtx, SSL_VERIFY_NONE, nil); {$IFNDEF CIL} SslCtxSetDefaultPasswdCb(FCtx, @PasswordCallback); SslCtxSetDefaultPasswdCbUserdata(FCtx, self); {$ENDIF} if server and (FCertificateFile = '') and (FCertificate = '') and (FPFXfile = '') and (FPFX = '') then begin CreateSelfSignedcert(FSocket.ResolveIPToName(FSocket.GetRemoteSinIP)); end; if not SetSSLKeys then Exit else begin Fssl := nil; Fssl := SslNew(Fctx); if Fssl = nil then begin SSLCheck; exit; end; end; end; Result := true; end; function TSSLOpenSSL.DeInit: Boolean; begin Result := True; if assigned (Fssl) then sslfree(Fssl); Fssl := nil; if assigned (Fctx) then begin SslCtxFree(Fctx); Fctx := nil; ErrRemoveState(0); end; FSSLEnabled := False; end; function TSSLOpenSSL.Prepare(server:Boolean): Boolean; begin Result := false; DeInit; if Init(server) then Result := true else DeInit; end; function TSSLOpenSSL.Connect: boolean; var x: integer; begin Result := False; if FSocket.Socket = INVALID_SOCKET then Exit; if Prepare(False) then begin {$IFDEF CIL} if sslsetfd(FSsl, FSocket.Socket.Handle.ToInt32) < 1 then {$ELSE} if sslsetfd(FSsl, FSocket.Socket) < 1 then {$ENDIF} begin SSLCheck; Exit; end; x := sslconnect(FSsl); if x < 1 then begin SSLcheck; Exit; end; if FverifyCert then if GetVerifyCert <> 0 then Exit; FSSLEnabled := True; Result := True; end; end; function TSSLOpenSSL.Accept: boolean; var x: integer; begin Result := False; if FSocket.Socket = INVALID_SOCKET then Exit; if Prepare(True) then begin {$IFDEF CIL} if sslsetfd(FSsl, FSocket.Socket.Handle.ToInt32) < 1 then {$ELSE} if sslsetfd(FSsl, FSocket.Socket) < 1 then {$ENDIF} begin SSLCheck; Exit; end; x := sslAccept(FSsl); if x < 1 then begin SSLcheck; Exit; end; FSSLEnabled := True; Result := True; end; end; function TSSLOpenSSL.Shutdown: boolean; begin if assigned(FSsl) then sslshutdown(FSsl); DeInit; Result := True; end; function TSSLOpenSSL.BiShutdown: boolean; var x: integer; begin if assigned(FSsl) then begin x := sslshutdown(FSsl); if x = 0 then begin Synsock.Shutdown(FSocket.Socket, 1); sslshutdown(FSsl); end; end; DeInit; Result := True; end; function TSSLOpenSSL.SendBuffer(Buffer: TMemory; Len: Integer): Integer; var err: integer; {$IFDEF CIL} s: ansistring; {$ENDIF} begin FLastError := 0; FLastErrorDesc := ''; repeat {$IFDEF CIL} s := StringOf(Buffer); Result := SslWrite(FSsl, s, Len); {$ELSE} Result := SslWrite(FSsl, Buffer , Len); {$ENDIF} err := SslGetError(FSsl, Result); until (err <> SSL_ERROR_WANT_READ) and (err <> SSL_ERROR_WANT_WRITE); if err = SSL_ERROR_ZERO_RETURN then Result := 0 else if (err <> 0) then FLastError := err; end; function TSSLOpenSSL.RecvBuffer(Buffer: TMemory; Len: Integer): Integer; var err: integer; {$IFDEF CIL} sb: stringbuilder; s: ansistring; {$ENDIF} begin FLastError := 0; FLastErrorDesc := ''; repeat {$IFDEF CIL} sb := StringBuilder.Create(Len); Result := SslRead(FSsl, sb, Len); if Result > 0 then begin sb.Length := Result; s := sb.ToString; System.Array.Copy(BytesOf(s), Buffer, length(s)); end; {$ELSE} Result := SslRead(FSsl, Buffer , Len); {$ENDIF} err := SslGetError(FSsl, Result); until (err <> SSL_ERROR_WANT_READ) and (err <> SSL_ERROR_WANT_WRITE); if err = SSL_ERROR_ZERO_RETURN then Result := 0; if (err <> 0) then FLastError := err; end; function TSSLOpenSSL.WaitingData: Integer; begin Result := sslpending(Fssl); end; function TSSLOpenSSL.GetSSLVersion: string; begin if not assigned(FSsl) then Result := '' else Result := SSlGetVersion(FSsl); end; function TSSLOpenSSL.GetPeerSubject: string; var cert: PX509; s: ansistring; {$IFDEF CIL} sb: StringBuilder; {$ENDIF} begin if not assigned(FSsl) then begin Result := ''; Exit; end; cert := SSLGetPeerCertificate(Fssl); if not assigned(cert) then begin Result := ''; Exit; end; {$IFDEF CIL} sb := StringBuilder.Create(4096); Result := X509NameOneline(X509GetSubjectName(cert), sb, 4096); {$ELSE} setlength(s, 4096); Result := X509NameOneline(X509GetSubjectName(cert), s, Length(s)); {$ENDIF} X509Free(cert); end; function TSSLOpenSSL.GetPeerName: string; var s: ansistring; begin s := GetPeerSubject; s := SeparateRight(s, '/CN='); Result := Trim(SeparateLeft(s, '/')); end; function TSSLOpenSSL.GetPeerIssuer: string; var cert: PX509; s: ansistring; {$IFDEF CIL} sb: StringBuilder; {$ENDIF} begin if not assigned(FSsl) then begin Result := ''; Exit; end; cert := SSLGetPeerCertificate(Fssl); if not assigned(cert) then begin Result := ''; Exit; end; {$IFDEF CIL} sb := StringBuilder.Create(4096); Result := X509NameOneline(X509GetIssuerName(cert), sb, 4096); {$ELSE} setlength(s, 4096); Result := X509NameOneline(X509GetIssuerName(cert), s, Length(s)); {$ENDIF} X509Free(cert); end; function TSSLOpenSSL.GetPeerFingerprint: string; var cert: PX509; x: integer; {$IFDEF CIL} sb: StringBuilder; {$ENDIF} begin if not assigned(FSsl) then begin Result := ''; Exit; end; cert := SSLGetPeerCertificate(Fssl); if not assigned(cert) then begin Result := ''; Exit; end; {$IFDEF CIL} sb := StringBuilder.Create(EVP_MAX_MD_SIZE); X509Digest(cert, EvpGetDigestByName('MD5'), sb, x); sb.Length := x; Result := sb.ToString; {$ELSE} setlength(Result, EVP_MAX_MD_SIZE); X509Digest(cert, EvpGetDigestByName('MD5'), Result, x); SetLength(Result, x); {$ENDIF} X509Free(cert); end; function TSSLOpenSSL.GetCertInfo: string; var cert: PX509; x, y: integer; b: PBIO; s: AnsiString; {$IFDEF CIL} sb: stringbuilder; {$ENDIF} begin if not assigned(FSsl) then begin Result := ''; Exit; end; cert := SSLGetPeerCertificate(Fssl); if not assigned(cert) then begin Result := ''; Exit; end; b := BioNew(BioSMem); try X509Print(b, cert); x := bioctrlpending(b); {$IFDEF CIL} sb := StringBuilder.Create(x); y := bioread(b, sb, x); if y > 0 then begin sb.Length := y; s := sb.ToString; end; {$ELSE} setlength(s,x); y := bioread(b,s,x); if y > 0 then setlength(s, y); {$ENDIF} Result := ReplaceString(s, LF, CRLF); finally BioFreeAll(b); end; end; function TSSLOpenSSL.GetCipherName: string; begin if not assigned(FSsl) then Result := '' else Result := SslCipherGetName(SslGetCurrentCipher(FSsl)); end; function TSSLOpenSSL.GetCipherBits: integer; var x: integer; begin if not assigned(FSsl) then Result := 0 else Result := SSLCipherGetBits(SslGetCurrentCipher(FSsl), x); end; function TSSLOpenSSL.GetCipherAlgBits: integer; begin if not assigned(FSsl) then Result := 0 else SSLCipherGetBits(SslGetCurrentCipher(FSsl), Result); end; function TSSLOpenSSL.GetVerifyCert: integer; begin if not assigned(FSsl) then Result := 1 else Result := SslGetVerifyResult(FSsl); end; {==============================================================================} initialization // if InitSSLInterface then // SSLImplementation := TSSLOpenSSL; end. TransGUI/synapse/source/lib/synaip.pas0000644000000000000000000002717711366572451017000 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.002.001 | |==============================================================================| | Content: IP address support procedures and functions | |==============================================================================| | Copyright (c)2006-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 2006-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(IP adress support procedures and functions)} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$R-} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$WARN SUSPICIOUS_TYPECAST OFF} {$ENDIF} unit synaip; interface uses SysUtils, SynaUtil; type {:binary form of IPv6 adress (for string conversion routines)} TIp6Bytes = array [0..15] of Byte; {:binary form of IPv6 adress (for string conversion routines)} TIp6Words = array [0..7] of Word; {:Returns @TRUE, if "Value" is a valid IPv4 address. Cannot be a symbolic Name!} function IsIP(const Value: string): Boolean; {:Returns @TRUE, if "Value" is a valid IPv6 address. Cannot be a symbolic Name!} function IsIP6(const Value: string): Boolean; {:Returns a string with the "Host" ip address converted to binary form.} function IPToID(Host: string): Ansistring; {:Convert IPv6 address from their string form to binary byte array.} function StrToIp6(value: string): TIp6Bytes; {:Convert IPv6 address from binary byte array to string form.} function Ip6ToStr(value: TIp6Bytes): string; {:Convert IPv4 address from their string form to binary.} function StrToIp(value: string): integer; {:Convert IPv4 address from binary to string form.} function IpToStr(value: integer): string; {:Convert IPv4 address to reverse form.} function ReverseIP(Value: AnsiString): AnsiString; {:Convert IPv6 address to reverse form.} function ReverseIP6(Value: AnsiString): AnsiString; {:Expand short form of IPv6 address to long form.} function ExpandIP6(Value: AnsiString): AnsiString; implementation {==============================================================================} function IsIP(const Value: string): Boolean; var TempIP: string; function ByteIsOk(const Value: string): Boolean; var x, n: integer; begin x := StrToIntDef(Value, -1); Result := (x >= 0) and (x < 256); // X may be in correct range, but value still may not be correct value! // i.e. "$80" if Result then for n := 1 to length(Value) do if not (AnsiChar(Value[n]) in ['0'..'9']) then begin Result := False; Break; end; end; begin TempIP := Value; Result := False; if not ByteIsOk(Fetch(TempIP, '.')) then Exit; if not ByteIsOk(Fetch(TempIP, '.')) then Exit; if not ByteIsOk(Fetch(TempIP, '.')) then Exit; if ByteIsOk(TempIP) then Result := True; end; {==============================================================================} function IsIP6(const Value: string): Boolean; var TempIP: string; s,t: string; x: integer; partcount: integer; zerocount: integer; First: Boolean; begin TempIP := Value; Result := False; if Value = '::' then begin Result := True; Exit; end; partcount := 0; zerocount := 0; First := True; while tempIP <> '' do begin s := fetch(TempIP, ':'); if not(First) and (s = '') then Inc(zerocount); First := False; if zerocount > 1 then break; Inc(partCount); if s = '' then Continue; if partCount > 8 then break; if tempIP = '' then begin t := SeparateRight(s, '%'); s := SeparateLeft(s, '%'); x := StrToIntDef('$' + t, -1); if (x < 0) or (x > $ffff) then break; end; x := StrToIntDef('$' + s, -1); if (x < 0) or (x > $ffff) then break; if tempIP = '' then if not((PartCount = 1) and (ZeroCount = 0)) then Result := True; end; end; {==============================================================================} function IPToID(Host: string): Ansistring; var s: string; i, x: Integer; begin Result := ''; for x := 0 to 3 do begin s := Fetch(Host, '.'); i := StrToIntDef(s, 0); Result := Result + AnsiChar(i); end; end; {==============================================================================} function StrToIp(value: string): integer; var s: string; i, x: Integer; begin Result := 0; for x := 0 to 3 do begin s := Fetch(value, '.'); i := StrToIntDef(s, 0); Result := (256 * Result) + i; end; end; {==============================================================================} function IpToStr(value: integer): string; var x1, x2: word; y1, y2: byte; begin Result := ''; x1 := value shr 16; x2 := value and $FFFF; y1 := x1 div $100; y2 := x1 mod $100; Result := inttostr(y1) + '.' + inttostr(y2) + '.'; y1 := x2 div $100; y2 := x2 mod $100; Result := Result + inttostr(y1) + '.' + inttostr(y2); end; {==============================================================================} function ExpandIP6(Value: AnsiString): AnsiString; var n: integer; s: ansistring; x: integer; begin Result := ''; if value = '' then exit; x := countofchar(value, ':'); if x > 7 then exit; if value[1] = ':' then value := '0' + value; if value[length(value)] = ':' then value := value + '0'; x := 8 - x; s := ''; for n := 1 to x do s := s + ':0'; s := s + ':'; Result := replacestring(value, '::', s); end; {==============================================================================} function StrToIp6(Value: string): TIp6Bytes; var IPv6: TIp6Words; Index: Integer; n: integer; b1, b2: byte; s: string; x: integer; begin for n := 0 to 15 do Result[n] := 0; for n := 0 to 7 do Ipv6[n] := 0; Index := 0; Value := ExpandIP6(value); if value = '' then exit; while Value <> '' do begin if Index > 7 then Exit; s := fetch(value, ':'); if s = '@' then break; if s = '' then begin IPv6[Index] := 0; end else begin x := StrToIntDef('$' + s, -1); if (x > 65535) or (x < 0) then Exit; IPv6[Index] := x; end; Inc(Index); end; for n := 0 to 7 do begin b1 := ipv6[n] div 256; b2 := ipv6[n] mod 256; Result[n * 2] := b1; Result[(n * 2) + 1] := b2; end; end; {==============================================================================} //based on routine by the Free Pascal development team function Ip6ToStr(value: TIp6Bytes): string; var i, x: byte; zr1,zr2: set of byte; zc1,zc2: byte; have_skipped: boolean; ip6w: TIp6words; begin zr1 := []; zr2 := []; zc1 := 0; zc2 := 0; for i := 0 to 7 do begin x := i * 2; ip6w[i] := value[x] * 256 + value[x + 1]; if ip6w[i] = 0 then begin include(zr2, i); inc(zc2); end else begin if zc1 < zc2 then begin zc1 := zc2; zr1 := zr2; zc2 := 0; zr2 := []; end; end; end; if zc1 < zc2 then begin zr1 := zr2; end; SetLength(Result, 8*5-1); SetLength(Result, 0); have_skipped := false; for i := 0 to 7 do begin if not(i in zr1) then begin if have_skipped then begin if Result = '' then Result := '::' else Result := Result + ':'; have_skipped := false; end; Result := Result + IntToHex(Ip6w[i], 1) + ':'; end else begin have_skipped := true; end; end; if have_skipped then if Result = '' then Result := '::0' else Result := Result + ':'; if Result = '' then Result := '::0'; if not (7 in zr1) then SetLength(Result, Length(Result)-1); Result := LowerCase(result); end; {==============================================================================} function ReverseIP(Value: AnsiString): AnsiString; var x: Integer; begin Result := ''; repeat x := LastDelimiter('.', Value); Result := Result + '.' + Copy(Value, x + 1, Length(Value) - x); Delete(Value, x, Length(Value) - x + 1); until x < 1; if Length(Result) > 0 then if Result[1] = '.' then Delete(Result, 1, 1); end; {==============================================================================} function ReverseIP6(Value: AnsiString): AnsiString; var ip6: TIp6bytes; n: integer; x, y: integer; begin ip6 := StrToIP6(Value); x := ip6[15] div 16; y := ip6[15] mod 16; Result := IntToHex(y, 1) + '.' + IntToHex(x, 1); for n := 14 downto 0 do begin x := ip6[n] div 16; y := ip6[n] mod 16; Result := Result + '.' + IntToHex(y, 1) + '.' + IntToHex(x, 1); end; end; {==============================================================================} end. TransGUI/synapse/source/lib/ldapsend.pas0000644000000000000000000010634411366572451017261 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.007.000 | |==============================================================================| | Content: LDAP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2003-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(LDAP client) Used RFC: RFC-2251, RFC-2254, RFC-2829, RFC-2830 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit ldapsend; interface uses SysUtils, Classes, blcksock, synautil, asn1util, synacode; const cLDAPProtocol = '389'; LDAP_ASN1_BIND_REQUEST = $60; LDAP_ASN1_BIND_RESPONSE = $61; LDAP_ASN1_UNBIND_REQUEST = $42; LDAP_ASN1_SEARCH_REQUEST = $63; LDAP_ASN1_SEARCH_ENTRY = $64; LDAP_ASN1_SEARCH_DONE = $65; LDAP_ASN1_SEARCH_REFERENCE = $73; LDAP_ASN1_MODIFY_REQUEST = $66; LDAP_ASN1_MODIFY_RESPONSE = $67; LDAP_ASN1_ADD_REQUEST = $68; LDAP_ASN1_ADD_RESPONSE = $69; LDAP_ASN1_DEL_REQUEST = $4A; LDAP_ASN1_DEL_RESPONSE = $6B; LDAP_ASN1_MODIFYDN_REQUEST = $6C; LDAP_ASN1_MODIFYDN_RESPONSE = $6D; LDAP_ASN1_COMPARE_REQUEST = $6E; LDAP_ASN1_COMPARE_RESPONSE = $6F; LDAP_ASN1_ABANDON_REQUEST = $70; LDAP_ASN1_EXT_REQUEST = $77; LDAP_ASN1_EXT_RESPONSE = $78; type {:@abstract(LDAP attribute with list of their values) This class holding name of LDAP attribute and list of their values. This is descendant of TStringList class enhanced by some new properties.} TLDAPAttribute = class(TStringList) private FAttributeName: AnsiString; FIsBinary: Boolean; protected function Get(Index: integer): string; override; procedure Put(Index: integer; const Value: string); override; procedure SetAttributeName(Value: AnsiString); published {:Name of LDAP attribute.} property AttributeName: AnsiString read FAttributeName Write SetAttributeName; {:Return @true when attribute contains binary data.} property IsBinary: Boolean read FIsBinary; end; {:@abstract(List of @link(TLDAPAttribute)) This object can hold list of TLDAPAttribute objects.} TLDAPAttributeList = class(TObject) private FAttributeList: TList; function GetAttribute(Index: integer): TLDAPAttribute; public constructor Create; destructor Destroy; override; {:Clear list.} procedure Clear; {:Return count of TLDAPAttribute objects in list.} function Count: integer; {:Add new TLDAPAttribute object to list.} function Add: TLDAPAttribute; {:Delete one TLDAPAttribute object from list.} procedure Del(Index: integer); {:Find and return attribute with requested name. Returns nil if not found.} function Find(AttributeName: AnsiString): TLDAPAttribute; {:Find and return attribute value with requested name. Returns empty string if not found.} function Get(AttributeName: AnsiString): string; {:List of TLDAPAttribute objects.} property Items[Index: Integer]: TLDAPAttribute read GetAttribute; default; end; {:@abstract(LDAP result object) This object can hold LDAP object. (their name and all their attributes with values)} TLDAPResult = class(TObject) private FObjectName: AnsiString; FAttributes: TLDAPAttributeList; public constructor Create; destructor Destroy; override; published {:Name of this LDAP object.} property ObjectName: AnsiString read FObjectName write FObjectName; {:Here is list of object attributes.} property Attributes: TLDAPAttributeList read FAttributes; end; {:@abstract(List of LDAP result objects) This object can hold list of LDAP objects. (for example result of LDAP SEARCH.)} TLDAPResultList = class(TObject) private FResultList: TList; function GetResult(Index: integer): TLDAPResult; public constructor Create; destructor Destroy; override; {:Clear all TLDAPResult objects in list.} procedure Clear; {:Return count of TLDAPResult objects in list.} function Count: integer; {:Create and add new TLDAPResult object to list.} function Add: TLDAPResult; {:List of TLDAPResult objects.} property Items[Index: Integer]: TLDAPResult read GetResult; default; end; {:Define possible operations for LDAP MODIFY operations.} TLDAPModifyOp = ( MO_Add, MO_Delete, MO_Replace ); {:Specify possible values for search scope.} TLDAPSearchScope = ( SS_BaseObject, SS_SingleLevel, SS_WholeSubtree ); {:Specify possible values about alias dereferencing.} TLDAPSearchAliases = ( SA_NeverDeref, SA_InSearching, SA_FindingBaseObj, SA_Always ); {:@abstract(Implementation of LDAP client) (version 2 and 3) Note: Are you missing properties for setting Username and Password? Look to parent @link(TSynaClient) object! Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TLDAPSend = class(TSynaClient) private FSock: TTCPBlockSocket; FResultCode: Integer; FResultString: AnsiString; FFullResult: AnsiString; FAutoTLS: Boolean; FFullSSL: Boolean; FSeq: integer; FResponseCode: integer; FResponseDN: AnsiString; FReferals: TStringList; FVersion: integer; FSearchScope: TLDAPSearchScope; FSearchAliases: TLDAPSearchAliases; FSearchSizeLimit: integer; FSearchTimeLimit: integer; FSearchResult: TLDAPResultList; FExtName: AnsiString; FExtValue: AnsiString; function Connect: Boolean; function BuildPacket(const Value: AnsiString): AnsiString; function ReceiveResponse: AnsiString; function DecodeResponse(const Value: AnsiString): AnsiString; function LdapSasl(Value: AnsiString): AnsiString; function TranslateFilter(Value: AnsiString): AnsiString; function GetErrorString(Value: integer): AnsiString; public constructor Create; destructor Destroy; override; {:Try to connect to LDAP server and start secure channel, when it is required.} function Login: Boolean; {:Try to bind to LDAP server with @link(TSynaClient.Username) and @link(TSynaClient.Password). If this is empty strings, then it do annonymous Bind. When you not call Bind on LDAPv3, then is automaticly used anonymous mode. This method using plaintext transport of password! It is not secure!} function Bind: Boolean; {:Try to bind to LDAP server with @link(TSynaClient.Username) and @link(TSynaClient.Password). If this is empty strings, then it do annonymous Bind. When you not call Bind on LDAPv3, then is automaticly used anonymous mode. This method using SASL with DIGEST-MD5 method for secure transfer of your password.} function BindSasl: Boolean; {:Close connection to LDAP server.} function Logout: Boolean; {:Modify content of LDAP attribute on this object.} function Modify(obj: AnsiString; Op: TLDAPModifyOp; const Value: TLDAPAttribute): Boolean; {:Add list of attributes to specified object.} function Add(obj: AnsiString; const Value: TLDAPAttributeList): Boolean; {:Delete this LDAP object from server.} function Delete(obj: AnsiString): Boolean; {:Modify object name of this LDAP object.} function ModifyDN(obj, newRDN, newSuperior: AnsiString; DeleteoldRDN: Boolean): Boolean; {:Try to compare Attribute value with this LDAP object.} function Compare(obj, AttributeValue: AnsiString): Boolean; {:Search LDAP base for LDAP objects by Filter.} function Search(obj: AnsiString; TypesOnly: Boolean; Filter: AnsiString; const Attributes: TStrings): Boolean; {:Call any LDAPv3 extended command.} function Extended(const Name, Value: AnsiString): Boolean; {:Try to start SSL/TLS connection to LDAP server.} function StartTLS: Boolean; published {:Specify version of used LDAP protocol. Default value is 3.} property Version: integer read FVersion Write FVersion; {:Result code of last LDAP operation.} property ResultCode: Integer read FResultCode; {:Human readable description of result code of last LDAP operation.} property ResultString: AnsiString read FResultString; {:Binary string with full last response of LDAP server. This string is encoded by ASN.1 BER encoding! You need this only for debugging.} property FullResult: AnsiString read FFullResult; {:If @true, then try to start TSL mode in Login procedure.} property AutoTLS: Boolean read FAutoTLS Write FAutoTLS; {:If @true, then use connection to LDAP server through SSL/TLS tunnel.} property FullSSL: Boolean read FFullSSL Write FFullSSL; {:Sequence number of last LDAp command. It is incremented by any LDAP command.} property Seq: integer read FSeq; {:Specify what search scope is used in search command.} property SearchScope: TLDAPSearchScope read FSearchScope Write FSearchScope; {:Specify how to handle aliases in search command.} property SearchAliases: TLDAPSearchAliases read FSearchAliases Write FSearchAliases; {:Specify result size limit in search command. Value 0 means without limit.} property SearchSizeLimit: integer read FSearchSizeLimit Write FSearchSizeLimit; {:Specify search time limit in search command (seconds). Value 0 means without limit.} property SearchTimeLimit: integer read FSearchTimeLimit Write FSearchTimeLimit; {:Here is result of search command.} property SearchResult: TLDAPResultList read FSearchResult; {:On each LDAP operation can LDAP server return some referals URLs. Here is their list.} property Referals: TStringList read FReferals; {:When you call @link(Extended) operation, then here is result Name returned by server.} property ExtName: AnsiString read FExtName; {:When you call @link(Extended) operation, then here is result Value returned by server.} property ExtValue: AnsiString read FExtValue; {:TCP socket used by all LDAP operations.} property Sock: TTCPBlockSocket read FSock; end; {:Dump result of LDAP SEARCH into human readable form. Good for debugging.} function LDAPResultDump(const Value: TLDAPResultList): AnsiString; implementation {==============================================================================} function TLDAPAttribute.Get(Index: integer): string; begin Result := inherited Get(Index); if FIsbinary then Result := DecodeBase64(Result); end; procedure TLDAPAttribute.Put(Index: integer; const Value: string); var s: AnsiString; begin s := Value; if FIsbinary then s := EncodeBase64(Value) else s :=UnquoteStr(s, '"'); inherited Put(Index, s); end; procedure TLDAPAttribute.SetAttributeName(Value: AnsiString); begin FAttributeName := Value; FIsBinary := Pos(';binary', Lowercase(value)) > 0; end; {==============================================================================} constructor TLDAPAttributeList.Create; begin inherited Create; FAttributeList := TList.Create; end; destructor TLDAPAttributeList.Destroy; begin Clear; FAttributeList.Free; inherited Destroy; end; procedure TLDAPAttributeList.Clear; var n: integer; x: TLDAPAttribute; begin for n := Count - 1 downto 0 do begin x := GetAttribute(n); if Assigned(x) then x.Free; end; FAttributeList.Clear; end; function TLDAPAttributeList.Count: integer; begin Result := FAttributeList.Count; end; function TLDAPAttributeList.Get(AttributeName: AnsiString): string; var x: TLDAPAttribute; begin Result := ''; x := self.Find(AttributeName); if x <> nil then if x.Count > 0 then Result := x[0]; end; function TLDAPAttributeList.GetAttribute(Index: integer): TLDAPAttribute; begin Result := nil; if Index < Count then Result := TLDAPAttribute(FAttributeList[Index]); end; function TLDAPAttributeList.Add: TLDAPAttribute; begin Result := TLDAPAttribute.Create; FAttributeList.Add(Result); end; procedure TLDAPAttributeList.Del(Index: integer); var x: TLDAPAttribute; begin x := GetAttribute(Index); if Assigned(x) then x.free; FAttributeList.Delete(Index); end; function TLDAPAttributeList.Find(AttributeName: AnsiString): TLDAPAttribute; var n: integer; x: TLDAPAttribute; begin Result := nil; AttributeName := lowercase(AttributeName); for n := 0 to Count - 1 do begin x := GetAttribute(n); if Assigned(x) then if lowercase(x.AttributeName) = Attributename then begin result := x; break; end; end; end; {==============================================================================} constructor TLDAPResult.Create; begin inherited Create; FAttributes := TLDAPAttributeList.Create; end; destructor TLDAPResult.Destroy; begin FAttributes.Free; inherited Destroy; end; {==============================================================================} constructor TLDAPResultList.Create; begin inherited Create; FResultList := TList.Create; end; destructor TLDAPResultList.Destroy; begin Clear; FResultList.Free; inherited Destroy; end; procedure TLDAPResultList.Clear; var n: integer; x: TLDAPResult; begin for n := Count - 1 downto 0 do begin x := GetResult(n); if Assigned(x) then x.Free; end; FResultList.Clear; end; function TLDAPResultList.Count: integer; begin Result := FResultList.Count; end; function TLDAPResultList.GetResult(Index: integer): TLDAPResult; begin Result := nil; if Index < Count then Result := TLDAPResult(FResultList[Index]); end; function TLDAPResultList.Add: TLDAPResult; begin Result := TLDAPResult.Create; FResultList.Add(Result); end; {==============================================================================} constructor TLDAPSend.Create; begin inherited Create; FReferals := TStringList.Create; FFullResult := ''; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FTimeout := 60000; FTargetPort := cLDAPProtocol; FAutoTLS := False; FFullSSL := False; FSeq := 0; FVersion := 3; FSearchScope := SS_WholeSubtree; FSearchAliases := SA_Always; FSearchSizeLimit := 0; FSearchTimeLimit := 0; FSearchResult := TLDAPResultList.Create; end; destructor TLDAPSend.Destroy; begin FSock.Free; FSearchResult.Free; FReferals.Free; inherited Destroy; end; function TLDAPSend.GetErrorString(Value: integer): AnsiString; begin case Value of 0: Result := 'Success'; 1: Result := 'Operations error'; 2: Result := 'Protocol error'; 3: Result := 'Time limit Exceeded'; 4: Result := 'Size limit Exceeded'; 5: Result := 'Compare FALSE'; 6: Result := 'Compare TRUE'; 7: Result := 'Auth method not supported'; 8: Result := 'Strong auth required'; 9: Result := '-- reserved --'; 10: Result := 'Referal'; 11: Result := 'Admin limit exceeded'; 12: Result := 'Unavailable critical extension'; 13: Result := 'Confidentality required'; 14: Result := 'Sasl bind in progress'; 16: Result := 'No such attribute'; 17: Result := 'Undefined attribute type'; 18: Result := 'Inappropriate matching'; 19: Result := 'Constraint violation'; 20: Result := 'Attribute or value exists'; 21: Result := 'Invalid attribute syntax'; 32: Result := 'No such object'; 33: Result := 'Alias problem'; 34: Result := 'Invalid DN syntax'; 36: Result := 'Alias dereferencing problem'; 48: Result := 'Inappropriate authentication'; 49: Result := 'Invalid credentials'; 50: Result := 'Insufficient access rights'; 51: Result := 'Busy'; 52: Result := 'Unavailable'; 53: Result := 'Unwilling to perform'; 54: Result := 'Loop detect'; 64: Result := 'Naming violation'; 65: Result := 'Object class violation'; 66: Result := 'Not allowed on non leaf'; 67: Result := 'Not allowed on RDN'; 68: Result := 'Entry already exists'; 69: Result := 'Object class mods prohibited'; 71: Result := 'Affects multiple DSAs'; 80: Result := 'Other'; else Result := '--unknown--'; end; end; function TLDAPSend.Connect: Boolean; begin // Do not call this function! It is calling by LOGIN method! FSock.CloseSocket; FSock.LineBuffer := ''; FSeq := 0; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError = 0 then FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError = 0 then if FFullSSL then FSock.SSLDoConnect; Result := FSock.LastError = 0; end; function TLDAPSend.BuildPacket(const Value: AnsiString): AnsiString; begin Inc(FSeq); Result := ASNObject(ASNObject(ASNEncInt(FSeq), ASN1_INT) + Value, ASN1_SEQ); end; function TLDAPSend.ReceiveResponse: AnsiString; var x: Byte; i,j: integer; begin Result := ''; FFullResult := ''; x := FSock.RecvByte(FTimeout); if x <> ASN1_SEQ then Exit; Result := AnsiChar(x); x := FSock.RecvByte(FTimeout); Result := Result + AnsiChar(x); if x < $80 then i := 0 else i := x and $7F; if i > 0 then Result := Result + FSock.RecvBufferStr(i, Ftimeout); if FSock.LastError <> 0 then begin Result := ''; Exit; end; //get length of LDAP packet j := 2; i := ASNDecLen(j, Result); //retreive rest of LDAP packet if i > 0 then Result := Result + FSock.RecvBufferStr(i, Ftimeout); if FSock.LastError <> 0 then begin Result := ''; Exit; end; FFullResult := Result; end; function TLDAPSend.DecodeResponse(const Value: AnsiString): AnsiString; var i, x: integer; Svt: Integer; s, t: AnsiString; begin Result := ''; FResultCode := -1; FResultstring := ''; FResponseCode := -1; FResponseDN := ''; FReferals.Clear; i := 1; ASNItem(i, Value, Svt); x := StrToIntDef(ASNItem(i, Value, Svt), 0); if (svt <> ASN1_INT) or (x <> FSeq) then Exit; s := ASNItem(i, Value, Svt); FResponseCode := svt; if FResponseCode in [LDAP_ASN1_BIND_RESPONSE, LDAP_ASN1_SEARCH_DONE, LDAP_ASN1_MODIFY_RESPONSE, LDAP_ASN1_ADD_RESPONSE, LDAP_ASN1_DEL_RESPONSE, LDAP_ASN1_MODIFYDN_RESPONSE, LDAP_ASN1_COMPARE_RESPONSE, LDAP_ASN1_EXT_RESPONSE] then begin FResultCode := StrToIntDef(ASNItem(i, Value, Svt), -1); FResponseDN := ASNItem(i, Value, Svt); FResultString := ASNItem(i, Value, Svt); if FResultString = '' then FResultString := GetErrorString(FResultCode); if FResultCode = 10 then begin s := ASNItem(i, Value, Svt); if svt = $A3 then begin x := 1; while x < Length(s) do begin t := ASNItem(x, s, Svt); FReferals.Add(t); end; end; end; end; Result := Copy(Value, i, Length(Value) - i + 1); end; function TLDAPSend.LdapSasl(Value: AnsiString): AnsiString; var nonce, cnonce, nc, realm, qop, uri, response: AnsiString; s: AnsiString; a1, a2: AnsiString; l: TStringList; n: integer; begin l := TStringList.Create; try nonce := ''; realm := ''; l.CommaText := Value; n := IndexByBegin('nonce=', l); if n >= 0 then nonce := UnQuoteStr(Trim(SeparateRight(l[n], 'nonce=')), '"'); n := IndexByBegin('realm=', l); if n >= 0 then realm := UnQuoteStr(Trim(SeparateRight(l[n], 'realm=')), '"'); cnonce := IntToHex(GetTick, 8); nc := '00000001'; qop := 'auth'; uri := 'ldap/' + FSock.ResolveIpToName(FSock.GetRemoteSinIP); a1 := md5(FUsername + ':' + realm + ':' + FPassword) + ':' + nonce + ':' + cnonce; a2 := 'AUTHENTICATE:' + uri; s := strtohex(md5(a1))+':' + nonce + ':' + nc + ':' + cnonce + ':' + qop +':'+strtohex(md5(a2)); response := strtohex(md5(s)); Result := 'username="' + Fusername + '",realm="' + realm + '",nonce="'; Result := Result + nonce + '",cnonce="' + cnonce + '",nc=' + nc + ',qop='; Result := Result + qop + ',digest-uri="' + uri + '",response=' + response; finally l.Free; end; end; function TLDAPSend.TranslateFilter(Value: AnsiString): AnsiString; var x: integer; s, t, l: AnsiString; r: string; c: Ansichar; attr, rule: AnsiString; dn: Boolean; begin Result := ''; if Value = '' then Exit; s := Value; if Value[1] = '(' then begin x := RPos(')', Value); s := Copy(Value, 2, x - 2); end; if s = '' then Exit; case s[1] of '!': // NOT rule (recursive call) begin Result := ASNOBject(TranslateFilter(GetBetween('(', ')', s)), $A2); end; '&': // AND rule (recursive call) begin repeat t := GetBetween('(', ')', s); s := Trim(SeparateRight(s, t)); if s <> '' then if s[1] = ')' then {$IFDEF CIL}Borland.Delphi.{$ENDIF}System.Delete(s, 1, 1); Result := Result + TranslateFilter(t); until s = ''; Result := ASNOBject(Result, $A0); end; '|': // OR rule (recursive call) begin repeat t := GetBetween('(', ')', s); s := Trim(SeparateRight(s, t)); if s <> '' then if s[1] = ')' then {$IFDEF CIL}Borland.Delphi.{$ENDIF}System.Delete(s, 1, 1); Result := Result + TranslateFilter(t); until s = ''; Result := ASNOBject(Result, $A1); end; else begin l := Trim(SeparateLeft(s, '=')); r := Trim(SeparateRight(s, '=')); if l <> '' then begin c := l[Length(l)]; case c of ':': // Extensible match begin {$IFDEF CIL}Borland.Delphi.{$ENDIF}System.Delete(l, Length(l), 1); dn := False; attr := ''; rule := ''; if Pos(':dn', l) > 0 then begin dn := True; l := ReplaceString(l, ':dn', ''); end; attr := Trim(SeparateLeft(l, ':')); rule := Trim(SeparateRight(l, ':')); if rule = l then rule := ''; if rule <> '' then Result := ASNObject(rule, $81); if attr <> '' then Result := Result + ASNObject(attr, $82); Result := Result + ASNObject(DecodeTriplet(r, '\'), $83); if dn then Result := Result + ASNObject(AsnEncInt($ff), $84) else Result := Result + ASNObject(AsnEncInt(0), $84); Result := ASNOBject(Result, $a9); end; '~': // Approx match begin {$IFDEF CIL}Borland.Delphi.{$ENDIF}System.Delete(l, Length(l), 1); Result := ASNOBject(l, ASN1_OCTSTR) + ASNOBject(DecodeTriplet(r, '\'), ASN1_OCTSTR); Result := ASNOBject(Result, $a8); end; '>': // Greater or equal match begin {$IFDEF CIL}Borland.Delphi.{$ENDIF}System.Delete(l, Length(l), 1); Result := ASNOBject(l, ASN1_OCTSTR) + ASNOBject(DecodeTriplet(r, '\'), ASN1_OCTSTR); Result := ASNOBject(Result, $a5); end; '<': // Less or equal match begin {$IFDEF CIL}Borland.Delphi.{$ENDIF}System.Delete(l, Length(l), 1); Result := ASNOBject(l, ASN1_OCTSTR) + ASNOBject(DecodeTriplet(r, '\'), ASN1_OCTSTR); Result := ASNOBject(Result, $a6); end; else // present if r = '*' then Result := ASNOBject(l, $87) else if Pos('*', r) > 0 then // substrings begin s := Fetch(r, '*'); if s <> '' then Result := ASNOBject(DecodeTriplet(s, '\'), $80); while r <> '' do begin if Pos('*', r) <= 0 then break; s := Fetch(r, '*'); Result := Result + ASNOBject(DecodeTriplet(s, '\'), $81); end; if r <> '' then Result := Result + ASNOBject(DecodeTriplet(r, '\'), $82); Result := ASNOBject(l, ASN1_OCTSTR) + ASNOBject(Result, ASN1_SEQ); Result := ASNOBject(Result, $a4); end else begin // Equality match Result := ASNOBject(l, ASN1_OCTSTR) + ASNOBject(DecodeTriplet(r, '\'), ASN1_OCTSTR); Result := ASNOBject(Result, $a3); end; end; end; end; end; end; function TLDAPSend.Login: Boolean; begin Result := False; if not Connect then Exit; Result := True; if FAutoTLS then Result := StartTLS; end; function TLDAPSend.Bind: Boolean; var s: AnsiString; begin s := ASNObject(ASNEncInt(FVersion), ASN1_INT) + ASNObject(FUsername, ASN1_OCTSTR) + ASNObject(FPassword, $80); s := ASNObject(s, LDAP_ASN1_BIND_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); Result := FResultCode = 0; end; function TLDAPSend.BindSasl: Boolean; var s, t: AnsiString; x, xt: integer; digreq: AnsiString; begin Result := False; if FPassword = '' then Result := Bind else begin digreq := ASNObject(ASNEncInt(FVersion), ASN1_INT) + ASNObject('', ASN1_OCTSTR) + ASNObject(ASNObject('DIGEST-MD5', ASN1_OCTSTR), $A3); digreq := ASNObject(digreq, LDAP_ASN1_BIND_REQUEST); Fsock.SendString(BuildPacket(digreq)); s := ReceiveResponse; t := DecodeResponse(s); if FResultCode = 14 then begin s := t; x := 1; t := ASNItem(x, s, xt); s := ASNObject(ASNEncInt(FVersion), ASN1_INT) + ASNObject('', ASN1_OCTSTR) + ASNObject(ASNObject('DIGEST-MD5', ASN1_OCTSTR) + ASNObject(LdapSasl(t), ASN1_OCTSTR), $A3); s := ASNObject(s, LDAP_ASN1_BIND_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); if FResultCode = 14 then begin Fsock.SendString(BuildPacket(digreq)); s := ReceiveResponse; DecodeResponse(s); end; Result := FResultCode = 0; end; end; end; function TLDAPSend.Logout: Boolean; begin Fsock.SendString(BuildPacket(ASNObject('', LDAP_ASN1_UNBIND_REQUEST))); FSock.CloseSocket; Result := True; end; function TLDAPSend.Modify(obj: AnsiString; Op: TLDAPModifyOp; const Value: TLDAPAttribute): Boolean; var s: AnsiString; n: integer; begin s := ''; for n := 0 to Value.Count -1 do s := s + ASNObject(Value[n], ASN1_OCTSTR); s := ASNObject(Value.AttributeName, ASN1_OCTSTR) + ASNObject(s, ASN1_SETOF); s := ASNObject(ASNEncInt(Ord(Op)), ASN1_ENUM) + ASNObject(s, ASN1_SEQ); s := ASNObject(s, ASN1_SEQ); s := ASNObject(obj, ASN1_OCTSTR) + ASNObject(s, ASN1_SEQ); s := ASNObject(s, LDAP_ASN1_MODIFY_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); Result := FResultCode = 0; end; function TLDAPSend.Add(obj: AnsiString; const Value: TLDAPAttributeList): Boolean; var s, t: AnsiString; n, m: integer; begin s := ''; for n := 0 to Value.Count - 1 do begin t := ''; for m := 0 to Value[n].Count - 1 do t := t + ASNObject(Value[n][m], ASN1_OCTSTR); t := ASNObject(Value[n].AttributeName, ASN1_OCTSTR) + ASNObject(t, ASN1_SETOF); s := s + ASNObject(t, ASN1_SEQ); end; s := ASNObject(obj, ASN1_OCTSTR) + ASNObject(s, ASN1_SEQ); s := ASNObject(s, LDAP_ASN1_ADD_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); Result := FResultCode = 0; end; function TLDAPSend.Delete(obj: AnsiString): Boolean; var s: AnsiString; begin s := ASNObject(obj, LDAP_ASN1_DEL_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); Result := FResultCode = 0; end; function TLDAPSend.ModifyDN(obj, newRDN, newSuperior: AnsiString; DeleteOldRDN: Boolean): Boolean; var s: AnsiString; begin s := ASNObject(obj, ASN1_OCTSTR) + ASNObject(newRDN, ASN1_OCTSTR); if DeleteOldRDN then s := s + ASNObject(ASNEncInt($ff), ASN1_BOOL) else s := s + ASNObject(ASNEncInt(0), ASN1_BOOL); if newSuperior <> '' then s := s + ASNObject(newSuperior, $80); s := ASNObject(s, LDAP_ASN1_MODIFYDN_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); Result := FResultCode = 0; end; function TLDAPSend.Compare(obj, AttributeValue: AnsiString): Boolean; var s: AnsiString; begin s := ASNObject(Trim(SeparateLeft(AttributeValue, '=')), ASN1_OCTSTR) + ASNObject(Trim(SeparateRight(AttributeValue, '=')), ASN1_OCTSTR); s := ASNObject(obj, ASN1_OCTSTR) + ASNObject(s, ASN1_SEQ); s := ASNObject(s, LDAP_ASN1_COMPARE_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; DecodeResponse(s); Result := FResultCode = 0; end; function TLDAPSend.Search(obj: AnsiString; TypesOnly: Boolean; Filter: AnsiString; const Attributes: TStrings): Boolean; var s, t, u: AnsiString; n, i, x: integer; r: TLDAPResult; a: TLDAPAttribute; begin FSearchResult.Clear; FReferals.Clear; s := ASNObject(obj, ASN1_OCTSTR); s := s + ASNObject(ASNEncInt(Ord(FSearchScope)), ASN1_ENUM); s := s + ASNObject(ASNEncInt(Ord(FSearchAliases)), ASN1_ENUM); s := s + ASNObject(ASNEncInt(FSearchSizeLimit), ASN1_INT); s := s + ASNObject(ASNEncInt(FSearchTimeLimit), ASN1_INT); if TypesOnly then s := s + ASNObject(ASNEncInt($ff), ASN1_BOOL) else s := s + ASNObject(ASNEncInt(0), ASN1_BOOL); if Filter = '' then Filter := '(objectclass=*)'; t := TranslateFilter(Filter); if t = '' then s := s + ASNObject('', ASN1_NULL) else s := s + t; t := ''; for n := 0 to Attributes.Count - 1 do t := t + ASNObject(Attributes[n], ASN1_OCTSTR); s := s + ASNObject(t, ASN1_SEQ); s := ASNObject(s, LDAP_ASN1_SEARCH_REQUEST); Fsock.SendString(BuildPacket(s)); repeat s := ReceiveResponse; t := DecodeResponse(s); if FResponseCode = LDAP_ASN1_SEARCH_ENTRY then begin //dekoduj zaznam r := FSearchResult.Add; n := 1; r.ObjectName := ASNItem(n, t, x); ASNItem(n, t, x); if x = ASN1_SEQ then begin while n < Length(t) do begin s := ASNItem(n, t, x); if x = ASN1_SEQ then begin i := n + Length(s); a := r.Attributes.Add; u := ASNItem(n, t, x); a.AttributeName := u; ASNItem(n, t, x); if x = ASN1_SETOF then while n < i do begin u := ASNItem(n, t, x); a.Add(u); end; end; end; end; end; if FResponseCode = LDAP_ASN1_SEARCH_REFERENCE then begin n := 1; while n < Length(t) do FReferals.Add(ASNItem(n, t, x)); end; until FResponseCode = LDAP_ASN1_SEARCH_DONE; Result := FResultCode = 0; end; function TLDAPSend.Extended(const Name, Value: AnsiString): Boolean; var s, t: AnsiString; x, xt: integer; begin s := ASNObject(Name, $80); if Value <> '' then s := s + ASNObject(Value, $81); s := ASNObject(s, LDAP_ASN1_EXT_REQUEST); Fsock.SendString(BuildPacket(s)); s := ReceiveResponse; t := DecodeResponse(s); Result := FResultCode = 0; if Result then begin x := 1; FExtName := ASNItem(x, t, xt); FExtValue := ASNItem(x, t, xt); end; end; function TLDAPSend.StartTLS: Boolean; begin Result := Extended('1.3.6.1.4.1.1466.20037', ''); if Result then begin Fsock.SSLDoConnect; Result := FSock.LastError = 0; end; end; {==============================================================================} function LDAPResultDump(const Value: TLDAPResultList): AnsiString; var n, m, o: integer; r: TLDAPResult; a: TLDAPAttribute; begin Result := 'Results: ' + IntToStr(Value.Count) + CRLF +CRLF; for n := 0 to Value.Count - 1 do begin Result := Result + 'Result: ' + IntToStr(n) + CRLF; r := Value[n]; Result := Result + ' Object: ' + r.ObjectName + CRLF; for m := 0 to r.Attributes.Count - 1 do begin a := r.Attributes[m]; Result := Result + ' Attribute: ' + a.AttributeName + CRLF; for o := 0 to a.Count - 1 do Result := Result + ' ' + a[o] + CRLF; end; end; end; end. TransGUI/synapse/source/lib/laz_synapse.pas0000644000000000000000000000115611466757142020015 0ustar rootroot{ This file was automatically created by Lazarus. Do not edit! This source is only used to compile and install the package. } unit laz_synapse; interface uses asn1util, blcksock, clamsend, dnssend, ftpsend, ftptsend, httpsend, imapsend, ldapsend, mimeinln, mimemess, mimepart, nntpsend, pingsend, pop3send, slogsend, smtpsend, snmpsend, sntpsend, synachar, synacode, synacrypt, synadbg, synafpc, synaicnv, synaip, synamisc, synaser, synautil, synsock, tlntsend, LazarusPackageIntf; implementation procedure Register; begin end; initialization RegisterPackage('laz_synapse', @Register); end. TransGUI/synapse/source/lib/ssl_sbb.pas0000644000000000000000000004771411366572451017123 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.000.003 | |==============================================================================| | Content: SSL support for SecureBlackBox | |==============================================================================| | Copyright (c)1999-2005, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2005. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Allen Drennan (adrennan@wiredred.com) | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(SSL plugin for Eldos SecureBlackBox) For handling keys and certificates you can use this properties: @link(TCustomSSL.CertCAFile), @link(TCustomSSL.CertCA), @link(TCustomSSL.TrustCertificateFile), @link(TCustomSSL.TrustCertificate), @link(TCustomSSL.PrivateKeyFile), @link(TCustomSSL.PrivateKey), @link(TCustomSSL.CertificateFile), @link(TCustomSSL.Certificate), @link(TCustomSSL.PFXFile). For usage of this properties and for possible formats of keys and certificates refer to SecureBlackBox documentation. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} unit ssl_sbb; interface uses SysUtils, Classes, Windows, blcksock, synsock, synautil, synacode, SBClient, SBServer, SBX509, SBWinCertStorage, SBCustomCertStorage, SBUtils, SBConstants, SBSessionPool; const DEFAULT_RECV_BUFFER=32768; type {:@abstract(class implementing SecureBlackbox SSL plugin.) Instance of this class will be created for each @link(TTCPBlockSocket). You not need to create instance of this class, all is done by Synapse itself!} TSSLSBB=class(TCustomSSL) protected FServer: Boolean; FElSecureClient:TElSecureClient; FElSecureServer:TElSecureServer; FElCertStorage:TElMemoryCertStorage; FElX509Certificate:TElX509Certificate; FElX509CACertificate:TElX509Certificate; FCipherSuites:TBits; private FRecvBuffer:String; FRecvBuffers:String; FRecvBuffersLock:TRTLCriticalSection; FRecvDecodedBuffers:String; function GetCipherSuite:Integer; procedure Reset; function Prepare(Server:Boolean):Boolean; procedure OnError(Sender:TObject; ErrorCode:Integer; Fatal:Boolean; Remote:Boolean); procedure OnSend(Sender:TObject;Buffer:Pointer;Size:LongInt); procedure OnReceive(Sender:TObject;Buffer:Pointer;MaxSize:LongInt;var Written:LongInt); procedure OnData(Sender:TObject;Buffer:Pointer;Size:LongInt); public constructor Create(const Value: TTCPBlockSocket); override; destructor Destroy; override; {:See @inherited} function LibVersion: String; override; {:See @inherited} function LibName: String; override; {:See @inherited and @link(ssl_sbb) for more details.} function Connect: boolean; override; {:See @inherited and @link(ssl_sbb) for more details.} function Accept: boolean; override; {:See @inherited} function Shutdown: boolean; override; {:See @inherited} function BiShutdown: boolean; override; {:See @inherited} function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override; {:See @inherited} function WaitingData: Integer; override; {:See @inherited} function GetSSLVersion: string; override; {:See @inherited} function GetPeerSubject: string; override; {:See @inherited} function GetPeerIssuer: string; override; {:See @inherited} function GetPeerName: string; override; {:See @inherited} function GetPeerFingerprint: string; override; {:See @inherited} function GetCertInfo: string; override; published property ElSecureClient:TElSecureClient read FElSecureClient write FElSecureClient; property ElSecureServer:TElSecureServer read FElSecureServer write FElSecureServer; property CipherSuites:TBits read FCipherSuites write FCipherSuites; property CipherSuite:Integer read GetCipherSuite; end; implementation var FAcceptThread:THandle=0; // on error procedure TSSLSBB.OnError(Sender:TObject; ErrorCode:Integer; Fatal:Boolean; Remote:Boolean); begin FLastErrorDesc:=''; FLastError:=ErrorCode; end; // on send procedure TSSLSBB.OnSend(Sender:TObject;Buffer:Pointer;Size:LongInt); var lResult:Integer; begin if FSocket.Socket=INVALID_SOCKET then Exit; lResult:=Send(FSocket.Socket,Buffer,Size,0); if lResult=SOCKET_ERROR then begin FLastErrorDesc:=''; FLastError:=WSAGetLastError; end; end; // on receive procedure TSSLSBB.OnReceive(Sender:TObject;Buffer:Pointer;MaxSize:LongInt;var Written:LongInt); begin if GetCurrentThreadId<>FAcceptThread then EnterCriticalSection(FRecvBuffersLock); try if Length(FRecvBuffers)<=MaxSize then begin Written:=Length(FRecvBuffers); Move(FRecvBuffers[1],Buffer^,Written); FRecvBuffers:=''; end else begin Written:=MaxSize; Move(FRecvBuffers[1],Buffer^,Written); Delete(FRecvBuffers,1,Written); end; finally if GetCurrentThreadId<>FAcceptThread then LeaveCriticalSection(FRecvBuffersLock); end; end; // on data procedure TSSLSBB.OnData(Sender:TObject;Buffer:Pointer;Size:LongInt); var lString:String; begin SetLength(lString,Size); Move(Buffer^,lString[1],Size); FRecvDecodedBuffers:=FRecvDecodedBuffers+lString; end; { inherited } constructor TSSLSBB.Create(const Value: TTCPBlockSocket); var loop1:Integer; begin inherited Create(Value); FServer:=FALSE; FElSecureClient:=NIL; FElSecureServer:=NIL; FElCertStorage:=NIL; FElX509Certificate:=NIL; FElX509CACertificate:=NIL; SetLength(FRecvBuffer,DEFAULT_RECV_BUFFER); FRecvBuffers:=''; InitializeCriticalSection(FRecvBuffersLock); FRecvDecodedBuffers:=''; FCipherSuites:=TBits.Create; if FCipherSuites<>NIL then begin FCipherSuites.Size:=SB_SUITE_LAST+1; for loop1:=SB_SUITE_FIRST to SB_SUITE_LAST do FCipherSuites[loop1]:=TRUE; end; end; destructor TSSLSBB.Destroy; begin Reset; inherited Destroy; if FCipherSuites<>NIL then FreeAndNIL(FCipherSuites); DeleteCriticalSection(FRecvBuffersLock); end; function TSSLSBB.LibVersion: String; begin Result:='SecureBlackBox'; end; function TSSLSBB.LibName: String; begin Result:='ssl_sbb'; end; function FileToString(lFile:String):String; var lStream:TMemoryStream; begin Result:=''; lStream:=TMemoryStream.Create; if lStream<>NIL then begin lStream.LoadFromFile(lFile); if lStream.Size>0 then begin lStream.Position:=0; SetLength(Result,lStream.Size); Move(lStream.Memory^,Result[1],lStream.Size); end; lStream.Free; end; end; function TSSLSBB.GetCipherSuite:Integer; begin if FServer then Result:=FElSecureServer.CipherSuite else Result:=FElSecureClient.CipherSuite; end; procedure TSSLSBB.Reset; begin if FElSecureServer<>NIL then FreeAndNIL(FElSecureServer); if FElSecureClient<>NIL then FreeAndNIL(FElSecureClient); if FElX509Certificate<>NIL then FreeAndNIL(FElX509Certificate); if FElX509CACertificate<>NIL then FreeAndNIL(FElX509CACertificate); if FElCertStorage<>NIL then FreeAndNIL(FElCertStorage); FSSLEnabled:=FALSE; end; function TSSLSBB.Prepare(Server:Boolean): Boolean; var loop1:Integer; lStream:TMemoryStream; lCertificate,lPrivateKey,lCertCA:String; begin Result:=FALSE; FServer:=Server; // reset, if necessary Reset; // init, certificate if FCertificateFile<>'' then lCertificate:=FileToString(FCertificateFile) else lCertificate:=FCertificate; if FPrivateKeyFile<>'' then lPrivateKey:=FileToString(FPrivateKeyFile) else lPrivateKey:=FPrivateKey; if FCertCAFile<>'' then lCertCA:=FileToString(FCertCAFile) else lCertCA:=FCertCA; if (lCertificate<>'') and (lPrivateKey<>'') then begin FElCertStorage:=TElMemoryCertStorage.Create(NIL); if FElCertStorage<>NIL then FElCertStorage.Clear; // apply ca certificate if lCertCA<>'' then begin FElX509CACertificate:=TElX509Certificate.Create(NIL); if FElX509CACertificate<>NIL then begin with FElX509CACertificate do begin lStream:=TMemoryStream.Create; try WriteStrToStream(lStream,lCertCA); lStream.Seek(0,soFromBeginning); LoadFromStream(lStream); finally lStream.Free; end; end; if FElCertStorage<>NIL then FElCertStorage.Add(FElX509CACertificate); end; end; // apply certificate FElX509Certificate:=TElX509Certificate.Create(NIL); if FElX509Certificate<>NIL then begin with FElX509Certificate do begin lStream:=TMemoryStream.Create; try WriteStrToStream(lStream,lCertificate); lStream.Seek(0,soFromBeginning); LoadFromStream(lStream); finally lStream.Free; end; lStream:=TMemoryStream.Create; try WriteStrToStream(lStream,lPrivateKey); lStream.Seek(0,soFromBeginning); LoadKeyFromStream(lStream); finally lStream.Free; end; if FElCertStorage<>NIL then FElCertStorage.Add(FElX509Certificate); end; end; end; // init, as server if FServer then begin FElSecureServer:=TElSecureServer.Create(NIL); if FElSecureServer<>NIL then begin // init, ciphers for loop1:=SB_SUITE_FIRST to SB_SUITE_LAST do FElSecureServer.CipherSuites[loop1]:=FCipherSuites[loop1]; FElSecureServer.Versions:=[sbSSL2,sbSSL3,sbTLS1]; FElSecureServer.ClientAuthentication:=FALSE; FElSecureServer.OnError:=OnError; FElSecureServer.OnSend:=OnSend; FElSecureServer.OnReceive:=OnReceive; FElSecureServer.OnData:=OnData; FElSecureServer.CertStorage:=FElCertStorage; Result:=TRUE; end; end else // init, as client begin FElSecureClient:=TElSecureClient.Create(NIL); if FElSecureClient<>NIL then begin // init, ciphers for loop1:=SB_SUITE_FIRST to SB_SUITE_LAST do FElSecureClient.CipherSuites[loop1]:=FCipherSuites[loop1]; FElSecureClient.Versions:=[sbSSL3,sbTLS1]; FElSecureClient.OnError:=OnError; FElSecureClient.OnSend:=OnSend; FElSecureClient.OnReceive:=OnReceive; FElSecureClient.OnData:=OnData; FElSecureClient.CertStorage:=FElCertStorage; Result:=TRUE; end; end; end; function TSSLSBB.Connect:Boolean; var lResult:Integer; begin Result:=FALSE; if FSocket.Socket=INVALID_SOCKET then Exit; if Prepare(FALSE) then begin FElSecureClient.Open; // reset FRecvBuffers:=''; FRecvDecodedBuffers:=''; // wait for open or error while (not FElSecureClient.Active) and (FLastError=0) do begin // data available? if FRecvBuffers<>'' then FElSecureClient.DataAvailable else begin // socket recv lResult:=Recv(FSocket.Socket,@FRecvBuffer[1],Length(FRecvBuffer),0); if lResult=SOCKET_ERROR then begin FLastErrorDesc:=''; FLastError:=WSAGetLastError; end else begin if lResult>0 then FRecvBuffers:=FRecvBuffers+Copy(FRecvBuffer,1,lResult) else Break; end; end; end; if FLastError<>0 then Exit; FSSLEnabled:=FElSecureClient.Active; Result:=FSSLEnabled; end; end; function TSSLSBB.Accept:Boolean; var lResult:Integer; begin Result:=FALSE; if FSocket.Socket=INVALID_SOCKET then Exit; if Prepare(TRUE) then begin FAcceptThread:=GetCurrentThreadId; FElSecureServer.Open; // reset FRecvBuffers:=''; FRecvDecodedBuffers:=''; // wait for open or error while (not FElSecureServer.Active) and (FLastError=0) do begin // data available? if FRecvBuffers<>'' then FElSecureServer.DataAvailable else begin // socket recv lResult:=Recv(FSocket.Socket,@FRecvBuffer[1],Length(FRecvBuffer),0); if lResult=SOCKET_ERROR then begin FLastErrorDesc:=''; FLastError:=WSAGetLastError; end else begin if lResult>0 then FRecvBuffers:=FRecvBuffers+Copy(FRecvBuffer,1,lResult) else Break; end; end; end; if FLastError<>0 then Exit; FSSLEnabled:=FElSecureServer.Active; Result:=FSSLEnabled; end; end; function TSSLSBB.Shutdown:Boolean; begin Result:=BiShutdown; end; function TSSLSBB.BiShutdown: boolean; begin Reset; Result:=TRUE; end; function TSSLSBB.SendBuffer(Buffer: TMemory; Len: Integer): Integer; begin if FServer then FElSecureServer.SendData(Buffer,Len) else FElSecureClient.SendData(Buffer,Len); Result:=Len; end; function TSSLSBB.RecvBuffer(Buffer: TMemory; Len: Integer): Integer; begin Result:=0; try // recv waiting, if necessary if FRecvDecodedBuffers='' then WaitingData; // received if Length(FRecvDecodedBuffers)FAcceptThread then EnterCriticalSection(FRecvBuffersLock); try lRecvBuffers:=FRecvBuffers<>''; finally if GetCurrentThreadId<>FAcceptThread then LeaveCriticalSection(FRecvBuffersLock); end; if lRecvBuffers then begin if FServer then FElSecureServer.DataAvailable else FElSecureClient.DataAvailable; end else begin // socket recv lResult:=Recv(FSocket.Socket,@FRecvBuffer[1],Length(FRecvBuffer),0); if lResult=SOCKET_ERROR then begin FLastErrorDesc:=''; FLastError:=WSAGetLastError; end else begin if GetCurrentThreadId<>FAcceptThread then EnterCriticalSection(FRecvBuffersLock); try FRecvBuffers:=FRecvBuffers+Copy(FRecvBuffer,1,lResult); finally if GetCurrentThreadId<>FAcceptThread then LeaveCriticalSection(FRecvBuffersLock); end; // data available? if GetCurrentThreadId<>FAcceptThread then EnterCriticalSection(FRecvBuffersLock); try lRecvBuffers:=FRecvBuffers<>''; finally if GetCurrentThreadId<>FAcceptThread then LeaveCriticalSection(FRecvBuffersLock); end; if lRecvBuffers then begin if FServer then FElSecureServer.DataAvailable else FElSecureClient.DataAvailable; end; end; end; // decoded buffers result Result:=Length(FRecvDecodedBuffers); end; function TSSLSBB.GetSSLVersion: string; begin Result:='SSLv3 or TLSv1'; end; function TSSLSBB.GetPeerSubject: string; begin Result := ''; // if FServer then // must return subject of the client certificate // else // must return subject of the server certificate end; function TSSLSBB.GetPeerName: string; begin Result := ''; // if FServer then // must return commonname of the client certificate // else // must return commonname of the server certificate end; function TSSLSBB.GetPeerIssuer: string; begin Result := ''; // if FServer then // must return issuer of the client certificate // else // must return issuer of the server certificate end; function TSSLSBB.GetPeerFingerprint: string; begin Result := ''; // if FServer then // must return a unique hash string of the client certificate // else // must return a unique hash string of the server certificate end; function TSSLSBB.GetCertInfo: string; begin Result := ''; // if FServer then // must return a text representation of the ASN of the client certificate // else // must return a text representation of the ASN of the server certificate end; {==============================================================================} initialization SSLImplementation := TSSLSBB; finalization end. TransGUI/synapse/source/lib/ssl_openssl_lib.pas0000644000000000000000000022226711466757142020667 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 003.006.002 | |==============================================================================| | Content: SSL support by OpenSSL | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2002-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} { Special thanks to Gregor Ibic (Intelicom d.o.o., http://www.intelicom.si) for good inspiration about begin with SSL programming. } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF VER125} {$DEFINE BCB} {$ENDIF} {$IFDEF BCB} {$ObjExportAll On} (*$HPPEMIT 'namespace ssl_openssl_lib { using System::Shortint; }' *) {$ENDIF} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {:@abstract(OpenSSL support) This unit is Pascal interface to OpenSSL library (used by @link(ssl_openssl) unit). OpenSSL is loaded dynamicly on-demand. If this library is not found in system, requested OpenSSL function just return errorcode. } unit ssl_openssl_lib; interface uses {$IFDEF CIL} System.Runtime.InteropServices, System.Text, {$ENDIF} Classes, synafpc, {$IFNDEF MSWINDOWS} {$IFDEF FPC} BaseUnix, SysUtils; {$ELSE} Libc, SysUtils; {$ENDIF} {$ELSE} Windows; {$ENDIF} {$IFDEF CIL} const {$IFDEF LINUX} DLLSSLName = 'libssl.so'; DLLUtilName = 'libcrypto.so'; {$ELSE} DLLSSLName = 'ssleay32.dll'; DLLUtilName = 'libeay32.dll'; {$ENDIF} {$ELSE} var {$IFNDEF MSWINDOWS} {$IFDEF DARWIN} DLLSSLName: string = 'libssl.dylib'; DLLUtilName: string = 'libcrypto.dylib'; {$ELSE} DLLSSLName: string = 'libssl.so'; DLLUtilName: string = 'libcrypto.so'; {$ENDIF} {$ELSE} DLLSSLName: string = 'ssleay32.dll'; DLLSSLName2: string = 'libssl32.dll'; DLLUtilName: string = 'libeay32.dll'; {$ENDIF} {$ENDIF} type {$IFDEF CIL} SslPtr = IntPtr; {$ELSE} SslPtr = Pointer; {$ENDIF} PSslPtr = ^SslPtr; PSSL_CTX = SslPtr; PSSL = SslPtr; PSSL_METHOD = SslPtr; PX509 = SslPtr; PX509_NAME = SslPtr; PEVP_MD = SslPtr; PInteger = ^Integer; PBIO_METHOD = SslPtr; PBIO = SslPtr; EVP_PKEY = SslPtr; PRSA = SslPtr; PASN1_UTCTIME = SslPtr; PASN1_INTEGER = SslPtr; PPasswdCb = SslPtr; PFunction = procedure; DES_cblock = array[0..7] of Byte; PDES_cblock = ^DES_cblock; des_ks_struct = packed record ks: DES_cblock; weak_key: Integer; end; des_key_schedule = array[1..16] of des_ks_struct; const EVP_MAX_MD_SIZE = 16 + 20; SSL_ERROR_NONE = 0; SSL_ERROR_SSL = 1; SSL_ERROR_WANT_READ = 2; SSL_ERROR_WANT_WRITE = 3; SSL_ERROR_WANT_X509_LOOKUP = 4; SSL_ERROR_SYSCALL = 5; //look at error stack/return value/errno SSL_ERROR_ZERO_RETURN = 6; SSL_ERROR_WANT_CONNECT = 7; SSL_ERROR_WANT_ACCEPT = 8; SSL_OP_NO_SSLv2 = $01000000; SSL_OP_NO_SSLv3 = $02000000; SSL_OP_NO_TLSv1 = $04000000; SSL_OP_ALL = $000FFFFF; SSL_VERIFY_NONE = $00; SSL_VERIFY_PEER = $01; OPENSSL_DES_DECRYPT = 0; OPENSSL_DES_ENCRYPT = 1; X509_V_OK = 0; X509_V_ILLEGAL = 1; X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2; X509_V_ERR_UNABLE_TO_GET_CRL = 3; X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE = 4; X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE = 5; X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = 6; X509_V_ERR_CERT_SIGNATURE_FAILURE = 7; X509_V_ERR_CRL_SIGNATURE_FAILURE = 8; X509_V_ERR_CERT_NOT_YET_VALID = 9; X509_V_ERR_CERT_HAS_EXPIRED = 10; X509_V_ERR_CRL_NOT_YET_VALID = 11; X509_V_ERR_CRL_HAS_EXPIRED = 12; X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD = 13; X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD = 14; X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD = 15; X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD = 16; X509_V_ERR_OUT_OF_MEM = 17; X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = 18; X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = 19; X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20; X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21; X509_V_ERR_CERT_CHAIN_TOO_LONG = 22; X509_V_ERR_CERT_REVOKED = 23; X509_V_ERR_INVALID_CA = 24; X509_V_ERR_PATH_LENGTH_EXCEEDED = 25; X509_V_ERR_INVALID_PURPOSE = 26; X509_V_ERR_CERT_UNTRUSTED = 27; X509_V_ERR_CERT_REJECTED = 28; //These are 'informational' when looking for issuer cert X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29; X509_V_ERR_AKID_SKID_MISMATCH = 30; X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH = 31; X509_V_ERR_KEYUSAGE_NO_CERTSIGN = 32; X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER = 33; X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION = 34; //The application is not happy X509_V_ERR_APPLICATION_VERIFICATION = 50; SSL_FILETYPE_ASN1 = 2; SSL_FILETYPE_PEM = 1; EVP_PKEY_RSA = 6; var SSLLibHandle: TLibHandle = 0; SSLUtilHandle: TLibHandle = 0; SSLLibFile: string = ''; SSLUtilFile: string = ''; {$IFDEF CIL} [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_get_error')] function SslGetError(s: PSSL; ret_code: Integer): Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_library_init')] function SslLibraryInit: Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_load_error_strings')] procedure SslLoadErrorStrings; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_set_cipher_list')] function SslCtxSetCipherList(arg0: PSSL_CTX; var str: String): Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_new')] function SslCtxNew(meth: PSSL_METHOD):PSSL_CTX; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_free')] procedure SslCtxFree (arg0: PSSL_CTX); external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_set_fd')] function SslSetFd(s: PSSL; fd: Integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSLv2_method')] function SslMethodV2 : PSSL_METHOD; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSLv3_method')] function SslMethodV3 : PSSL_METHOD; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'TLSv1_method')] function SslMethodTLSV1:PSSL_METHOD; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSLv23_method')] function SslMethodV23 : PSSL_METHOD; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_PrivateKey')] function SslCtxUsePrivateKey(ctx: PSSL_CTX; pkey: SslPtr):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_PrivateKey_ASN1')] function SslCtxUsePrivateKeyASN1(pk: integer; ctx: PSSL_CTX; d: String; len: integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_RSAPrivateKey_file')] function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: String; _type: Integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_certificate')] function SslCtxUseCertificate(ctx: PSSL_CTX; x: SslPtr):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_certificate_ASN1')] function SslCtxUseCertificateASN1(ctx: PSSL_CTX; len: integer; d: String):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_certificate_file')] function SslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: String; _type: Integer):Integer;external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_use_certificate_chain_file')] function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: String):Integer;external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_check_private_key')] function SslCtxCheckPrivateKeyFile(ctx: PSSL_CTX):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_set_default_passwd_cb')] procedure SslCtxSetDefaultPasswdCb(ctx: PSSL_CTX; cb: PPasswdCb); external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_set_default_passwd_cb_userdata')] procedure SslCtxSetDefaultPasswdCbUserdata(ctx: PSSL_CTX; u: IntPtr); external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_load_verify_locations')] function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; CAfile: string; CApath: String):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_ctrl')] function SslCtxCtrl(ctx: PSSL_CTX; cmd: integer; larg: integer; parg: IntPtr): integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_new')] function SslNew(ctx: PSSL_CTX):PSSL; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_free')] procedure SslFree(ssl: PSSL); external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_accept')] function SslAccept(ssl: PSSL):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_connect')] function SslConnect(ssl: PSSL):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_shutdown')] function SslShutdown(s: PSSL):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_read')] function SslRead(ssl: PSSL; buf: StringBuilder; num: Integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_peek')] function SslPeek(ssl: PSSL; buf: StringBuilder; num: Integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_write')] function SslWrite(ssl: PSSL; buf: String; num: Integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_pending')] function SslPending(ssl: PSSL):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_get_version')] function SslGetVersion(ssl: PSSL):String; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_get_peer_certificate')] function SslGetPeerCertificate(s: PSSL):PX509; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CTX_set_verify')] procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: PFunction); external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_get_current_cipher')] function SSLGetCurrentCipher(s: PSSL): SslPtr; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CIPHER_get_name')] function SSLCipherGetName(c: SslPtr):String; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_CIPHER_get_bits')] function SSLCipherGetBits(c: SslPtr; var alg_bits: Integer):Integer; external; [DllImport(DLLSSLName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSL_get_verify_result')] function SSLGetVerifyResult(ssl: PSSL):Integer;external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_new')] function X509New: PX509; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_free')] procedure X509Free(x: PX509); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_NAME_oneline')] function X509NameOneline(a: PX509_NAME; buf: StringBuilder; size: Integer): String; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_get_subject_name')] function X509GetSubjectName(a: PX509):PX509_NAME; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_get_issuer_name')] function X509GetIssuerName(a: PX509):PX509_NAME; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_NAME_hash')] function X509NameHash(x: PX509_NAME):Cardinal; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_digest')] function X509Digest (data: PX509; _type: PEVP_MD; md: StringBuilder; var len: Integer):Integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_set_version')] function X509SetVersion(x: PX509; version: integer): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_set_pubkey')] function X509SetPubkey(x: PX509; pkey: EVP_PKEY): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_set_issuer_name')] function X509SetIssuerName(x: PX509; name: PX509_NAME): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_NAME_add_entry_by_txt')] function X509NameAddEntryByTxt(name: PX509_NAME; field: string; _type: integer; bytes: string; len, loc, _set: integer): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_sign')] function X509Sign(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_print')] function X509print(b: PBIO; a: PX509): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_gmtime_adj')] function X509GmtimeAdj(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_set_notBefore')] function X509SetNotBefore(x: PX509; tm: PASN1_UTCTIME): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_set_notAfter')] function X509SetNotAfter(x: PX509; tm: PASN1_UTCTIME): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'X509_get_serialNumber')] function X509GetSerialNumber(x: PX509): PASN1_INTEGER; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'EVP_PKEY_new')] function EvpPkeyNew: EVP_PKEY; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'EVP_PKEY_free')] procedure EvpPkeyFree(pk: EVP_PKEY); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'EVP_PKEY_assign')] function EvpPkeyAssign(pkey: EVP_PKEY; _type: integer; key: Prsa): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'EVP_get_digestbyname')] function EvpGetDigestByName(Name: String): PEVP_MD; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'EVP_cleanup')] procedure EVPcleanup; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'SSLeay_version')] function SSLeayversion(t: integer): String; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ERR_error_string_n')] procedure ErrErrorString(e: integer; buf: StringBuilder; len: integer); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ERR_get_error')] function ErrGetError: integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ERR_clear_error')] procedure ErrClearError; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ERR_free_strings')] procedure ErrFreeStrings; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ERR_remove_state')] procedure ErrRemoveState(pid: integer); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'OPENSSL_add_all_algorithms_noconf')] procedure OPENSSLaddallalgorithms; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'CRYPTO_cleanup_all_ex_data')] procedure CRYPTOcleanupAllExData; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'RAND_screen')] procedure RandScreen; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'BIO_new')] function BioNew(b: PBIO_METHOD): PBIO; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'BIO_free_all')] procedure BioFreeAll(b: PBIO); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'BIO_s_mem')] function BioSMem: PBIO_METHOD; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'BIO_ctrl_pending')] function BioCtrlPending(b: PBIO): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'BIO_read')] function BioRead(b: PBIO; Buf: StringBuilder; Len: integer): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'BIO_write')] function BioWrite(b: PBIO; var Buf: String; Len: integer): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'd2i_PKCS12_bio')] function d2iPKCS12bio(b:PBIO; Pkcs12: SslPtr): SslPtr; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'PKCS12_parse')] function PKCS12parse(p12: SslPtr; pass: string; var pkey, cert, ca: SslPtr): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'PKCS12_free')] procedure PKCS12free(p12: SslPtr); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'RSA_generate_key')] function RsaGenerateKey(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ASN1_UTCTIME_new')] function Asn1UtctimeNew: PASN1_UTCTIME; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ASN1_UTCTIME_free')] procedure Asn1UtctimeFree(a: PASN1_UTCTIME); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'ASN1_INTEGER_set')] function Asn1IntegerSet(a: PASN1_INTEGER; v: integer): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'i2d_X509_bio')] function i2dX509bio(b: PBIO; x: PX509): integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'i2d_PrivateKey_bio')] function i2dPrivateKeyBio(b: PBIO; pkey: EVP_PKEY): integer; external; // 3DES functions [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'DES_set_odd_parity')] procedure DESsetoddparity(Key: des_cblock); external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'DES_set_key_checked')] function DESsetkeychecked(key: des_cblock; schedule: des_key_schedule): Integer; external; [DllImport(DLLUtilName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'DES_ecb_encrypt')] procedure DESecbencrypt(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer); external; {$ELSE} // libssl.dll function SslGetError(s: PSSL; ret_code: Integer):Integer; function SslLibraryInit:Integer; procedure SslLoadErrorStrings; // function SslCtxSetCipherList(arg0: PSSL_CTX; str: PChar):Integer; function SslCtxSetCipherList(arg0: PSSL_CTX; var str: AnsiString):Integer; function SslCtxNew(meth: PSSL_METHOD):PSSL_CTX; procedure SslCtxFree(arg0: PSSL_CTX); function SslSetFd(s: PSSL; fd: Integer):Integer; function SslMethodV2:PSSL_METHOD; function SslMethodV3:PSSL_METHOD; function SslMethodTLSV1:PSSL_METHOD; function SslMethodV23:PSSL_METHOD; function SslCtxUsePrivateKey(ctx: PSSL_CTX; pkey: SslPtr):Integer; function SslCtxUsePrivateKeyASN1(pk: integer; ctx: PSSL_CTX; d: AnsiString; len: integer):Integer; // function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: PChar; _type: Integer):Integer; function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: AnsiString; _type: Integer):Integer; function SslCtxUseCertificate(ctx: PSSL_CTX; x: SslPtr):Integer; function SslCtxUseCertificateASN1(ctx: PSSL_CTX; len: integer; d: AnsiString):Integer; function SslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: AnsiString; _type: Integer):Integer; // function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: PChar):Integer; function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: AnsiString):Integer; function SslCtxCheckPrivateKeyFile(ctx: PSSL_CTX):Integer; procedure SslCtxSetDefaultPasswdCb(ctx: PSSL_CTX; cb: PPasswdCb); procedure SslCtxSetDefaultPasswdCbUserdata(ctx: PSSL_CTX; u: SslPtr); // function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: PChar; const CApath: PChar):Integer; function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: AnsiString; const CApath: AnsiString):Integer; function SslCtxCtrl(ctx: PSSL_CTX; cmd: integer; larg: integer; parg: SslPtr): integer; function SslNew(ctx: PSSL_CTX):PSSL; procedure SslFree(ssl: PSSL); function SslAccept(ssl: PSSL):Integer; function SslConnect(ssl: PSSL):Integer; function SslShutdown(ssl: PSSL):Integer; function SslRead(ssl: PSSL; buf: SslPtr; num: Integer):Integer; function SslPeek(ssl: PSSL; buf: SslPtr; num: Integer):Integer; function SslWrite(ssl: PSSL; buf: SslPtr; num: Integer):Integer; function SslPending(ssl: PSSL):Integer; function SslGetVersion(ssl: PSSL):AnsiString; function SslGetPeerCertificate(ssl: PSSL):PX509; procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: PFunction); function SSLGetCurrentCipher(s: PSSL):SslPtr; function SSLCipherGetName(c: SslPtr): AnsiString; function SSLCipherGetBits(c: SslPtr; var alg_bits: Integer):Integer; function SSLGetVerifyResult(ssl: PSSL):Integer; // libeay.dll function X509New: PX509; procedure X509Free(x: PX509); function X509NameOneline(a: PX509_NAME; var buf: AnsiString; size: Integer):AnsiString; function X509GetSubjectName(a: PX509):PX509_NAME; function X509GetIssuerName(a: PX509):PX509_NAME; function X509NameHash(x: PX509_NAME):Cardinal; // function SslX509Digest(data: PX509; _type: PEVP_MD; md: PChar; len: PInteger):Integer; function X509Digest(data: PX509; _type: PEVP_MD; md: AnsiString; var len: Integer):Integer; function X509print(b: PBIO; a: PX509): integer; function X509SetVersion(x: PX509; version: integer): integer; function X509SetPubkey(x: PX509; pkey: EVP_PKEY): integer; function X509SetIssuerName(x: PX509; name: PX509_NAME): integer; function X509NameAddEntryByTxt(name: PX509_NAME; field: Ansistring; _type: integer; bytes: Ansistring; len, loc, _set: integer): integer; function X509Sign(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer; function X509GmtimeAdj(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME; function X509SetNotBefore(x: PX509; tm: PASN1_UTCTIME): integer; function X509SetNotAfter(x: PX509; tm: PASN1_UTCTIME): integer; function X509GetSerialNumber(x: PX509): PASN1_INTEGER; function EvpPkeyNew: EVP_PKEY; procedure EvpPkeyFree(pk: EVP_PKEY); function EvpPkeyAssign(pkey: EVP_PKEY; _type: integer; key: Prsa): integer; function EvpGetDigestByName(Name: AnsiString): PEVP_MD; procedure EVPcleanup; // function ErrErrorString(e: integer; buf: PChar): PChar; function SSLeayversion(t: integer): Ansistring; procedure ErrErrorString(e: integer; var buf: Ansistring; len: integer); function ErrGetError: integer; procedure ErrClearError; procedure ErrFreeStrings; procedure ErrRemoveState(pid: integer); procedure OPENSSLaddallalgorithms; procedure CRYPTOcleanupAllExData; procedure RandScreen; function BioNew(b: PBIO_METHOD): PBIO; procedure BioFreeAll(b: PBIO); function BioSMem: PBIO_METHOD; function BioCtrlPending(b: PBIO): integer; function BioRead(b: PBIO; var Buf: AnsiString; Len: integer): integer; function BioWrite(b: PBIO; Buf: AnsiString; Len: integer): integer; function d2iPKCS12bio(b:PBIO; Pkcs12: SslPtr): SslPtr; function PKCS12parse(p12: SslPtr; pass: Ansistring; var pkey, cert, ca: SslPtr): integer; procedure PKCS12free(p12: SslPtr); function RsaGenerateKey(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA; function Asn1UtctimeNew: PASN1_UTCTIME; procedure Asn1UtctimeFree(a: PASN1_UTCTIME); function Asn1IntegerSet(a: PASN1_INTEGER; v: integer): integer; function i2dX509bio(b: PBIO; x: PX509): integer; function i2dPrivateKeyBio(b: PBIO; pkey: EVP_PKEY): integer; // 3DES functions procedure DESsetoddparity(Key: des_cblock); function DESsetkeychecked(key: des_cblock; schedule: des_key_schedule): Integer; procedure DESecbencrypt(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer); {$ENDIF} function IsSSLloaded: Boolean; function InitSSLInterface: Boolean; function DestroySSLInterface: Boolean; implementation uses SyncObjs; {$IFNDEF CIL} type // libssl.dll TSslGetError = function(s: PSSL; ret_code: Integer):Integer; cdecl; TSslLibraryInit = function:Integer; cdecl; TSslLoadErrorStrings = procedure; cdecl; TSslCtxSetCipherList = function(arg0: PSSL_CTX; str: PAnsiChar):Integer; cdecl; TSslCtxNew = function(meth: PSSL_METHOD):PSSL_CTX; cdecl; TSslCtxFree = procedure(arg0: PSSL_CTX); cdecl; TSslSetFd = function(s: PSSL; fd: Integer):Integer; cdecl; TSslMethodV2 = function:PSSL_METHOD; cdecl; TSslMethodV3 = function:PSSL_METHOD; cdecl; TSslMethodTLSV1 = function:PSSL_METHOD; cdecl; TSslMethodV23 = function:PSSL_METHOD; cdecl; TSslCtxUsePrivateKey = function(ctx: PSSL_CTX; pkey: sslptr):Integer; cdecl; TSslCtxUsePrivateKeyASN1 = function(pk: integer; ctx: PSSL_CTX; d: sslptr; len: integer):Integer; cdecl; TSslCtxUsePrivateKeyFile = function(ctx: PSSL_CTX; const _file: PAnsiChar; _type: Integer):Integer; cdecl; TSslCtxUseCertificate = function(ctx: PSSL_CTX; x: SslPtr):Integer; cdecl; TSslCtxUseCertificateASN1 = function(ctx: PSSL_CTX; len: Integer; d: SslPtr):Integer; cdecl; TSslCtxUseCertificateFile = function(ctx: PSSL_CTX; const _file: PAnsiChar; _type: Integer):Integer; cdecl; TSslCtxUseCertificateChainFile = function(ctx: PSSL_CTX; const _file: PAnsiChar):Integer; cdecl; TSslCtxCheckPrivateKeyFile = function(ctx: PSSL_CTX):Integer; cdecl; TSslCtxSetDefaultPasswdCb = procedure(ctx: PSSL_CTX; cb: SslPtr); cdecl; TSslCtxSetDefaultPasswdCbUserdata = procedure(ctx: PSSL_CTX; u: SslPtr); cdecl; TSslCtxLoadVerifyLocations = function(ctx: PSSL_CTX; const CAfile: PAnsiChar; const CApath: PAnsiChar):Integer; cdecl; TSslCtxCtrl = function(ctx: PSSL_CTX; cmd: integer; larg: integer; parg: SslPtr): integer; cdecl; TSslNew = function(ctx: PSSL_CTX):PSSL; cdecl; TSslFree = procedure(ssl: PSSL); cdecl; TSslAccept = function(ssl: PSSL):Integer; cdecl; TSslConnect = function(ssl: PSSL):Integer; cdecl; TSslShutdown = function(ssl: PSSL):Integer; cdecl; TSslRead = function(ssl: PSSL; buf: PAnsiChar; num: Integer):Integer; cdecl; TSslPeek = function(ssl: PSSL; buf: PAnsiChar; num: Integer):Integer; cdecl; TSslWrite = function(ssl: PSSL; const buf: PAnsiChar; num: Integer):Integer; cdecl; TSslPending = function(ssl: PSSL):Integer; cdecl; TSslGetVersion = function(ssl: PSSL):PAnsiChar; cdecl; TSslGetPeerCertificate = function(ssl: PSSL):PX509; cdecl; TSslCtxSetVerify = procedure(ctx: PSSL_CTX; mode: Integer; arg2: SslPtr); cdecl; TSSLGetCurrentCipher = function(s: PSSL):SslPtr; cdecl; TSSLCipherGetName = function(c: Sslptr):PAnsiChar; cdecl; TSSLCipherGetBits = function(c: SslPtr; alg_bits: PInteger):Integer; cdecl; TSSLGetVerifyResult = function(ssl: PSSL):Integer; cdecl; // libeay.dll TX509New = function: PX509; cdecl; TX509Free = procedure(x: PX509); cdecl; TX509NameOneline = function(a: PX509_NAME; buf: PAnsiChar; size: Integer):PAnsiChar; cdecl; TX509GetSubjectName = function(a: PX509):PX509_NAME; cdecl; TX509GetIssuerName = function(a: PX509):PX509_NAME; cdecl; TX509NameHash = function(x: PX509_NAME):Cardinal; cdecl; TX509Digest = function(data: PX509; _type: PEVP_MD; md: PAnsiChar; len: PInteger):Integer; cdecl; TX509print = function(b: PBIO; a: PX509): integer; cdecl; TX509SetVersion = function(x: PX509; version: integer): integer; cdecl; TX509SetPubkey = function(x: PX509; pkey: EVP_PKEY): integer; cdecl; TX509SetIssuerName = function(x: PX509; name: PX509_NAME): integer; cdecl; TX509NameAddEntryByTxt = function(name: PX509_NAME; field: PAnsiChar; _type: integer; bytes: PAnsiChar; len, loc, _set: integer): integer; cdecl; TX509Sign = function(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer; cdecl; TX509GmtimeAdj = function(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME; cdecl; TX509SetNotBefore = function(x: PX509; tm: PASN1_UTCTIME): integer; cdecl; TX509SetNotAfter = function(x: PX509; tm: PASN1_UTCTIME): integer; cdecl; TX509GetSerialNumber = function(x: PX509): PASN1_INTEGER; cdecl; TEvpPkeyNew = function: EVP_PKEY; cdecl; TEvpPkeyFree = procedure(pk: EVP_PKEY); cdecl; TEvpPkeyAssign = function(pkey: EVP_PKEY; _type: integer; key: Prsa): integer; cdecl; TEvpGetDigestByName = function(Name: PAnsiChar): PEVP_MD; cdecl; TEVPcleanup = procedure; cdecl; TSSLeayversion = function(t: integer): PAnsiChar; cdecl; TErrErrorString = procedure(e: integer; buf: PAnsiChar; len: integer); cdecl; TErrGetError = function: integer; cdecl; TErrClearError = procedure; cdecl; TErrFreeStrings = procedure; cdecl; TErrRemoveState = procedure(pid: integer); cdecl; TOPENSSLaddallalgorithms = procedure; cdecl; TCRYPTOcleanupAllExData = procedure; cdecl; TRandScreen = procedure; cdecl; TBioNew = function(b: PBIO_METHOD): PBIO; cdecl; TBioFreeAll = procedure(b: PBIO); cdecl; TBioSMem = function: PBIO_METHOD; cdecl; TBioCtrlPending = function(b: PBIO): integer; cdecl; TBioRead = function(b: PBIO; Buf: PAnsiChar; Len: integer): integer; cdecl; TBioWrite = function(b: PBIO; Buf: PAnsiChar; Len: integer): integer; cdecl; Td2iPKCS12bio = function(b:PBIO; Pkcs12: SslPtr): SslPtr; cdecl; TPKCS12parse = function(p12: SslPtr; pass: PAnsiChar; var pkey, cert, ca: SslPtr): integer; cdecl; TPKCS12free = procedure(p12: SslPtr); cdecl; TRsaGenerateKey = function(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA; cdecl; TAsn1UtctimeNew = function: PASN1_UTCTIME; cdecl; TAsn1UtctimeFree = procedure(a: PASN1_UTCTIME); cdecl; TAsn1IntegerSet = function(a: PASN1_INTEGER; v: integer): integer; cdecl; Ti2dX509bio = function(b: PBIO; x: PX509): integer; cdecl; Ti2dPrivateKeyBio= function(b: PBIO; pkey: EVP_PKEY): integer; cdecl; // 3DES functions TDESsetoddparity = procedure(Key: des_cblock); cdecl; TDESsetkeychecked = function(key: des_cblock; schedule: des_key_schedule): Integer; cdecl; TDESecbencrypt = procedure(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer); cdecl; //thread lock functions TCRYPTOnumlocks = function: integer; cdecl; TCRYPTOSetLockingCallback = procedure(cb: Sslptr); cdecl; var // libssl.dll _SslGetError: TSslGetError = nil; _SslLibraryInit: TSslLibraryInit = nil; _SslLoadErrorStrings: TSslLoadErrorStrings = nil; _SslCtxSetCipherList: TSslCtxSetCipherList = nil; _SslCtxNew: TSslCtxNew = nil; _SslCtxFree: TSslCtxFree = nil; _SslSetFd: TSslSetFd = nil; _SslMethodV2: TSslMethodV2 = nil; _SslMethodV3: TSslMethodV3 = nil; _SslMethodTLSV1: TSslMethodTLSV1 = nil; _SslMethodV23: TSslMethodV23 = nil; _SslCtxUsePrivateKey: TSslCtxUsePrivateKey = nil; _SslCtxUsePrivateKeyASN1: TSslCtxUsePrivateKeyASN1 = nil; _SslCtxUsePrivateKeyFile: TSslCtxUsePrivateKeyFile = nil; _SslCtxUseCertificate: TSslCtxUseCertificate = nil; _SslCtxUseCertificateASN1: TSslCtxUseCertificateASN1 = nil; _SslCtxUseCertificateFile: TSslCtxUseCertificateFile = nil; _SslCtxUseCertificateChainFile: TSslCtxUseCertificateChainFile = nil; _SslCtxCheckPrivateKeyFile: TSslCtxCheckPrivateKeyFile = nil; _SslCtxSetDefaultPasswdCb: TSslCtxSetDefaultPasswdCb = nil; _SslCtxSetDefaultPasswdCbUserdata: TSslCtxSetDefaultPasswdCbUserdata = nil; _SslCtxLoadVerifyLocations: TSslCtxLoadVerifyLocations = nil; _SslCtxCtrl: TSslCtxCtrl = nil; _SslNew: TSslNew = nil; _SslFree: TSslFree = nil; _SslAccept: TSslAccept = nil; _SslConnect: TSslConnect = nil; _SslShutdown: TSslShutdown = nil; _SslRead: TSslRead = nil; _SslPeek: TSslPeek = nil; _SslWrite: TSslWrite = nil; _SslPending: TSslPending = nil; _SslGetVersion: TSslGetVersion = nil; _SslGetPeerCertificate: TSslGetPeerCertificate = nil; _SslCtxSetVerify: TSslCtxSetVerify = nil; _SSLGetCurrentCipher: TSSLGetCurrentCipher = nil; _SSLCipherGetName: TSSLCipherGetName = nil; _SSLCipherGetBits: TSSLCipherGetBits = nil; _SSLGetVerifyResult: TSSLGetVerifyResult = nil; // libeay.dll _X509New: TX509New = nil; _X509Free: TX509Free = nil; _X509NameOneline: TX509NameOneline = nil; _X509GetSubjectName: TX509GetSubjectName = nil; _X509GetIssuerName: TX509GetIssuerName = nil; _X509NameHash: TX509NameHash = nil; _X509Digest: TX509Digest = nil; _X509print: TX509print = nil; _X509SetVersion: TX509SetVersion = nil; _X509SetPubkey: TX509SetPubkey = nil; _X509SetIssuerName: TX509SetIssuerName = nil; _X509NameAddEntryByTxt: TX509NameAddEntryByTxt = nil; _X509Sign: TX509Sign = nil; _X509GmtimeAdj: TX509GmtimeAdj = nil; _X509SetNotBefore: TX509SetNotBefore = nil; _X509SetNotAfter: TX509SetNotAfter = nil; _X509GetSerialNumber: TX509GetSerialNumber = nil; _EvpPkeyNew: TEvpPkeyNew = nil; _EvpPkeyFree: TEvpPkeyFree = nil; _EvpPkeyAssign: TEvpPkeyAssign = nil; _EvpGetDigestByName: TEvpGetDigestByName = nil; _EVPcleanup: TEVPcleanup = nil; _SSLeayversion: TSSLeayversion = nil; _ErrErrorString: TErrErrorString = nil; _ErrGetError: TErrGetError = nil; _ErrClearError: TErrClearError = nil; _ErrFreeStrings: TErrFreeStrings = nil; _ErrRemoveState: TErrRemoveState = nil; _OPENSSLaddallalgorithms: TOPENSSLaddallalgorithms = nil; _CRYPTOcleanupAllExData: TCRYPTOcleanupAllExData = nil; _RandScreen: TRandScreen = nil; _BioNew: TBioNew = nil; _BioFreeAll: TBioFreeAll = nil; _BioSMem: TBioSMem = nil; _BioCtrlPending: TBioCtrlPending = nil; _BioRead: TBioRead = nil; _BioWrite: TBioWrite = nil; _d2iPKCS12bio: Td2iPKCS12bio = nil; _PKCS12parse: TPKCS12parse = nil; _PKCS12free: TPKCS12free = nil; _RsaGenerateKey: TRsaGenerateKey = nil; _Asn1UtctimeNew: TAsn1UtctimeNew = nil; _Asn1UtctimeFree: TAsn1UtctimeFree = nil; _Asn1IntegerSet: TAsn1IntegerSet = nil; _i2dX509bio: Ti2dX509bio = nil; _i2dPrivateKeyBio: Ti2dPrivateKeyBio = nil; // 3DES functions _DESsetoddparity: TDESsetoddparity = nil; _DESsetkeychecked: TDESsetkeychecked = nil; _DESecbencrypt: TDESecbencrypt = nil; //thread lock functions _CRYPTOnumlocks: TCRYPTOnumlocks = nil; _CRYPTOSetLockingCallback: TCRYPTOSetLockingCallback = nil; {$ENDIF} var SSLCS: TCriticalSection; SSLloaded: boolean = false; {$IFNDEF CIL} Locks: TList; {$ENDIF} {$IFNDEF CIL} // libssl.dll function SslGetError(s: PSSL; ret_code: Integer):Integer; begin if InitSSLInterface and Assigned(_SslGetError) then Result := _SslGetError(s, ret_code) else Result := SSL_ERROR_SSL; end; function SslLibraryInit:Integer; begin if InitSSLInterface and Assigned(_SslLibraryInit) then Result := _SslLibraryInit else Result := 1; end; procedure SslLoadErrorStrings; begin if InitSSLInterface and Assigned(_SslLoadErrorStrings) then _SslLoadErrorStrings; end; //function SslCtxSetCipherList(arg0: PSSL_CTX; str: PChar):Integer; function SslCtxSetCipherList(arg0: PSSL_CTX; var str: AnsiString):Integer; begin if InitSSLInterface and Assigned(_SslCtxSetCipherList) then Result := _SslCtxSetCipherList(arg0, PAnsiChar(str)) else Result := 0; end; function SslCtxNew(meth: PSSL_METHOD):PSSL_CTX; begin if InitSSLInterface and Assigned(_SslCtxNew) then Result := _SslCtxNew(meth) else Result := nil; end; procedure SslCtxFree(arg0: PSSL_CTX); begin if InitSSLInterface and Assigned(_SslCtxFree) then _SslCtxFree(arg0); end; function SslSetFd(s: PSSL; fd: Integer):Integer; begin if InitSSLInterface and Assigned(_SslSetFd) then Result := _SslSetFd(s, fd) else Result := 0; end; function SslMethodV2:PSSL_METHOD; begin if InitSSLInterface and Assigned(_SslMethodV2) then Result := _SslMethodV2 else Result := nil; end; function SslMethodV3:PSSL_METHOD; begin if InitSSLInterface and Assigned(_SslMethodV3) then Result := _SslMethodV3 else Result := nil; end; function SslMethodTLSV1:PSSL_METHOD; begin if InitSSLInterface and Assigned(_SslMethodTLSV1) then Result := _SslMethodTLSV1 else Result := nil; end; function SslMethodV23:PSSL_METHOD; begin if InitSSLInterface and Assigned(_SslMethodV23) then Result := _SslMethodV23 else Result := nil; end; function SslCtxUsePrivateKey(ctx: PSSL_CTX; pkey: SslPtr):Integer; begin if InitSSLInterface and Assigned(_SslCtxUsePrivateKey) then Result := _SslCtxUsePrivateKey(ctx, pkey) else Result := 0; end; function SslCtxUsePrivateKeyASN1(pk: integer; ctx: PSSL_CTX; d: AnsiString; len: integer):Integer; begin if InitSSLInterface and Assigned(_SslCtxUsePrivateKeyASN1) then Result := _SslCtxUsePrivateKeyASN1(pk, ctx, Sslptr(d), len) else Result := 0; end; //function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: PChar; _type: Integer):Integer; function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: AnsiString; _type: Integer):Integer; begin if InitSSLInterface and Assigned(_SslCtxUsePrivateKeyFile) then Result := _SslCtxUsePrivateKeyFile(ctx, PAnsiChar(_file), _type) else Result := 0; end; function SslCtxUseCertificate(ctx: PSSL_CTX; x: SslPtr):Integer; begin if InitSSLInterface and Assigned(_SslCtxUseCertificate) then Result := _SslCtxUseCertificate(ctx, x) else Result := 0; end; function SslCtxUseCertificateASN1(ctx: PSSL_CTX; len: integer; d: AnsiString):Integer; begin if InitSSLInterface and Assigned(_SslCtxUseCertificateASN1) then Result := _SslCtxUseCertificateASN1(ctx, len, SslPtr(d)) else Result := 0; end; function SslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: AnsiString; _type: Integer):Integer; begin if InitSSLInterface and Assigned(_SslCtxUseCertificateFile) then Result := _SslCtxUseCertificateFile(ctx, PAnsiChar(_file), _type) else Result := 0; end; //function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: PChar):Integer; function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: AnsiString):Integer; begin if InitSSLInterface and Assigned(_SslCtxUseCertificateChainFile) then Result := _SslCtxUseCertificateChainFile(ctx, PAnsiChar(_file)) else Result := 0; end; function SslCtxCheckPrivateKeyFile(ctx: PSSL_CTX):Integer; begin if InitSSLInterface and Assigned(_SslCtxCheckPrivateKeyFile) then Result := _SslCtxCheckPrivateKeyFile(ctx) else Result := 0; end; procedure SslCtxSetDefaultPasswdCb(ctx: PSSL_CTX; cb: PPasswdCb); begin if InitSSLInterface and Assigned(_SslCtxSetDefaultPasswdCb) then _SslCtxSetDefaultPasswdCb(ctx, cb); end; procedure SslCtxSetDefaultPasswdCbUserdata(ctx: PSSL_CTX; u: SslPtr); begin if InitSSLInterface and Assigned(_SslCtxSetDefaultPasswdCbUserdata) then _SslCtxSetDefaultPasswdCbUserdata(ctx, u); end; //function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: PChar; const CApath: PChar):Integer; function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: AnsiString; const CApath: AnsiString):Integer; begin if InitSSLInterface and Assigned(_SslCtxLoadVerifyLocations) then Result := _SslCtxLoadVerifyLocations(ctx, SslPtr(CAfile), SslPtr(CApath)) else Result := 0; end; function SslCtxCtrl(ctx: PSSL_CTX; cmd: integer; larg: integer; parg: SslPtr): integer; begin if InitSSLInterface and Assigned(_SslCtxCtrl) then Result := _SslCtxCtrl(ctx, cmd, larg, parg) else Result := 0; end; function SslNew(ctx: PSSL_CTX):PSSL; begin if InitSSLInterface and Assigned(_SslNew) then Result := _SslNew(ctx) else Result := nil; end; procedure SslFree(ssl: PSSL); begin if InitSSLInterface and Assigned(_SslFree) then _SslFree(ssl); end; function SslAccept(ssl: PSSL):Integer; begin if InitSSLInterface and Assigned(_SslAccept) then Result := _SslAccept(ssl) else Result := -1; end; function SslConnect(ssl: PSSL):Integer; begin if InitSSLInterface and Assigned(_SslConnect) then Result := _SslConnect(ssl) else Result := -1; end; function SslShutdown(ssl: PSSL):Integer; begin if InitSSLInterface and Assigned(_SslShutdown) then Result := _SslShutdown(ssl) else Result := -1; end; //function SslRead(ssl: PSSL; buf: PChar; num: Integer):Integer; function SslRead(ssl: PSSL; buf: SslPtr; num: Integer):Integer; begin if InitSSLInterface and Assigned(_SslRead) then Result := _SslRead(ssl, PAnsiChar(buf), num) else Result := -1; end; //function SslPeek(ssl: PSSL; buf: PChar; num: Integer):Integer; function SslPeek(ssl: PSSL; buf: SslPtr; num: Integer):Integer; begin if InitSSLInterface and Assigned(_SslPeek) then Result := _SslPeek(ssl, PAnsiChar(buf), num) else Result := -1; end; //function SslWrite(ssl: PSSL; const buf: PChar; num: Integer):Integer; function SslWrite(ssl: PSSL; buf: SslPtr; num: Integer):Integer; begin if InitSSLInterface and Assigned(_SslWrite) then Result := _SslWrite(ssl, PAnsiChar(buf), num) else Result := -1; end; function SslPending(ssl: PSSL):Integer; begin if InitSSLInterface and Assigned(_SslPending) then Result := _SslPending(ssl) else Result := 0; end; //function SslGetVersion(ssl: PSSL):PChar; function SslGetVersion(ssl: PSSL):AnsiString; begin if InitSSLInterface and Assigned(_SslGetVersion) then Result := _SslGetVersion(ssl) else Result := ''; end; function SslGetPeerCertificate(ssl: PSSL):PX509; begin if InitSSLInterface and Assigned(_SslGetPeerCertificate) then Result := _SslGetPeerCertificate(ssl) else Result := nil; end; //procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: SslPtr); procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: PFunction); begin if InitSSLInterface and Assigned(_SslCtxSetVerify) then _SslCtxSetVerify(ctx, mode, @arg2); end; function SSLGetCurrentCipher(s: PSSL):SslPtr; begin if InitSSLInterface and Assigned(_SSLGetCurrentCipher) then {$IFDEF CIL} {$ELSE} Result := _SSLGetCurrentCipher(s) {$ENDIF} else Result := nil; end; //function SSLCipherGetName(c: SslPtr):PChar; function SSLCipherGetName(c: SslPtr):AnsiString; begin if InitSSLInterface and Assigned(_SSLCipherGetName) then Result := _SSLCipherGetName(c) else Result := ''; end; //function SSLCipherGetBits(c: SslPtr; alg_bits: PInteger):Integer; function SSLCipherGetBits(c: SslPtr; var alg_bits: Integer):Integer; begin if InitSSLInterface and Assigned(_SSLCipherGetBits) then Result := _SSLCipherGetBits(c, @alg_bits) else Result := 0; end; function SSLGetVerifyResult(ssl: PSSL):Integer; begin if InitSSLInterface and Assigned(_SSLGetVerifyResult) then Result := _SSLGetVerifyResult(ssl) else Result := X509_V_ERR_APPLICATION_VERIFICATION; end; // libeay.dll function X509New: PX509; begin if InitSSLInterface and Assigned(_X509New) then Result := _X509New else Result := nil; end; procedure X509Free(x: PX509); begin if InitSSLInterface and Assigned(_X509Free) then _X509Free(x); end; //function SslX509NameOneline(a: PX509_NAME; buf: PChar; size: Integer):PChar; function X509NameOneline(a: PX509_NAME; var buf: AnsiString; size: Integer):AnsiString; begin if InitSSLInterface and Assigned(_X509NameOneline) then Result := _X509NameOneline(a, PAnsiChar(buf),size) else Result := ''; end; function X509GetSubjectName(a: PX509):PX509_NAME; begin if InitSSLInterface and Assigned(_X509GetSubjectName) then Result := _X509GetSubjectName(a) else Result := nil; end; function X509GetIssuerName(a: PX509):PX509_NAME; begin if InitSSLInterface and Assigned(_X509GetIssuerName) then Result := _X509GetIssuerName(a) else Result := nil; end; function X509NameHash(x: PX509_NAME):Cardinal; begin if InitSSLInterface and Assigned(_X509NameHash) then Result := _X509NameHash(x) else Result := 0; end; //function SslX509Digest(data: PX509; _type: PEVP_MD; md: PChar; len: PInteger):Integer; function X509Digest(data: PX509; _type: PEVP_MD; md: AnsiString; var len: Integer):Integer; begin if InitSSLInterface and Assigned(_X509Digest) then Result := _X509Digest(data, _type, PAnsiChar(md), @len) else Result := 0; end; function EvpPkeyNew: EVP_PKEY; begin if InitSSLInterface and Assigned(_EvpPkeyNew) then Result := _EvpPkeyNew else Result := nil; end; procedure EvpPkeyFree(pk: EVP_PKEY); begin if InitSSLInterface and Assigned(_EvpPkeyFree) then _EvpPkeyFree(pk); end; function SSLeayversion(t: integer): Ansistring; begin if InitSSLInterface and Assigned(_SSLeayversion) then Result := PAnsiChar(_SSLeayversion(t)) else Result := ''; end; procedure ErrErrorString(e: integer; var buf: Ansistring; len: integer); begin if InitSSLInterface and Assigned(_ErrErrorString) then _ErrErrorString(e, Pointer(buf), len); buf := PAnsiChar(Buf); end; function ErrGetError: integer; begin if InitSSLInterface and Assigned(_ErrGetError) then Result := _ErrGetError else Result := SSL_ERROR_SSL; end; procedure ErrClearError; begin if InitSSLInterface and Assigned(_ErrClearError) then _ErrClearError; end; procedure ErrFreeStrings; begin if InitSSLInterface and Assigned(_ErrFreeStrings) then _ErrFreeStrings; end; procedure ErrRemoveState(pid: integer); begin if InitSSLInterface and Assigned(_ErrRemoveState) then _ErrRemoveState(pid); end; procedure OPENSSLaddallalgorithms; begin if InitSSLInterface and Assigned(_OPENSSLaddallalgorithms) then _OPENSSLaddallalgorithms; end; procedure EVPcleanup; begin if InitSSLInterface and Assigned(_EVPcleanup) then _EVPcleanup; end; procedure CRYPTOcleanupAllExData; begin if InitSSLInterface and Assigned(_CRYPTOcleanupAllExData) then _CRYPTOcleanupAllExData; end; procedure RandScreen; begin if InitSSLInterface and Assigned(_RandScreen) then _RandScreen; end; function BioNew(b: PBIO_METHOD): PBIO; begin if InitSSLInterface and Assigned(_BioNew) then Result := _BioNew(b) else Result := nil; end; procedure BioFreeAll(b: PBIO); begin if InitSSLInterface and Assigned(_BioFreeAll) then _BioFreeAll(b); end; function BioSMem: PBIO_METHOD; begin if InitSSLInterface and Assigned(_BioSMem) then Result := _BioSMem else Result := nil; end; function BioCtrlPending(b: PBIO): integer; begin if InitSSLInterface and Assigned(_BioCtrlPending) then Result := _BioCtrlPending(b) else Result := 0; end; //function BioRead(b: PBIO; Buf: PChar; Len: integer): integer; function BioRead(b: PBIO; var Buf: AnsiString; Len: integer): integer; begin if InitSSLInterface and Assigned(_BioRead) then Result := _BioRead(b, PAnsiChar(Buf), Len) else Result := -2; end; //function BioWrite(b: PBIO; Buf: PChar; Len: integer): integer; function BioWrite(b: PBIO; Buf: AnsiString; Len: integer): integer; begin if InitSSLInterface and Assigned(_BioWrite) then Result := _BioWrite(b, PAnsiChar(Buf), Len) else Result := -2; end; function X509print(b: PBIO; a: PX509): integer; begin if InitSSLInterface and Assigned(_X509print) then Result := _X509print(b, a) else Result := 0; end; function d2iPKCS12bio(b:PBIO; Pkcs12: SslPtr): SslPtr; begin if InitSSLInterface and Assigned(_d2iPKCS12bio) then Result := _d2iPKCS12bio(b, Pkcs12) else Result := nil; end; function PKCS12parse(p12: SslPtr; pass: Ansistring; var pkey, cert, ca: SslPtr): integer; begin if InitSSLInterface and Assigned(_PKCS12parse) then Result := _PKCS12parse(p12, SslPtr(pass), pkey, cert, ca) else Result := 0; end; procedure PKCS12free(p12: SslPtr); begin if InitSSLInterface and Assigned(_PKCS12free) then _PKCS12free(p12); end; function RsaGenerateKey(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA; begin if InitSSLInterface and Assigned(_RsaGenerateKey) then Result := _RsaGenerateKey(bits, e, callback, cb_arg) else Result := nil; end; function EvpPkeyAssign(pkey: EVP_PKEY; _type: integer; key: Prsa): integer; begin if InitSSLInterface and Assigned(_EvpPkeyAssign) then Result := _EvpPkeyAssign(pkey, _type, key) else Result := 0; end; function X509SetVersion(x: PX509; version: integer): integer; begin if InitSSLInterface and Assigned(_X509SetVersion) then Result := _X509SetVersion(x, version) else Result := 0; end; function X509SetPubkey(x: PX509; pkey: EVP_PKEY): integer; begin if InitSSLInterface and Assigned(_X509SetPubkey) then Result := _X509SetPubkey(x, pkey) else Result := 0; end; function X509SetIssuerName(x: PX509; name: PX509_NAME): integer; begin if InitSSLInterface and Assigned(_X509SetIssuerName) then Result := _X509SetIssuerName(x, name) else Result := 0; end; function X509NameAddEntryByTxt(name: PX509_NAME; field: Ansistring; _type: integer; bytes: Ansistring; len, loc, _set: integer): integer; begin if InitSSLInterface and Assigned(_X509NameAddEntryByTxt) then Result := _X509NameAddEntryByTxt(name, PAnsiChar(field), _type, PAnsiChar(Bytes), len, loc, _set) else Result := 0; end; function X509Sign(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer; begin if InitSSLInterface and Assigned(_X509Sign) then Result := _X509Sign(x, pkey, md) else Result := 0; end; function Asn1UtctimeNew: PASN1_UTCTIME; begin if InitSSLInterface and Assigned(_Asn1UtctimeNew) then Result := _Asn1UtctimeNew else Result := nil; end; procedure Asn1UtctimeFree(a: PASN1_UTCTIME); begin if InitSSLInterface and Assigned(_Asn1UtctimeFree) then _Asn1UtctimeFree(a); end; function X509GmtimeAdj(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME; begin if InitSSLInterface and Assigned(_X509GmtimeAdj) then Result := _X509GmtimeAdj(s, adj) else Result := nil; end; function X509SetNotBefore(x: PX509; tm: PASN1_UTCTIME): integer; begin if InitSSLInterface and Assigned(_X509SetNotBefore) then Result := _X509SetNotBefore(x, tm) else Result := 0; end; function X509SetNotAfter(x: PX509; tm: PASN1_UTCTIME): integer; begin if InitSSLInterface and Assigned(_X509SetNotAfter) then Result := _X509SetNotAfter(x, tm) else Result := 0; end; function i2dX509bio(b: PBIO; x: PX509): integer; begin if InitSSLInterface and Assigned(_i2dX509bio) then Result := _i2dX509bio(b, x) else Result := 0; end; function i2dPrivateKeyBio(b: PBIO; pkey: EVP_PKEY): integer; begin if InitSSLInterface and Assigned(_i2dPrivateKeyBio) then Result := _i2dPrivateKeyBio(b, pkey) else Result := 0; end; function EvpGetDigestByName(Name: AnsiString): PEVP_MD; begin if InitSSLInterface and Assigned(_EvpGetDigestByName) then Result := _EvpGetDigestByName(PAnsiChar(Name)) else Result := nil; end; function Asn1IntegerSet(a: PASN1_INTEGER; v: integer): integer; begin if InitSSLInterface and Assigned(_Asn1IntegerSet) then Result := _Asn1IntegerSet(a, v) else Result := 0; end; function X509GetSerialNumber(x: PX509): PASN1_INTEGER; begin if InitSSLInterface and Assigned(_X509GetSerialNumber) then Result := _X509GetSerialNumber(x) else Result := nil; end; // 3DES functions procedure DESsetoddparity(Key: des_cblock); begin if InitSSLInterface and Assigned(_DESsetoddparity) then _DESsetoddparity(Key); end; function DESsetkeychecked(key: des_cblock; schedule: des_key_schedule): Integer; begin if InitSSLInterface and Assigned(_DESsetkeychecked) then Result := _DESsetkeychecked(key, schedule) else Result := -1; end; procedure DESecbencrypt(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer); begin if InitSSLInterface and Assigned(_DESecbencrypt) then _DESecbencrypt(Input, output, ks, enc); end; procedure locking_callback(mode, ltype: integer; lfile: PChar; line: integer); cdecl; begin if (mode and 1) > 0 then TCriticalSection(Locks[ltype]).Enter else TCriticalSection(Locks[ltype]).Leave; end; procedure InitLocks; var n: integer; max: integer; begin Locks := TList.Create; max := _CRYPTOnumlocks; for n := 1 to max do Locks.Add(TCriticalSection.Create); _CRYPTOsetlockingcallback(@locking_callback); end; procedure FreeLocks; var n: integer; begin _CRYPTOsetlockingcallback(nil); for n := 0 to Locks.Count - 1 do TCriticalSection(Locks[n]).Free; Locks.Free; end; {$ENDIF} function LoadLib(const Value: String): HModule; begin {$IFDEF CIL} Result := LoadLibrary(Value); {$ELSE} Result := LoadLibrary(PChar(Value)); {$ENDIF} end; function GetProcAddr(module: HModule; const ProcName: string): SslPtr; begin {$IFDEF CIL} Result := GetProcAddress(module, ProcName); {$ELSE} Result := GetProcAddress(module, PChar(ProcName)); {$ENDIF} end; function InitSSLInterface: Boolean; var s: string; x: integer; begin SSLCS.Enter; try if not IsSSLloaded then begin {$IFDEF CIL} SSLLibHandle := 1; SSLUtilHandle := 1; {$ELSE} SSLLibHandle := LoadLib(DLLSSLName); SSLUtilHandle := LoadLib(DLLUtilName); {$IFDEF MSWINDOWS} if (SSLLibHandle = 0) then SSLLibHandle := LoadLib(DLLSSLName2); {$ENDIF} {$ENDIF} if (SSLLibHandle <> 0) and (SSLUtilHandle <> 0) then begin {$IFNDEF CIL} _SslGetError := GetProcAddr(SSLLibHandle, 'SSL_get_error'); _SslLibraryInit := GetProcAddr(SSLLibHandle, 'SSL_library_init'); _SslLoadErrorStrings := GetProcAddr(SSLLibHandle, 'SSL_load_error_strings'); _SslCtxSetCipherList := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_cipher_list'); _SslCtxNew := GetProcAddr(SSLLibHandle, 'SSL_CTX_new'); _SslCtxFree := GetProcAddr(SSLLibHandle, 'SSL_CTX_free'); _SslSetFd := GetProcAddr(SSLLibHandle, 'SSL_set_fd'); _SslMethodV2 := GetProcAddr(SSLLibHandle, 'SSLv2_method'); _SslMethodV3 := GetProcAddr(SSLLibHandle, 'SSLv3_method'); _SslMethodTLSV1 := GetProcAddr(SSLLibHandle, 'TLSv1_method'); _SslMethodV23 := GetProcAddr(SSLLibHandle, 'SSLv23_method'); _SslCtxUsePrivateKey := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_PrivateKey'); _SslCtxUsePrivateKeyASN1 := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_PrivateKey_ASN1'); //use SSL_CTX_use_RSAPrivateKey_file instead SSL_CTX_use_PrivateKey_file, //because SSL_CTX_use_PrivateKey_file not support DER format. :-O _SslCtxUsePrivateKeyFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_RSAPrivateKey_file'); _SslCtxUseCertificate := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate'); _SslCtxUseCertificateASN1 := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate_ASN1'); _SslCtxUseCertificateFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate_file'); _SslCtxUseCertificateChainFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate_chain_file'); _SslCtxCheckPrivateKeyFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_check_private_key'); _SslCtxSetDefaultPasswdCb := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_default_passwd_cb'); _SslCtxSetDefaultPasswdCbUserdata := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_default_passwd_cb_userdata'); _SslCtxLoadVerifyLocations := GetProcAddr(SSLLibHandle, 'SSL_CTX_load_verify_locations'); _SslCtxCtrl := GetProcAddr(SSLLibHandle, 'SSL_CTX_ctrl'); _SslNew := GetProcAddr(SSLLibHandle, 'SSL_new'); _SslFree := GetProcAddr(SSLLibHandle, 'SSL_free'); _SslAccept := GetProcAddr(SSLLibHandle, 'SSL_accept'); _SslConnect := GetProcAddr(SSLLibHandle, 'SSL_connect'); _SslShutdown := GetProcAddr(SSLLibHandle, 'SSL_shutdown'); _SslRead := GetProcAddr(SSLLibHandle, 'SSL_read'); _SslPeek := GetProcAddr(SSLLibHandle, 'SSL_peek'); _SslWrite := GetProcAddr(SSLLibHandle, 'SSL_write'); _SslPending := GetProcAddr(SSLLibHandle, 'SSL_pending'); _SslGetPeerCertificate := GetProcAddr(SSLLibHandle, 'SSL_get_peer_certificate'); _SslGetVersion := GetProcAddr(SSLLibHandle, 'SSL_get_version'); _SslCtxSetVerify := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_verify'); _SslGetCurrentCipher := GetProcAddr(SSLLibHandle, 'SSL_get_current_cipher'); _SslCipherGetName := GetProcAddr(SSLLibHandle, 'SSL_CIPHER_get_name'); _SslCipherGetBits := GetProcAddr(SSLLibHandle, 'SSL_CIPHER_get_bits'); _SslGetVerifyResult := GetProcAddr(SSLLibHandle, 'SSL_get_verify_result'); _X509New := GetProcAddr(SSLUtilHandle, 'X509_new'); _X509Free := GetProcAddr(SSLUtilHandle, 'X509_free'); _X509NameOneline := GetProcAddr(SSLUtilHandle, 'X509_NAME_oneline'); _X509GetSubjectName := GetProcAddr(SSLUtilHandle, 'X509_get_subject_name'); _X509GetIssuerName := GetProcAddr(SSLUtilHandle, 'X509_get_issuer_name'); _X509NameHash := GetProcAddr(SSLUtilHandle, 'X509_NAME_hash'); _X509Digest := GetProcAddr(SSLUtilHandle, 'X509_digest'); _X509print := GetProcAddr(SSLUtilHandle, 'X509_print'); _X509SetVersion := GetProcAddr(SSLUtilHandle, 'X509_set_version'); _X509SetPubkey := GetProcAddr(SSLUtilHandle, 'X509_set_pubkey'); _X509SetIssuerName := GetProcAddr(SSLUtilHandle, 'X509_set_issuer_name'); _X509NameAddEntryByTxt := GetProcAddr(SSLUtilHandle, 'X509_NAME_add_entry_by_txt'); _X509Sign := GetProcAddr(SSLUtilHandle, 'X509_sign'); _X509GmtimeAdj := GetProcAddr(SSLUtilHandle, 'X509_gmtime_adj'); _X509SetNotBefore := GetProcAddr(SSLUtilHandle, 'X509_set_notBefore'); _X509SetNotAfter := GetProcAddr(SSLUtilHandle, 'X509_set_notAfter'); _X509GetSerialNumber := GetProcAddr(SSLUtilHandle, 'X509_get_serialNumber'); _EvpPkeyNew := GetProcAddr(SSLUtilHandle, 'EVP_PKEY_new'); _EvpPkeyFree := GetProcAddr(SSLUtilHandle, 'EVP_PKEY_free'); _EvpPkeyAssign := GetProcAddr(SSLUtilHandle, 'EVP_PKEY_assign'); _EVPCleanup := GetProcAddr(SSLUtilHandle, 'EVP_cleanup'); _EvpGetDigestByName := GetProcAddr(SSLUtilHandle, 'EVP_get_digestbyname'); _SSLeayversion := GetProcAddr(SSLUtilHandle, 'SSLeay_version'); _ErrErrorString := GetProcAddr(SSLUtilHandle, 'ERR_error_string_n'); _ErrGetError := GetProcAddr(SSLUtilHandle, 'ERR_get_error'); _ErrClearError := GetProcAddr(SSLUtilHandle, 'ERR_clear_error'); _ErrFreeStrings := GetProcAddr(SSLUtilHandle, 'ERR_free_strings'); _ErrRemoveState := GetProcAddr(SSLUtilHandle, 'ERR_remove_state'); _OPENSSLaddallalgorithms := GetProcAddr(SSLUtilHandle, 'OPENSSL_add_all_algorithms_noconf'); _CRYPTOcleanupAllExData := GetProcAddr(SSLUtilHandle, 'CRYPTO_cleanup_all_ex_data'); _RandScreen := GetProcAddr(SSLUtilHandle, 'RAND_screen'); _BioNew := GetProcAddr(SSLUtilHandle, 'BIO_new'); _BioFreeAll := GetProcAddr(SSLUtilHandle, 'BIO_free_all'); _BioSMem := GetProcAddr(SSLUtilHandle, 'BIO_s_mem'); _BioCtrlPending := GetProcAddr(SSLUtilHandle, 'BIO_ctrl_pending'); _BioRead := GetProcAddr(SSLUtilHandle, 'BIO_read'); _BioWrite := GetProcAddr(SSLUtilHandle, 'BIO_write'); _d2iPKCS12bio := GetProcAddr(SSLUtilHandle, 'd2i_PKCS12_bio'); _PKCS12parse := GetProcAddr(SSLUtilHandle, 'PKCS12_parse'); _PKCS12free := GetProcAddr(SSLUtilHandle, 'PKCS12_free'); _RsaGenerateKey := GetProcAddr(SSLUtilHandle, 'RSA_generate_key'); _Asn1UtctimeNew := GetProcAddr(SSLUtilHandle, 'ASN1_UTCTIME_new'); _Asn1UtctimeFree := GetProcAddr(SSLUtilHandle, 'ASN1_UTCTIME_free'); _Asn1IntegerSet := GetProcAddr(SSLUtilHandle, 'ASN1_INTEGER_set'); _i2dX509bio := GetProcAddr(SSLUtilHandle, 'i2d_X509_bio'); _i2dPrivateKeyBio := GetProcAddr(SSLUtilHandle, 'i2d_PrivateKey_bio'); // 3DES functions _DESsetoddparity := GetProcAddr(SSLUtilHandle, 'DES_set_odd_parity'); _DESsetkeychecked := GetProcAddr(SSLUtilHandle, 'DES_set_key_checked'); _DESecbencrypt := GetProcAddr(SSLUtilHandle, 'DES_ecb_encrypt'); // _CRYPTOnumlocks := GetProcAddr(SSLUtilHandle, 'CRYPTO_num_locks'); _CRYPTOsetlockingcallback := GetProcAddr(SSLUtilHandle, 'CRYPTO_set_locking_callback'); {$ENDIF} {$IFDEF CIL} SslLibraryInit; SslLoadErrorStrings; OPENSSLaddallalgorithms; RandScreen; {$ELSE} SetLength(s, 1024); x := GetModuleFilename(SSLLibHandle,PChar(s),Length(s)); SetLength(s, x); SSLLibFile := s; SetLength(s, 1024); x := GetModuleFilename(SSLUtilHandle,PChar(s),Length(s)); SetLength(s, x); SSLUtilFile := s; //init library if assigned(_SslLibraryInit) then _SslLibraryInit; if assigned(_SslLoadErrorStrings) then _SslLoadErrorStrings; if assigned(_OPENSSLaddallalgorithms) then _OPENSSLaddallalgorithms; if assigned(_RandScreen) then _RandScreen; if assigned(_CRYPTOnumlocks) and assigned(_CRYPTOsetlockingcallback) then InitLocks; {$ENDIF} Result := True; SSLloaded := True; end else begin //load failed! if SSLLibHandle <> 0 then begin {$IFNDEF CIL} FreeLibrary(SSLLibHandle); {$ENDIF} SSLLibHandle := 0; end; if SSLUtilHandle <> 0 then begin {$IFNDEF CIL} FreeLibrary(SSLUtilHandle); {$ENDIF} SSLLibHandle := 0; end; Result := False; end; end else //loaded before... Result := true; finally SSLCS.Leave; end; end; function DestroySSLInterface: Boolean; begin SSLCS.Enter; try if IsSSLLoaded then begin //deinit library {$IFNDEF CIL} if assigned(_CRYPTOnumlocks) and assigned(_CRYPTOsetlockingcallback) then FreeLocks; {$ENDIF} EVPCleanup; CRYPTOcleanupAllExData; ErrRemoveState(0); end; SSLloaded := false; if SSLLibHandle <> 0 then begin {$IFNDEF CIL} FreeLibrary(SSLLibHandle); {$ENDIF} SSLLibHandle := 0; end; if SSLUtilHandle <> 0 then begin {$IFNDEF CIL} FreeLibrary(SSLUtilHandle); {$ENDIF} SSLLibHandle := 0; end; {$IFNDEF CIL} _SslGetError := nil; _SslLibraryInit := nil; _SslLoadErrorStrings := nil; _SslCtxSetCipherList := nil; _SslCtxNew := nil; _SslCtxFree := nil; _SslSetFd := nil; _SslMethodV2 := nil; _SslMethodV3 := nil; _SslMethodTLSV1 := nil; _SslMethodV23 := nil; _SslCtxUsePrivateKey := nil; _SslCtxUsePrivateKeyASN1 := nil; _SslCtxUsePrivateKeyFile := nil; _SslCtxUseCertificate := nil; _SslCtxUseCertificateASN1 := nil; _SslCtxUseCertificateFile := nil; _SslCtxUseCertificateChainFile := nil; _SslCtxCheckPrivateKeyFile := nil; _SslCtxSetDefaultPasswdCb := nil; _SslCtxSetDefaultPasswdCbUserdata := nil; _SslCtxLoadVerifyLocations := nil; _SslCtxCtrl := nil; _SslNew := nil; _SslFree := nil; _SslAccept := nil; _SslConnect := nil; _SslShutdown := nil; _SslRead := nil; _SslPeek := nil; _SslWrite := nil; _SslPending := nil; _SslGetPeerCertificate := nil; _SslGetVersion := nil; _SslCtxSetVerify := nil; _SslGetCurrentCipher := nil; _SslCipherGetName := nil; _SslCipherGetBits := nil; _SslGetVerifyResult := nil; _X509New := nil; _X509Free := nil; _X509NameOneline := nil; _X509GetSubjectName := nil; _X509GetIssuerName := nil; _X509NameHash := nil; _X509Digest := nil; _X509print := nil; _X509SetVersion := nil; _X509SetPubkey := nil; _X509SetIssuerName := nil; _X509NameAddEntryByTxt := nil; _X509Sign := nil; _X509GmtimeAdj := nil; _X509SetNotBefore := nil; _X509SetNotAfter := nil; _X509GetSerialNumber := nil; _EvpPkeyNew := nil; _EvpPkeyFree := nil; _EvpPkeyAssign := nil; _EVPCleanup := nil; _EvpGetDigestByName := nil; _SSLeayversion := nil; _ErrErrorString := nil; _ErrGetError := nil; _ErrClearError := nil; _ErrFreeStrings := nil; _ErrRemoveState := nil; _OPENSSLaddallalgorithms := nil; _CRYPTOcleanupAllExData := nil; _RandScreen := nil; _BioNew := nil; _BioFreeAll := nil; _BioSMem := nil; _BioCtrlPending := nil; _BioRead := nil; _BioWrite := nil; _d2iPKCS12bio := nil; _PKCS12parse := nil; _PKCS12free := nil; _RsaGenerateKey := nil; _Asn1UtctimeNew := nil; _Asn1UtctimeFree := nil; _Asn1IntegerSet := nil; _i2dX509bio := nil; _i2dPrivateKeyBio := nil; // 3DES functions _DESsetoddparity := nil; _DESsetkeychecked := nil; _DESecbencrypt := nil; // _CRYPTOnumlocks := nil; _CRYPTOsetlockingcallback := nil; {$ENDIF} finally SSLCS.Leave; end; Result := True; end; function IsSSLloaded: Boolean; begin Result := SSLLoaded; end; initialization begin SSLCS:= TCriticalSection.Create; end; finalization begin {$IFNDEF CIL} DestroySSLInterface; {$ENDIF} SSLCS.Free; end; end. TransGUI/synapse/source/lib/slogsend.pas0000644000000000000000000002332411366572451017301 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.002.003 | |==============================================================================| | Content: SysLog client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2001-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Christian Brosius | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(BSD SYSLOG protocol) Used RFC: RFC-3164 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$Q-} {$H+} unit slogsend; interface uses SysUtils, Classes, blcksock, synautil; const cSysLogProtocol = '514'; FCL_Kernel = 0; FCL_UserLevel = 1; FCL_MailSystem = 2; FCL_System = 3; FCL_Security = 4; FCL_Syslogd = 5; FCL_Printer = 6; FCL_News = 7; FCL_UUCP = 8; FCL_Clock = 9; FCL_Authorization = 10; FCL_FTP = 11; FCL_NTP = 12; FCL_LogAudit = 13; FCL_LogAlert = 14; FCL_Time = 15; FCL_Local0 = 16; FCL_Local1 = 17; FCL_Local2 = 18; FCL_Local3 = 19; FCL_Local4 = 20; FCL_Local5 = 21; FCL_Local6 = 22; FCL_Local7 = 23; type {:@abstract(Define possible priority of Syslog message)} TSyslogSeverity = (Emergency, Alert, Critical, Error, Warning, Notice, Info, Debug); {:@abstract(encoding or decoding of SYSLOG message)} TSyslogMessage = class(TObject) private FFacility:Byte; FSeverity:TSyslogSeverity; FDateTime:TDateTime; FTag:String; FMessage:String; FLocalIP:String; function GetPacketBuf:String; procedure SetPacketBuf(Value:String); public {:Reset values to defaults} procedure Clear; published {:Define facilicity of Syslog message. For specify you may use predefined FCL_* constants. Default is "FCL_Local0".} property Facility:Byte read FFacility write FFacility; {:Define possible priority of Syslog message. Default is "Debug".} property Severity:TSyslogSeverity read FSeverity write FSeverity; {:date and time of Syslog message} property DateTime:TDateTime read FDateTime write FDateTime; {:This is used for identify process of this message. Default is filename of your executable file.} property Tag:String read FTag write FTag; {:Text of your message for log.} property LogMessage:String read FMessage write FMessage; {:IP address of message sender.} property LocalIP:String read FLocalIP write FLocalIP; {:This property holds encoded binary SYSLOG packet} property PacketBuf:String read GetPacketBuf write SetPacketBuf; end; {:@abstract(This object implement BSD SysLog client) Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TSyslogSend = class(TSynaClient) private FSock: TUDPBlockSocket; FSysLogMessage: TSysLogMessage; public constructor Create; destructor Destroy; override; {:Send Syslog UDP packet defined by @link(SysLogMessage).} function DoIt: Boolean; published {:Syslog message for send} property SysLogMessage:TSysLogMessage read FSysLogMessage write FSysLogMessage; end; {:Simply send packet to specified Syslog server.} function ToSysLog(const SyslogServer: string; Facil: Byte; Sever: TSyslogSeverity; const Content: string): Boolean; implementation function TSyslogMessage.GetPacketBuf:String; begin Result := '<' + IntToStr((FFacility * 8) + Ord(FSeverity)) + '>'; Result := Result + CDateTime(FDateTime) + ' '; Result := Result + FLocalIP + ' '; Result := Result + FTag + ': ' + FMessage; end; procedure TSyslogMessage.SetPacketBuf(Value:String); var StrBuf:String; IntBuf,Pos:Integer; begin if Length(Value) < 1 then exit; Pos := 1; if Value[Pos] <> '<' then exit; Inc(Pos); // Facility and Severity StrBuf := ''; while (Value[Pos] <> '>')do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; IntBuf := StrToInt(StrBuf); FFacility := IntBuf div 8; case (IntBuf mod 8)of 0:FSeverity := Emergency; 1:FSeverity := Alert; 2:FSeverity := Critical; 3:FSeverity := Error; 4:FSeverity := Warning; 5:FSeverity := Notice; 6:FSeverity := Info; 7:FSeverity := Debug; end; // DateTime Inc(Pos); StrBuf := ''; // Month while (Value[Pos] <> ' ')do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; StrBuf := StrBuf + Value[Pos]; Inc(Pos); // Day while (Value[Pos] <> ' ')do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; StrBuf := StrBuf + Value[Pos]; Inc(Pos); // Time while (Value[Pos] <> ' ')do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; FDateTime := DecodeRFCDateTime(StrBuf); Inc(Pos); // LocalIP StrBuf := ''; while (Value[Pos] <> ' ')do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; FLocalIP := StrBuf; Inc(Pos); // Tag StrBuf := ''; while (Value[Pos] <> ':')do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; FTag := StrBuf; // LogMessage Inc(Pos); StrBuf := ''; while (Pos <= Length(Value))do begin StrBuf := StrBuf + Value[Pos]; Inc(Pos); end; FMessage := TrimSP(StrBuf); end; procedure TSysLogMessage.Clear; begin FFacility := FCL_Local0; FSeverity := Debug; FTag := ExtractFileName(ParamStr(0)); FMessage := ''; FLocalIP := '0.0.0.0'; end; //------------------------------------------------------------------------------ constructor TSyslogSend.Create; begin inherited Create; FSock := TUDPBlockSocket.Create; FSock.Owner := self; FSysLogMessage := TSysLogMessage.Create; FTargetPort := cSysLogProtocol; end; destructor TSyslogSend.Destroy; begin FSock.Free; FSysLogMessage.Free; inherited Destroy; end; function TSyslogSend.DoIt: Boolean; var L: TStringList; begin Result := False; L := TStringList.Create; try FSock.ResolveNameToIP(FSock.Localname, L); if L.Count < 1 then FSysLogMessage.LocalIP := '0.0.0.0' else FSysLogMessage.LocalIP := L[0]; finally L.Free; end; FSysLogMessage.DateTime := Now; if Length(FSysLogMessage.PacketBuf) <= 1024 then begin FSock.Connect(FTargetHost, FTargetPort); FSock.SendString(FSysLogMessage.PacketBuf); Result := FSock.LastError = 0; end; end; {==============================================================================} function ToSysLog(const SyslogServer: string; Facil: Byte; Sever: TSyslogSeverity; const Content: string): Boolean; begin with TSyslogSend.Create do try TargetHost :=SyslogServer; SysLogMessage.Facility := Facil; SysLogMessage.Severity := Sever; SysLogMessage.LogMessage := Content; Result := DoIt; finally Free; end; end; end. TransGUI/synapse/source/lib/smtpsend.pas0000644000000000000000000005766011366572451017332 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 003.005.001 | |==============================================================================| | Content: SMTP client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(SMTP client) Used RFC: RFC-1869, RFC-1870, RFC-1893, RFC-2034, RFC-2104, RFC-2195, RFC-2487, RFC-2554, RFC-2821 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit smtpsend; interface uses SysUtils, Classes, blcksock, synautil, synacode; const cSmtpProtocol = '25'; type {:@abstract(Implementation of SMTP and ESMTP procotol), include some ESMTP extensions, include SSL/TLS too. Note: Are you missing properties for setting Username and Password for ESMTP? Look to parent @link(TSynaClient) object! Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TSMTPSend = class(TSynaClient) private FSock: TTCPBlockSocket; FResultCode: Integer; FResultString: string; FFullResult: TStringList; FESMTPcap: TStringList; FESMTP: Boolean; FAuthDone: Boolean; FESMTPSize: Boolean; FMaxSize: Integer; FEnhCode1: Integer; FEnhCode2: Integer; FEnhCode3: Integer; FSystemName: string; FAutoTLS: Boolean; FFullSSL: Boolean; procedure EnhancedCode(const Value: string); function ReadResult: Integer; function AuthLogin: Boolean; function AuthCram: Boolean; function AuthPlain: Boolean; function Helo: Boolean; function Ehlo: Boolean; function Connect: Boolean; public constructor Create; destructor Destroy; override; {:Connects to SMTP server (defined in @link(TSynaClient.TargetHost)) and begin SMTP session. (First try ESMTP EHLO, next old HELO handshake). Parses ESMTP capabilites and if you specified Username and password and remote server can handle AUTH command, try login by AUTH command. Preffered login method is CRAM-MD5 (if safer!). If all OK, result is @true, else result is @false.} function Login: Boolean; {:Close SMTP session (QUIT command) and disconnect from SMTP server.} function Logout: Boolean; {:Send RSET SMTP command for reset SMTP session. If all OK, result is @true, else result is @false.} function Reset: Boolean; {:Send NOOP SMTP command for keep SMTP session. If all OK, result is @true, else result is @false.} function NoOp: Boolean; {:Send MAIL FROM SMTP command for set sender e-mail address. If sender's e-mail address is empty string, transmited message is error message. If size not 0 and remote server can handle SIZE parameter, append SIZE parameter to request. If all OK, result is @true, else result is @false.} function MailFrom(const Value: string; Size: Integer): Boolean; {:Send RCPT TO SMTP command for set receiver e-mail address. It cannot be an empty string. If all OK, result is @true, else result is @false.} function MailTo(const Value: string): Boolean; {:Send DATA SMTP command and transmit message data. If all OK, result is @true, else result is @false.} function MailData(const Value: Tstrings): Boolean; {:Send ETRN SMTP command for start sending of remote queue for domain in Value. If all OK, result is @true, else result is @false.} function Etrn(const Value: string): Boolean; {:Send VRFY SMTP command for check receiver e-mail address. It cannot be an empty string. If all OK, result is @true, else result is @false.} function Verify(const Value: string): Boolean; {:Call STARTTLS command for upgrade connection to SSL/TLS mode.} function StartTLS: Boolean; {:Return string descriptive text for enhanced result codes stored in @link(EnhCode1), @link(EnhCode2) and @link(EnhCode3).} function EnhCodeString: string; {:Try to find specified capability in ESMTP response.} function FindCap(const Value: string): string; published {:result code of last SMTP command.} property ResultCode: Integer read FResultCode; {:result string of last SMTP command (begin with string representation of result code).} property ResultString: string read FResultString; {:All result strings of last SMTP command (result is maybe multiline!).} property FullResult: TStringList read FFullResult; {:List of ESMTP capabilites of remote ESMTP server. (If you connect to ESMTP server only!).} property ESMTPcap: TStringList read FESMTPcap; {:@TRUE if you successfuly logged to ESMTP server.} property ESMTP: Boolean read FESMTP; {:@TRUE if you successfuly pass authorisation to remote server.} property AuthDone: Boolean read FAuthDone; {:@TRUE if remote server can handle SIZE parameter.} property ESMTPSize: Boolean read FESMTPSize; {:When @link(ESMTPsize) is @TRUE, contains max length of message that remote server can handle.} property MaxSize: Integer read FMaxSize; {:First digit of Enhanced result code. If last operation does not have enhanced result code, values is 0.} property EnhCode1: Integer read FEnhCode1; {:Second digit of Enhanced result code. If last operation does not have enhanced result code, values is 0.} property EnhCode2: Integer read FEnhCode2; {:Third digit of Enhanced result code. If last operation does not have enhanced result code, values is 0.} property EnhCode3: Integer read FEnhCode3; {:name of our system used in HELO and EHLO command. Implicit value is internet address of your machine.} property SystemName: string read FSystemName Write FSystemName; {:If is set to true, then upgrade to SSL/TLS mode if remote server support it.} property AutoTLS: Boolean read FAutoTLS Write FAutoTLS; {:SSL/TLS mode is used from first contact to server. Servers with full SSL/TLS mode usualy using non-standard TCP port!} property FullSSL: Boolean read FFullSSL Write FFullSSL; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; end; {:A very useful function and example of its use would be found in the TSMTPsend object. Send maildata (text of e-mail with all SMTP headers! For example when text of message is created by @link(TMimemess) object) from "MailFrom" e-mail address to "MailTo" e-mail address (If you need more then one receiver, then separate their addresses by comma). Function sends e-mail to a SMTP server defined in "SMTPhost" parameter. Username and password are used for authorization to the "SMTPhost". If you don't want authorization, set "Username" and "Password" to empty strings. If e-mail message is successfully sent, the result returns @true. If you need use different port number then standard, then add this port number to SMTPhost after colon. (i.e. '127.0.0.1:1025')} function SendToRaw(const MailFrom, MailTo, SMTPHost: string; const MailData: TStrings; const Username, Password: string): Boolean; {:A very useful function and example of its use would be found in the TSMTPsend object. Send "Maildata" (text of e-mail without any SMTP headers!) from "MailFrom" e-mail address to "MailTo" e-mail address with "Subject". (If you need more then one receiver, then separate their addresses by comma). This function constructs all needed SMTP headers (with DATE header) and sends the e-mail to the SMTP server defined in the "SMTPhost" parameter. If the e-mail message is successfully sent, the result will be @TRUE. If you need use different port number then standard, then add this port number to SMTPhost after colon. (i.e. '127.0.0.1:1025')} function SendTo(const MailFrom, MailTo, Subject, SMTPHost: string; const MailData: TStrings): Boolean; {:A very useful function and example of its use would be found in the TSMTPsend object. Sends "MailData" (text of e-mail without any SMTP headers!) from "MailFrom" e-mail address to "MailTo" e-mail address (If you need more then one receiver, then separate their addresses by comma). This function sends the e-mail to the SMTP server defined in the "SMTPhost" parameter. Username and password are used for authorization to the "SMTPhost". If you dont want authorization, set "Username" and "Password" to empty Strings. If the e-mail message is successfully sent, the result will be @TRUE. If you need use different port number then standard, then add this port number to SMTPhost after colon. (i.e. '127.0.0.1:1025')} function SendToEx(const MailFrom, MailTo, Subject, SMTPHost: string; const MailData: TStrings; const Username, Password: string): Boolean; implementation constructor TSMTPSend.Create; begin inherited Create; FFullResult := TStringList.Create; FESMTPcap := TStringList.Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.ConvertLineEnd := true; FTimeout := 60000; FTargetPort := cSmtpProtocol; FSystemName := FSock.LocalName; FAutoTLS := False; FFullSSL := False; end; destructor TSMTPSend.Destroy; begin FSock.Free; FESMTPcap.Free; FFullResult.Free; inherited Destroy; end; procedure TSMTPSend.EnhancedCode(const Value: string); var s, t: string; e1, e2, e3: Integer; begin FEnhCode1 := 0; FEnhCode2 := 0; FEnhCode3 := 0; s := Copy(Value, 5, Length(Value) - 4); t := Trim(SeparateLeft(s, '.')); s := Trim(SeparateRight(s, '.')); if t = '' then Exit; if Length(t) > 1 then Exit; e1 := StrToIntDef(t, 0); if e1 = 0 then Exit; t := Trim(SeparateLeft(s, '.')); s := Trim(SeparateRight(s, '.')); if t = '' then Exit; if Length(t) > 3 then Exit; e2 := StrToIntDef(t, 0); t := Trim(SeparateLeft(s, ' ')); if t = '' then Exit; if Length(t) > 3 then Exit; e3 := StrToIntDef(t, 0); FEnhCode1 := e1; FEnhCode2 := e2; FEnhCode3 := e3; end; function TSMTPSend.ReadResult: Integer; var s: String; begin Result := 0; FFullResult.Clear; repeat s := FSock.RecvString(FTimeout); FResultString := s; FFullResult.Add(s); if FSock.LastError <> 0 then Break; until Pos('-', s) <> 4; s := FFullResult[0]; if Length(s) >= 3 then Result := StrToIntDef(Copy(s, 1, 3), 0); FResultCode := Result; EnhancedCode(s); end; function TSMTPSend.AuthLogin: Boolean; begin Result := False; FSock.SendString('AUTH LOGIN' + CRLF); if ReadResult <> 334 then Exit; FSock.SendString(EncodeBase64(FUsername) + CRLF); if ReadResult <> 334 then Exit; FSock.SendString(EncodeBase64(FPassword) + CRLF); Result := ReadResult = 235; end; function TSMTPSend.AuthCram: Boolean; var s: ansistring; begin Result := False; FSock.SendString('AUTH CRAM-MD5' + CRLF); if ReadResult <> 334 then Exit; s := Copy(FResultString, 5, Length(FResultString) - 4); s := DecodeBase64(s); s := HMAC_MD5(s, FPassword); s := FUsername + ' ' + StrToHex(s); FSock.SendString(EncodeBase64(s) + CRLF); Result := ReadResult = 235; end; function TSMTPSend.AuthPlain: Boolean; var s: ansistring; begin Result := False; s := ansichar(0) + FUsername + ansichar(0) + FPassword; FSock.SendString('AUTH PLAIN ' + EncodeBase64(s) + CRLF); Result := ReadResult = 235; end; function TSMTPSend.Connect: Boolean; begin FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError = 0 then FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError = 0 then if FFullSSL then FSock.SSLDoConnect; Result := FSock.LastError = 0; end; function TSMTPSend.Helo: Boolean; var x: Integer; begin FSock.SendString('HELO ' + FSystemName + CRLF); x := ReadResult; Result := (x >= 250) and (x <= 259); end; function TSMTPSend.Ehlo: Boolean; var x: Integer; begin FSock.SendString('EHLO ' + FSystemName + CRLF); x := ReadResult; Result := (x >= 250) and (x <= 259); end; function TSMTPSend.Login: Boolean; var n: Integer; auths: string; s: string; begin Result := False; FESMTP := True; FAuthDone := False; FESMTPcap.clear; FESMTPSize := False; FMaxSize := 0; if not Connect then Exit; if ReadResult <> 220 then Exit; if not Ehlo then begin FESMTP := False; if not Helo then Exit; end; Result := True; if FESMTP then begin for n := 1 to FFullResult.Count - 1 do FESMTPcap.Add(Copy(FFullResult[n], 5, Length(FFullResult[n]) - 4)); if (not FullSSL) and FAutoTLS and (FindCap('STARTTLS') <> '') then if StartTLS then begin Ehlo; FESMTPcap.Clear; for n := 1 to FFullResult.Count - 1 do FESMTPcap.Add(Copy(FFullResult[n], 5, Length(FFullResult[n]) - 4)); end else begin Result := False; Exit; end; if not ((FUsername = '') and (FPassword = '')) then begin s := FindCap('AUTH '); if s = '' then s := FindCap('AUTH='); auths := UpperCase(s); if s <> '' then begin if Pos('CRAM-MD5', auths) > 0 then FAuthDone := AuthCram; if (not FauthDone) and (Pos('PLAIN', auths) > 0) then FAuthDone := AuthPlain; if (not FauthDone) and (Pos('LOGIN', auths) > 0) then FAuthDone := AuthLogin; end; end; s := FindCap('SIZE'); if s <> '' then begin FESMTPsize := True; FMaxSize := StrToIntDef(Copy(s, 6, Length(s) - 5), 0); end; end; end; function TSMTPSend.Logout: Boolean; begin FSock.SendString('QUIT' + CRLF); Result := ReadResult = 221; FSock.CloseSocket; end; function TSMTPSend.Reset: Boolean; begin FSock.SendString('RSET' + CRLF); Result := ReadResult div 100 = 2; end; function TSMTPSend.NoOp: Boolean; begin FSock.SendString('NOOP' + CRLF); Result := ReadResult div 100 = 2; end; function TSMTPSend.MailFrom(const Value: string; Size: Integer): Boolean; var s: string; begin s := 'MAIL FROM:<' + Value + '>'; if FESMTPsize and (Size > 0) then s := s + ' SIZE=' + IntToStr(Size); FSock.SendString(s + CRLF); Result := ReadResult div 100 = 2; end; function TSMTPSend.MailTo(const Value: string): Boolean; begin FSock.SendString('RCPT TO:<' + Value + '>' + CRLF); Result := ReadResult div 100 = 2; end; function TSMTPSend.MailData(const Value: TStrings): Boolean; var n: Integer; s: string; t: string; x: integer; begin Result := False; FSock.SendString('DATA' + CRLF); if ReadResult <> 354 then Exit; t := ''; x := 1500; for n := 0 to Value.Count - 1 do begin s := Value[n]; if Length(s) >= 1 then if s[1] = '.' then s := '.' + s; if Length(t) + Length(s) >= x then begin FSock.SendString(t); t := ''; end; t := t + s + CRLF; end; if t <> '' then FSock.SendString(t); FSock.SendString('.' + CRLF); Result := ReadResult div 100 = 2; end; function TSMTPSend.Etrn(const Value: string): Boolean; var x: Integer; begin FSock.SendString('ETRN ' + Value + CRLF); x := ReadResult; Result := (x >= 250) and (x <= 259); end; function TSMTPSend.Verify(const Value: string): Boolean; var x: Integer; begin FSock.SendString('VRFY ' + Value + CRLF); x := ReadResult; Result := (x >= 250) and (x <= 259); end; function TSMTPSend.StartTLS: Boolean; begin Result := False; if FindCap('STARTTLS') <> '' then begin FSock.SendString('STARTTLS' + CRLF); if (ReadResult = 220) and (FSock.LastError = 0) then begin Fsock.SSLDoConnect; Result := FSock.LastError = 0; end; end; end; function TSMTPSend.EnhCodeString: string; var s, t: string; begin s := IntToStr(FEnhCode2) + '.' + IntToStr(FEnhCode3); t := ''; if s = '0.0' then t := 'Other undefined Status'; if s = '1.0' then t := 'Other address status'; if s = '1.1' then t := 'Bad destination mailbox address'; if s = '1.2' then t := 'Bad destination system address'; if s = '1.3' then t := 'Bad destination mailbox address syntax'; if s = '1.4' then t := 'Destination mailbox address ambiguous'; if s = '1.5' then t := 'Destination mailbox address valid'; if s = '1.6' then t := 'Mailbox has moved'; if s = '1.7' then t := 'Bad sender''s mailbox address syntax'; if s = '1.8' then t := 'Bad sender''s system address'; if s = '2.0' then t := 'Other or undefined mailbox status'; if s = '2.1' then t := 'Mailbox disabled, not accepting messages'; if s = '2.2' then t := 'Mailbox full'; if s = '2.3' then t := 'Message Length exceeds administrative limit'; if s = '2.4' then t := 'Mailing list expansion problem'; if s = '3.0' then t := 'Other or undefined mail system status'; if s = '3.1' then t := 'Mail system full'; if s = '3.2' then t := 'System not accepting network messages'; if s = '3.3' then t := 'System not capable of selected features'; if s = '3.4' then t := 'Message too big for system'; if s = '3.5' then t := 'System incorrectly configured'; if s = '4.0' then t := 'Other or undefined network or routing status'; if s = '4.1' then t := 'No answer from host'; if s = '4.2' then t := 'Bad connection'; if s = '4.3' then t := 'Routing server failure'; if s = '4.4' then t := 'Unable to route'; if s = '4.5' then t := 'Network congestion'; if s = '4.6' then t := 'Routing loop detected'; if s = '4.7' then t := 'Delivery time expired'; if s = '5.0' then t := 'Other or undefined protocol status'; if s = '5.1' then t := 'Invalid command'; if s = '5.2' then t := 'Syntax error'; if s = '5.3' then t := 'Too many recipients'; if s = '5.4' then t := 'Invalid command arguments'; if s = '5.5' then t := 'Wrong protocol version'; if s = '6.0' then t := 'Other or undefined media error'; if s = '6.1' then t := 'Media not supported'; if s = '6.2' then t := 'Conversion required and prohibited'; if s = '6.3' then t := 'Conversion required but not supported'; if s = '6.4' then t := 'Conversion with loss performed'; if s = '6.5' then t := 'Conversion failed'; if s = '7.0' then t := 'Other or undefined security status'; if s = '7.1' then t := 'Delivery not authorized, message refused'; if s = '7.2' then t := 'Mailing list expansion prohibited'; if s = '7.3' then t := 'Security conversion required but not possible'; if s = '7.4' then t := 'Security features not supported'; if s = '7.5' then t := 'Cryptographic failure'; if s = '7.6' then t := 'Cryptographic algorithm not supported'; if s = '7.7' then t := 'Message integrity failure'; s := '???-'; if FEnhCode1 = 2 then s := 'Success-'; if FEnhCode1 = 4 then s := 'Persistent Transient Failure-'; if FEnhCode1 = 5 then s := 'Permanent Failure-'; Result := s + t; end; function TSMTPSend.FindCap(const Value: string): string; var n: Integer; s: string; begin s := UpperCase(Value); Result := ''; for n := 0 to FESMTPcap.Count - 1 do if Pos(s, UpperCase(FESMTPcap[n])) = 1 then begin Result := FESMTPcap[n]; Break; end; end; {==============================================================================} function SendToRaw(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 // if you need SOCKS5 support, uncomment next lines: // SMTP.Sock.SocksIP := '127.0.0.1'; // SMTP.Sock.SocksPort := '1080'; // 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.TargetHost := Trim(SeparateLeft(SMTPHost, ':')); s := Trim(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(Trim(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; function SendToEx(const MailFrom, MailTo, Subject, SMTPHost: string; const MailData: TStrings; const Username, Password: string): Boolean; var t: TStrings; begin t := TStringList.Create; try t.Assign(MailData); t.Insert(0, ''); t.Insert(0, 'X-mailer: Synapse - Delphi & Kylix TCP/IP library by Lukas Gebauer'); t.Insert(0, 'Subject: ' + Subject); t.Insert(0, 'Date: ' + Rfc822DateTime(now)); t.Insert(0, 'To: ' + MailTo); t.Insert(0, 'From: ' + MailFrom); Result := SendToRaw(MailFrom, MailTo, SMTPHost, t, Username, Password); finally t.Free; end; end; function SendTo(const MailFrom, MailTo, Subject, SMTPHost: string; const MailData: TStrings): Boolean; begin Result := SendToEx(MailFrom, MailTo, Subject, SMTPHost, MailData, '', ''); end; end. TransGUI/synapse/source/lib/imapsend.pas0000644000000000000000000006216511366572451017271 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 002.005.002 | |==============================================================================| | Content: IMAP4rev1 client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2001-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(IMAP4 rev1 protocol client) Used RFC: RFC-2060, RFC-2595 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit imapsend; interface uses SysUtils, Classes, blcksock, synautil; const cIMAPProtocol = '143'; type {:@abstract(Implementation of IMAP4 protocol.) Note: Are you missing properties for setting Username and Password? Look to parent @link(TSynaClient) object! Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TIMAPSend = class(TSynaClient) protected FSock: TTCPBlockSocket; FTagCommand: integer; FResultString: string; FFullResult: TStringList; FIMAPcap: TStringList; FAuthDone: Boolean; FSelectedFolder: string; FSelectedCount: integer; FSelectedRecent: integer; FSelectedUIDvalidity: integer; FUID: Boolean; FAutoTLS: Boolean; FFullSSL: Boolean; function ReadResult: string; function AuthLogin: Boolean; function Connect: Boolean; procedure ParseMess(Value:TStrings); procedure ParseFolderList(Value:TStrings); procedure ParseSelect; procedure ParseSearch(Value:TStrings); procedure ProcessLiterals; public constructor Create; destructor Destroy; override; {:By this function you can call any IMAP command. Result of this command is in adequate properties.} function IMAPcommand(Value: string): string; {:By this function you can call any IMAP command what need upload any data. Result of this command is in adequate properties.} function IMAPuploadCommand(Value: string; const Data:TStrings): string; {:Call CAPABILITY command and fill IMAPcap property by new values.} function Capability: Boolean; {:Connect to IMAP server and do login to this server. This command begin session.} function Login: Boolean; {:Disconnect from IMAP server and terminate session session. If exists some deleted and non-purged messages, these messages are not deleted!} function Logout: Boolean; {:Do NOOP. It is for prevent disconnect by timeout.} function NoOp: Boolean; {:Lists folder names. You may specify level of listing. If you specify FromFolder as empty string, return is all folders in system.} function List(FromFolder: string; const FolderList: TStrings): Boolean; {:Lists folder names what match search criteria. You may specify level of listing. If you specify FromFolder as empty string, return is all folders in system.} function ListSearch(FromFolder, Search: string; const FolderList: TStrings): Boolean; {:Lists subscribed folder names. You may specify level of listing. If you specify FromFolder as empty string, return is all subscribed folders in system.} function ListSubscribed(FromFolder: string; const FolderList: TStrings): Boolean; {:Lists subscribed folder names what matching search criteria. You may specify level of listing. If you specify FromFolder as empty string, return is all subscribed folders in system.} function ListSearchSubscribed(FromFolder, Search: string; const FolderList: TStrings): Boolean; {:Create a new folder.} function CreateFolder(FolderName: string): Boolean; {:Delete a folder.} function DeleteFolder(FolderName: string): Boolean; {:Rename folder names.} function RenameFolder(FolderName, NewFolderName: string): Boolean; {:Subscribe folder.} function SubscribeFolder(FolderName: string): Boolean; {:Unsubscribe folder.} function UnsubscribeFolder(FolderName: string): Boolean; {:Select folder.} function SelectFolder(FolderName: string): Boolean; {:Select folder, but only for reading. Any changes are not allowed!} function SelectROFolder(FolderName: string): Boolean; {:Close a folder. (end of Selected state)} function CloseFolder: Boolean; {:Ask for given status of folder. I.e. if you specify as value 'UNSEEN', result is number of unseen messages in folder. For another status indentificator check IMAP documentation and documentation of your IMAP server (each IMAP server can have their own statuses.)} function StatusFolder(FolderName, Value: string): integer; {:Hardly delete all messages marked as 'deleted' in current selected folder.} function ExpungeFolder: Boolean; {:Touch to folder. (use as update status of folder, etc.)} function CheckFolder: Boolean; {:Append given message to specified folder.} function AppendMess(ToFolder: string; const Mess: TStrings): Boolean; {:'Delete' message from current selected folder. It mark message as Deleted. Real deleting will be done after sucessfull @link(CloseFolder) or @link(ExpungeFolder)} function DeleteMess(MessID: integer): boolean; {:Get full message from specified message in selected folder.} function FetchMess(MessID: integer; const Mess: TStrings): Boolean; {:Get message headers only from specified message in selected folder.} function FetchHeader(MessID: integer; const Headers: TStrings): Boolean; {:Return message size of specified message from current selected folder.} function MessageSize(MessID: integer): integer; {:Copy message from current selected folder to another folder.} function CopyMess(MessID: integer; ToFolder: string): Boolean; {:Return message numbers from currently selected folder as result of searching. Search criteria is very complex language (see to IMAP specification) similar to SQL (but not same syntax!).} function SearchMess(Criteria: string; const FoundMess: TStrings): Boolean; {:Sets flags of message from current selected folder.} function SetFlagsMess(MessID: integer; Flags: string): Boolean; {:Gets flags of message from current selected folder.} function GetFlagsMess(MessID: integer; var Flags: string): Boolean; {:Add flags to message's flags.} function AddFlagsMess(MessID: integer; Flags: string): Boolean; {:Remove flags from message's flags.} function DelFlagsMess(MessID: integer; Flags: string): Boolean; {:Call STARTTLS command for upgrade connection to SSL/TLS mode.} function StartTLS: Boolean; {:return UID of requested message ID.} function GetUID(MessID: integer; var UID : Integer): Boolean; {:Try to find given capabily in capabilty string returned from IMAP server.} function FindCap(const Value: string): string; published {:Status line with result of last operation.} property ResultString: string read FResultString; {:Full result of last IMAP operation.} property FullResult: TStringList read FFullResult; {:List of server capabilites.} property IMAPcap: TStringList read FIMAPcap; {:Authorization is successful done.} property AuthDone: Boolean read FAuthDone; {:Turn on or off usage of UID (unicate identificator) of messages instead only sequence numbers.} property UID: Boolean read FUID Write FUID; {:Name of currently selected folder.} property SelectedFolder: string read FSelectedFolder; {:Count of messages in currently selected folder.} property SelectedCount: integer read FSelectedCount; {:Count of not-visited messages in currently selected folder.} property SelectedRecent: integer read FSelectedRecent; {:This number with name of folder is unique indentificator of folder. (If someone delete folder and next create new folder with exactly same name of folder, this number is must be different!)} property SelectedUIDvalidity: integer read FSelectedUIDvalidity; {:If is set to true, then upgrade to SSL/TLS mode if remote server support it.} property AutoTLS: Boolean read FAutoTLS Write FAutoTLS; {:SSL/TLS mode is used from first contact to server. Servers with full SSL/TLS mode usualy using non-standard TCP port!} property FullSSL: Boolean read FFullSSL Write FFullSSL; {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; end; implementation constructor TIMAPSend.Create; begin inherited Create; FFullResult := TStringList.Create; FIMAPcap := TStringList.Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.ConvertLineEnd := True; FSock.SizeRecvBuffer := 32768; FSock.SizeSendBuffer := 32768; FTimeout := 60000; FTargetPort := cIMAPProtocol; FTagCommand := 0; FSelectedFolder := ''; FSelectedCount := 0; FSelectedRecent := 0; FSelectedUIDvalidity := 0; FUID := False; FAutoTLS := False; FFullSSL := False; end; destructor TIMAPSend.Destroy; begin FSock.Free; FIMAPcap.Free; FFullResult.Free; inherited Destroy; end; function TIMAPSend.ReadResult: string; var s: string; x, l: integer; begin Result := ''; FFullResult.Clear; FResultString := ''; repeat s := FSock.RecvString(FTimeout); if Pos('S' + IntToStr(FTagCommand) + ' ', s) = 1 then begin FResultString := s; break; end else FFullResult.Add(s); if (s <> '') and (s[Length(s)]='}') then begin s := Copy(s, 1, Length(s) - 1); x := RPos('{', s); s := Copy(s, x + 1, Length(s) - x); l := StrToIntDef(s, -1); if l <> -1 then begin s := FSock.RecvBufferStr(l, FTimeout); FFullResult.Add(s); end; end; until FSock.LastError <> 0; s := Trim(separateright(FResultString, ' ')); Result:=uppercase(Trim(separateleft(s, ' '))); end; procedure TIMAPSend.ProcessLiterals; var l: TStringList; n, x: integer; b: integer; s: string; begin l := TStringList.Create; try l.Assign(FFullResult); FFullResult.Clear; b := 0; for n := 0 to l.Count - 1 do begin s := l[n]; if b > 0 then begin FFullResult[FFullresult.Count - 1] := FFullResult[FFullresult.Count - 1] + s; inc(b); if b > 2 then b := 0; end else begin if (s <> '') and (s[Length(s)]='}') then begin x := RPos('{', s); Delete(s, x, Length(s) - x + 1); b := 1; end else b := 0; FFullResult.Add(s); end; end; finally l.Free; end; end; function TIMAPSend.IMAPcommand(Value: string): string; begin Inc(FTagCommand); FSock.SendString('S' + IntToStr(FTagCommand) + ' ' + Value + CRLF); Result := ReadResult; end; function TIMAPSend.IMAPuploadCommand(Value: string; const Data:TStrings): string; var l: integer; begin Inc(FTagCommand); l := Length(Data.Text); FSock.SendString('S' + IntToStr(FTagCommand) + ' ' + Value + ' {'+ IntToStr(l) + '}' + CRLF); FSock.RecvString(FTimeout); FSock.SendString(Data.Text + CRLF); Result := ReadResult; end; procedure TIMAPSend.ParseMess(Value:TStrings); var n: integer; begin Value.Clear; for n := 0 to FFullResult.Count - 2 do if FFullResult[n][Length(FFullResult[n])] = '}' then begin Value.Text := FFullResult[n + 1]; Break; end; end; procedure TIMAPSend.ParseFolderList(Value:TStrings); var n, x: integer; s: string; begin ProcessLiterals; Value.Clear; for n := 0 to FFullResult.Count - 1 do begin s := FFullResult[n]; if (s <> '') and (Pos('\NOSELECT', UpperCase(s)) = 0) then begin if s[Length(s)] = '"' then begin Delete(s, Length(s), 1); x := RPos('"', s); end else x := RPos(' ', s); if (x > 0) then Value.Add(Copy(s, x + 1, Length(s) - x)); end; end; end; procedure TIMAPSend.ParseSelect; var n: integer; s, t: string; begin ProcessLiterals; FSelectedCount := 0; FSelectedRecent := 0; FSelectedUIDvalidity := 0; for n := 0 to FFullResult.Count - 1 do begin s := uppercase(FFullResult[n]); if Pos(' EXISTS', s) > 0 then begin t := Trim(separateleft(s, ' EXISTS')); t := Trim(separateright(t, '* ')); FSelectedCount := StrToIntDef(t, 0); end; if Pos(' RECENT', s) > 0 then begin t := Trim(separateleft(s, ' RECENT')); t := Trim(separateright(t, '* ')); FSelectedRecent := StrToIntDef(t, 0); end; if Pos('UIDVALIDITY', s) > 0 then begin t := Trim(separateright(s, 'UIDVALIDITY ')); t := Trim(separateleft(t, ']')); FSelectedUIDvalidity := StrToIntDef(t, 0); end; end; end; procedure TIMAPSend.ParseSearch(Value:TStrings); var n: integer; s: string; begin ProcessLiterals; Value.Clear; for n := 0 to FFullResult.Count - 1 do begin s := uppercase(FFullResult[n]); if Pos('* SEARCH', s) = 1 then begin s := Trim(SeparateRight(s, '* SEARCH')); while s <> '' do Value.Add(Fetch(s, ' ')); end; end; end; function TIMAPSend.FindCap(const Value: string): string; var n: Integer; s: string; begin s := UpperCase(Value); Result := ''; for n := 0 to FIMAPcap.Count - 1 do if Pos(s, UpperCase(FIMAPcap[n])) = 1 then begin Result := FIMAPcap[n]; Break; end; end; function TIMAPSend.AuthLogin: Boolean; begin Result := IMAPcommand('LOGIN "' + FUsername + '" "' + FPassword + '"') = 'OK'; end; function TIMAPSend.Connect: Boolean; begin FSock.CloseSocket; FSock.Bind(FIPInterface, cAnyPort); if FSock.LastError = 0 then FSock.Connect(FTargetHost, FTargetPort); if FSock.LastError = 0 then if FFullSSL then FSock.SSLDoConnect; Result := FSock.LastError = 0; end; function TIMAPSend.Capability: Boolean; var n: Integer; s, t: string; begin Result := False; FIMAPcap.Clear; s := IMAPcommand('CAPABILITY'); if s = 'OK' then begin ProcessLiterals; for n := 0 to FFullResult.Count - 1 do if Pos('* CAPABILITY ', FFullResult[n]) = 1 then begin s := Trim(SeparateRight(FFullResult[n], '* CAPABILITY ')); while not (s = '') do begin t := Trim(separateleft(s, ' ')); s := Trim(separateright(s, ' ')); if s = t then s := ''; FIMAPcap.Add(t); end; end; Result := True; end; end; function TIMAPSend.Login: Boolean; var s: string; begin FSelectedFolder := ''; FSelectedCount := 0; FSelectedRecent := 0; FSelectedUIDvalidity := 0; Result := False; FAuthDone := False; if not Connect then Exit; s := FSock.RecvString(FTimeout); if Pos('* PREAUTH', s) = 1 then FAuthDone := True else if Pos('* OK', s) = 1 then FAuthDone := False else Exit; if Capability then begin if Findcap('IMAP4rev1') = '' then Exit; if FAutoTLS and (Findcap('STARTTLS') <> '') then if StartTLS then Capability; end; Result := AuthLogin; end; function TIMAPSend.Logout: Boolean; begin Result := IMAPcommand('LOGOUT') = 'OK'; FSelectedFolder := ''; FSock.CloseSocket; end; function TIMAPSend.NoOp: Boolean; begin Result := IMAPcommand('NOOP') = 'OK'; end; function TIMAPSend.List(FromFolder: string; const FolderList: TStrings): Boolean; begin Result := IMAPcommand('LIST "' + FromFolder + '" *') = 'OK'; ParseFolderList(FolderList); end; function TIMAPSend.ListSearch(FromFolder, Search: string; const FolderList: TStrings): Boolean; begin Result := IMAPcommand('LIST "' + FromFolder + '" "' + Search +'"') = 'OK'; ParseFolderList(FolderList); end; function TIMAPSend.ListSubscribed(FromFolder: string; const FolderList: TStrings): Boolean; begin Result := IMAPcommand('LSUB "' + FromFolder + '" *') = 'OK'; ParseFolderList(FolderList); end; function TIMAPSend.ListSearchSubscribed(FromFolder, Search: string; const FolderList: TStrings): Boolean; begin Result := IMAPcommand('LSUB "' + FromFolder + '" "' + Search +'"') = 'OK'; ParseFolderList(FolderList); end; function TIMAPSend.CreateFolder(FolderName: string): Boolean; begin Result := IMAPcommand('CREATE "' + FolderName + '"') = 'OK'; end; function TIMAPSend.DeleteFolder(FolderName: string): Boolean; begin Result := IMAPcommand('DELETE "' + FolderName + '"') = 'OK'; end; function TIMAPSend.RenameFolder(FolderName, NewFolderName: string): Boolean; begin Result := IMAPcommand('RENAME "' + FolderName + '" "' + NewFolderName + '"') = 'OK'; end; function TIMAPSend.SubscribeFolder(FolderName: string): Boolean; begin Result := IMAPcommand('SUBSCRIBE "' + FolderName + '"') = 'OK'; end; function TIMAPSend.UnsubscribeFolder(FolderName: string): Boolean; begin Result := IMAPcommand('UNSUBSCRIBE "' + FolderName + '"') = 'OK'; end; function TIMAPSend.SelectFolder(FolderName: string): Boolean; begin Result := IMAPcommand('SELECT "' + FolderName + '"') = 'OK'; FSelectedFolder := FolderName; ParseSelect; end; function TIMAPSend.SelectROFolder(FolderName: string): Boolean; begin Result := IMAPcommand('EXAMINE "' + FolderName + '"') = 'OK'; FSelectedFolder := FolderName; ParseSelect; end; function TIMAPSend.CloseFolder: Boolean; begin Result := IMAPcommand('CLOSE') = 'OK'; FSelectedFolder := ''; end; function TIMAPSend.StatusFolder(FolderName, Value: string): integer; var n: integer; s, t: string; begin Result := -1; Value := Uppercase(Value); if IMAPcommand('STATUS "' + FolderName + '" (' + Value + ')' ) = 'OK' then begin ProcessLiterals; for n := 0 to FFullResult.Count - 1 do begin s := FFullResult[n]; // s := UpperCase(FFullResult[n]); if (Pos('* ', s) = 1) and (Pos(FolderName, s) >= 1) and (Pos(Value, s) > 0 ) then begin t := SeparateRight(s, Value); t := SeparateLeft(t, ')'); t := trim(t); Result := StrToIntDef(t, -1); Break; end; end; end; end; function TIMAPSend.ExpungeFolder: Boolean; begin Result := IMAPcommand('EXPUNGE') = 'OK'; end; function TIMAPSend.CheckFolder: Boolean; begin Result := IMAPcommand('CHECK') = 'OK'; end; function TIMAPSend.AppendMess(ToFolder: string; const Mess: TStrings): Boolean; begin Result := IMAPuploadCommand('APPEND "' + ToFolder + '"', Mess) = 'OK'; end; function TIMAPSend.DeleteMess(MessID: integer): boolean; var s: string; begin s := 'STORE ' + IntToStr(MessID) + ' +FLAGS.SILENT (\Deleted)'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; end; function TIMAPSend.FetchMess(MessID: integer; const Mess: TStrings): Boolean; var s: string; begin s := 'FETCH ' + IntToStr(MessID) + ' (RFC822)'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; ParseMess(Mess); end; function TIMAPSend.FetchHeader(MessID: integer; const Headers: TStrings): Boolean; var s: string; begin s := 'FETCH ' + IntToStr(MessID) + ' (RFC822.HEADER)'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; ParseMess(Headers); end; function TIMAPSend.MessageSize(MessID: integer): integer; var n: integer; s, t: string; begin Result := -1; s := 'FETCH ' + IntToStr(MessID) + ' (RFC822.SIZE)'; if FUID then s := 'UID ' + s; if IMAPcommand(s) = 'OK' then begin ProcessLiterals; for n := 0 to FFullResult.Count - 1 do begin s := UpperCase(FFullResult[n]); if (Pos('* ', s) = 1) and (Pos('RFC822.SIZE', s) > 0 ) then begin t := SeparateRight(s, 'RFC822.SIZE '); t := Trim(SeparateLeft(t, ')')); t := Trim(SeparateLeft(t, ' ')); Result := StrToIntDef(t, -1); Break; end; end; end; end; function TIMAPSend.CopyMess(MessID: integer; ToFolder: string): Boolean; var s: string; begin s := 'COPY ' + IntToStr(MessID) + ' "' + ToFolder + '"'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; end; function TIMAPSend.SearchMess(Criteria: string; const FoundMess: TStrings): Boolean; var s: string; begin s := 'SEARCH ' + Criteria; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; ParseSearch(FoundMess); end; function TIMAPSend.SetFlagsMess(MessID: integer; Flags: string): Boolean; var s: string; begin s := 'STORE ' + IntToStr(MessID) + ' FLAGS.SILENT (' + Flags + ')'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; end; function TIMAPSend.AddFlagsMess(MessID: integer; Flags: string): Boolean; var s: string; begin s := 'STORE ' + IntToStr(MessID) + ' +FLAGS.SILENT (' + Flags + ')'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; end; function TIMAPSend.DelFlagsMess(MessID: integer; Flags: string): Boolean; var s: string; begin s := 'STORE ' + IntToStr(MessID) + ' -FLAGS.SILENT (' + Flags + ')'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; end; function TIMAPSend.GetFlagsMess(MessID: integer; var Flags: string): Boolean; var s: string; n: integer; begin Flags := ''; s := 'FETCH ' + IntToStr(MessID) + ' (FLAGS)'; if FUID then s := 'UID ' + s; Result := IMAPcommand(s) = 'OK'; ProcessLiterals; for n := 0 to FFullResult.Count - 1 do begin s := uppercase(FFullResult[n]); if (Pos('* ', s) = 1) and (Pos('FLAGS', s) > 0 ) then begin s := SeparateRight(s, 'FLAGS'); s := Separateright(s, '('); Flags := Trim(SeparateLeft(s, ')')); end; end; end; function TIMAPSend.StartTLS: Boolean; begin Result := False; if FindCap('STARTTLS') <> '' then begin if IMAPcommand('STARTTLS') = 'OK' then begin Fsock.SSLDoConnect; Result := FSock.LastError = 0; end; end; end; //Paul Buskermolen function TIMAPSend.GetUID(MessID: integer; var UID : Integer): boolean; var s, sUid: string; n: integer; begin sUID := ''; s := 'FETCH ' + IntToStr(MessID) + ' UID'; Result := IMAPcommand(s) = 'OK'; ProcessLiterals; for n := 0 to FFullResult.Count - 1 do begin s := uppercase(FFullResult[n]); if Pos('FETCH (UID', s) >= 1 then begin s := Separateright(s, '(UID '); sUID := Trim(SeparateLeft(s, ')')); end; end; UID := StrToIntDef(sUID, 0); end; {==============================================================================} end. TransGUI/synapse/source/lib/synaicnv.pas0000644000000000000000000002545111366572451017320 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.001 | |==============================================================================| | Content: ICONV support for Win32, Linux and .NET | |==============================================================================| | Copyright (c)2004-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2004-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} //old Delphi does not have MSWINDOWS define. {$IFDEF WIN32} {$IFNDEF MSWINDOWS} {$DEFINE MSWINDOWS} {$ENDIF} {$ENDIF} {:@abstract(LibIconv support) This unit is Pascal interface to LibIconv library for charset translations. LibIconv is loaded dynamicly on-demand. If this library is not found in system, requested LibIconv function just return errorcode. } unit synaicnv; interface uses {$IFDEF CIL} System.Runtime.InteropServices, System.Text, {$ENDIF} synafpc, {$IFNDEF MSWINDOWS} {$IFNDEF FPC} Libc, {$ENDIF} SysUtils; {$ELSE} Windows; {$ENDIF} const {$IFNDEF MSWINDOWS} DLLIconvName = 'libiconv.so'; {$ELSE} DLLIconvName = 'iconv.dll'; {$ENDIF} type size_t = Cardinal; {$IFDEF CIL} iconv_t = IntPtr; {$ELSE} iconv_t = Pointer; {$ENDIF} argptr = iconv_t; var iconvLibHandle: TLibHandle = 0; function SynaIconvOpen(const tocode, fromcode: Ansistring): iconv_t; function SynaIconvOpenTranslit(const tocode, fromcode: Ansistring): iconv_t; function SynaIconvOpenIgnore(const tocode, fromcode: Ansistring): iconv_t; function SynaIconv(cd: iconv_t; inbuf: AnsiString; var outbuf: AnsiString): integer; function SynaIconvClose(var cd: iconv_t): integer; function SynaIconvCtl(cd: iconv_t; request: integer; argument: argptr): integer; function IsIconvloaded: Boolean; function InitIconvInterface: Boolean; function DestroyIconvInterface: Boolean; const ICONV_TRIVIALP = 0; // int *argument ICONV_GET_TRANSLITERATE = 1; // int *argument ICONV_SET_TRANSLITERATE = 2; // const int *argument ICONV_GET_DISCARD_ILSEQ = 3; // int *argument ICONV_SET_DISCARD_ILSEQ = 4; // const int *argument implementation uses SyncObjs; {$IFDEF CIL} [DllImport(DLLIconvName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'libiconv_open')] function _iconv_open(tocode: string; fromcode: string): iconv_t; external; [DllImport(DLLIconvName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'libiconv')] function _iconv(cd: iconv_t; var inbuf: IntPtr; var inbytesleft: size_t; var outbuf: IntPtr; var outbytesleft: size_t): size_t; external; [DllImport(DLLIconvName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'libiconv_close')] function _iconv_close(cd: iconv_t): integer; external; [DllImport(DLLIconvName, CharSet = CharSet.Ansi, SetLastError = False, CallingConvention= CallingConvention.cdecl, EntryPoint = 'libiconvctl')] function _iconvctl(cd: iconv_t; request: integer; argument: argptr): integer; external; {$ELSE} type Ticonv_open = function(tocode: pAnsichar; fromcode: pAnsichar): iconv_t; cdecl; Ticonv = function(cd: iconv_t; var inbuf: pointer; var inbytesleft: size_t; var outbuf: pointer; var outbytesleft: size_t): size_t; cdecl; Ticonv_close = function(cd: iconv_t): integer; cdecl; Ticonvctl = function(cd: iconv_t; request: integer; argument: argptr): integer; cdecl; var _iconv_open: Ticonv_open = nil; _iconv: Ticonv = nil; _iconv_close: Ticonv_close = nil; _iconvctl: Ticonvctl = nil; {$ENDIF} var IconvCS: TCriticalSection; Iconvloaded: boolean = false; function SynaIconvOpen (const tocode, fromcode: Ansistring): iconv_t; begin {$IFDEF CIL} try Result := _iconv_open(tocode, fromcode); except on Exception do Result := iconv_t(-1); end; {$ELSE} if InitIconvInterface and Assigned(_iconv_open) then Result := _iconv_open(PAnsiChar(tocode), PAnsiChar(fromcode)) else Result := iconv_t(-1); {$ENDIF} end; function SynaIconvOpenTranslit (const tocode, fromcode: Ansistring): iconv_t; begin Result := SynaIconvOpen(tocode + '//IGNORE//TRANSLIT', fromcode); end; function SynaIconvOpenIgnore (const tocode, fromcode: Ansistring): iconv_t; begin Result := SynaIconvOpen(tocode + '//IGNORE', fromcode); end; function SynaIconv (cd: iconv_t; inbuf: AnsiString; var outbuf: AnsiString): integer; var {$IFDEF CIL} ib, ob: IntPtr; ibsave, obsave: IntPtr; l: integer; {$ELSE} ib, ob: Pointer; {$ENDIF} ix, ox: size_t; begin {$IFDEF CIL} l := Length(inbuf) * 4; ibsave := IntPtr.Zero; obsave := IntPtr.Zero; try ibsave := Marshal.StringToHGlobalAnsi(inbuf); obsave := Marshal.AllocHGlobal(l); ib := ibsave; ob := obsave; ix := Length(inbuf); ox := l; _iconv(cd, ib, ix, ob, ox); Outbuf := Marshal.PtrToStringAnsi(obsave, l); setlength(Outbuf, l - ox); Result := Length(inbuf) - ix; finally Marshal.FreeCoTaskMem(ibsave); Marshal.FreeHGlobal(obsave); end; {$ELSE} if InitIconvInterface and Assigned(_iconv) then begin setlength(Outbuf, Length(inbuf) * 4); ib := Pointer(inbuf); ob := Pointer(Outbuf); ix := Length(inbuf); ox := Length(Outbuf); _iconv(cd, ib, ix, ob, ox); setlength(Outbuf, cardinal(Length(Outbuf)) - ox); Result := Cardinal(Length(inbuf)) - ix; end else begin Outbuf := ''; Result := 0; end; {$ENDIF} end; function SynaIconvClose(var cd: iconv_t): integer; begin if cd = iconv_t(-1) then begin Result := 0; Exit; end; {$IFDEF CIL} try; Result := _iconv_close(cd) except on Exception do Result := -1; end; cd := iconv_t(-1); {$ELSE} if InitIconvInterface and Assigned(_iconv_close) then Result := _iconv_close(cd) else Result := -1; cd := iconv_t(-1); {$ENDIF} end; function SynaIconvCtl (cd: iconv_t; request: integer; argument: argptr): integer; begin {$IFDEF CIL} Result := _iconvctl(cd, request, argument) {$ELSE} if InitIconvInterface and Assigned(_iconvctl) then Result := _iconvctl(cd, request, argument) else Result := 0; {$ENDIF} end; function InitIconvInterface: Boolean; begin IconvCS.Enter; try if not IsIconvloaded then begin {$IFDEF CIL} IconvLibHandle := 1; {$ELSE} IconvLibHandle := LoadLibrary(PChar(DLLIconvName)); {$ENDIF} if (IconvLibHandle <> 0) then begin {$IFNDEF CIL} _iconv_open := GetProcAddress(IconvLibHandle, PAnsiChar(AnsiString('libiconv_open'))); _iconv := GetProcAddress(IconvLibHandle, PAnsiChar(AnsiString('libiconv'))); _iconv_close := GetProcAddress(IconvLibHandle, PAnsiChar(AnsiString('libiconv_close'))); _iconvctl := GetProcAddress(IconvLibHandle, PAnsiChar(AnsiString('libiconvctl'))); {$ENDIF} Result := True; Iconvloaded := True; end else begin //load failed! if IconvLibHandle <> 0 then begin {$IFNDEF CIL} FreeLibrary(IconvLibHandle); {$ENDIF} IconvLibHandle := 0; end; Result := False; end; end else //loaded before... Result := true; finally IconvCS.Leave; end; end; function DestroyIconvInterface: Boolean; begin IconvCS.Enter; try Iconvloaded := false; if IconvLibHandle <> 0 then begin {$IFNDEF CIL} FreeLibrary(IconvLibHandle); {$ENDIF} IconvLibHandle := 0; end; {$IFNDEF CIL} _iconv_open := nil; _iconv := nil; _iconv_close := nil; _iconvctl := nil; {$ENDIF} finally IconvCS.Leave; end; Result := True; end; function IsIconvloaded: Boolean; begin Result := IconvLoaded; end; initialization begin IconvCS:= TCriticalSection.Create; end; finalization begin {$IFNDEF CIL} DestroyIconvInterface; {$ENDIF} IconvCS.Free; end; end. TransGUI/synapse/source/lib/synadbg.pas0000644000000000000000000001344711366572451017117 0ustar rootroot{==============================================================================| | Project : Ararat Synapse | 001.001.001 | |==============================================================================| | Content: Socket debug tools | |==============================================================================| | Copyright (c)2008-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2008-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Socket debug tools) Routines for help with debugging of events on the Sockets. } {$IFDEF UNICODE} {$WARN IMPLICIT_STRING_CAST OFF} {$WARN IMPLICIT_STRING_CAST_LOSS OFF} {$ENDIF} unit synadbg; interface uses blcksock, synsock, synautil, classes, sysutils; type TSynaDebug = class(TObject) class procedure HookStatus(Sender: TObject; Reason: THookSocketReason; const Value: string); class procedure HookMonitor(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer); end; procedure AppendToLog(const value: Ansistring); var LogFile: string; implementation procedure AppendToLog(const value: Ansistring); var st: TFileStream; s: string; h, m, ss, ms: word; dt: Tdatetime; begin if fileexists(LogFile) then st := TFileStream.Create(LogFile, fmOpenReadWrite or fmShareDenyWrite) else st := TFileStream.Create(LogFile, fmCreate or fmShareDenyWrite); try st.Position := st.Size; dt := now; decodetime(dt, h, m, ss, ms); s := formatdatetime('yyyymmdd-hhnnss', dt) + format('.%.3d', [ms]) + ' ' + value; WriteStrToStream(st, s); finally st.free; end; end; class procedure TSynaDebug.HookStatus(Sender: TObject; Reason: THookSocketReason; const Value: string); var s: string; begin case Reason of HR_ResolvingBegin: s := 'HR_ResolvingBegin'; HR_ResolvingEnd: s := 'HR_ResolvingEnd'; HR_SocketCreate: s := 'HR_SocketCreate'; HR_SocketClose: s := 'HR_SocketClose'; HR_Bind: s := 'HR_Bind'; HR_Connect: s := 'HR_Connect'; HR_CanRead: s := 'HR_CanRead'; HR_CanWrite: s := 'HR_CanWrite'; HR_Listen: s := 'HR_Listen'; HR_Accept: s := 'HR_Accept'; HR_ReadCount: s := 'HR_ReadCount'; HR_WriteCount: s := 'HR_WriteCount'; HR_Wait: s := 'HR_Wait'; HR_Error: s := 'HR_Error'; else s := '-unknown-'; end; s := inttohex(integer(Sender), 8) + s + ': ' + value + CRLF; AppendToLog(s); end; class procedure TSynaDebug.HookMonitor(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer); var s, d: Ansistring; begin setlength(s, len); move(Buffer^, pointer(s)^, len); if writing then d := '-> ' else d := '<- '; s :=inttohex(integer(Sender), 8) + d + s + CRLF; AppendToLog(s); end; initialization begin Logfile := changefileext(paramstr(0), '.slog'); end; end. TransGUI/rpc.pas0000644000000000000000000005672212261763702012523 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit rpc; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, httpsend, syncobjs, fpjson, jsonparser, ssl_openssl; resourcestring sTransmissionAt = 'Transmission%s at %s:%s'; const DefaultRpcPath = '/transmission/rpc'; type TAdvInfoType = (aiNone, aiGeneral, aiFiles, aiPeers, aiTrackers, aiStats); TRefreshTypes = (rtTorrents, rtDetails, rtSession); TRefreshType = set of TRefreshTypes; TRpc = class; { TRpcThread } TRpcThread = class(TThread) private ResultData: TJSONData; FRpc: TRpc; function GetAdvInfo: TAdvInfoType; function GetCurTorrentId: cardinal; function GetRefreshInterval: TDateTime; function GetStatus: string; procedure SetStatus(const AValue: string); function GetTorrents: boolean; procedure GetPeers(TorrentId: integer); procedure GetFiles(TorrentId: integer); procedure GetTrackers(TorrentId: integer); procedure GetStats; procedure GetInfo(TorrentId: integer); procedure GetSessionInfo; procedure DoFillTorrentsList; procedure DoFillPeersList; procedure DoFillFilesList; procedure DoFillInfo; procedure DoFillTrackersList; procedure DoFillStats; procedure DoFillSessionInfo; procedure NotifyCheckStatus; procedure CheckStatusHandler(Data: PtrInt); protected procedure Execute; override; public constructor Create; destructor Destroy; override; property Status: string read GetStatus write SetStatus; property RefreshInterval: TDateTime read GetRefreshInterval; property CurTorrentId: cardinal read GetCurTorrentId; property AdvInfo: TAdvInfoType read GetAdvInfo; end; TRpc = class private FLock: TCriticalSection; FStatus: string; FInfoStatus: string; FConnected: boolean; FTorrentFields: string; FRPCVersion: integer; XTorrentSession: string; FMainThreadId: TThreadID; FRpcPath: string; function GetConnected: boolean; function GetConnecting: boolean; function GetInfoStatus: string; function GetStatus: string; function GetTorrentFields: string; procedure SetInfoStatus(const AValue: string); procedure SetStatus(const AValue: string); procedure SetTorrentFields(const AValue: string); procedure CreateHttp; public Http: THTTPSend; HttpLock: TCriticalSection; RpcThread: TRpcThread; Url: string; RefreshInterval: TDateTime; CurTorrentId: cardinal; AdvInfo: TAdvInfoType; RefreshNow: TRefreshType; RequestFullInfo: boolean; ReconnectAllowed: boolean; RequestStartTime: TDateTime; constructor Create; destructor Destroy; override; procedure InitSSL; procedure Lock; procedure Unlock; procedure Connect; procedure Disconnect; function SendRequest(req: TJSONObject; ReturnArguments: boolean = True; ATimeOut: integer = -1): TJSONObject; function RequestInfo(TorrentId: integer; const Fields: array of const; const ExtraFields: array of string): TJSONObject; function RequestInfo(TorrentId: integer; const Fields: array of const): TJSONObject; property Status: string read GetStatus write SetStatus; property InfoStatus: string read GetInfoStatus write SetInfoStatus; property Connected: boolean read GetConnected; property Connecting: boolean read GetConnecting; property TorrentFields: string read GetTorrentFields write SetTorrentFields; property RPCVersion: integer read FRPCVersion; property RpcPath: string read FRpcPath write FRpcPath; end; var RemotePathDelimiter: char = '/'; implementation uses Main, ssl_openssl_lib, synafpc, blcksock; { TRpcThread } procedure TRpcThread.Execute; var t, tt: TDateTime; i: integer; ai: TAdvInfoType; begin try GetSessionInfo; NotifyCheckStatus; if not FRpc.FConnected then Terminate; t:=Now - 1; tt:=Now; while not Terminated do begin if Now - t >= RefreshInterval then begin FRpc.RefreshNow:=FRpc.RefreshNow + [rtTorrents, rtDetails]; t:=Now; end; if Now - tt >= RefreshInterval*5 then begin Include(FRpc.RefreshNow, rtSession); tt:=Now; end; if Status = '' then if rtTorrents in FRpc.RefreshNow then begin GetTorrents; Exclude(FRpc.RefreshNow, rtTorrents); t:=Now; end else if rtDetails in FRpc.RefreshNow then begin i:=CurTorrentId; ai:=AdvInfo; if i <> 0 then begin case ai of aiGeneral: GetInfo(i); aiPeers: GetPeers(i); aiFiles: GetFiles(i); aiTrackers: GetTrackers(i); end; end; case ai of aiStats: GetStats; end; if (i = CurTorrentId) and (ai = AdvInfo) then Exclude(FRpc.RefreshNow, rtDetails); end else if rtSession in FRpc.RefreshNow then begin GetSessionInfo; Exclude(FRpc.RefreshNow, rtSession); end; if Status <> '' then begin NotifyCheckStatus; Sleep(100); end; if FRpc.RefreshNow = [] then Sleep(50); end; except Status:=Exception(ExceptObject).Message; FRpc.RpcThread:=nil; NotifyCheckStatus; end; FRpc.RpcThread:=nil; FRpc.FConnected:=False; FRpc.FRPCVersion:=0; Sleep(20); end; constructor TRpcThread.Create; begin inherited Create(True); end; destructor TRpcThread.Destroy; begin inherited Destroy; end; procedure TRpcThread.SetStatus(const AValue: string); begin FRpc.Status:=AValue; end; procedure TRpcThread.DoFillTorrentsList; begin MainForm.FillTorrentsList(ResultData as TJSONArray); end; procedure TRpcThread.DoFillPeersList; begin MainForm.FillPeersList(ResultData as TJSONArray); end; procedure TRpcThread.DoFillFilesList; var t: TJSONObject; dir: widestring; begin if ResultData = nil then begin MainForm.ClearDetailsInfo; exit; end; t:=ResultData as TJSONObject; if RpcObj.RPCVersion >= 4 then dir:=t.Strings['downloadDir'] else dir:=''; MainForm.FillFilesList(t.Integers['id'], t.Arrays['files'], t.Arrays['priorities'], t.Arrays['wanted'], dir); end; procedure TRpcThread.DoFillInfo; begin MainForm.FillGeneralInfo(ResultData as TJSONObject); end; procedure TRpcThread.DoFillTrackersList; begin MainForm.FillTrackersList(ResultData as TJSONObject); end; procedure TRpcThread.DoFillStats; begin MainForm.FillStatistics(ResultData as TJSONObject); end; procedure TRpcThread.DoFillSessionInfo; begin MainForm.FillSessionInfo(ResultData as TJSONObject); end; procedure TRpcThread.NotifyCheckStatus; begin if not Terminated then Application.QueueAsyncCall(@CheckStatusHandler, 0); end; procedure TRpcThread.CheckStatusHandler(Data: PtrInt); begin if csDestroying in MainForm.ComponentState then exit; MainForm.CheckStatus; end; procedure TRpcThread.GetSessionInfo; var req, args, args2: TJSONObject; s: string; begin req:=TJSONObject.Create; try req.Add('method', 'session-get'); args:=FRpc.SendRequest(req); if args <> nil then try FRpc.FConnected:=True; if args.IndexOfName('rpc-version') >= 0 then FRpc.FRPCVersion := args.Integers['rpc-version'] else FRpc.FRPCVersion := 0; if args.IndexOfName('version') >= 0 then s:=' ' + args.Strings['version'] else s:=''; FRpc.InfoStatus:=Format(sTransmissionAt, [s, FRpc.Http.TargetHost, FRpc.Http.TargetPort]); if FRpc.RPCVersion >= 15 then begin // Requesting free space in download dir req.Free; req:=TJSONObject.Create; req.Add('method', 'free-space'); args2:=TJSONObject.Create; try args2.Add('path', args.Strings['download-dir']); req.Add('arguments', args2); args2:=FRpc.SendRequest(req); if args2 <> nil then args.Floats['download-dir-free-space']:=args2.Floats['size-bytes'] else begin args.Floats['download-dir-free-space']:=-1; FRpc.Status:=''; end; finally args2.Free; end; end; ResultData:=args; if not Terminated then Synchronize(@DoFillSessionInfo); finally args.Free; end else ASSERT(FRpc.Status <> ''); finally req.Free; end; end; function TRpcThread.GetTorrents: boolean; var args: TJSONObject; ExtraFields: array of string; sl: TStringList; i: integer; begin Result:=False; sl:=TStringList.Create; try FRpc.Lock; try sl.CommaText:=FRpc.FTorrentFields; finally FRpc.Unlock; end; if FRpc.RPCVersion < 7 then begin i:=sl.IndexOf('trackers'); if FRpc.RequestFullInfo then begin if i < 0 then sl.Add('trackers'); end else if i >= 0 then sl.Delete(i); end; i:=sl.IndexOf('downloadDir'); if FRpc.RequestFullInfo then begin if i < 0 then sl.Add('downloadDir'); end else if i >= 0 then sl.Delete(i); SetLength(ExtraFields, sl.Count); for i:=0 to sl.Count - 1 do ExtraFields[i]:=sl[i]; finally sl.Free; end; args:=FRpc.RequestInfo(0, ['id', 'name', 'status', 'errorString', 'announceResponse', 'recheckProgress', 'sizeWhenDone', 'leftUntilDone', 'rateDownload', 'rateUpload', 'trackerStats', 'metadataPercentComplete'], ExtraFields); try if (args <> nil) and not Terminated then begin FRpc.RequestFullInfo:=False; ResultData:=args.Arrays['torrents']; Synchronize(@DoFillTorrentsList); Result:=True; end; finally args.Free; end; end; procedure TRpcThread.GetPeers(TorrentId: integer); var args: TJSONObject; t: TJSONArray; begin args:=FRpc.RequestInfo(TorrentId, ['peers']); try if args <> nil then begin t:=args.Arrays['torrents']; if t.Count > 0 then ResultData:=t.Objects[0].Arrays['peers'] else ResultData:=nil; if not Terminated then Synchronize(@DoFillPeersList); end; finally args.Free; end; end; procedure TRpcThread.GetFiles(TorrentId: integer); var args: TJSONObject; t: TJSONArray; begin args:=FRpc.RequestInfo(TorrentId, ['id', 'files','priorities','wanted','downloadDir']); try if args <> nil then begin t:=args.Arrays['torrents']; if t.Count > 0 then ResultData:=t.Objects[0] else ResultData:=nil; if not Terminated then Synchronize(@DoFillFilesList); end; finally args.Free; end; end; procedure TRpcThread.GetTrackers(TorrentId: integer); var args: TJSONObject; t: TJSONArray; begin args:=FRpc.RequestInfo(TorrentId, ['id','trackers','trackerStats', 'nextAnnounceTime']); try if args <> nil then begin t:=args.Arrays['torrents']; if t.Count > 0 then ResultData:=t.Objects[0] else ResultData:=nil; if not Terminated then Synchronize(@DoFillTrackersList); end; finally args.Free; end; end; procedure TRpcThread.GetStats; var req, args: TJSONObject; begin req:=TJSONObject.Create; try req.Add('method', 'session-stats'); args:=FRpc.SendRequest(req); if args <> nil then try ResultData:=args; if not Terminated then Synchronize(@DoFillStats); finally args.Free; end; finally req.Free; end; end; procedure TRpcThread.GetInfo(TorrentId: integer); var args: TJSONObject; t: TJSONArray; begin args:=FRpc.RequestInfo(TorrentId, ['totalSize', 'sizeWhenDone', 'leftUntilDone', 'pieceCount', 'pieceSize', 'haveValid', 'hashString', 'comment', 'downloadedEver', 'uploadedEver', 'corruptEver', 'errorString', 'announceResponse', 'downloadLimit', 'downloadLimitMode', 'uploadLimit', 'uploadLimitMode', 'maxConnectedPeers', 'nextAnnounceTime', 'dateCreated', 'creator', 'eta', 'peersSendingToUs', 'seeders','peersGettingFromUs','leechers', 'uploadRatio', 'addedDate', 'doneDate', 'activityDate', 'downloadLimited', 'uploadLimited', 'downloadDir', 'id', 'pieces', 'trackerStats', 'secondsDownloading', 'secondsSeeding']); try if args <> nil then begin t:=args.Arrays['torrents']; if t.Count > 0 then ResultData:=t.Objects[0] else ResultData:=nil; if not Terminated then Synchronize(@DoFillInfo); end; finally args.Free; end; end; function TRpcThread.GetAdvInfo: TAdvInfoType; begin FRpc.Lock; try Result:=FRpc.AdvInfo; finally FRpc.Unlock; end; end; function TRpcThread.GetCurTorrentId: cardinal; begin FRpc.Lock; try Result:=FRpc.CurTorrentId; finally FRpc.Unlock; end; end; function TRpcThread.GetRefreshInterval: TDateTime; begin FRpc.Lock; try Result:=FRpc.RefreshInterval; finally FRpc.Unlock; end; end; function TRpcThread.GetStatus: string; begin Result:=FRpc.Status; end; { TRpc } constructor TRpc.Create; begin inherited; FMainThreadId:=GetCurrentThreadId; FLock:=TCriticalSection.Create; HttpLock:=TCriticalSection.Create; RefreshNow:=[]; CreateHttp; end; destructor TRpc.Destroy; begin Http.Free; HttpLock.Free; FLock.Free; inherited Destroy; end; procedure TRpc.InitSSL; {$ifdef unix} {$ifndef darwin} procedure CheckOpenSSL; const OpenSSLVersions: array[1..2] of string = ('0.9.8', '1.0.0'); var hLib1, hLib2: TLibHandle; i: integer; begin for i:=Low(OpenSSLVersions) to High(OpenSSLVersions) do begin hlib1:=LoadLibrary(PChar('libssl.so.' + OpenSSLVersions[i])); hlib2:=LoadLibrary(PChar('libcrypto.so.' + OpenSSLVersions[i])); if hLib2 <> 0 then FreeLibrary(hLib2); if hLib1 <> 0 then FreeLibrary(hLib1); if (hLib1 <> 0) and (hLib2 <> 0) then begin DLLSSLName:='libssl.so.' + OpenSSLVersions[i]; DLLUtilName:='libcrypto.so.' + OpenSSLVersions[i]; break; end; end; end; {$endif darwin} {$endif unix} begin if IsSSLloaded then exit; {$ifdef unix} {$ifndef darwin} CheckOpenSSL; {$endif darwin} {$endif unix} if InitSSLInterface then SSLImplementation := TSSLOpenSSL; CreateHttp; end; function TRpc.SendRequest(req: TJSONObject; ReturnArguments: boolean; ATimeOut: integer): TJSONObject; var obj: TJSONData; res: TJSONObject; jp: TJSONParser; s: string; i, j, OldTimeOut, RetryCnt: integer; locked, r: boolean; begin if FRpcPath = '' then FRpcPath:=DefaultRpcPath; Status:=''; Result:=nil; RetryCnt:=2; i:=0; repeat Inc(i); HttpLock.Enter; locked:=True; try OldTimeOut:=Http.Timeout; RequestStartTime:=Now; Http.Document.Clear; s:=req.AsJSON; Http.Document.Write(PChar(s)^, Length(s)); s:=''; Http.Headers.Clear; if XTorrentSession <> '' then Http.Headers.Add(XTorrentSession); if ATimeOut >= 0 then Http.Timeout:=ATimeOut; try r:=Http.HTTPMethod('POST', Url + FRpcPath); finally Http.Timeout:=OldTimeOut; end; if not r then begin if FMainThreadId <> GetCurrentThreadId then ReconnectAllowed:=True; Status:=Http.Sock.LastErrorDesc; break; end else begin if Http.ResultCode = 409 then begin XTorrentSession:=''; for j:=0 to Http.Headers.Count - 1 do if Pos('x-transmission-session-id:', AnsiLowerCase(Http.Headers[j])) > 0 then begin XTorrentSession:=Http.Headers[j]; break; end; if XTorrentSession <> '' then begin if i = RetryCnt then begin if FMainThreadId <> GetCurrentThreadId then ReconnectAllowed:=True; Status:='Session ID error.'; end; continue; end; end; if Http.ResultCode = 301 then begin s:=Trim(Http.Headers.Values['Location']); if (s <> '') and (i = 1) then begin j:=Length(s); if Copy(s, j - 4, MaxInt) = '/web/' then SetLength(s, j - 4) else if Copy(s, j - 3, MaxInt) = '/web' then SetLength(s, j - 3); FRpcPath:=s + 'rpc'; Inc(RetryCnt); continue; end; end; if Http.ResultCode <> 200 then begin if Http.Headers.Count > 0 then begin SetString(s, Http.Document.Memory, Http.Document.Size); j:=Pos('', LowerCase(s)); if j > 0 then System.Delete(s, 1, j - 1); s:=StringReplace(s, #13#10, '', [rfReplaceAll]); s:=StringReplace(s, #13, '', [rfReplaceAll]); s:=StringReplace(s, #10, '', [rfReplaceAll]); s:=StringReplace(s, #9, ' ', [rfReplaceAll]); s:=StringReplace(s, '"', '"', [rfReplaceAll, rfIgnoreCase]); s:=StringReplace(s, '
', LineEnding, [rfReplaceAll, rfIgnoreCase]); s:=StringReplace(s, '

', LineEnding, [rfReplaceAll, rfIgnoreCase]); s:=StringReplace(s, '', LineEnding, [rfReplaceAll, rfIgnoreCase]); s:=StringReplace(s, '
  • ', LineEnding+'* ', [rfReplaceAll, rfIgnoreCase]); j:=1; while j <= Length(s) do begin if s[j] = '<' then begin while (j <= Length(s)) and (s[j] <> '>') do System.Delete(s, j, 1); System.Delete(s, j, 1); end else Inc(j); end; while Pos(' ', s) > 0 do s:=StringReplace(s, ' ', ' ', [rfReplaceAll]); while Pos(LineEnding + ' ', s) > 0 do s:=StringReplace(s, LineEnding + ' ', LineEnding, [rfReplaceAll]); s:=Trim(s); end else s:=''; if s = '' then begin s:=Http.ResultString; if s = '' then if Http.ResultCode = 0 then s:='Invalid server response.' else s:=Format('HTTP error: %d', [Http.ResultCode]); end; Status:=s; break; end; Http.Document.Position:=0; jp:=TJSONParser.Create(Http.Document); HttpLock.Leave; locked:=False; RequestStartTime:=0; try try obj:=jp.Parse; Http.Document.Clear; finally jp.Free; end; except on E: Exception do begin Status:=e.Message; break; end; end; try if obj is TJSONObject then begin res:=obj as TJSONObject; s:=res.Strings['result']; if AnsiCompareText(s, 'success') <> 0 then begin if Trim(s) = '' then s:='Unknown error.'; Status:=s; end else begin if ReturnArguments then begin Result:=res.Objects['arguments']; if Result = nil then Status:='Arguments object not found.' else begin res.Extract(Result); FreeAndNil(obj); end; end else Result:=res; if Result <> nil then obj:=nil; end; break; end else begin Status:='Invalid server response.'; break; end; finally obj.Free; end; end; finally RequestStartTime:=0; if locked then HttpLock.Leave; end; until i >= RetryCnt; end; function TRpc.RequestInfo(TorrentId: integer; const Fields: array of const; const ExtraFields: array of string): TJSONObject; var req, args: TJSONObject; _fields: TJSONArray; i: integer; begin Result:=nil; req:=TJSONObject.Create; try req.Add('method', 'torrent-get'); args:=TJSONObject.Create; if TorrentId <> 0 then args.Add('ids', TJSONArray.Create([TorrentId])); _fields:=TJSONArray.Create(Fields); for i:=Low(ExtraFields) to High(ExtraFields) do _fields.Add(ExtraFields[i]); args.Add('fields', _fields); req.Add('arguments', args); Result:=SendRequest(req); finally req.Free; end; end; function TRpc.RequestInfo(TorrentId: integer; const Fields: array of const): TJSONObject; begin Result:=RequestInfo(TorrentId, Fields, []); end; function TRpc.GetStatus: string; begin Lock; try Result:=FStatus; UniqueString(Result); finally Unlock; end; end; function TRpc.GetTorrentFields: string; begin Lock; try Result:=FTorrentFields; UniqueString(Result); finally Unlock; end; end; procedure TRpc.SetInfoStatus(const AValue: string); begin Lock; try FInfoStatus:=AValue; UniqueString(FStatus); finally Unlock; end; end; function TRpc.GetConnected: boolean; begin Result:=Assigned(RpcThread) and FConnected; end; function TRpc.GetConnecting: boolean; begin Result:=not FConnected and Assigned(RpcThread); end; function TRpc.GetInfoStatus: string; begin Lock; try Result:=FInfoStatus; UniqueString(Result); finally Unlock; end; end; procedure TRpc.SetStatus(const AValue: string); begin Lock; try FStatus:=AValue; UniqueString(FStatus); finally Unlock; end; end; procedure TRpc.SetTorrentFields(const AValue: string); begin Lock; try FTorrentFields:=AValue; UniqueString(FTorrentFields); finally Unlock; end; end; procedure TRpc.CreateHttp; begin Http.Free; Http:=THTTPSend.Create; Http.Protocol:='1.1'; Http.Timeout:=30000; Http.Headers.NameValueSeparator:=':'; end; procedure TRpc.Lock; begin FLock.Enter; end; procedure TRpc.Unlock; begin FLock.Leave; end; procedure TRpc.Connect; begin CurTorrentId:=0; XTorrentSession:=''; RequestFullInfo:=True; ReconnectAllowed:=False; RefreshNow:=[]; RpcThread:=TRpcThread.Create; with RpcThread do begin FreeOnTerminate:=True; FRpc:=Self; Suspended:=False; end; end; procedure TRpc.Disconnect; begin if Assigned(RpcThread) then begin RpcThread.Terminate; while Assigned(RpcThread) do begin Application.ProcessMessages; try Http.Sock.CloseSocket; except end; Sleep(20); end; end; Status:=''; RequestStartTime:=0; FRpcPath:=''; end; end. TransGUI/vargrid.pas0000644000000000000000000012273112261763702013367 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit VarGrid; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Grids, VarList, Graphics, Controls, LMessages, Forms, StdCtrls, LCLType, ExtCtrls; type TVarGrid = class; TCellOption = (coDrawCheckBox, coDrawTreeButton); TCellOptions = set of TCellOption; TCellAttributes = record Text: string; ImageIndex: integer; Indent: integer; Options: TCellOptions; State: TCheckBoxState; Expanded: boolean; end; TOnCellAttributes = procedure (Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes) of object; TOnDrawCellEvent = procedure (Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean) of object; TOnSortColumnEvent = procedure (Sender: TVarGrid; var ASortCol: integer) of object; TCellNotifyEvent = procedure (Sender: TVarGrid; ACol, ARow, ADataCol: integer) of object; TOnQuickSearch = procedure (Sender: TVarGrid; var SearchText: string; var ARow: integer) of object; { TVarGridStringEditor } TVarGridStringEditor = class(TStringCellEditor) protected procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID; procedure msg_SetBounds(var Msg: TGridMessage); message GM_SETBOUNDS; end; { TVarGrid } TVarGrid = class(TCustomDrawGrid) private FFirstVisibleColumn: integer; FHideSelection: boolean; FImages: TImageList; FItems: TVarList; FItemsChanging: boolean; FColumnsMap: array of integer; FMultiSelect: boolean; FOnAfterSort: TNotifyEvent; FOnCellAttributes: TOnCellAttributes; FOnCheckBoxClick: TCellNotifyEvent; FOnDrawCell: TOnDrawCellEvent; FOnEditorHide: TNotifyEvent; FOnEditorShow: TNotifyEvent; FOnQuickSearch: TOnQuickSearch; FOnTreeButtonClick: TCellNotifyEvent; FSelCount: integer; FAnchor: integer; FSortColumn: integer; FOnSortColumn: TOnSortColumnEvent; FRow: integer; FHintCell: TPoint; FCurSearch: string; FSearchTimer: TTimer; FOldOpt: TGridOptions; FNoDblClick: boolean; FStrEditor: TVarGridStringEditor; function GetRow: integer; function GetRowSelected(RowIndex: integer): boolean; function GetRowVisible(RowIndex: integer): boolean; function GetSortOrder: TSortOrder; procedure ItemsChanged(Sender: TObject); procedure SetHideSelection(const AValue: boolean); procedure SetRow(const AValue: integer); procedure SetRowSelected(RowIndex: integer; const AValue: boolean); procedure SetRowVisible(RowIndex: integer; const AValue: boolean); procedure SetSortColumn(const AValue: integer); procedure SetSortOrder(const AValue: TSortOrder); procedure UpdateColumnsMap; procedure UpdateSelCount; procedure SelectRange(OldRow, NewRow: integer); procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW; function CellNeedsCheckboxBitmaps(const aCol,aRow: Integer): boolean; procedure DrawCellCheckboxBitmaps(const aCol,aRow: Integer; const aRect: TRect); function FindRow(const SearchStr: string; StartRow: integer): integer; procedure DoSearchTimer(Sender: TObject); protected procedure SizeChanged(OldColCount, OldRowCount: Integer); override; procedure DrawCell(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); override; procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); override; procedure PrepareCanvas(aCol,aRow: Integer; aState:TGridDrawState); override; procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override; procedure MouseMove(Shift: TShiftState; X,Y: Integer);override; procedure KeyDown(var Key : Word; Shift : TShiftState); override; procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override; procedure DoOnCellAttributes(ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); procedure HeaderClick(IsColumn: Boolean; index: Integer); override; procedure AutoAdjustColumn(aCol: Integer); override; procedure VisualChange; override; procedure DrawColumnText(aCol,aRow: Integer; aRect: TRect; aState:TGridDrawState); override; procedure DblClick; override; procedure Click; override; procedure GetCheckBoxState(const aCol, aRow:Integer; var aState:TCheckboxState); override; procedure SetCheckboxState(const aCol, aRow:Integer; const aState: TCheckboxState); override; procedure SetupCell(ACol, ARow: integer; AState: TGridDrawState; out CellAttribs: TCellAttributes); procedure DoOnCheckBoxClick(ACol, ARow: integer); procedure DoOnTreeButtonClick(ACol, ARow: integer); function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override; function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override; function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override; procedure DrawRow(aRow: Integer); override; function GetCells(ACol, ARow: Integer): string; override; function GetEditText(ACol, ARow: Longint): string; override; procedure SetEditText(ACol, ARow: Longint; const Value: string); override; procedure DoEditorShow; override; procedure DoEditorHide; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function EditorByStyle(Style: TColumnButtonStyle): TWinControl; override; procedure RemoveSelection; procedure SelectAll; procedure Sort; reintroduce; function ColToDataCol(ACol: integer): integer; function DataColToCol(ADataCol: integer): integer; procedure EnsureSelectionVisible; procedure EnsureRowVisible(ARow: integer); procedure BeginUpdate; reintroduce; procedure EndUpdate(aRefresh: boolean = true); reintroduce; procedure EditCell(ACol, ARow: integer); property Items: TVarList read FItems; property RowSelected[RowIndex: integer]: boolean read GetRowSelected write SetRowSelected; property RowVisible[RowIndex: integer]: boolean read GetRowVisible write SetRowVisible; property SelCount: integer read FSelCount; property Row: integer read GetRow write SetRow; property FirstVisibleColumn: integer read FFirstVisibleColumn; published property Align; property AlternateColor; property Anchors; property BorderSpacing; property BorderStyle; property Color; property Columns; property Constraints; property DragCursor; property DragKind; property DragMode; property Enabled; property FixedCols; property FixedRows; property Font; property GridLineWidth; property Options; property ParentColor default false; property ParentFont; property ParentShowHint default false; property PopupMenu; property RowCount; property ScrollBars; property ShowHint default True; property TabOrder; property TabStop; property TitleFont; property TitleImageList; property TitleStyle default tsNative; property Visible; property OnClick; property OnDblClick; property OnEnter; property OnExit; property OnHeaderClick; property OnHeaderSized; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnMouseWheelDown; property OnMouseWheelUp; property OnContextPopup; property OnDragDrop; property OnDragOver; property OnEndDock; property OnEndDrag; property OnStartDock; property OnStartDrag; property OnUTF8KeyPress; property OnResize; property OnGetEditText; property OnSetEditText; property Images: TImageList read FImages write FImages; property MultiSelect: boolean read FMultiSelect write FMultiSelect default False; property SortColumn: integer read FSortColumn write SetSortColumn default -1; property SortOrder: TSortOrder read GetSortOrder write SetSortOrder default soAscending; property HideSelection: boolean read FHideSelection write SetHideSelection default False; property OnCellAttributes: TOnCellAttributes read FOnCellAttributes write FOnCellAttributes; property OnDrawCell: TOnDrawCellEvent read FOnDrawCell write FOnDrawCell; property OnSortColumn: TOnSortColumnEvent read FOnSortColumn write FOnSortColumn; property OnAfterSort: TNotifyEvent read FOnAfterSort write FOnAfterSort; property OnCheckBoxClick: TCellNotifyEvent read FOnCheckBoxClick write FOnCheckBoxClick; property OnTreeButtonClick: TCellNotifyEvent read FOnTreeButtonClick write FOnTreeButtonClick; property OnQuickSearch: TOnQuickSearch read FOnQuickSearch write FOnQuickSearch; property OnEditorShow: TNotifyEvent read FOnEditorShow write FOnEditorShow; property OnEditorHide: TNotifyEvent read FOnEditorHide write FOnEditorHide; end; procedure Register; implementation uses Variants, Math, GraphType, lclintf, Themes, types, lclproc {$ifdef LCLcarbon} , carbonproc {$endif LCLcarbon}; const roSelected = 1; roCurRow = 2; procedure Register; begin RegisterComponents('TransGUI', [TVarGrid]); end; { TVarGridStringEditor } procedure TVarGridStringEditor.msg_SetGrid(var Msg: TGridMessage); begin inherited; Msg.Options:=Msg.Options and not EO_AUTOSIZE; end; procedure TVarGridStringEditor.msg_SetBounds(var Msg: TGridMessage); var ca: TCellAttributes; begin with Msg do begin TVarGrid(Grid).SetupCell(Col, Row, [], ca); with CellRect do begin Inc(Left, ca.Indent); if coDrawTreeButton in ca.Options then Inc(Left, Bottom - Top); if coDrawCheckBox in ca.Options then Inc(Left, Bottom - Top); if (ca.ImageIndex <> -1) and Assigned(TVarGrid(Grid).Images) then Inc(Left, TVarGrid(Grid).Images.Width + 2); Dec(Left, 3); Dec(Top, 1); SetBounds(Left, Top, Right-Left, Bottom-Top); end; end; end; { TVarGrid } procedure TVarGrid.ItemsChanged(Sender: TObject); var i, OldRows, OldCols: integer; pt: TPoint; begin FItemsChanging:=True; try Perform(CM_MouseLeave, 0, 0); // Hack to call ResetHotCell to workaround a bug OldRows:=RowCount; OldCols:=Columns.Count; i:=FItems.RowCnt + FixedRows; if (FRow = -1) and (inherited Row >= i) and (i > FixedRows) then inherited Row:=i - 1; RowCount:=i; if FRow <> -1 then begin Row:=FRow; FRow:=-1; end; UpdateSelCount; while Columns.Count > FItems.ColCnt do Columns.Delete(Columns.Count - 1); if Columns.Count <> FItems.ColCnt then begin Columns.BeginUpdate; try for i:=Columns.Count to FItems.ColCnt - 1 do Columns.Add; finally Columns.EndUpdate; end; end; if (OldRows <> RowCount) or (OldCols <> Columns.Count) then begin if Parent <> nil then HandleNeeded; ResetSizes; end else Invalidate; pt:=ScreenToClient(Mouse.CursorPos); if PtInRect(ClientRect, pt) then MouseMove([], pt.x, pt.y); finally FItemsChanging:=False; end; end; procedure TVarGrid.SetHideSelection(const AValue: boolean); begin if FHideSelection=AValue then exit; FHideSelection:=AValue; Invalidate; end; procedure TVarGrid.SetRow(const AValue: integer); var i, r: integer; begin if FItems.IsUpdating then FRow:=AValue else begin r:=AValue + FixedRows; if r <> inherited Row then begin i:=LeftCol; inherited Row:=r; LeftCol:=i; end; end; end; function TVarGrid.GetRowSelected(RowIndex: integer): boolean; begin Result:=LongBool(FItems.RowOptions[RowIndex] and roSelected); end; function TVarGrid.GetRowVisible(RowIndex: integer): boolean; begin Result:=RowHeights[RowIndex + FixedRows] > 0; end; function TVarGrid.GetSortOrder: TSortOrder; begin Result:=inherited SortOrder; end; function TVarGrid.GetRow: integer; begin if FItems.IsUpdating and (FRow <> -1) then Result:=FRow else begin Result:=inherited Row - FixedRows; end; end; procedure TVarGrid.SetRowSelected(RowIndex: integer; const AValue: boolean); var i, j: integer; begin i:=FItems.RowOptions[RowIndex]; if AValue then begin j:=i or roSelected; if j <> i then Inc(FSelCount); end else begin j:=i and not roSelected; if j <> i then Dec(FSelCount); end; FItems.RowOptions[RowIndex]:=j; InvalidateRow(RowIndex + FixedRows); if FSelCount <= 1 then InvalidateRow(inherited Row); end; procedure TVarGrid.SetRowVisible(RowIndex: integer; const AValue: boolean); begin if AValue then RowHeights[RowIndex + FixedRows]:=DefaultRowHeight else RowHeights[RowIndex + FixedRows]:=0; end; procedure TVarGrid.SetSortColumn(const AValue: integer); begin if FSortColumn=AValue then exit; FSortColumn:=AValue; if FSortColumn >= 0 then Options:=Options + [goHeaderPushedLook, goHeaderHotTracking] else Options:=Options - [goHeaderPushedLook, goHeaderHotTracking]; Sort; end; procedure TVarGrid.SetSortOrder(const AValue: TSortOrder); begin if SortOrder = AValue then exit; inherited SortOrder:=AValue; Sort; end; procedure TVarGrid.UpdateColumnsMap; var i, j: integer; begin FFirstVisibleColumn:=-1; SetLength(FColumnsMap, Columns.Count); j:=0; for i:=0 to Columns.Count - 1 do with Columns[i] do begin if (FFirstVisibleColumn < 0) and Visible then FFirstVisibleColumn:=i; FColumnsMap[j]:=ID - 1; Inc(j); end; SetLength(FColumnsMap, j); end; procedure TVarGrid.UpdateSelCount; var i: integer; begin FSelCount:=0; for i:=0 to FItems.Count - 1 do if RowSelected[i] then Inc(FSelCount); end; procedure TVarGrid.SelectRange(OldRow, NewRow: integer); var dir: integer; sel: boolean; begin if OldRow = NewRow then exit; if FAnchor = -1 then FAnchor:=OldRow; dir:=Sign(NewRow - OldRow); if Sign(FAnchor - OldRow) <> Sign(FAnchor - NewRow) then while OldRow <> FAnchor do begin RowSelected[OldRow]:=False; Inc(OldRow, dir); end; sel:=Abs(FAnchor - OldRow) < Abs(FAnchor - NewRow); while OldRow <> NewRow do begin RowSelected[OldRow]:=sel; Inc(OldRow, dir); end; RowSelected[NewRow]:=True; end; procedure TVarGrid.CMHintShow(var Message: TCMHintShow); var ca: TCellAttributes; pt: TPoint; wd: integer; R: TRect; begin with Message.HintInfo^ do begin pt:=MouseToCell(CursorPos); if (pt.x >= FixedCols) and (pt.y >= 0) then begin R:=CellRect(pt.x, pt.y); if PtInRect(R, CursorPos) then begin SetupCell(pt.x, pt.y, [], ca); if ca.Text <> '' then begin wd:=Canvas.TextWidth(ca.Text); Inc(R.Left, ca.Indent); if coDrawTreeButton in ca.Options then Inc(R.Left, R.Bottom - R.Top); if coDrawCheckBox in ca.Options then Inc(R.Left, R.Bottom - R.Top); if (ca.ImageIndex <> -1) and Assigned(FImages) then Inc(R.Left, FImages.Width + 2); if (R.Right <= R.Left) or (R.Right - R.Left < wd + 5) then begin HintStr:=ca.Text; R.Top:=(R.Top + R.Bottom - Canvas.TextHeight(ca.Text)) div 2 - 4; Dec(R.Left); HintPos:=ClientToScreen(R.TopLeft); end; FHintCell:=pt; end else Message.Result:=1; end else Message.Result:=1; end; end; end; function TVarGrid.CellNeedsCheckboxBitmaps(const aCol, aRow: Integer): boolean; var C: TGridColumn; begin Result := false; if (aRow>=FixedRows) and Columns.Enabled then begin C := ColumnFromGridColumn(aCol); result := (C<>nil) and (C.ButtonStyle=cbsCheckboxColumn) end; end; procedure TVarGrid.DrawCellCheckboxBitmaps(const aCol, aRow: Integer; const aRect: TRect); var AState: TCheckboxState; begin AState := cbUnchecked; GetCheckBoxState(aCol, aRow, aState); DrawGridCheckboxBitmaps(aCol, aRow, aRect, aState); end; function TVarGrid.FindRow(const SearchStr: string; StartRow: integer): integer; var i, c: integer; s, ss: string; v: variant; begin Result:=-1; if Columns.Count = 0 then exit; c:=SortColumn; if (c < 0) or (c >= Items.ColCnt) then c:=0; ss:=UTF8UpperCase(SearchStr); for i:=StartRow to Items.Count - 1 do begin v:=Items[c, i]; if VarIsNull(v) or VarIsEmpty(v) then s:='' else s:=UTF8UpperCase(UTF8Encode(widestring(v))); if Copy(s, 1, Length(ss)) = ss then begin Result:=i; break; end; end; end; procedure TVarGrid.DoSearchTimer(Sender: TObject); begin FSearchTimer.Enabled:=False; FCurSearch:=''; end; procedure TVarGrid.SizeChanged(OldColCount, OldRowCount: Integer); begin if not FItemsChanging and (FItems <> nil) then begin FItems.ColCnt:=Columns.Count; FItems.RowCnt:=RowCount - FixedRows; UpdateColumnsMap; end; inherited; end; procedure TVarGrid.DrawCell(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState); var ca: TCellAttributes; // ts: TTextStyle; dd, IsHeader: boolean; R, RR: TRect; det: TThemedElementDetails; sz: TSize; i: integer; begin RR:=aRect; IsHeader:=(gdFixed in aState) and (aRow=0) and (aCol>=FirstGridColumn); if not IsHeader and MultiSelect and (FSelCount > 0) then if (aRow >= FixedRows) and (aCol >= FixedCols) and RowSelected[aRow - FixedRows] then Include(aState, gdSelected) else Exclude(aState, gdSelected); PrepareCanvas(aCol, aRow, aState); if DefaultDrawing then SetupCell(aCol, aRow, aState, ca); if not IsHeader or (TitleStyle<>tsNative) then Canvas.FillRect(aRect); if not IsHeader then begin dd:=True; if Assigned(FOnDrawCell) then begin R:=CellRect(aCol, aRow); if goVertLine in Options then Dec(R.Right, 1); if goHorzLine in Options then Dec(R.Bottom, 1); FOnDrawCell(Self, aCol, aRow - FixedRows, ColToDataCol(aCol), aState, R, dd); end; if DefaultDrawing and dd then begin if CellNeedsCheckboxBitmaps(aCol,aRow) then DrawCellCheckboxBitmaps(aCol,aRow,aRect) else begin Inc(aRect.Left, ca.Indent); if coDrawTreeButton in ca.Options then begin R:=aRect; R.Right:=R.Left + (R.Bottom - R.Top); aRect.Left:=R.Right; if ThemeServices.ThemesEnabled then begin if ca.Expanded then det:=ThemeServices.GetElementDetails(ttGlyphOpened) else det:=ThemeServices.GetElementDetails(ttGlyphClosed); sz:=ThemeServices.GetDetailSize(det); with R do begin Left:=(Left + Right - sz.cx) div 2; Top:=(Top + Bottom - sz.cy) div 2; R:=Bounds(Left, Top, sz.cx, sz.cy); end; ThemeServices.DrawElement(Canvas.Handle, det, R, nil); end else with Canvas do begin i:=(R.Bottom - R.Top) div 4; InflateRect(R, -i, -i); if (R.Right - R.Left) and 1 = 0 then Dec(R.Right); if (R.Bottom - R.Top) and 1 = 0 then Dec(R.Bottom); Pen.Color:=clWindowText; Rectangle(R); InflateRect(R, -1, -1); Brush.Color:=clWindow; FillRect(R); InflateRect(R, -1, -1); i:=(R.Top + R.Bottom) div 2; MoveTo(R.Left, i); LineTo(R.Right, i); if not ca.Expanded then begin i:=(R.Left + R.Right) div 2; MoveTo(i, R.Top); LineTo(i, R.Bottom); end; end; end; if coDrawCheckBox in ca.Options then begin R:=aRect; R.Right:=R.Left + (R.Bottom - R.Top); aRect.Left:=R.Right; DrawGridCheckboxBitmaps(aCol, aRow, R, ca.State); end; if (ca.ImageIndex <> -1) and Assigned(FImages) then begin FImages.Draw(Canvas, aRect.Left + 2, (aRect.Bottom + aRect.Top - FImages.Height) div 2, ca.ImageIndex, gdeNormal); Inc(aRect.Left, FImages.Width + 2); end; if ca.Text <> '' then begin { if Canvas.TextStyle.Alignment <> taLeftJustify then if (aRect.Right <= aRect.Left) or (aRect.Right - aRect.Left < Canvas.TextWidth(ca.Text) + 9) then begin ts:=Canvas.TextStyle; ts.Alignment:=taLeftJustify; Canvas.TextStyle:=ts; end; DrawCellText(aCol, aRow, aRect, aState, ca.Text); } with aRect do begin Inc(Top, 2); Inc(Left, constCellPadding); Dec(Right, constCellPadding); if RightRight then Left:=Right; if BottomBottom then Top:=Bottom; if (Left <> Right) and (Top <> Bottom) then begin if Canvas.TextStyle.Alignment <> taLeftJustify then begin i:=Canvas.TextWidth(ca.Text); if i < Right - Left then case Canvas.TextStyle.Alignment of taRightJustify: Left:=Right - i; taCenter: Left:=(Left + Right - i) div 2; end; end; ExtUTF8Out(Canvas.Handle, Left, Top, ETO_OPAQUE or ETO_CLIPPED, @aRect, PChar(ca.Text), Length(ca.Text), nil); end; end; end; end; end; end; if gdFixed in aState then DefaultDrawCell(aCol, aRow, RR, aState) else DrawCellGrid(aCol, aRow, RR, aState); end; procedure TVarGrid.ColRowMoved(IsColumn: Boolean; FromIndex, ToIndex: Integer); begin inherited ColRowMoved(IsColumn, FromIndex, ToIndex); UpdateColumnsMap; end; procedure TVarGrid.PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState); var F: TCustomForm; begin if FHideSelection and (FSelCount = 0) then begin F:=GetParentForm(Self); if (F <> nil) and (F.ActiveControl <> Self) then aState:=aState - [gdSelected]; end; inherited PrepareCanvas(aCol, aRow, aState); with Canvas do if (Font.Color = clWindow) and (Brush.Color = clHighlight) then begin Font.Color:=clHighlightText; {$ifdef LCLgtk2} Brush.Color:=ColorToRGB(Brush.Color); // Workaround for LCL bug {$endif LCLgtk2} end; end; procedure TVarGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var pt: TPoint; IsCtrl, CheckBoxClicked: boolean; ca: TCellAttributes; R, RR: TRect; begin {$ifdef LCLcarbon} IsCtrl:=ssMeta in GetCarbonShiftState; {$else} IsCtrl:=ssCtrl in Shift; {$endif LCLcarbon} CheckBoxClicked:=False; pt:=MouseToCell(Point(X,Y)); if ssLeft in Shift then begin SetupCell(pt.x, pt.y, [], ca); RR:=CellRect(pt.x, pt.y); Inc(RR.Left, ca.Indent); if (RR.Left <= RR.Right) and (coDrawTreeButton in ca.Options) then begin R:=RR; R.Right:=R.Left + (R.Bottom - R.Top); if R.Right > RR.Right then R.Right:=RR.Right; if PtInRect(R, Point(X,Y)) then begin DoOnTreeButtonClick(pt.x, pt.y); InvalidateCell(pt.x, pt.y); if Assigned(OnDblClick) and (ssDouble in Shift) then FNoDblClick:=True; end; Inc(RR.Left, RR.Bottom - RR.Top); end; if (RR.Left <= RR.Right) and (coDrawCheckBox in ca.Options) then begin R:=RR; R.Right:=R.Left + (R.Bottom - R.Top); if R.Right > RR.Right then R.Right:=RR.Right; if PtInRect(R, Point(X,Y)) then begin DoOnCheckBoxClick(pt.x, pt.y); InvalidateCell(pt.x, pt.y); CheckBoxClicked:=True; if Assigned(OnDblClick) and (ssDouble in Shift) then FNoDblClick:=True; end; end; end; if (ssRight in Shift) {$ifdef darwin} or (Shift*[ssLeft, ssCtrl] = [ssLeft, ssCtrl]) {$endif} then begin SetFocus; if (pt.x >= FixedCols) and (pt.y >= FixedRows) then begin if MultiSelect and (SelCount > 0) and not RowSelected[pt.y - FixedRows] then RemoveSelection; Row:=pt.y - FixedRows; end; end else if MultiSelect and (ssLeft in Shift) and (pt.x >= FixedCols) and (pt.y >= FixedRows) then begin if IsCtrl then begin if SelCount = 0 then RowSelected[Row]:=True; RowSelected[pt.y - FixedRows]:=not RowSelected[pt.y - FixedRows]; FAnchor:=-1; end else if ssShift in Shift then SelectRange(Row, pt.y - FixedRows) else begin if (SelCount > 0) and not CheckBoxClicked then RemoveSelection; FAnchor:=-1; end; end; inherited MouseDown(Button, Shift, X, Y); end; procedure TVarGrid.MouseMove(Shift: TShiftState; X, Y: Integer); var pt: TPoint; begin inherited MouseMove(Shift, X, Y); pt:=MouseToCell(Point(x, y)); if (FHintCell.x <> -1) and ((FHintCell.x <> pt.x) or (FHintCell.y <> pt.y)) then begin Application.CancelHint; FHintCell.x:=-1; end; end; procedure TVarGrid.KeyDown(var Key: Word; Shift: TShiftState); var r, k: integer; ca: TCellAttributes; begin if EditorMode then begin if Key = VK_ESCAPE then begin EditorHide; SetFocus; end; exit; end; r:=Row; k:=Key; if (Shift = []) and ( (k = VK_SPACE) or (k = VK_LEFT) or (k = VK_RIGHT) or (k = VK_ADD) or (k = VK_SUBTRACT) ) then begin SetupCell(FixedCols, inherited Row, [], ca); case k of VK_SPACE: if coDrawCheckBox in ca.Options then begin DoOnCheckBoxClick(FixedCols, inherited Row); Key:=0; exit; end; VK_LEFT, VK_SUBTRACT: if (coDrawTreeButton in ca.Options) and ca.Expanded then begin DoOnTreeButtonClick(FixedCols, inherited Row); Key:=0; exit; end; VK_RIGHT, VK_ADD: if (coDrawTreeButton in ca.Options) and not ca.Expanded then begin DoOnTreeButtonClick(FixedCols, inherited Row); Key:=0; exit; end; end; end; inherited KeyDown(Key, Shift); if MultiSelect then begin if ssCtrl in Shift then begin if k = VK_SPACE then RowSelected[Row]:=not RowSelected[Row]; FAnchor:=-1; end else if ssShift in Shift then begin SelectRange(r, Row); end else if k in [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_HOME, VK_END, VK_NEXT, VK_PRIOR] then begin if SelCount > 0 then RemoveSelection; FAnchor:=-1; end; end; if (Key = VK_RETURN) and (Shift = []) and Assigned(OnDblClick) then OnDblClick(Self); end; procedure TVarGrid.UTF8KeyPress(var UTF8Key: TUTF8Char); var i, r: integer; begin inherited UTF8KeyPress(UTF8Key); if UTF8Key = #0 then exit; FSearchTimer.Enabled:=False; FSearchTimer.Enabled:=True; if FCurSearch = '' then i:=0 else i:=Row; FCurSearch:=FCurSearch + UTF8Key; if Assigned(FOnQuickSearch) then begin r:=i; FOnQuickSearch(Self, FCurSearch, r); if r <> i then Row:=r; end else begin i:=FindRow(FCurSearch, i); if i >= 0 then Row:=i; end; end; procedure TVarGrid.DoOnCellAttributes(ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); begin if Assigned(FOnCellAttributes) then FOnCellAttributes(Self, ACol, ARow, ADataCol, AState, CellAttribs); end; procedure TVarGrid.HeaderClick(IsColumn: Boolean; index: Integer); var i: integer; begin inherited HeaderClick(IsColumn, index); if IsColumn and (FSortColumn >= 0) then begin fGridState:=gsNormal; i:=ColToDataCol(index); if FSortColumn = i then begin if SortOrder = soAscending then SortOrder:=soDescending else SortOrder:=soAscending; end else begin SortOrder:=soAscending; SortColumn:=i; end; end; end; procedure TVarGrid.AutoAdjustColumn(aCol: Integer); var i, j, wd, h, fr: integer; ca: TCellAttributes; begin wd:=4; fr:=FixedRows; for i:=0 to FItems.Count - 1 do begin h:=RowHeights[i + fr]; if h > 0 then begin SetupCell(aCol, i + fr, [], ca); j:=Canvas.TextWidth(ca.Text) + 6; Inc(j, ca.Indent); if coDrawTreeButton in ca.Options then Inc(j, h); if coDrawCheckBox in ca.Options then Inc(j, h); if (ca.ImageIndex <> -1) and Assigned(FImages) then Inc(j, FImages.Width + 2); if j > wd then wd:=j; end; end; ColumnFromGridColumn(aCol).Width:=wd; end; procedure TVarGrid.VisualChange; begin inherited VisualChange; if HandleAllocated then DefaultRowHeight:=Canvas.TextHeight('Xy') + 5; UpdateColumnsMap; end; procedure TVarGrid.DrawColumnText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState); var R: TRect; i: integer; begin if (gdFixed in aState) and (aRow=0) and (aCol>=FirstGridColumn) then begin R:=aRect; if FSortColumn = ColToDataCol(aCol) then begin R.Right:=R.Left + R.Bottom - R.Top; InflateRect(R, -5, -5); OffsetRect(R, -3, 0); Dec(R.Bottom, 2); aRect.Left:=R.Right + 2; end; inherited DrawColumnText(aCol, aRow, aRect, aState); if FSortColumn = ColToDataCol(aCol) then with Canvas do begin Pen.Color:=clGrayText; i:=(R.Left + R.Right) div 2; if SortOrder = soAscending then begin MoveTo(i, R.Top); LineTo(R.Right, R.Bottom); LineTo(R.Left, R.Bottom); LineTo(i, R.Top); end else begin MoveTo(R.TopLeft); LineTo(R.Right, R.Top); LineTo(i, R.Bottom); LineTo(R.TopLeft); end; end; end; end; procedure TVarGrid.DblClick; var pt: TPoint; begin if FNoDblClick then begin FNoDblClick:=False; exit; end; pt:=MouseToCell(ScreenToClient(Mouse.CursorPos)); if (pt.y < FixedRows) and (pt.y = 0) and (Cursor <> crHSplit) then exit; inherited DblClick; end; procedure TVarGrid.Click; begin if Assigned(OnClick) then OnClick(Self); end; procedure TVarGrid.GetCheckBoxState(const aCol, aRow: Integer; var aState: TCheckboxState); var s: string; begin if (aCol >= FixedCols) and (aRow >= FixedRows) then begin s:=Items[ColToDataCol(aCol), aRow - FixedRows]; with Columns[GridColumnFromColumnIndex(aCol)] do if s = ValueChecked then aState:=cbChecked else if s = ValueUnchecked then aState:=cbUnchecked else aState:=cbGrayed; end; inherited GetCheckBoxState(aCol, aRow, aState); end; procedure TVarGrid.SetCheckboxState(const aCol, aRow: Integer; const aState: TCheckboxState); var s: string; begin if (aCol >= FixedCols) and (aRow >= FixedRows) then begin with Columns[GridColumnFromColumnIndex(aCol)] do case aState of cbUnchecked: s:=ValueUnchecked; cbChecked: s:=ValueChecked; else s:='?'; end; Items[ColToDataCol(aCol), aRow - FixedRows]:=s; end; inherited SetCheckboxState(aCol, aRow, aState); end; procedure TVarGrid.SetupCell(ACol, ARow: integer; AState: TGridDrawState; out CellAttribs: TCellAttributes); var v: variant; dc: integer; begin if (ACol < 0) or (ARow < 0) then exit; CellAttribs.ImageIndex:=-1; CellAttribs.Indent:=0; CellAttribs.Options:=[]; CellAttribs.State:=cbUnchecked; CellAttribs.Expanded:=True; if ACol >= FixedCols then begin dc:=ColToDataCol(ACol); if ARow >= FixedRows then begin v:=Items[dc, ARow - FixedRows]; if not VarIsNull(v) and not VarIsEmpty(v) then CellAttribs.Text:=UTF8Encode(WideString(v)) else CellAttribs.Text:=''; end else CellAttribs.Text:=ColumnFromGridColumn(ACol).Title.Caption; end else dc:=-1; DoOnCellAttributes(ACol - FixedCols, ARow - FixedRows, dc, AState, CellAttribs); end; procedure TVarGrid.DoOnCheckBoxClick(ACol, ARow: integer); var i, dc, c: integer; ca: TCellAttributes; st: TCheckBoxState; begin if Assigned(FOnCheckBoxClick) then begin dc:=ColToDataCol(ACol); c:=ACol - FixedCols; FOnCheckBoxClick(Self, c, ARow - FixedRows, dc); if (SelCount > 0) and RowSelected[ARow - FixedRows] then begin SetupCell(ACol, ARow, [], ca); st:=ca.State; for i:=0 to Items.Count - 1 do if RowSelected[i] then begin SetupCell(ACol, i + FixedRows, [], ca); if (coDrawCheckBox in ca.Options) and (ca.State <> st) then FOnCheckBoxClick(Self, c, i, dc); end; end; end; end; procedure TVarGrid.DoOnTreeButtonClick(ACol, ARow: integer); begin if Assigned(FOnTreeButtonClick) then FOnTreeButtonClick(Self, ACol - FixedCols, ARow - FixedRows, ColToDataCol(ACol)); end; function TVarGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; begin Result := False; if Assigned(OnMouseWheelDown) then OnMouseWheelDown(Self, Shift, MousePos, Result); end; function TVarGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; begin Result := False; if Assigned(OnMouseWheelUp) then OnMouseWheelUp(Self, Shift, MousePos, Result); end; function TVarGrid.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; begin Result:=inherited DoMouseWheel(Shift, WheelDelta, MousePos); if not Result then begin if Mouse.WheelScrollLines = -1 then GridMouseWheel(Shift, -WheelDelta*VisibleRowCount div 120) else GridMouseWheel(Shift, -WheelDelta*Mouse.WheelScrollLines div 120); Result := True; end; end; constructor TVarGrid.Create(AOwner: TComponent); begin FRow:=-1; FHintCell.x:=-1; inherited Create(AOwner); FixedRows:=1; FixedCols:=0; Options:=[goRowSelect, goThumbTracking, goVertLine, goHorzLine, goColSizing, goColMoving, goDblClickAutoSize, goFixedHorzLine, goFixedVertLine]; MouseWheelOption:=mwGrid; FItems:=TVarList.Create(1, 0); FItems.OnDataChanged:=@ItemsChanged; ItemsChanged(nil); TitleStyle:=tsNative; FAnchor:=-1; FSortColumn:=-1; ShowHint:=True; SetLength(FColumnsMap, 1); FColumnsMap[0]:=0; FSearchTimer:=TTimer.Create(Self); with FSearchTimer do begin Enabled:=False; Interval:=1500; OnTimer:=@DoSearchTimer; end; FastEditing:=False; EditorBorderStyle:=bsSingle; end; destructor TVarGrid.Destroy; begin inherited Destroy; FItems.Free; end; function TVarGrid.EditorByStyle(Style: TColumnButtonStyle): TWinControl; begin if Style = cbsAuto then begin if FStrEditor = nil then begin FStrEditor:=TVarGridStringEditor.Create(Self); FStrEditor.Name :='VGStringEditor'; FStrEditor.Text:=''; FStrEditor.Visible:=False; FStrEditor.Align:=alNone; FStrEditor.BorderStyle:=bsSingle; end; Result:=FStrEditor; end else Result:=inherited EditorByStyle(Style); end; procedure TVarGrid.RemoveSelection; var i: integer; begin for i:=0 to FItems.Count - 1 do RowSelected[i]:=False; FSelCount:=0; end; procedure TVarGrid.SelectAll; var i: integer; begin for i:=0 to FItems.Count - 1 do RowSelected[i]:=True; end; procedure TVarGrid.Sort; var i, c: integer; begin if (FSortColumn >= 0) and (FItems.Count > 0) then begin c:=FSortColumn; if Assigned(FOnSortColumn) then FOnSortColumn(Self, c); if not FItems.IsUpdating and (Row >= 0) and (Row < FItems.Count) then FItems.RowOptions[Row]:=FItems.RowOptions[Row] or roCurRow; FItems.Sort(c, SortOrder = soDescending); if not FItems.IsUpdating then begin if Assigned(FOnAfterSort) then FOnAfterSort(Self); for i:=0 to FItems.Count - 1 do if LongBool(FItems.RowOptions[i] and roCurRow) then begin FItems.RowOptions[i]:=FItems.RowOptions[i] and not roCurRow; Row:=i; break; end; Invalidate; end; end; end; function TVarGrid.ColToDataCol(ACol: integer): integer; begin if (ACol >= FixedCols) and (ACol <= High(FColumnsMap)) then Result:=FColumnsMap[ACol] else Result:=-1; end; function TVarGrid.DataColToCol(ADataCol: integer): integer; var i: integer; begin for i:=FixedCols to High(FColumnsMap) do if FColumnsMap[i] = ADataCol then begin Result:=i; exit; end; Result:=-1; end; procedure TVarGrid.EnsureSelectionVisible; var i: integer; begin if FSelCount > 0 then for i:=0 to FItems.Count - 1 do if RowSelected[i] then begin Row:=i; break; end; EnsureRowVisible(Row); end; procedure TVarGrid.EnsureRowVisible(ARow: integer); begin ARow:=ARow + FixedRows; if ARow < TopRow then TopRow:=ARow else if ARow > GCache.FullVisibleGrid.Bottom then TopRow:=ARow - (GCache.FullVisibleGrid.Bottom - GCache.FullVisibleGrid.Top); end; procedure TVarGrid.BeginUpdate; begin inherited BeginUpdate; Items.BeginUpdate; end; procedure TVarGrid.EndUpdate(aRefresh: boolean); begin inherited EndUpdate(aRefresh); Items.EndUpdate; end; procedure TVarGrid.EditCell(ACol, ARow: integer); begin SetFocus; FOldOpt:=Options; Options:=Options + [goEditing]; EditorShowInCell(DataColToCol(ACol), ARow + FixedRows); end; procedure TVarGrid.DrawRow(aRow: Integer); var Gds: TGridDrawState; aCol: Integer; Rs: Boolean; R: TRect; ClipArea: Trect; {$ifdef LCLgtk2} Rgn: HRGN; {$endif LCLgtk2} procedure DoDrawCell; begin with GCache do begin if (aCol=HotCell.x) and (aRow=HotCell.y) and not ((PushedCell.X<>-1) and (PushedCell.Y<>-1)) then begin Include(gds, gdHot); HotCellPainted:=True; end; if ClickCellPushed and (aCol=PushedCell.x) and (aRow=PushedCell.y) then begin Include(gds, gdPushed); end; end; {$ifdef LCLgtk2} Rgn := CreateRectRgn(R.Left, R.Top, R.Right, R.Bottom); SelectClipRgn(Canvas.Handle, Rgn); DeleteObject(Rgn); {$endif LCLgtk2} DrawCell(aCol, aRow, R, gds); end; function HorizontalIntersect(const aRect,bRect: TRect): boolean; begin result := (aRect.Left < bRect.Right) and (aRect.Right > bRect.Left); end; begin // Upper and Lower bounds for this row R.Left:=0; ColRowToOffSet(False, True, aRow, R.Top, R.Bottom); if R.Bottom <= R.Top then exit; // is this row within the ClipRect? ClipArea := Canvas.ClipRect; if (R.Top >= ClipArea.Bottom) or (R.Bottom < ClipArea.Top) then exit; // Draw columns in this row with GCache.VisibleGrid do begin for aCol:=left to Right do begin ColRowToOffset(True, True, aCol, R.Left, R.Right); if (R.Right <= R.Left) or not HorizontalIntersect(R, ClipArea) then continue; gds := []; Rs := (goRowSelect in Options); if ARow R.Left) and HorizontalIntersect(R, ClipArea) then DoDrawCell; end; {$ifdef LCLgtk2} with ClipArea do Rgn := CreateRectRgn(Left, Top, Right, Bottom); SelectClipRgn(Canvas.Handle, Rgn); DeleteObject(Rgn); {$endif LCLgtk2} // Draw the focus Rect if FocusRectVisible and (ARow=inherited Row) and ((Rs and (ARow>=Top) and (ARow<=Bottom)) or IsCellVisible(Col,ARow)) then begin if EditorMode then begin //if EditorAlwaysShown and (FEditor<>nil) and FEditor.Visible then begin //DebugLn('No Draw Focus Rect'); end else begin ColRowToOffset(True, True, Col, R.Left, R.Right); // is this column within the ClipRect? if HorizontalIntersect(R, ClipArea) then DrawFocusRect(Col,inherited Row, R); end; end; end; end; function TVarGrid.GetCells(ACol, ARow: Integer): string; var dc: integer; v: variant; begin Result:=''; dc:=ColToDataCol(ACol); if ARow >= FixedRows then begin v:=Items[dc, ARow - FixedRows]; if not VarIsNull(v) and not VarIsEmpty(v) then Result:=UTF8Encode(WideString(v)); end; end; function TVarGrid.GetEditText(ACol, ARow: Longint): string; begin Result:=GetCells(ACol, ARow); if Assigned(OnGetEditText) then OnGetEditText(self, aCol - FixedCols, aRow - FixedRows, Result); end; procedure TVarGrid.SetEditText(ACol, ARow: Longint; const Value: string); var dc: integer; begin if not (gfEditingDone in GridFlags) then exit; if Assigned(OnSetEditText) then OnSetEditText(Self, aCol - FixedCols, aRow - FixedRows, Value) else begin dc:=ColToDataCol(ACol); if ARow >= FixedRows then Items[dc, ARow - FixedRows]:=UTF8Decode(Value); end; end; procedure TVarGrid.DoEditorShow; begin inherited DoEditorShow; if Assigned(OnEditorShow) then OnEditorShow(Self); end; procedure TVarGrid.DoEditorHide; begin try inherited DoEditorHide; finally Options:=FOldOpt; end; if Assigned(OnEditorHide) then OnEditorHide(Self); end; end. TransGUI/VERSION.txt0000644000000000000000000000000512261612702013070 0ustar rootroot5.0.1TransGUI/daemonoptions.pas0000644000000000000000000001761112261763702014610 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit DaemonOptions; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Spin, ComCtrls, CheckLst, EditBtn, MaskEdit, ButtonPanel, BaseForm; resourcestring sPortTestSuccess = 'Incoming port tested successfully.'; sPortTestFailed = 'Incoming port is closed. Check your firewall settings.'; sEncryptionDisabled = 'Encryption disabled'; sEncryptionEnabled = 'Encryption enabled'; sEncryptionRequired = 'Encryption required'; SNoDownloadDir = 'The downloads directory was not specified.'; SNoIncompleteDir = 'The directory for incomplete files was not specified.'; // SNoBlocklistURL = 'The blocklist URL was not specified.'; SInvalidTime = 'The invalid time value was entered.'; type { TDaemonOptionsForm } TDaemonOptionsForm = class(TBaseForm) btTestPort: TButton; Buttons: TButtonPanel; cbBlocklist: TCheckBox; cbDHT: TCheckBox; cbUpQueue: TCheckBox; cbEncryption: TComboBox; cbMaxDown: TCheckBox; cbMaxUp: TCheckBox; cbPEX: TCheckBox; cbPortForwarding: TCheckBox; cbRandomPort: TCheckBox; cbIncompleteDir: TCheckBox; cbPartExt: TCheckBox; cbSeedRatio: TCheckBox; cbLPD: TCheckBox; cbIdleSeedLimit: TCheckBox; cbAltEnabled: TCheckBox; cbAutoAlt: TCheckBox; cbStalled: TCheckBox; cbUTP: TCheckBox; cbDownQueue: TCheckBox; edAltTimeEnd: TMaskEdit; edDownQueue: TSpinEdit; edUpQueue: TSpinEdit; edStalledTime: TSpinEdit; tabQueue: TTabSheet; txDays: TLabel; txFrom: TLabel; edDownloadDir: TEdit; edIncompleteDir: TEdit; edBlocklistURL: TEdit; edMaxDown: TSpinEdit; edAltDown: TSpinEdit; edMaxPeers: TSpinEdit; edMaxUp: TSpinEdit; edAltUp: TSpinEdit; edPort: TSpinEdit; edSeedRatio: TFloatSpinEdit; gbBandwidth: TGroupBox; edIdleSeedLimit: TSpinEdit; gbAltSpeed: TGroupBox; edAltTimeBegin: TMaskEdit; txAltUp: TLabel; txAltDown: TLabel; txMinutes1: TLabel; txTo: TLabel; txKbs3: TLabel; txKbs4: TLabel; txMinutes: TLabel; txMB: TLabel; txCacheSize: TLabel; Page: TPageControl; edCacheSize: TSpinEdit; tabNetwork: TTabSheet; tabBandwidth: TTabSheet; tabDownload: TTabSheet; txDownloadDir: TLabel; txEncryption: TLabel; txKbs1: TLabel; txKbs2: TLabel; txPeerLimit: TLabel; txPort: TLabel; procedure btOKClick(Sender: TObject); procedure btTestPortClick(Sender: TObject); procedure cbAutoAltClick(Sender: TObject); procedure cbBlocklistClick(Sender: TObject); procedure cbIdleSeedLimitClick(Sender: TObject); procedure cbIncompleteDirClick(Sender: TObject); procedure cbMaxDownClick(Sender: TObject); procedure cbMaxUpClick(Sender: TObject); procedure cbRandomPortClick(Sender: TObject); procedure cbSeedRatioClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } end; implementation uses main, utils, fpjson; { TDaemonOptionsForm } procedure TDaemonOptionsForm.cbMaxDownClick(Sender: TObject); begin edMaxDown.Enabled:=cbMaxDown.Checked; end; procedure TDaemonOptionsForm.btTestPortClick(Sender: TObject); var req, res: TJSONObject; begin AppBusy; req:=TJSONObject.Create; try req.Add('method', 'port-test'); res:=RpcObj.SendRequest(req, False); AppNormal; if res = nil then MainForm.CheckStatus(False) else if res.Objects['arguments'].Integers['port-is-open'] <> 0 then MessageDlg(sPortTestSuccess, mtInformation, [mbOk], 0) else MessageDlg(sPortTestFailed, mtError, [mbOK], 0); res.Free; finally req.Free; end; end; procedure TDaemonOptionsForm.cbAutoAltClick(Sender: TObject); var i: integer; begin edAltTimeBegin.Enabled:=cbAutoAlt.Checked; edAltTimeEnd.Enabled:=cbAutoAlt.Checked; txFrom.Enabled:=cbAutoAlt.Checked; txTo.Enabled:=cbAutoAlt.Checked; txDays.Enabled:=cbAutoAlt.Checked; for i:=1 to 7 do gbAltSpeed.FindChildControl(Format('cbDay%d', [i])).Enabled:=cbAutoAlt.Checked; end; procedure TDaemonOptionsForm.cbBlocklistClick(Sender: TObject); begin if not edBlocklistURL.Visible then exit; edBlocklistURL.Enabled:=cbBlocklist.Checked; if edBlocklistURL.Enabled then edBlocklistURL.Color:=clWindow else edBlocklistURL.ParentColor:=True; end; procedure TDaemonOptionsForm.cbIdleSeedLimitClick(Sender: TObject); begin edIdleSeedLimit.Enabled:=cbIdleSeedLimit.Checked; end; procedure TDaemonOptionsForm.btOKClick(Sender: TObject); begin edDownloadDir.Text:=Trim(edDownloadDir.Text); if edDownloadDir.Text = '' then begin Page.ActivePage:=tabDownload; edDownloadDir.SetFocus; MessageDlg(SNoDownloadDir, mtError, [mbOK], 0); exit; end; edIncompleteDir.Text:=Trim(edIncompleteDir.Text); if cbIncompleteDir.Checked and (edIncompleteDir.Text = '') then begin Page.ActivePage:=tabDownload; edIncompleteDir.SetFocus; MessageDlg(SNoIncompleteDir, mtError, [mbOK], 0); exit; end; edBlocklistURL.Text:=Trim(edBlocklistURL.Text); if cbAutoAlt.Checked then begin if StrToTimeDef(edAltTimeBegin.Text, -1) < 0 then begin Page.ActivePage:=tabBandwidth; edAltTimeBegin.SetFocus; MessageDlg(SInvalidTime, mtError, [mbOK], 0); exit; end; if StrToTimeDef(edAltTimeEnd.Text, -1) < 0 then begin Page.ActivePage:=tabBandwidth; edAltTimeEnd.SetFocus; MessageDlg(SInvalidTime, mtError, [mbOK], 0); exit; end; end; ModalResult:=mrOK; end; procedure TDaemonOptionsForm.cbIncompleteDirClick(Sender: TObject); begin edIncompleteDir.Enabled:=cbIncompleteDir.Checked; if edIncompleteDir.Enabled then edIncompleteDir.Color:=clWindow else edIncompleteDir.ParentColor:=True; end; procedure TDaemonOptionsForm.cbMaxUpClick(Sender: TObject); begin edMaxUp.Enabled:=cbMaxUp.Checked; end; procedure TDaemonOptionsForm.cbRandomPortClick(Sender: TObject); begin edPort.Enabled:=not cbRandomPort.Checked; end; procedure TDaemonOptionsForm.cbSeedRatioClick(Sender: TObject); begin edSeedRatio.Enabled:=cbSeedRatio.Checked; end; procedure TDaemonOptionsForm.FormCreate(Sender: TObject); var i, j, x, wd: integer; cb: TCheckBox; begin Page.ActivePageIndex:=0; cbEncryption.Items.Add(sEncryptionDisabled); cbEncryption.Items.Add(sEncryptionEnabled); cbEncryption.Items.Add(sEncryptionRequired); Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@btOKClick; x:=edAltTimeBegin.Left; wd:=(gbAltSpeed.ClientWidth - x - BorderWidth) div 7; for i:=1 to 7 do begin cb:=TCheckBox.Create(gbAltSpeed); cb.Parent:=gbAltSpeed; j:=i + 1; if j > 7 then Dec(j, 7); cb.Caption:=SysToUTF8(FormatSettings.ShortDayNames[j]); cb.Name:=Format('cbDay%d', [j]); cb.Left:=x; cb.Top:=txDays.Top - (cb.Height - txDays.Height) div 2; Inc(x, wd); end; end; initialization {$I daemonoptions.lrs} end. TransGUI/transgui.res0000644000000000000000000026120012261763702013566 0ustar rootroot ÿÿÿÿ ÿÿÿÿ Your application description here. true T ÿÿÿÿT4VS_VERSION_INFO½ïþ?´StringFileInfo040904E4< CompanyNameYury SidorovXFileDescriptionTransmission Remote GUI4 InternalNametransguit(LegalCopyrightCopyright (c) 2008-2014 by Yury SidorovD OriginalFilenametransgui.exePProductNameTransmission Remote GUI0ProductVersion5.0.1Comments0FileVersion5.0.1.0,LegalTrademarksDVarFileInfo$Translation ä>0ÿÿMAINICON 00 ¨%  ¨ h ÿÿÿÿ‰PNG  IHDR\r¨f IDATxœì½y”$wuçûùEDîKeí{uUoên5Ý­Ý-#°a„ÁàíÌ1>ãçcìgìñŒçÙoŽm`¼cÍû±–„ ³ŒIH-¡–Ôê­–®½*÷¬\â÷{DÜŒ¨R7j©«%ëž'2#"#2#ãnß»)c [ô£GÚu±l›¿¸þzŽ|ã$míºœíß޸ͯe¼õºSË!&t¼l”×@Â@Ê@Há½NØÐcC/ÐiAИ*üµ‚P,˶ݲëòÇ_ÿ:{nÿž-Ú\r^è/°EÏ snÜæ“,Ù¦|>6`ë[ƒm Ë@SÐmÃC‰AorQHF å@*ñ8`ã=tÑÐwÐÀüâ1øL~/'Œw¨{‘nÇù´%þÐY´¾2`©`·6à†µ¸Ný ØaÞ(ìÊÀhúЙdš§›Y ¤:ÞCæÜ:•Äãn¿êµ~îAxÍ1xW>gG•¶µAÃßoAë2èváöðàãÆ}Õ-ÚLÚHZëú+œ•|ŸÙö1 hQ¥z-8ë-¥®Í³# ÃY°c@ÌRx‡ãÒÖØÊ¢ Ö@9`§¼÷Ô€„±׺þ{Û_[@Ì_7ðJÅ{¯*à4@ï÷„Æ?< +J©Ï³å\4ÚH–e½P—nûí>‰ï)hÛÖ€‹Rp\¥.+õ^Õ mcÇ¢@ L\1lP ” –0kÔ?yÄ?F¼ŠÇȱ _ÐÁcnOëË—A÷ÏÑg=Ô½sX-ÐûÀY‚¿Ÿ=uêê}0éäÅ)mˆImEži­±,‹¿þë¿æÑG%™L>Ö€Xß(¥\c Æ”R(¥”el[¥ˆ‹N¦R9XýÂÞÐ,^ßkY³ÆDÆðN¤#`|Ó^ ˉÞVñ$J9xLÝÀcúš¬ï´9Ôõ·Å„DÄß_À Dü$Ç4ð„EÓ?·VÊ5Æ~ ýôÝÿÏm{ÉK,£µV/œÀý‘¤- àéÑGåî»ï&“É\,`J)åuÆ(ãQ—Rê¥Ñhô­fóá–ÖŸ(Õ蘟혟¿)£õ/¬i}åØÝ@Fk“׌gÆ[¢™<†´ @»㺄¤Nh[“Àpüù–¿Ž†ÖUÿx¹–¸„މ†ÎÕêÆØIÛÖ©Fãgoÿó?ÿø{>ò‘/clµå l*m € ¤d2I&“ÙL K)¾ï.šÞ3jYÖu±Xì©Têºt:=ÇM«ÕR‹++¿»ÿþÓ}33×Ç•êL+E—m›N­Ýˆ§õ­¬‡ª·ÿtƒÇ˜ã¶ü n<³¾HÀ°Mî‹àiìž%`ûçkáiuEà£X¡}QAñ·ÉÚ&@û@Tk³ xà–[~ûÔoþæÇ_òm´fË Ø<ÚHZëuËs¤°?¯1ï-`·mÛ¯ŠÇãoL¥RW%“ÉÎD"aÇQ–ei¥”‰Çãª#—»âŒã\±øoÿÆÄꪭm|`Oâî‚8¼0tÓß×Äc^Ïüφ¾XÄß/¾Ì­üµhø°e €²l éå\ ÿxK¡€†1vζM¦Ñ8|ûŸÿù+ã#¹kË Ø\Ú/…™Þ%`úð’H$ò“±Xìuétú`"‘HÆãqlÛÆ²,<ŸÀ²,˶m”RXJé‰K.1SѨµ|çv¶\¦©McÚÌ(Œ.Aüû žÆíñ÷ý/TÄcȨŒ!$ <æÖx ½Œ—” ë‰%àúçYÃK ”ä¢ ±ý¥æŸ?£ûÁž~à·7êõ»¢±X÷Ø¢ §-päpíå‘1æ\LŸUJ]‰DÞÇoH¥R{‰„‹Å°,Ë(¥\¥–eYJ)ÛËÒš{÷rrmùÛogÜVðo@‹ c6ñ´r‹Àç„Þö?׳ÜÐçãxŒêøÇfýÔÂøÄ €vö`Ûç_õ¯>N‚âÖÀšLMk+gÛœ|ä‘×<ú­oõ_~øð¼ÑZ)ÛÞB¯7¶ÀR³Ù¤^¯FŸæ„ÌxeYÑh4Ìô>ˆwS"‘¸!™LŽ' ‰Dž Ó¯[”e1†m—_ÎS““äî¿G)0† Ác®2Éíx WÃcFÑÂ’³L VãÆ Ðÿ\èw§ À>2l|€ÏßNO ˆ{‘ð÷Ǽ„"w†¿ùñ¿ôòÇïð/¹ålm € ¤¡¡!víÚE*•Bk½‘éM4Õ–e©r¹l¦¦¦zÇyy4}S"‘¸>•JÄãqÇÁ÷絘ö–eÙÏÄðgJÑ‹1òêWóäÉ“ì_^ÆUŠŠï÷“{¨ú¯%A'‚'$Ößð×ús 2µ¿]Ž B,Šùç.ú×êö‚H@Ã?‡˜ImÑrÀäw¿ûºf³yG$Ùr6‰¶òž#Éxß}÷±°°€ã8Êc…™¾T*™ÙÙÙÇüðüüüM…Báp*•êÇãÆ¶í6ˆbúgÍðÁ¢ñ8ß»ç"Ÿü$ýxÉ6Ýx>¸hWaž¿F€ˆ/¨}† ? å'f¼¸b=(+AÐ~×ÿìZ蜮ÿ^r$©ס¬‚nضu¯ë~ï7¾öµ+¯¸þz­]WY[nÀÓ–p´¶¶f×ëuÇq-ËÒ!¦ÍüüüO …—¥R©žD"aúúúü|ŒeY›ÆôÂøa+ ¢—^{-<öÙï}˲0ZÓI†ÆOy‚<~Á\‚Ћg!ˆß/I?†¹AQÇCýx‚G"Nèø†ÿb‚AÒ„Kà ëWÙÞwÝéXÖà–¸E@[à91­µeÛ¶I§ÓîÜÜÇï9zôèáùùù·ùš¾'‘H˜mÛ¶ ÎfjúL6W /fÇM71óÔSt—JT•"kLÛÏ–PàÆ°žÄäm<ì3aÛÄ·ð´¿’. '‰E"D:ýãìÐgAõØo䫸 È|í£8øÊW vmхЖxdŒQ¶më'Ÿ|ÒùèG?úÒ™™™·—Ëå7Äãñ¾d2iFGG•¤êÏÙŸ¶Œ¿q»Ñš½ûö±ôS?Ŭï 𬇙/hÿnÿaPq ÊL S]bø$A`Æ›Ð{Ç?§”‹‹Ð m“䢰µa §Ní¾|áù´%žù~¿U­Võ­·Þzc<ÿ€ã8»›Í¦388ˆã8Zkmð2ùÎ ¹ß m®ýJ)bJqÅë^Ç]GŽ0ôÈ#D”¢eL” !@ùWñA Ï—¯0{ÏÌ€/ï¯áióœ¡I€3@ ÄÂ0`Í?¶ƒÀ:Á?ŸŸ2¼cSÿÔÿà´%žc,¥”þêW¿:úØcý¿o{ÛÛz_ûÚ׺GŽq?úѪV«eÅãž|!?¼`´·—K~ög™{òI&êu,?A(\d/1~1÷…‘5ž0ä (;þÚÆCòT4 BR Aª1þv‰óã!pêxÀ&H$’ü Æ.ôÜ¢€^ð(€Ö~H"ZkÛ‰DÜ÷¼ç=oÝ·oß?õ÷÷7\×¾ìe/£Z­róÍ7S*•H$À… ó5õŸq?@<Îú§ ~ñ‹ XI­qñ4¼„î¤ '\wk¤öVñ´¿”ý b/C‘Ðg×€Ápúo#ô™:ž`YÁ³l<02BŽlùÛ J¹McìïG"ßüƒ|ùöýû-­µ¶¬­º€ ¡Üøaú•åuÑêêîîOg³¤R)»\.óõ¯«¯¾š÷¾÷½üÕ_ý•J…çj <“F.ûÒ±—¿õ­¹çŠEZJ¡ü4á(žßŸ%Ëxš]âö.óK”@„´ûÚ˜âèà1wÌÿœ„ý ⟠°6$ÿ_ªE€ˆ5Ñ0†–m“o6“ù¥%1[‘€ ¤LHýÄCQ\ZÂñ“;^¤dá[K±h”Õéé3Ÿ½ï>ö]v?vÍ5d‡oç;\~Ùeüʯü úЇÐZ‰Dð³ùž?mŽÅu]^ràÝp¥Ûn£ÃÇÇfgyó;ÞÁøÈßýîw¹öÚk¹á†¸ë®»Èd2çÅÀ›¥íϵh×å%‡ñèË_NùK_"ª–1ÔÌû/uú‹xRO㹂° Äù%G@êþ!¨6„@óKš¯à ’b,µbHbP‹ Œ¸æ÷%l@³eÌ–öß$zÁ€ÖÚïXé¾,a|ïÅK€÷dág† ã—ÅjœR¥°\—•ï|‡¬×ùÙw½‹ããÜ{ï½¼ò¯`nnŽ©©)‰ÄE±Îw7 +fÇë_ω/}‰¤_¼$¡:1åËx¥½ž0°¼Ü«¶†F—ëÐâÉÍm˜øÒLTIøUÿ¸–R4\—ŽH¤ÒÙÓã]^©üùa§îâ’ØÊë¯ ü˜·¦áþÝðνîô2Ñt/X)°’@Úº^­‰=ʧ>þq¦¦§I&“<|ä‡&Nã8‘HdÝFÛËÆ}ácεï|ÇqÖ];‚ërðe/£±{·WdY¬áiÚ<–^õß·ð˜°H`ÖK1zR8$ñ~¹¡â.ˆàæ–ð ¸"äaŒ†®[óÏѲ,V€Ý×]79±¿×õØÚz|/”þ£ßA©r5\.KÃm#ðÍÝð–ý·è»¬ž6ËøK èTŠžZ ŽåÓŸþ4Õj•b±Èêê*/}éKÑZ·™ù™˜þ|y&¦3~ø½6†ñÑQº^õ*ÎÄÚ þ"¾¸$ÿD ÌpiàY!Ðி?ŒHÌ^ª%1HÊ€%kP„‰¤—ü÷e ·X¤ôØc|öŽ;ˆG£=z”ÁÁAFGG½¬¼Xì™þBÿ\ÛÚ‚!!fY\úªW±æ84´ÆVªí›— rþEû* Nlnq ÿ>H €4© «Br h.‰Eâó×ýïP%`ú2˜ªRv ?ñö·ÿ«Š-0pèÇ^²í1þPL©ßé‚wë‚-±kÑVÒ;ÏÂc~Ù^z½ºÊ±ï}o sø¯à©§žâ²Ë.ãî»ï>kXðB|ü³…7n“÷aÀh;C‡¸kß>jGŽSŠŒ1äýÁË ¿‹0ⓘøþP68(¨~¸ZP´¼¸R_ Ñ„ AP‰@ð´µmw¾ÕrÆöïÿÓ~Ꮆò[£mÑ…Ñ‹B„æÑ]L²”ŸYÚò†Vþj7¼wÀ˜ÞAÐQpc`ÅÁ–𓄤ló·L ÅT–Î:F)šÕ•îú÷gb|WkÆÇÇÙ±cÓÓÓøm¾6Ô;×¶L/¯å½ÖšÑáaz¯½–ü‘#m0O˜º†gºKl>Ü&,ŒØK7 aâhèò`…›{HÂäø7°a| eÝ´¬VAkÇD"ùÏï{ßÚ‘[€›G/ p‘IžA~yÓ¼.éñ?–ÜŒpƒŠ¤¿M4X†ÐЇ.`®1tµÕUòÙ,_üÊWxûÏÿ<'OždïÞ½,//?cnÀ…2ýÙ@x­üa1Ûfï+_ÉW?ô!ÒZcüß*5ÿòÞ!æ!y%‚@¸³oø~È"]„%AHHÃ?¯¸ âˆö¯ƒ©+ÕZÕ:¢#‘[ÞÛmÿéªoTþȳ-ó“èG]„Ãz»bJýϸiIJ>ãûÌ¿®_½$´Ó‡Ó\å„án72 CLæ´1t//3uê÷?ð×]wõzññq¦§§‰F£ç!x.“þ|µýFæo·ÑÒš=W\Á—GGq§¦¨*EÊâ¬/Ù„úªá:©*½º¿?<L09Ÿƒ'`Âf¾øûòy_èšRºnLDE"Ÿxßm·½óªo4n³‰‰l1ÿ&Òªð†kx³îb ~3 ÿe˜l¸iP1°‰ÄZÚd‹ö’UyˆåÄt5 G‘ ¼6¤J%2¥÷|ç;ìÚ¹“Ó§Os饗²´´ô­€s †çªí72¿,­V‹ít\y%õ©)r–EÄuI´é’>ÿâ•ð„]7žÉ/í¿ÂiÀe¼Ä!7´H°/ _8é[ :ïõSt\×}ÿýð‡÷êo´ZN4ºÅü›L?Š Œî_›‚¿€ƒÃ¾¹[ZUm¬b“4¢ÕÖe¨%I+b&‡GbA`ç€êÒ3©ß¾ï>úúû©ÕjŒŒŒ0??¿Î 8¦®Úþ\@kMÊq¿æŽß~;Ý~u Â3ýåw‹5Áà è$‘‚°©/¡ÁpE¡dûI\¿†gþ7ý{,ƒ´ê>´…_iÂ'ú¶m³½eö_úQm­ïBÖ†ßÏÁ¯Ý|–´²3¾ó:‘h*±Â&lxÐ…Y´×êð·w+E¾Z%S.óÈѣؿŸ¡¡!öìÙC>ŸÇqœçMÛËvX?ÇàÒk¯åÛŽÃh«¾vy$ƒOJ‡!h#ðÛ¥f@Rˆen€ ³—Pÿ 혿©['®Ô÷mcÞ<`À1ƈüØ¢‹@?* ]‰Ú‚ÃYøßð{Ð3÷uÔŸa/ÈýÆžœ@‡N®pkáeÆùc´ÛMêç%N2Ø, j =@ei‰Ùtšï>ðãLLLÐÛÛK>ŸÇŸpѵýF¦·ÓÞ¾kÑíÛ©=ù$F)ÆÐ¿á^I.øf— °A÷Eã Ø'L/Qƒ°¿_¤-ÜXœ|c~]CÑòæ¶¶Z_\úaa­±áGà··Ýã¥îZþØkâ¬CÕþ0ëM™hwE Fh%ýsIž{¸×~Ø%pÁ+¹­Õ(U«r„ééiŒ1d2,ËzZúnx±mûiëg³l´(6 c 60rè«@Ó˜¶_^Ú~mv†& °±Ím¿Jþù€Ÿ[SÛ/ÆàŸùßÖ×°e÷?ôÃjØ ´öž‘_í‡lƒTòY)PÒSN¤9êáÄ£°ÙOo6n ½Tljo+/ÒòJü`S• éj•'Žç©cǸä’KèíímGÎGË?œs¦½?öc<Œi Jóƒ •þ€Q[Òt!=ü$5UN$¾­4¸ §øJ\›€±—ðjº 4¿˜Å2^+MÐ;4”"n ‰åe–R)ýþ÷9xèÃÃÃëÚ†m–o®×O»‘J¡ülº¾áaLo/­ÅÅ6˜)Z_: ‰É/}%Í/|’ç/  ¦º VÓ»]Ÿ‰Â{ Lâ1þºLãÐ<€-ºÈô‚ €gAJù¾a~rüÃ.J{¡#;–ô““PœÄ¯¥Û­0ª€[²_°Aö…©“¬/dI¤Ç†;à„Ûb s4üó•! ¤ŠEJkkü¾Ùl’ÏçY˜Ÿgrrwd„Úâ"q¥¨û]‚DÂz—G‚´—¹wí4^o¿[UñN7…ßn5þm5?L[VÀŧ`)¯#ŒVð{ûáÿÙã¥îº8R‰&¾½äêG ²Ø¤ å¥R»^ÇÓòÿ<âÏËÈ+1é3¬ï…·Q@ÄXþŠû¯[¾,XI§ùþ÷¿ÏåW\Á¥—^J½^oÏó»˜Ú¼>ŒÕj•ÅÅEΜ9ÃÂÂ¥b+Ã`‘ ÒΈ”P^¸u÷ÆŠ¿° °ê­5`ª`7¡…¿uàhX T‚-ßñ\̯ý>‘ÚwOž-…ïÍ=žWàÔ<Ú²íóij+Oëw§áïÂMÝà*жoò'|Îpι´¸²BÛ¥—h©1<3V2ßDx&O¨0¸!aIý{Á‘òKâY ^(`wwóıcœ>uŠ]»v‹ÅÐZ?+­Ï^Û fgg™eee…F£áM2ÖÛ2ƒƒ^]¿Ö(‚¿XG‚ôo¼‡5‚¸¾´î*‚n‚Õò„ôÝ ø¿ܯí†ÿõ³½n‡gýß!`æm.=¯@)ÕV¡BÛÎu8~ºWÀ'öÂî>ï½%Í&DÛJÅž|0ËŸT˜]Pî³Jrl—PŸ’&ž/ 0l‚¬¶º¿O-)øC\)²åj•Ù…ž|òI®¼ê*úúú¨V«ë¬€‹¥íËå2n¨íšÖ×uQkkdûûYŠFQMå't?Å…j°.g¿Öë[nTl NÇàÿ¶àý.K6Þz]ëÇs ?!éÉ'ŸÄ Z*=+ ”—î¼wï^Ò™ÌÖ8ñsÐó"äæçóyî¿ÿ~¯3ëÏdÈçó¾=¸Î°( - ÿiܼb)hÙàȰI™=/h¾L®i_ï©›'xp«À0ž9Üß7â/ƒ,…EÄŸ_ã ±`PêÀ"/×H” ‘ÏSìèàèã3==Íàà`;dw>Ì^ mÔöÅb‘ÙÙYΜ9³NÛ ¹®‹Öc ®ëb´&™ÍRÉf).-µ‡…„9Õ€z®ĪZ· ªì1~# 7ÛðÇ–ÏGëŸí½ö­¦¼ï}Ôo¾åºÏʰm›B¡À?Ýz+W^uZë¶òÙ¢€ž 7ÿ¡‡âÕ¯~5¶ßÜW}@Ó7=ñ˜_·¼J½ìßÞ&æåò;bÖKŽpb¶‡“qNá¡÷Ò¾k Ï4ïò¯ká1nŸN©x“Üu‰ ÈpL™^#–Ì´“ »Qì@„ñcì±r™X½Î©©)Nø``,ó4ñy0˜ÂÛDÛÏḬ̀¸¸H¹\¦Õ šçj­Û‹¸bm!Ðj‹F±{{)--µ¢Â”pÂOHëë2˜&Ø®—qyg~xÄOæ‘çÔôçÚgŒÁ ¥¥FÛŸap¾dÛ6ù|žfèlÑÓéywÀûs„AÔúÑ`âï§3ð‰+à¦@²XÂø’¦.Õ £ñRŦð<^ÆÓÔ xlA!0·¤¯Êc#%±5ÿó9‚PŸD*xÀ—¸Žuô´oâù³1­I•ˬ¬¬pìÉ'Y^^fttt¾W?HÛ·Z- …sssmm_¯×Ï©íÏ%´ëz I½½¦tô¨r”³®"œYSSJ×ÁÒÆX|;ÿøªeÛ„=“¿®÷åJ…|½Žó@iƒÜ¢§Óó‚ÿPú¾hèOµñ²ú&ºàŸÁUÝÐR`Ç@‰Ù/š^âÔŠ@K‡«ú4. ~}’`öœY)<ÐãfÏb¨$ÀHhQ€d|v ;NÂfÒÀ"hr‘ÀÐü›D¡ÀjWÇžzŠééiFGG×\ç«íþý3iû$ÜV Û²Htu©<óG†‰hÑvgLM)]Õ2ƶ"‘§"ð?¬fóÑ_Ó7Þ—–ålÌßNÙË;˲°-ëÙ[–å]xËïÿtÑ€0pÊ8ŽRͦmµZÏmQJk£-K×»»‡[}}?aÅb?a:;_bŒ‰'™z}H4F"‘`iq‘Ø@?J)Óhµæ¬Fóq·ÿ3;wgduå¸å%ýXÆs ¶ùE¤‹.fff8vìX{<Ö0ZƒeY¦ÙÔ¹îM»àÓû õªø,ñçà $q}ñMEªÐZBk³îVñÀ!<¦ ×ý £×€`Á€Š A›à2Ç^J_Ó¡í‚Hõ\¿¶à²_¾_¼Z%R«1}æ ÓSS\vÙe¸®Ëââ"³³³ÌÏÏS*•6]ÛŸk›kŒWØÑaVgfpÀŽS¥RŸÔ##ï_8fG©VËVM?‡Ò¶2F»‘ˆjf³ôøø/7Óé·f3™žÎÎN“_ZR^˜ÓÑ++Ëfdd„¥¥e’CCtww355eç"ÑA×è%Ǿ>>2ò?ZKK·µNžü3kuõˆmŒRë ½-Údºè.ÀÑ£G¹÷Þ{Éf³´Z-/Дã¨V>Ïðââ{.U*3¦•ôÓzÅßÓy#Œ#­§Äf“ÒÅVÐy‹ z­“ ,—€v|9>Ëú1W’ÒNùt ²2à²U€U‚p X’ˆ$8€RŠ„1$ªÕ¶°¼¼L±Xähkw¡ÍÔöç\».V$‚ÕÑn&™üŒ~ßÚÀÀ£!ÆW(e”eicŒÑõz¤22òj7‘xOçððá¨ãD,£Íö‰íîÊê*‰á!Õß×§NŸž´‡†°l W»ŒmÛF¡P ×Ùi†‡‡ÍéÓ§ÍÄè(-×MMU«¿¿òªŸk,/ýiã‰'þÐ4›U¥”‡'lÑ&ÓEµ–––˜™™Ù˜…¦ŒÖVòÜ\&sw£TzeÊçsahñóÃhÂÚÞÁcLÇp«þÒKÐ…¦ ­$ñÆüsýk,áùøaÌ@Ðûð´Ü ž+ ãÁ  ©'wDð‚4Á,‰Nˆ{“¨Õ(–ËÌÎÎ299ÉÐЕJ¥|1´ý9q]£”R‰\nµ±{÷MÍÁÁwÆ·PÊ•a¥b±Ûó3Ý]]ïnö÷ìîVƒƒƒfqqÑíïï·ͦ]©VÙ66ÆüüŽíL%™šœ"—ËÑlµÈ¯æQ+++ `xd”¹¹3fblÌm6›öÔZíw#W\ñyägêÅâãÉDbK\ºè@x\•ç¶Z­–ŠÆbͽ;v\Õ«Ô›W?ÿyz”²”1íЛ0äÛ‡çÈ Áܺ4p„ >/Y}2Œr Éûz4¬¹nž t( =âRÈ(+i&tE@¤ñB‘`®½tv Ê…óÆxY‹Õ*V£Á´Ÿ£¿cÇ2™ Åb˲ž¦ù/D<ÓZkm,­U$“©Õ‡†žp0¨f3Še5Qªi´Æm6wc~9ÿÒàÐÐ@45c&¶o×sssV"‘°c±§OŸ¦§§›F£A¹RaÛè(ùÕ<–e‘Íf™ž™!×™CY°ºšgphb±ÀÚZ]mÛ¶Í^XX0ýÝÝ-Wëý;öíûblrò¦¹……‡£Ñ¨­µÞ›H½ºB´ŽoØ­VËtwwë+¯ºê8p¯îîÞw&™4£Œ_ß_ºÏ*ÖÏ_^táJxZ"/Þ!H ,!g5ÀFav1á!/JÒä È(í¨ÿ™,ë5¾X7R¿m6‰­­±šÏ355…ÖšžžÖÖÖhµZO[šÍæY·o\\×]·>Ÿã\×U-×ŶíaÛuÇl¯™ZS·ZªQ«]o\÷–lGÇ÷R™ÌoïØµ«|bµ”2ƒCCV©T²«Õª’&'±XŒt*ÍÂÂ]¸Z“Ïçéëï£X(b´¦3—cqa‘t:E4ayy™ÞÞ^*• •JEíܹÓI§R-ǶÇ8ðù¾žžƒ…BÁU%Ø¢M¡ç-Èck­Ý‰‰‰]—^zéÿÎår×ôõéH2© […cÇÚñùögüµÄ÷Sx€Û”ÿz” R ¾y^x/Šç£§ñÒ ^ ÀI‚zÿŠ|™ åU7€‘x~xº­ ½á$Ót”"¤AOA±D¤b1­¼œ±µ5 …ËKK,,,0<<ÌC=tV `³´}xz­×q»ÙjõÔëubÑèÏ+ø•X,öcÛÆÇm˶ÖÚSÓÓÓv<™ ‰rfö }}}Ôëu_㱺ºŠ¥,r¹ÓÓÓttt`Y6+«+ R®T¨7Œ °¸¸H<'‘H099IwO7õz­]gï¾}î©S§F¶o»½P,¾¦X*=•N§-³&ܺè@)¥´Öv<oíÛ·ï}}}ŸÞ¶mÛà®]»ÜH$b¹J)wb‚3ÇŽÑKuÐÄ*Øü€¿–XÑÆýc%W?,ô¬+Ô¯§ý}e‚Ö×’Û/Œ,oãx ,®‡„ô[Rd)'6î`áÖaŒÊŽV«”êu—–˜åòË/'¶ xž-³Ÿ£Ÿs`Y–Æü–mÛèë8°V_cxxXwvvºÓÓÓÖàà /h6š 2??O<'™J1yú4ݸڥP(022L!_ÀÕš®®.fggI¥SÄ¢1æææèéé¡^¯S­VeeeDZɤ3ÌÌÌNgH&“v,k)•›¸ü²Ë>rϽ÷®V«n*•RZ?-gh‹ž%]lsJc¬l6ÛºüòËellìß<8xÙe—¹±XÌ®T*J7›Ä{z8‘N³f Z©¶¤™&;‡â CÊq‹xVA… ½…gÊ×ñü~8ƒ§ñûý÷=þ—”$©”˜ph¯D ½e¨¥ßñ†(^.€0¿:V"°>œiûÅ.¬­aù`~nÇqèïï§Z­>£ >æøLþu¯Ûn@«E®³óU:ÐÙÕ©ûúúÜþþ~kvvÎΤ3Êq–––èëï£V«Q­VéïïgeyÛ¶ÉærÌÏÏÓёŲ,VVWèëí¥R­P¯×ééîaqi‰h,J*•baaÎÎ.\×¥X,ÒÛÛG¡P@ûnÂÜÜ]ÝÝÎÐР«áº];wþ¾ïÙÒ@å5FÝ¢LS(@uvvºxÿèèèß^yå•öÎ;u­V³WWW)—Ëä²Yf˜%h/%À€g⯗ :ÓÈdÙ’_ö/\ HÌ‘„ Éä“–Uái·<|@Bâ¤<Ù^ÙÎò ç*hŽ\” úìII­Á F›Mœz|±ÈâÒ…B±±1ÆÓÀsñíÏÅðgaþöÒÑÑ¡³Ù¬®V«V__Ÿ½²¼‚ÑšîînææçI§RÄãqæççéééi'.õ÷÷SÈçÑZÓÕÙÅÂÂ"©TŠX,ÆâÂ"½==4êu*å2}½}¬®¬bYYÉd=¡‘_]¥§§‡JµJ£Ñ$×ÑÖÚN$zûöíÿ}ûøøÕjµeŒ±+• ù|þ¬‹üÎ-:7]À£,ËRZkÝÓÓóÁl6ûk/{ÙËZÉdÒÊçóV¥Rñ}ÿùÏ+˲Œ$ž IFå¡C‡¶º ƒ. }óÍ7ÿe½^ÿµ×¾öµ-˲ìÕÕUU«Õp]·mºE"z»»ééïçÉînÎÏ“ò;Ò$ ˜Çà1¿˜ÜÒ 3Žçógýí‚“ê>‰&ñ˜µÃ]"èý'7Áe½Ù/n†„ò¤^¢FŒXqÿ™'®:2z\ÚkEj5Jõ:E_ìß¿ŸžžΜ9C$9g8ð||û³1üÙ@ ¾V'“ÎÐÙÙÉää$Ùl‘H”3g<°om­N¹TfllŒ•ÕUÀÐÕÕÅ™3gÈd2ÄcqNÏŸ¦¯¯—F£IµZellŒ|>²¹Ž3gfHgÓ8N„™åÙ¶KQ«­16&x€C:nƒˆŽ±lÛv³¹ÜŽËú½{¿ýíÿó–[n±>ô¡¹®Á8÷¹UxVºbÑô?øÁ?šššúõ7½éM­h4ê‹EÕl61Æà8±˜—žÍféëïg´¿ŸÒÀO(¯/½tâõ}è*x œ#`* Á•CïcxV‚´¯’±ÖM‚Ü1á¥Õ•\‹Ðv©…—á<Cªkþu¤ä¸‚b”*Ã5<%õá¼×xe·V£U¯³âGZ­£££ëÂÏdÖ?Sã;;;) ´Z-zzzXX˜'“L&YX˜§»»c «ù<ýý”+eêõ:™L†É)/«”Åääi’©µZµí2”+Z­Ý]Ý,.-zçM$XZZ¢««×u)•JôôôP,1ÆË嘟Ÿ£»«Ëîééqc‰Ä¯îؾýþîïþνóÎ;mÛ¶ÛnSx‘„´-:;mª µ¶-Ër?úѾññÇÿ¯ï~÷»µã8öââ"ÆÏ5ÅbX–Õž~F‰ÅbŒŽŒäû““ì]]%«C‰`(EùÆ ÐñÙ%\W#è( *…0Ÿ&È2 ƒŠâ«K¨O´»|>\,f| %‹'¤JQ°ƒAWâšÿùF½ŽÕl²šÏ³¶¶ÆÌÌ »víâk_ûÚ:æÜlm¶íBÕj•µµ5¨”ËÔj5¶mÛÆÒÒ¶mÓÑÑÁôô4¹Ž,Ëbjrб±1¶mÛÆöíÛ%â¶ZD"Ž?AWWÍfƒÇephˆF£AµZctdÔ úxÀôô ™tÛö†ýÔªUýýäóyeÛ6»wíúãééé{~÷w÷ÔOþäOZ‘HDÖ¶6dÿÁüÁ¦œÈgp«T*™O}êS|Ýë^·c÷îÝzuuÕjµZc°m»­ù‰Éd²½v]—…¹9ޝ®Òµ¼ÌV“YBu¢¹…!¥g½0›hùp[kñ½Å,—®¿Äö!@ñåºRe(ø€Ôˆ‹ ×ÍŸÂâ.H™p•À"ŽÄ"\ ÒÓƒI¥&•N³gÏî»ï¾v Öº-ÂÚú\¯Ÿi9ó‡…ÃÚÚ===¤ÓifÎÌÐÛÛ ÀâÒb(•JtvvR©T¸úškxã~ŠÞ¾^²™ ­V‹üêª÷ŸG£ìܹ“ë®»ŽÁAÒÙ ÍF“'NÐÓÓMÄqXX\d`` ˆ, °¼´„c;är9ææféÌubYËË+jhhÈ-—+ÙLºç¡‡¾}zzں馛Œ¸—[t~´i€RÞÌùL&COOÏWNŸ>ýšk®¹†H$B<oûg¢ù%=XR„·mÛÆöñqž8q‚#³³ì)±ýÖTR«/©¶(óH]@Á&ñºûˆ(9úÄì7Ž ýWeýSò[ñx/1êŸOJ‡×Bç«€bó7ñf(cH*EÕ×rsssÄãqyôÑG‰ÇãO7CÛ‡µ~øµ0Qww7³³³ÄãqÒé4““Stuvãº.ÓÓÓì¹d‡.;ÄÐÐÅB›oþkîýÖ·(—Jm Âv2é4###ìß¿ŸÃ¯|%¯¾áÕÌÏÏcŒ¡Z©°¸´D:&q˜¥§§—z½N¥Z Y6™l–3gÎN§Èd2¶«]·³»ûwïÜy×Ç>ö±¿Ë[Þb¿þõ¯F<`‹Ú4 <ô_)e€éý×ý?®¹æšt"‘0€A°Qû˶x@ÈÆŸ$I× ñóË¡÷-Æ—ìÀ&ÞØ°5ßÕI °fÛŒ‹ÅصkÍf“‡~Çqžæ§o–¶?›€hµZd2‰+++ŒŒx¹üµZ•\g'Zk^õªWñên`~až}ìcüÅ_ü%÷Ýwår™F«…RŠH4ŠeYÔëuÎÌÎòÐÃóõ¯'N°û’K¸á†˜ãĉŒŒŽ°¼´ŒRŠÎÎ.æææèÈf‰D£,..20ÐO­¶F¥R¡¿¿Ÿ¥Å%’©„ŠFc ¸r5Ÿ¿ããÿøÊOÿôO[Fk½å œmªð™ßËß}÷Ý»•R—I$ƶm‹ÅÚ ‹Å¼‡%%‰´×år™™™N‹t­®ÒOƒNà uW{Úàž”CÀø’Ú+yú°¾e—€sJÿûEû Aiïž’œ…ª¿¯ÊúI9¡.î?Wµü&§Ù\ŽÌލñqŒã ]—D2ÉØØÆ.»ì2¾ýío{á3ßÅÚ˜îû\üûäÈûF£A:¦««‹D"ÁÞp#W\q?ô7ß|3÷áóÔñãÄãqR©Žãy“ÒÕH"=±X¬mŽK9x<‘`iy™ûï»ý/ÙÏË^ö2î½÷^¢±Éd‚ùùyúúúh4”ŠEXY]ÅCwwssód2¢Ñ(ù|ÞÊår®1æ€cÛ…{ï½÷[;wît:¤·ð€g¦M¾`mÛ¶mö+_ùÊÛ¶:tHc¬L* bÙ6K‹‹œ^Z¢¸²ÂÞF£=Å×"èΞßn)¾¹Œðho #Jg_q#$ÃP¶ à'£±çñ°†ZèçT ü:Ð'ÖDtÝ“EÕ—=qÇ!ÑÝMrçN¥ÆÇÑ©”× Å¯„Œ8Ôj5.¿ürNž<ÉSO=ÕvÎWœ¯¶žöZÖ¯xÅ+8|ø0Û·oç‘#GøÀûßÏ?~ò“LÏÌJ¥ÚŒ/LŸL&bbb‚ññqFGG¢¿¿ŸÞÞ^²Ù,J)굑H„Õ|ž333\÷ò—“ËåX^^fuuljÐÑÑÁÜܹ\˶Y^Z¢ ŸJ¥B­¶F_?‹K‹8ŽÃÀÀ€šž™QÙLæŠb±xǧ?󙥷l¹çE›.`½pçw¾ã†npâñ¸qG…™>l¢ÉCT­V™œšâä‰L–Jt•Jthê°–^Âc:R~ÁYDÈ1†ÀT¡a´—ÏÍâ ˆ9/¡Fy/BCÂ|-Ÿéý¢#« –&¡Ô‘$ü·ËÞö¶‡ ==‡¶­•1J…ï¶mÛ†ëºLLLà8ßúÖ·Î*.TÛŸíµ¬•R\ø0>ø øÀ¸õÖ[Y\^&N“L&×2Ëf³lß¾ýû÷³sçNúûûÉår¤R)’É$©d’T:M6›¥««‹x"AÉõ5sæ •J™·¾õmLOOs⸇äWó´Ü½½½Ì/,H&I%SÌÍy¹®vÉçó P,•RÊu"‘t:‘ØsêôéOœžœT[®À3ÓEbŒOéK_Úï8Î¥»wïÖÆ¿ƒdÀôÆJ¥'Ožä»ßý.÷ß?§Nbey™¥j•r¥ÂX«µ®>?œ$‚a‘€ÉËÚ^²ø¤xGÚ†K&_¸±¨ôä^ú„@1&ME}íojÓ›Ø-o*ŽJÂT>“‚ߊZÖï·Œ¹?×ÕõDtdäçWæç³N$â—“% (õŠevîÜÉW¿úÕgôï/TÛ‡×òßÜyçÜqÇ”Ëe/ÃÏæÈwíììd÷îÝ—ΤI%S¬®®z…©)öìÙÃÞ}û˜™žF_pÔïe–K ô°¼ì„]]H˜Íf‰Æ¢RFm5›M·X*íìÈdÊß¼çž{_l®ÀùDdžËÏ=÷ᢕkíMº¹ñÆ?xÇw¼å5¯y-Ò ì·Â:yò$§Nj7Ál6›¬­­‘ˆÇÉ&“Lvtp¢VcA% 0²€uo_!¨ä“¾‚âIBÔ ˆ5ˆ'É@Ò «#<·è£“`üH€ízš^Å`! _uàŸÜ ä `´VD'ï»ov|bâ¶H"ñ«£•Ríx•RŠF£A¥R!›Í255Å5×\ÃÐÐÇo· ;6 füðµÖhxL &.:ŽCgg'ãã㌌ŒÍfÛ³ c±¹\ŽÞÞ^ßG÷23šÍ&µZ•|¾ÀÌÌ ÓÓÓØ6õzÇœJµÊ‡ÿîïø£÷½Ã׿Ö[om ù¹yºººh6”Ëe†‡G( #Ùó$“I/eZ»V¶#K6þ/3³³Ÿç»ÞõÄ•W^i]zé¥ZžÇ’ÂÃq_,tÑ€eY°~üÇüžÛo¿ýs_úÒ—Þôú׿޵OŸ>ÍSþTœB¡@½^§ÙlÒh4X[[óÞ·ZÄ£QL:Í‘d’\µÚîÑ·ˆÇ˜Ý5Î…‹ªxŒ.>%U¸A€ü‹ ”£{ ŒïNké•VÝíÀRî‰Àg|X0 Ìú€ƒ¶,K¯­­Q>räÃÛ·¿{eaÁq"c¼fm Y*•c~~žV«Å®]»8zôh».xFp¶×g[o|¦L:M­ê9Y‘H„ÞÞ^ÆÇÇ"•Jy%·–M*¤··Á:;;‰D£ Ú5¸®×6•J’ÍvÐÓÛCOwG?ŠÖ^§ ÙÙY¾ô(ÿrç¼ãï`phãï³›L&ÃôÌ4ÙlǶX]ÍÓß×ÇÚÚµZÍï%°ŠB©‰ñ ÷±ÇëÙwÉ%óï÷Þûªßùßá¶Ûn#¾`®€ŸGy„_ûµ_#N·#-B–eQ.—¹æškøÀ>Àsrµ!ˆÖšz½Îk_ûÚ?¿í¶ÛÞ¸k×.ûرcœ8q‚ÅÅEŠÅ"µZ­ÍôõzF£Ñ®‚³l›t"ÁB.Çéµ5úµÆÆK¸‘âiÎÅcØ*An€øöb¶ è'Dø/€O°$4-ÿ°X.X"â0íÀÝÜaÁ7ÙP2Q›éQª])¬´v /•&séô“+‹‹û|w©-”R”J%”RÔj58xð ·ÝvÛ:6OÛŸ‹"‘±XŒ¡¡!ÆÇÇ ‘Hx1þˆÔ 288è•ò* m4 Ú²5¶E«m”ÿpŽm#ðÈ#°}ûövýÿí·ßÎþ/áM7ÝÄ?ÿó?{Õ‚CCK%Ü–KggW»åX"™`zfšŽ\ƒ¡X*Ðß×ÖÚŽF£n"ÿñû÷¿÷ _øÂŸ|êSŸrÞþö··\×mG+žO’û¼¼¼Ì]wÝ…mÛëÆ´?W’ó„î<[º¨]-ËÒ>ø 511ñMcÌßøÆ7~zÇŽ®ÖÚ®×ë^ l©Ô¶¤pE¤c,£;—ËâXµJ3Ÿ'×ù7F€ÔCÀè‚ÀKn¾Äë%„(³¥¿ ! Z¬„†7âÊö“~´ OÆáßløßV (4 ’SÊ¿b)ï_Ñ-¥ŒîîžP¿\ÊfÉ)†âñ¸iµZV±,‹Z­FÓï½âÄ .½ôR2™ F£”{¼YÚ~#i­éèè`Ïž= ‹ÅPJÇéîîn£ûÉd”Âh±ÊR(¹ÛÆÂ¶ *ÅRÞmê ÙlÐl5YYYá‰'ž`eu•Oýã'ù“?ûSvlßÎòò2Ѩ×r¬»«›f³I¹\fddÄ+r ¹ óó$âAK±ÑÑQ+ŸÏÓÝÓó} IDAT;½==_xç;ßùØ‹ÁpÛ¶Éår›&òù<étú¹§ þÏ@ÇŽc||œÃ‡ÿù—¿üå7îÞ½ÛŽÇãPáŒ6Ñ*Q?{LâÈÑH„ÁFCŸÉdLí¾ûH6j,iÖ!æ¼4îÿ~c‚üÿ>Çjô-×pi²a% Ùð¾<T΢éåÈl<å͸s›±˜•Ïd®éßµë?Gz{ßX*³ÙdÒ¤Ó)†Ú¦²m›V«E½^Ƕm¦¦¦xå+_ÉØØ>ú(‰D⬚ÿ­7¾>2ÆH$Á‰8¤R)¦»»»mRã;:Êcvo²ŸÂ(ƒ2m Þϳ°Œ_ ¢µËÀÀ …B‘r©ÌÒÒ‹‹‹<ø½ïqëg>Ëo¸‘b±È±ãODIgÒÌLŸ¡ÃÇVý¦!kkkTk5FF¼È•P¥RÉU–Õ}àÀ›ÿík_{Q¸ÀºJÅÍ<ßs¥çc2. ÖáÇïùò—¿|û£>ú–‰‰ ½¸¸h×j5´Ö´Z-o*—.lR©”I%“&åužQ…|Þr’I¦ÊeŠ=dRžiÞFÏÊÕ‚U¼ü€0h'f»ë#õ!aaY>û†fSðí(ü Äÿ' ® ·ÆWÊ­A7c±˜56v#ƒƒ¿Ö›L^—N§íâÊŠéìèpÕÜü¼ŠD"OëX#e­kkkd2æçç©Õj\rÉ%|ï{ßó²é6¸g[o|}¾ÔÜj¼‘a™l†»v180à7ö´Ð!áãÍ~cBcÞ(cü·Šöl` …c{½Õjµ{ ìØ±ƒb±HµVãŸþéÓ\}ÍÕ¼ôÚkyèᇥ\*ã¶štv ¶-ƒt*Åôô4ÙlÊRä yúûû¤š=66æÎLO_¿ß¾÷~á _øŸ·|êSÎ;^@WàÅHý.,,,pêÔ)/ôÚkÿâßøÆ›:¤öìÙ£s¹•JÅcˆzþ¦ÒZ[õz]Õ fX^Z2ÅRéLuf扨ã|i-~¹U.¿ÞVÊÅ[âúúÕwÆèŒT6ߤatô÷y›í?Ûìö Éh¤íRY¦‹…_M¬òÁ`ÍFµFÑHT‹éõK}Ül65A_XXÀ–-[àr¹´¿Ñ+ýù¾˜µ‘„¥¿Ïó<šÍ& E8G˜àSúúQßÊþ0û®“wY ψª  Ï`2QˆÝ.B¡âñ>T«U øô3Ï`vvûõ_Çü?þ# §ƒu vØí0x ø|>t»"êõ:ÃÊ@¦¬v “&R6—õMŒõ¹ßY÷Òz×=³ÙŒZ­†cÇŽ‚x<þW?üá/?pà€»Ùje›år^Ìå(!‡i½þ*I&çy“iÅÐht¡‹m¨š±£4E€ÿÚ’#§D *˜W‘e˜€’ 8'%#pȼH4ΈŒN åù¡¼î2Xoå{ß àL±²H’ÅÂÉ‘Èu|(ôYO,ö‘j¥bxžö…ÂR¡T$¡h˜s8XZ^†Ëå‚ÅlÁòÊ2ü~?8ŽC±P„Ýnך£ë‘”R ¸îºëÐßߣGÂjµž×ò¿Õu>ßøXÅar¹<ǃ#œ6€•}¯îÜè•¥Œ¡(Yf¿Ñ«QB@;6“Ù A10ЇR©ˆááa9rÍV >ðö\y%öìÙƒçž{FÝnáp…B¥)‘HÀáphØ@P0Be ¯×Ëçr9Éæ°ÿÒ–™™/ìß¿ÿo/uVུ޵£W/(§Ó £ÑˆS§NÉÇ'n·û°Á`ØY*•\œÑ˜E*UÆÒl6v1³‹›ÀãáÑÓÎ2 2YJŸ°ÔÛïu ÙS¯Tò´ÛM9® QL_³m[Ëçõ.‹¢xŽ"©®¼úI”ý½îáù^ûbÆ´ãŒq»Ýòj*%¿òòËvרØGùXìóÄ`¸<ìñpb§C6›4::J …O)e-°³k0›Œðû}H&Ym½ÃáDbuÁ§Ë‰z½®}Ÿ*€jÓTžç‘ÍfÑn·155…C‡±¿HÁ¿µ?߯󼢲oCGY¦*ËAý|å!Õ…T)% JņêÊ2£&“.—ýý¨T*ÈårX^^ÆÒò2þõk_ßÜ~; ùž|êI„Â!H’ŒJµŠh$‚z½†n—Í(( H¶XK$ P*•àr:¹®Ø…Ø÷ú}¾Gn¾ùæ÷AèR®w]ýMLL00Éá@.—£ ƒ! `ME‡†ˆ{ÇþìÙ³àÙ¡^)o$”2ŽRú #Ïcyq•jF£ÕR ÷÷«¯º JSˆ´+µ–ç­,FéeXB·+¿øÒK±¯ýëŸ øý7÷õ÷.Ÿ;Ÿ×+û|>)Is‘P„oµZ(Kèëg=ý †P—K´Z-Äb1òyH²„°/Œb©øºn¶Ç¡ÓéhÙ‘f³‰L&ƒmÛ¶áž{î¹(á+Ö^ýÎ Àh4¢P,0AÖõEfº€ õÆ}!Lºžg ) ¢(ª½—çx˜L&ˆ¢ˆH8‚b¡ˆr¹ŒB¡€J¥‚§Ÿy¸î:ìØ¹¯¾v6« k™5ØlV˜M&d2xt}#‘Ê¥’FIÎdÖ‰DH¹R–$*û·ÌÎþ?~ê©þïP€­w“ øìg?« #ž~úi|ík_£<ÏJ)'Ë2½é#¡;ff¤ûï½v›í­2¤TÎs# OYÚ‰²°Ê’DÉ,ûù–B´!à8ƒAæ8Nš;u §Nžœ}èá‡?{úÌ™÷õõù>põÕ(•Jòð®¥V‡?}æÜ.«€³gÏ" ÀÀóX^ËÀïóC’eäò9„BAH¢ˆZ­†H$¦âÔp:¨*a€¾& Ûí‚çyPJ1??-[¶°éºÝ®‚¾Ÿ_\¬µß¨8ŽÇq0™LhÔh4š¬IAˆ’7¥j|ÏT$Õ3) Z|èÔ†v–×í'Gˆ‚˜ ‚Ö=xhh'Nœ@«ÕÂ_ÿ:þû—ÿ¿ú‘à±Ç¾v§x0Ž¢R&ít8‘N§a³2vb­ZE$E±X€`eÄ¥V«Å÷õõIk™Ìõ›§¦þýû÷ÿ?ÿ;ø U‹Uæ'„Bd0ô¢Ì~ƒÑx1›d0%"Q@Û8ž—ÈÏSŽçñV7Âq<ÏŒFL&éìÙ³ü_}ùË7~îsŸ{âž{ï=ŽûÜe—_îýÍßú-ÉãõÉ—Û²e O8Æ4‡ÂÈd2>‘JÂjµÂî´c-“ÛãÝáD.Ÿ‡ÛíÑ\{·Û «Íªžö£(sÕ!ÏóX]]…ßïG?Úíöy-–^U!>ßv¡I:*YEíߨRfò<§É7QXT½4é¡ý MÆ T®½ `xš6™Œp»ÜèïÀàà B¡ŒF#NŸ9ƒ‡x333ðz=±\.Ãçó¡Õl¡ÕjÁëó¢P(Àîpê¼^/ ¥A€×ë庢ˆ¾þþÛý>ßøÍ·Ü"?~œ3 ï5÷çq½ë @½è€Þ¾®Š ,­ôF.o¶½¥Æ÷²,sÇÉÝnWúÖ=÷¸nýýß¿ù޽{_üñ“O>vùž=ºêškŒƒƒƒÒõ×_£ÑȧÓ)nbr…BÇaûŽíÈåóh6›lLV¡Y’Dµ±Ø€ÙÌGàr9±¶¶³…uº)•ʯ³Æ’$AßF=“É Ýncrrrñc£Õ>Ÿð¿™À«B¯¿U‰Xív•jEóD¦¢=ó´Ø €¬( ýïD×yÊÞk! X‘H„ކ‡‡Yé±Áˆüà ¼ðüóøåoD dE-¬Ál.ŸƒËíFWÑjµ´Þ;kNÚj4Ùs…<‰D"’ÉlöU–$ìÝ»—t: zTïçõž@?~Ö§]qõyBÇqm·ÛòþG‰ÿÞùÝ/ß½oß Âqÿæñûv|ì×~~àÚH’(b||œ,9sú4FFGa2™°¸°ˆÄcq”+„B!ÈTF¹\F,G»ÝÖFfU+UTkU„‚!Ôj5ˆ’ó–Jèt:ðzÏAŸ´X,Èåsè1ú ¸ü”ÈŸ_fɉ‚šB­ Tqûéë½Ê¤ìó8žƒÉh‚Å"Áëõ¢¯¯OË ¬­­áĉxøß¯ÿúo`ó¦M¨5Ø\@¯×‹F£¡t¥¶#•JÁíö ÕjC’D8öœÇf³ ³ÙÂýf‚½ÞDâ{·ÜrËü/jVà’)¥2º¢¨ ÌïÎ’e™ñóÙ…--.,põå/ßxëg>óƒÕDâ'œÁøû³[¶º?þñKÑhT6 Üôô4¿²º‚r¥Œééi¬®®¢Ñh`brKËK% XY^ëßÇ®t~¿ét&³ €ù|N§“aɬ‚>¿}ñ>|ðúëÁó<ùÞ÷PTÒX@¨*BÈ:@E­ÿ°öúÍh4jï·X,ÈçóÐ,¼ŠâSÊ,½¬PÅœ²¶W'À^ÒJ{ÖŸ9*6ŽƒÑhRª#ˆÆ¢Åb8ßýÎ}8}ê>ò«¿ŠJ‰…&V«Åb^¯—åÿAa³ÛP(àñz4N…ÝnG¡P€Ûí&.§Sêˆbpvfæ/¥_àPà’{*šüNË¿’BãŽãäF£!÷¾ûœÏ?ÿü¯—ŠÅÏgóù;vî$#ÃÃ4›ÍJ333œÛíâ<ˆÉ‰Iˆ¢ˆs‹g1µiš­–––0==F½ŽT2ÙÙT+¬e³˜ÅZ6 ƒÇ¦ÍÓH&èv»D±TB·Ûen38.lÛ¶ ƒËKKxàþûqèµ×P(0>>  ¥ÿT Ë2 ‰dYÆøø8µ¸ü°öç 'TP©T¢êîMÀYÞ_ÖÜ¢bëú5®K!ª¿‘’&$„€P”2̈7ð0›Ì° VôÅûP.1šðÂÂr…î»ï>üÅ—¿ŒÍÓ›17?‡jµªy+‰Ä*üþ€ÖrÌ*H$’šrA°bm-ÇC!©Q¯ÿöÔÄÄSû÷ïÿç_ĬÀûî(4—'„PŽãh³Ù”Ÿüñc߸óÎß][[ûôØÄÄÓã¦3[¶ÐmÛ¶É‹g¹X,Æ{½^:t¡P>Ÿ_9ˆH4·Û¢¯¯‡ÀààAÀ±cÇ´¾}‹ aØÀÉ'‰D JÖ2kˆÆ¢pØíøý†BXXXÀ“O>‰S'O¢ÞhÀ¬´½®TWÀh4j½ÿhŒÀz½Ž¬¢pžxâ‰7Tº=Ÿ _è±Á`@³ÙD¹\†Ëå†(vY*P9ߌ÷£öwTûè_µüZրꔂª ¯ã@(Ïs0šŒ0‰æu¡@¡P@.—ÃËàÁÀG?ö1”+œD±«³ð 2ót @™RV8¤† ¤"¢i€Fð¤ê5Àžæˆâ˜ÍEÆò+•J( ¨Õjh4xàpå•WbçÎX:·BjÕ‘0ŠÅ,& #Êå5„Ãa”+e­ÁI.—SA¸\.I–¤ðèÈÈ_üäÀOÞ~Çäø…!]r§¢ÝoãDB¯°Î¤…ùyü¿_ùÊ/Ý|óÍ÷ÏÍÍ”dùóãÞøÃR(–EQ䦦¦ø\.d"‰M›6!—Ë!³–ÁäÄ2é 2Ù5LmšB:F¡XÀ¦Í›L¦P©V±Ii\Y¯×155…d2 Q199‰sç΢^¯c×î݈D"ؾ}¦¦¦ðÊÁøÚ?ÿ3¾ö/ÿ‚C¯½¢µÔRÓ|~¿ãããšõÖ§ÑÔ°`y™ÕÄb1Ȳ £2„ã­Äöã|5Ö?߸6õsÍf6#©T,1¦æ:J0í¹÷òú¿¬”«lÁ^ ‘2"T^ˆò»S€1¾©Â×îV› }}}èïïG,ƒÁ`@:“Á߸±H W]u%R©ì;Sµ¼>/J¥,kdª*°r¹§Š¥""‘o4™$ÁjýO›¦¦>ýè£þBe.¹@€ŸÊÕb¥å„£”ÊNG2›LÆÍSS¹óÎ;ÿH–å«¶ïÜÉ  ÐB¡ ŽŽr·‡?räÀq<ææç122 àÌ™3,Ë8sæ ÆÆÇ Ë2æçç119Q’°¸¸ˆ©©)´Ûmœ;wÓÓ3èv:H¬®bjÓ&Í=½þú_ÂðÐêÕ:öï/¾ø"Ïž¥T›‰  „¾‰¦Úcß¡XT¯H¯ŒF#’É$`ppkkkër¼UÿBéßc0°f …by#P\zª+ùU2*8HP‡RYý¡@ Ï@éÒÀJ<­N@·8ÂÁh`…¼^/k®xÅR Ï<õ4.Û}®¾æüä'?ÝnG±X„ÍΦ8ÖëuD£Q‹%ÍcªV«ˆ„#(—Ë0›Ì00¶8çöx`4þ<‘Hüè–OúÜŽ;¸é_€Pà’^×ãM³ø:ŽÄqœc ¿ÿæ=W\ñ“˯¸âÁX<~ͮݻqåž=¥2|>ïöxÈÉS'át9qæÌ)xÜnøý~?q.—>Ÿ'Nœ€×ë…×ãÅñãÇàóúpüè1CAx<œ8yñx<7Ž?†ÒÓápà“ŸüOðùüø_ÿü¿ð¥/ýöÝ}75š«Š0 ‚€h4Љ‰ ­¶ÏçÃÌÌ ®»î:ø|>­Á£¾êã8”Ëe‹EŒ¯³üzkþF–ýBÖþ|ϫݙìv;ò…¼ßè¹ýʯ¨Q|T²—ú&â(ÀŸ¢¨Tú¢2²2@ЋŌH$‚x<Žááa˜ŒFtDßùÎwËåpÓM7¡Ñl ÝnÃãö0F ’.m4êðz½Œ, ë»èõzQ*—a2›I8–DYŽMMMýwIqÇwüBd.¹p‘…-<¥”¶ÛmÉépÇGGÏíñ|Æét  P™ÊÔç÷ËÛ¶mçÉÊ•*fgf4T~óæÍXYYF»ÝÅÔÔ æççÑé´±yóf,..¢Óé`hxgÏB–e cîÌ(dŒŽŒbnnžÇ€ÒžËétáÚk?Ž#H$Øw×>|ûÛ߯üÂ!šÐ«.½ÍfƒÏçcÃ1,Fø|>F&ŠÇa·Ûa±XÐßßÕÕÕu^€þ\-..bbbBZoÛÚ¿Ñó<Ï3 P@·+‚(A@ +~?Q³€®…ZDÁàÛÏ>C½ ¥ Ôà‚€€r'%`·‹ë¸+++837‡ï|ûÛ¸mï^|å º.#ý´ÚðÇüÈf³ZÞF£©xEX­V-\PÆ’ó6›UrØlŸšûñ£>ºïÞ{ïå÷wWR«2ßë’+}ÁÊ…ßF¸v»-YÌfã@ÿg&&&îpº\ý­V‹ÎÌÌÊN§©TŠC.»†•åeÌÌΠQo •Javf•Jɤ÷çóH¥RزeÅb‰Ä*¶mÛŽR±„T:…­[·"›Í"“É`×®ÈårH§SضmZíÆÇDZmÛ6œ>uûîÞ‡<ñ–––`4™àp8@eŠv‡qöN'ü~?<L&–ãƒ@4…ÕjÕçyÄb1ï®_*(µ²²‚Í›7ã°õÃ:ÞL¸/F!¨UF`±XTpt™ˆêÈÌbSH2Ã% Š%Wù„RP¥"PËÈ€LhÈ"E™(m›)GÀSV™h1³†¤±XŒ…EV9øÌÓOãŠ=WàCúö?ú(VVVár»ÐévÐî´ÍfÙïB)šÍ&¢±Š…‚vîËå2‡È¹sç …þ2³¶öÔ§?ýé•íÛ·s333ïÛPà=qDò8ŽãdY&­VKê‹Å>´eËìK;vîø§¡áá>“É$íܹ‹öÅc\"™àB¡ dYÆÂ"£èr„ÃüÂ<úúûÁñ<ÎÌA8!æçÑׇL)NŸ>A œ9s}}qp‡ùùy £+Š8~ü8fff0=3ƒ_ú³?ÃÇ?þ[ø×ù¤Òi¸Ün­Yo`Ý_‡‡‡1>>Žp8¬=¾òÊ+qÕUWattT‹ýU—[–ex<øýþ×ÕN¨€a*•Ïóèëë¥ôM]ù·¼Qè rÔâÞÐk‰¨OçÊP ÕWÁ>%C +éB*3Æ ¬Pˆ íe´Ž*ÇŨ–F£èëëÃÐÐZ©à®oܳф-[¶¢ÓéÀát¨¤t:´;¸\.”*Ëh6[ðxÜ(‹ƒãy^rºÝ}›6mú3Qñw÷wÀû¶Xè’{*Àu¾EášÍ&@'ÆÆþ<ÿŸn“SSRµR%ý}}| àǩӧaØà‰¹¹9S{}83wv»Ÿ§NŸ‚ÅlAÀïÃÜü<A€ÇãÅÜÜœÂá0Nœ8AGpêÔ)جVxÜKEüʯü †††ðÔSOáûïÇ3Ï<ƒµl‚ °Þô:`/ ¯¯’$¡£\x*¯ÝçóÁh4jÖ^eó)ǫݎŒŒ`eeåu @yžG.—C§ÓA?–——5öà[µöoöœþ>M9•J%­p D-ÿXï/®×Xªý”ª»¯ë é|ÂpU!På}ÐBÂ<%%i1›át:Ö…Éd§ÏœÁ<€ßþÄ'°}ûv,..‚RÀîp JÁ­´SëtºƒÈf×àpØ É2ó¢QäóyxÜn^”e©Q¯ÿþèðð“wíÛ÷íßüÍßä?ò‘HïG/à’+œ¯©¾Z­Jv«µÿòË/ÿ¦Y®¶ÙlÒž+ö|!ÏK¢ˆH,†•刢ˆh4Šd2 I’E‘H±¸|l KË˨Õjزe ’©jµ¦7O#•JivKKK¨ÕêØ±cΞ=‹Z­†ë®»¡Pb·‹……üÍÿüŸxòÉ'Q©Va±X4\–e‚ ô¶‹ÃívCÍŠ«]uÕUš­^àõÇ.Š"†‡‡Q­VQ¯××]t„H’„••ŒŽŽâ¥—^Z§.&Þ3Ž€¾¦Àét¢X,B–%]&µö’5aÆzªŸÖ @߀0ß(Õ (¨³ýš‚aeĽÁj ±®(Âh¡@¹\FµZéø?¾ IDATÅÃ?ŒÙÙY\ýõ8vôÜnš$YÖËÍA»Ý…?@.›ÓÀÂf³h4†R©L8ƒccc½¼²òìm·Ý–¸á†8£Ñ(¿ß¸—\h¼pôb\Q¹F£!9lö¡+¯ÜóˆÝéÜl0Ä={öðµZdײ˜˜G©XD6—ÅèÈjõ:òù<ÆÆÆÐ¨7Ig0>>ŽR¹Œd2©©)Ôj5,//cbbn©d “S“¨T+X^ZÆäÔ$ZͬV+n¼ñFp‡ï?ööïß—^z ÕZ V› N—K£ÂÚl6D"D£Q8mDöôô46mڣшƒ¢Ûíj¨úùý9¡Úä•Òª?G”R,--áŠ+®€ÅbyøÓþ[Qét’(+Ýõß¿OGýW‰þëŽKCþ5V`o.‚š ŠWÀˆCœ*€U x˜Íˆ¢ˆX,†r¥‚|>Ó§N¡X,â»÷݇?ýÒ—p͵×àØ±c¬FÀãA³Ñ€,KJ±P.— b—Í_øýÈær°ÙFÐj5¸ÁiuuupÛÖ­ú“þë­·ÞJî¼óNˆ¢ø¾¢ _ò#!ºÿ9ŽC§Óá†ì°ÛûöìÙ³ßíõN@ܺm›¡Ûíª“_ ƒ A8†ÑdÄâé3ˆD"ŒŠ{ò$¡08Ž`qqQ#;v ‘Hv›'OD0GŽ;Ž¡ÁAlÞ¼™¡óž~ê)Ü}÷ÝxõÕWÑév!Ün7 W(ÕX€‘H6› 6› ˜ÅÈȈÖCßjµâòË/ÇË/¿¼NøßlQʆ_®®®®Ë¨nh"‘ÐÀÄZ­¶núîOkíÏ'üê}«ÕÊ&óÔkŒ©(KêŽ*¿ Ìb|µBP#þôX€z0Uz¬+$¢jv ×Cºb¨0 J/;â±Ê¥òù<Ò™ ¾ò ~ðAüçßùœ={Ùl6« «‰U¸Ý4M¥E™t.§]e‹ßïG.—S2v^–eÉç÷ntdäGwÝu׃ïÇPà’+¶XŒ×ívI£Ñ Fƒ;¶oÿ¿ƒ¡Ð”L©8<8h°X,˜ŸŸ‡Ûí†ÃáÄüÜ<œ'‚ Μ9›Í¿ß¹ù9&¬7Î;«Í·Çƒ¹¹9Ø뼸¸B88íNdÖÖ°ûòËpõ•Wauu|ï{xüñÇqøðaHŠko¶X4ôz½ˆÇã°Z­p¹\ÁÌÌ 5†Ÿ ¢q‡h4Š-[¶àµ×^ƒÑh|ÓÔ'!D)muh]yÔ Nõ2™ dYF4ÅÜÜÜâkí7Z~õ{Y=#ŠÅ"¤–Ĩ¼Z P§Êuq>Uæ0êh‚T@ïêùJ™0QSŒèõ+4›Ì-"ü?ⱸÖC°Z«aÿþý¸úê«qÕUW¡V«¢\­€#l6‰ÜZíDI„ÃéD&“Ã逤(‚X4†|>P0HšÍb±Øß,//¿pÛm·¥ßo¡À%WªuSfòı±±?õõý6oàÅ€ÇkðúýZ^<‰"‘X…Œþ–/ïŠ"FFFL&ÑiuÐ?Öl6‹V«…Ñ‘QdR)´;mŒOÏ ‘H`mm »víÂàà |>(¥øú¿ý¾{ÿýXXXÐÈ:Çiy½^/b±ü~?A€ÏçÃÄĦ§§‹Å`±X´FšjŒ¯Ú¡¡!Ôj5ÌÍÍilÀ -Y–a6›áñxH$Öuäá866—Ë!cqqƒá‚~1Ö~ã0 Ðh2¢X(`phm0]g× tBTÆ Ó3wžR  ”¨ÂTáVRÂêuèê ”–¤„j^o0Àl¶À*tEQª°ŠÁùùy¤ÓiüÛ¿þ+nÿÓ/bû¶xäÑGÐßßZ­Bì6’É$\n¥yˆØEÈBfm v‡’,¡Ñh s™LFâ üÈe»v}ñÙ^øoï·Pà’§ á”RžR*ú¼ÞÝÓÓÓbµZa2šøH8ŒR±ˆJ¹‚ѱQŠ,ÿ;91R±„\.‡ÑÑQÔë dÖ2B§ÓA:fýóºmds9  \*¡Z«â7~ã7”‘Û'pÏÝßÄãO<ŽùùypJÊK¹F£Æ»÷z½зiÓ&LMM! Ád21pÊdº ¸ÓÓÓ¨×ëH¥RoÙ(•JëÒ€@Ïbž={“““š·¼žt±Öþ| @µvN‡¥rT’á%UPmüÁØ@ ”¬!J Si£W¯ ¿r`çmÙÜË&ôÎ Ïq0x œN—Òy™Ñ„³Ù,^úÉOðÜ<‹«¯¹¯~ §öTzP »Ý†t2 ·Ëv»ƒn§ƒP0¨q”b,> É¥bñ†~´o߾⦅BK¦Ô £^¯ã‡?ü¡b&'&þÒëóYeY–Bá/Ê’Éâ}qÈ’„d2‰xœåïWVVX—“ gNŸ†ßç‡Õjũӧáñx`³Ú°°0§Ó ›ÍŽ` €?üaòyüÍÿøx≠•N7`ÓUå™Íf„B!D"¸\.Øl6Äb1LOOcbb^¯W³ôªàë…ç!Û·oÇsÏ=‡J¥ƒÁp^% ¦û}ôQ\vÙeGÐz`eeEë&´‘ ôÓZû7òÔiÁËËˬSÆõ×_‡R…¨*ªÔûkVÒõö_Sšç¯;7³Dì?ƒÁ£I†ÅbF0D¹/Ž|>Ïf 6øÖ·¾…‘±1ÜtÓM¸çž{”4¢«É¤ ²Ù”v‡ét .— ÝNGÃòù<)˜L&ƒ‘ÑÑ¿M$“/ÝvÛmk7Üp1ôç=¸ä àôéÓÈåó<Ñãv_3>1q=àõzx«ÕŠ……yxIìvǶmÝzûOøã[o½•¿óÎ;ÅŸ÷Pà’ïy.Ÿ'Çñ’$‰ããÿÅátr”R) ò+Ë+à8¡PÉDàBáR©$QB|8Žt:…f«‰©É)dÖ2¨×똜˜@>ŸG¡PÄÕW_É©IÜß}xðÁ‘H$]×éÔ.D‡Ãh4Š`0‡Ã¯×‹¡¡!LOO£¿¿_³°*v±‚¯.U\.Öèå—_¾`ðä“O–––088¸®@ˆçyäóyÔëuÍZ©8ÀÛµöç»U•Ïó8wîvlߎn—õ-lµZèt;l|˜"ô½‚aÅ©'½4_#`Kùé=U‹hï±¾u;6ž)‹n§ñX ÅbÅbù|Ï>û,¶ïØo¼>ø ‡Q«Up°Ù¬ p¹”f¢]„B, °;jµâñ8îÜ’l³Ûÿhh`àGwíÛ÷Øû!¸ä 'I’l1›ûÃÑÈ$IB__Éåò¨ÕjE±TD¥ZÅðð°ÖfdtõzkkY £Ùl •Lapp¢(auu“SSÂWþöoñèþý „Àæphî¥ÊÐ ƒ°Z­Ø´i6mÚ„`0¨쨂¯{+øú¥"ü‘H›7oÆ‘#GÖ‚ªõ_^^Æ+¯¼€)€n·«½®~Ž$±ã …B˜ŸŸ_Çx;Ö^¿¯÷ÝåtáO¾ðÄâqìÞ½ÓÓÌ; h6›èŠ]Æìƒ:¨ÇTûR…þÛ‹ë/€*Ï(µº¸wÁ‚ „$àFX(…(vÑ_éG>Ï®Ÿj­†û¾óìܹÛ¶nÃÒÒÌf3Ö²køü¨×)`·³0Àív¡Ûé¢ÝîÀ"ŸËÁf³0xØŒýÝÒòòË·íÝ›ûy.µ Ê>tÍfóV›Õ6‡i»Óáˆ×I”N¥ƉÕU„"xg a6™qfn~Ÿv› ó p»\˜œœÄ¿þË×ð½G…ÙlÖšm‚ Uà©í¸¶lÙ‚-[¶ ho6›×!úoGðõKµâ£££¨ÕjXTJ†U Ïd2áùçŸGµZ!kkkÈçóðûý뀣££ëÈ·cí7Þ×/QáõyA8Ï>û,ž{î9€ËéÂØø(>ó™[±sçN´;ð´öáTT  ÚsL{JB~@ zÆ<JTÚ0©Á³Å[—…%…°´¼Œ……|çÛ߯ï|êSؽ{7^{í5˜Œ&˜ « x<´š ØHgÒp:ï&¢X*Âf³qf³YœŸŸŸØ¹cÇŸ¼|ðàÞ[o½•»óÎ;¥Ÿ×PàRû-lÄ—) ÎÆãqŒFyey>¯6« +«Ëp:]p:X^^†Íá€×ãÅÒÒ+£Û./¯Àh0°P!™‚(ŠÇóÏ=‡ï?ö}X,­¢-`×®]˜šš‚ÓéÄæÍ›ñ‰O|7Üpb±l6ìv»Vš»Ñò¿c®|Öìì,B¡&Ø<Ï£V«iî¿Â@*•ÒjTKOf2p«Ùç¸7î ¼‘ðfžÁù–(Šp8ÔŽR ÀoüŒCY†@–u¬?ÒÙZß"œê Ad}ܯvR½ õjx@{–ãÙ`A°ÀífžÝÈÈœdJñØþý8z䮹úȲ §Ó‰zѬ­6+ ¥"\nZíº]N§…B6ehm£Q‡Ëé‚,˼ÙlF8ùo~¿ÿÊ}ûöIû÷ïçßêt¡ i¹Ðöny—Ze©çp:Ç<^/V–W`2 ‘H$„Ãa¤’)H’„X4ŠL&N§ƒÑÑQä²9Ôë5Œ¡T*£P,`xx…B?úÁ Ê2¬V6z+`ûöíZLÙe—áŠ+®€ ZµÙÆþ»éÖ©M>vìØgŸ}Õj.— /¼ðÎ;§ œ;w333ëBžç±¶¶†f³ ¯×‹\.§Õ­¿]k¯~‡~©Ý‰ûúúSSSøÔ§>…nøìvx"vBT–ŽÓ£óˆ‚æ«ÆŸ¬'éöBÙQ]¢@uÔ 8 Èf ,ÁPýe6 ”*<ðÀý˜˜œÄ?x=8ˆR¹ ¿×‡F]mî@:“‚Ëåd=Úøý ‚Ï¡\)“¡¡!yyyŲijêïÿãÙg¯¾mïÞÆ 7Ü@L&ÓCuÖC©TºèvcV«õ ³Fog]J@À<³Ùdò‹EÔ ŒŽ X,¢Z­bxD‰ûK%  ¡Þ¨#—Ëa``ív™L±x²,#‘LhóäN?Žt&£ ·ÃáÀîÝ»ár¹`6›ñÁnÀìÌ ; #Ìf“f!wWðµ ¸ÅbÁŽ;ðÜsÏA’$Íúë@:Ö(¿ Aårn·[SêE¸1Î×ï…–þ"ÛÈ=$ ñxwÜq>ùÉOÂ`4¢V­¢P,‚ã4s¯aj¡sí±Ž ¨¥ õì?@G š"Ñô‰ò·ìUÅ3 T– ‡Ùb†]d©[5XY]Å«¯Âýß½Ÿ¹õ3˜››G.—ƒ`°²Â¸´Z½lÀÚÚšÆh4¬î ÄÚˆ ‚Àq‘l6ûö[·ÞþÊk¯}IÍ œ¯¥¸zMmÛ¶ O?ýôE ²,˰Ûíøƒ?ø­íÙ;Yš|©=€ €¥ÝéÌ3b±(»"Òé4¢Ñ(@D2‰P(ƒÇÒÒ|>, áö¸át80¿°»Ýǃd2‰R©YÔ8ŽÃìì,Âá0!øÐ/ÿ2¶Ìn$KàÙlÖÜ,Y’5×òg±T!÷z½¸ì²Ëðè£âàÁƒz,IB!(›Í¢¿¿_éÑß –––°iÓ&Mé­ÐOkí7 ¾vH›Ú‰DÀñ­b°T*áG?ú!®½ö\uõU¨V+¨”ËZÍF2™„Û­– w ‘Ë3€ Vg݃ŠÅ"œN'S ¢Øý#¿Ï÷ïûöí{å _øÂy§ ©çÝåráÚk¯}«—h“µ].$IzǯËK‰¬óZ­V{rb>ŸKKçàr¹àp8°²²»Í¯Ç‹Õ•›¶ã ™L‚‚P8Œd*Y–F±–YC½Ñ`­ÄÄ~¿_Ê®ºê*lÛºO8˜Í&-}&Š"dÈŠ%ûžå»C¡Î;‡Z­¶Žú«.µ!èFfàòòòëÈ@o5¶ßÈ0T}ã­z¿ÛíÂá`xŒØíö¬JþQîkµý샙×RzºãRöKôniï!PhÆÊwÉJóQÂõàGÀyX,¬‚Á`ƒƒƒ€ÉdB:³†;¿þux=^ìÚµ™µ5x}>Ô P*Ãnw(î„$©ÃF=(–‹ZåeµZ…Ûí"VÁ*uº]ûömÛ¾*˲iïÞ½r§Ó!çãv¨çW’¤‹ÞÞÍõ^Q¦F½žÁY¦ˆ„#¬üTR{m ÍV±h …RÕJñx ÕJÅb±X F¹|>¯’(iÅ8ýýý¬Ÿ?.Û½[»Ð °ÈTF§Ó$+Í*åSíg±Ø¾ Š"öï߯=·qé„èÀl6 Q5À x½Û¿ñûÎ'øúí|¯*AP¯×ÑiwΫ,)(ÇzhÂOYjO3îl'g™(­ÄõáÔ!ÐIõp”Lƒ#p„c™3ì;¢Ñ¨’ª ‚ç98xO?ý4¦§§188Ï¡\*±y‚íDQ„ËåD¡Xd)@BѨ7àñxX[q%KT«Õùx,.™áò™ééÛöïßoÝ{/œ¿¥¸ ò^ìön®÷‚àÒkk‰ù¹yŒcp`%Åe‹Çãh6Èæ²ˆÆ¢EéT ¡H ‰Õ¤–º[]]…ÏσN§ÃÚ>‡Ã0™Lغu+Œf: âN8‚nWB»Õbå­”B’em£Jó àüùN-5¦{úé§qèÐ!­iãÊçó¬-—â¨õ *ýÕétâB¤”‹µöê}ÕõWoUO#›Í‚ç ŠµïýƒÚúKMãkªœGÒ+:ßi%*â¯@ ]˜ ¼  š „5]aÝ„mºÐÈÈc3Š"î¿ï>œ]<‹~ô£¨Vkk+^(Âåt¡«ô p»YË0u¾@µZ…ÇãA­V@‡¹v§h4òÇ¡`pæ–›o?νլÀ¥^—< HY?hK³ÙL:ôj!‰pÁP.//! Â`0`5‘€WI &«pºœp¹œX]Ya©@Ÿ‰DƒÙlN©Öâa2aµZáóùFÑnµ˜&æ8H¢ŒN§ Qêͳ—%‰)Q„$1»(нaïâɸ뮻ØIÙ`¹Õ˜¾Ñh ›Íjq¾*Ìív9…°²ñ¢ûi­½^øõJ@CyžÇZ6Û U·œ•ùë@:@ÉÙ+€ bõÕJÁuû ' Ê>PÍí§TW(Ä´Š¢Stô" €Rp †c40† E° b¡@?ŒF#––—ñàƒ Ž`tt‚Õ‚V«I’`w:P(öÚŠ7ëMÍú›L&˜M&”+x¼Ôuâ°;$Ájól޴髲,s{÷îÅÏKKñK­€B J‡>]ªT©ÍnÓJaF#‚’©$d™" #“ΠÓé""§Pbc±Êå*Õ KçQPšRÕØ~ =¬@W¨zšBP„^­8ìµÞ¤UÊód=g€Éh‚Õj…ÃÎèÞ£££¬UÇá…^ÀãO|×_=¢Ñ(²Ù5¸Ýnt;tÚxë/Xð\Ïú³.M‚E@¡X„ßïç.—$×lýÂþýû埗éB—Z¨P §Ïœ9ô£ü€ qS““H§Óh6›ˆÅ¢(•*¨”+ˆÅb¨×ëÈòˆF£ètºÈd2‡Â€d2…P0·Û ½fµZ5tŸÊºÝnO@dÕõ—zñ.(Ä®¨Lá$e|™&ô”ÀÛSêç<ôÐC( çÿô+™L®£«.*•ÒÀÌwÒÚoÜT  …:ÝÇëeSqýßH‰Qí}êbªA^Ý‹œ(…Dšg´ÜÜøÉª¾!J 1¦ ëáÐ×ׇá‘aØl6FþöwP¯×±sÇNH’ ‡uv8%–ôx<(•Ê0™Ùd§r¹ ¯ÇƒF“‡6« ²$q‚U@ÿÀÀm~Ÿoòæ[nù¹.¥P…_¢”v !èv»+O<ñÄŠÁ`@4£j*P’e¤Ò)%$à‘H$áóù «+,càt`uuUkÆÙétõW’Ðét™+©ðÍU—”‚J,Þ—eT’•¦—2DI†¤Ô¯K]V~+É2ÚíŽNpÞF ’yDQÄÝwß}ÁÏQŸW ]Tœ@ý µŠÅbÑõíZû7Rj£$IÈes0xP*ƒ’^(EtU< V5‹¯yʳúyÁ^Ø@u)A—Áéþ”(ä Æ 0Â"°Z­‡ÂB<‡Édbƒ÷ßx<Ž+¯¼år’$Áår¡Äè¿ ß¢ ¯Ç‹jµ žã!V”Š%ÖK ÓF»Ý&ÃÃR»ÓñMMNþƒ,IØ»wï{~ºÐ¥V@GÙG=|äð­÷»ÅÂøÚv› n‰ÕL&#þR©  !›Í¢Óí ¢gsì<^8BP®”Q¯×!‰Ê/1!W-?»/+ ,·M–ØëTb¯©Œn§Yy¯(‰E‰Ý{¡ÁÅüЪexæ™gðÚk¯iä™7ZŠ’\‡¨@¹\F>Ϩ«¢(¾cÖþ|¯æšÎdÀñ! IDAT’$¡^g“…«Õ*xž‡ (–ŠÚȱn§ ·Ëb¹¨Íl·Û|(–Ý^ï''ÆÇï‘GÑB÷šð^ Úª„&€ÕÇ¿ÿýÅr¹ŒíÛwÐd" ¯y&Q‰D/äÙ4—H •Jår‘H­V kÙ,úúáñx Ë2VWVËåÏå€F%Ï+V^–dH’*쪠«`ZO °‹RVÀC& Tbœ‚N»ƒnWdÞ‚Ì.ðóåæyž‡$Iøæ7¿©=§_*`JÅd2á×~í׉D Ë2 …‚öwª7‘ÏçÑh4´Ï~'¬ýù„_½ít:ðù|xâñÇQ(XcRЧO”PJ7>¸Mi ,Ô§©&ìzŽë,¬[;}‡Úª\ý&êŒA“û0Ðß¡¡!X,äóyܽo >øÁ"³¶A¡ÿ–Ëex<½Øßn·+a‚2eLA·ÇJ¥ ޵¤#­v ±Xì/<.×À§?ýiéèÑ£œª´ß+ë½àH``@ @@ýÌÜܱC¯B0$‚ÕŠ@0€z½Žb±ˆH$‚v§µL¡P„ét ~Ÿf“™õ~w¹`·ÙÐítaµÙN§‘Íf±šXÕi¨é1-@ï±&¼T³øj—[™Êd I%É èŠ"dY!€,1 °Ûí00Q“ézðïСCëÀ?µYµÒ^¯ŸÿüçñòË/㡇¿øEÐPÕÝ'„ Ùl2֚łn·ûŽY{U!l Úí6œNÖ)ø‘GÉbÖÎÕ)ª¹Õj«¾ÕQ€{¡Âú j½0ëC =œÈ\}bQ z=LF¦¬6" M8‚ð<|?þÑ0=3ƒ€ß«ÍƬ¿‡ÕjE±T‚ËåF·ÛA§ÓfÙu¸(Ç£R©Áãö Ùl»Í&¹=žø¦M›¾ô^4zÉ!D šj”Ò2!¤ÙÅÅGù^ª^¯ãÊ={¨$JH¥S½ô_"§Ó‡ÃɦäX,ðú¼H¦’ „C0Dz5ËèëïG»ÝÆÂÂ2™ jµ*Õ‹t½BPãÊÜTIýÁd(ö‹yg1H{Vù&eÔÅ „c^€Áh€Åb Xðû188ˆÁÁ˜L,//ãÛ÷Þ‹@ ˆ±±1H’›Õ†¢’÷ïv»è´;p»=(—ÊFƒ•²Jn+#&„˜MfŒONþ_FžßvÛmR§Óáô„­K¹.¹0›Ìղ뼀R¥rú?žùŠÁÀ“þþ~ê÷ûX:¯ZA4E³ÙD6—C8‚,ËHg2øý0ð¤’)xÜnX,*Õª6fqqéT N§GÿU]Ò«TÏ!³<·Ì@(™RH’’¤ºÏ‘D_`5²$AY¨À¦ xèá‡ËåX ?Ï㦛nÂO<矟úÔ§`·Û5Á×·ôE[¶lÁþábqq4k ô˜‚*øÓZû·²©8€ÉdB À‘#Gð‹/(€ ’9!2tlD¨‚ÜsçÕø_ Tìt½|ô2êc5åØ áöÚ ØCÇÃh0B6à5ÅÈèèÿÏÝ›ÆHvé™Ï¹7ö}ÍÈ}ϪbuQ)jiÉݺ-ïFn©EÁ­i·Ì»í± ̆óÃŒa`>V©¾óC»“§œóÀ@ý%A&4tÆ»›f ôâw܉ŒÂ‘}–íàó™\ÛºÆ_þË…­k[üëý¯yâ=O0éšÚù/ºËjÀ0™Lò÷þÞߣ×ëy Ѳ,º])ŠÒf¢?ÉBÿi¿ÞîññxœP(ÄÉÉ _ÿú×U šð$Pº~C½Ïþ{¬öñïdj<Öÿ2›÷Ä?ʺmRO0™êEÀ83Иƒ!dFå÷¤L8!ŸË³ººê™‡ììîò•/™\>Ç/\¼(µõ±X piOÔþ†i¨2Aš‰X–E¯×#•NS«ÕÈe³"‹±´´ü¿ûLsê}îsöp8»xh@¿hÛq¸pá‚ 8®ëZÈ,  4…]àॗ^ºw|tÄã?îNMMÉEÞn«2@Öú…éi,˦X,‘ËçeptD*" srr"íà îlßfoŸV³uª^uUïk°Ç·% vzg‚*ôt®\ jÀuÚí6œ„þÏÿ‹ó 4R¦,Ëõ̽¥ xæ™gXYY¡ÙlzÜü`0`ww¿ß üyìöoÇa4 ……BƒA^ÿþ%®_¿F0P×kÌV8`îx±A*ðOƒxžÀg"OðÀþS¼çP˜`t! ù;LÓ$è÷ ‡‰Å£ÌÎHV 05… ¼ð |ëßäãÿ8‘H„vGªëÁ`ÈkJ+3Û¶I&’ÔkuoRt»Õ"›Í‘HÄv\wãé÷½ï_ÛÚâ·û·‡] <ô`jj $# û4Øš›W¯^{ñÅyÏ/¼‡p(ÄáÑ‘rw1½EF9:: ‰N¥8*aÒìää„ÑpÄÊò ‘H”R©ÄÝ;w(•Jê>Yç{Ð vÀ=µØ]%²½îÁqý¯þÚõ]‰H@ž¯…EÕJ•v»ãíL£ÁÛqކ GC\W¦õãñô®è8¡Pˆ_þå_¦ßï{ àh4¢\.{mÌ.ØwºÛÿ¨2À0 ’ɤjrøú׿A¿?@˜r8¸Vü)‰$‰£h‘ޏ«¨RÇBhªP_ƒS—d|mø±Ð8ÁWQˆ aàóûå¤áˆì7Y\\buu•xlÛËKËô‡CÖÖ×ÿ™ã8‰‡] <ü Àª|>O6›uKñ %X{ó7¶^ûÞk<öøã,,,P©TétdŸ@W „ ÓlÛ¦T*’ËåðûLŽI¦ä„_=nªP X,rûö6zCí¨²/àt­íØ/ñÛs©Ñçãû‰úÛŽƒcH¹±¤üõ¼Ê~ld{b#×u¼™{28 q˶<¼B^6ù7G£Qþæßü›r,— '''4 øy}éů¯Q§#mÚƒ333œ={Ö›ŸøgögÉÉÆjǧô.BžÇ‚æè5ÐgLô¡ëþSlâ¸öwÇßd  «pCCxe†@k ¦é#”ÍB±xœ9%ÎårŒ,‹¯~å«l^Ýäýï?†aÈÁ" ¶Æ¢1êµ:ñx×qév:RØnaú|D"aêõ†¼×øýv"™|ü½/êRÀ|X¥ÀC†aÐjµxöÙgùõ_ÿux0PÑ+•Ë7¾þõ¯µæfgE¡0í25•ǧˀTŠh$ÊÑñáp˜t:íñýù\Žr¹Â`0`qq‘p$Â`8äæÍ›ì0R ÜV-ÀÎäÎí¸^w «Ò½p=Ù°‡8§˜½ó;ªuØ Ðhc,l×Á²¤PȶmlËÂq]¬ÑÛ–5¶5´¼š^§‹: øÕ_ýUΜ9ãõ²÷û}J¥ÒO$êùi¾ëßmÛ6FÃ30ÙØØàÂ… „B!Z²Þ% Òjµxá…$Ò^›J裨­b”ú}÷‡˜¯ÃOEþy Þ{®b´Y˜´s…TúCY” ”²kŸnŠDȤR,//³¶¶F4¥\­ð§ò§ôú}­i4š¤RÒ`8’J%©7äîhªû{½>¶m‘H&©V«är9#‘HÉfÿ~>—{æóŸÿ¼õ°J‡ô1øÿà¸>ŸÏ±m{$„Д`S}?¾téÒöáá! óÄb1’É$ÇÅcÏú»\.3 ) 4 ÚméãÞï÷©TËLMMy™d*ÉÑÑ!·oߦݒ‘Ü} …÷ôëŠ*´ÛÕ=ïš KÕùüsOi d€Ç±Æ H Cð0õe9RNlÛ6¶FðmÕ|cIl`82Hßz_„Ãa~ý×ý#pppð3×ýoWÿ;ޤ2Íf“ÅÅEžyæ.^¼H$¡T*1 Èd2d³Yo¨êõë×¹¶uP0ˆcË]Æh¼—ª+Qи`Ÿ'ÒcN=þCˆSOƒ00´(I•]œ¦#eÓ•` H$!‹R(X]]e~~¿ßÏo¼ÁÿárñâEr¹ÜX ]«Å:m¹û·Ôg*RWÌ€ãÈŽÁd2)B¡c;NèÉ'Ÿü¿lj|N•ÞƒïŠ`rÞââ"Ï>û,üpкwîÞ½úµ¯~ÕZZZ.\àää„N§Ãôô4½^j¥ÊÔ”‹E2™ þ@€ããcñ8ñxŒÃÃC¢Ñ(s³sŒ†#®]»F±(Õs^óˆ«wý €ÆêÆ!×~›f"µÈ½r³› ”­³[f®Î lG}Yžùˆ5²Y#,kÄh8ÂŽ<µü'>ñ }ôQoì”^ï$ Щ¾^øív›••~í×~¿úWÿ*¹\Žýý}ƒ…BT*E§Ó¡Õj155E8Æu]^~ù%9ëÀgzèý)°Fê'2ï_](êP§íÞ¡»GŽ­ÇÇX€kI±~¨‡3È?J*åd¡H$J"gaaõõu²Ù,ýÁ€ï|ûÛlooóô3Ïx›Ëp8$¥MCƒ‚¡†ì Œ¬‰¤ÂÔ¤ªN§cÌÍÍÙ¡HøÉ'{ìs[ª€?_ówE˜lùøô§?­Á@­ ì )Ážëº{ßúæ7wG£«««®®õ}>ÇGGÄ“q¢qÙø Éd2”ŠE×%ŸŸ¢Z­Ñïõ™.…B†ÁÞýûܾ}‡^¯‡pµÍ—£Ñ>p¹ƒ»Ž—RŽË ìIN‡`0ȧ?ýiºÝ.ŽãÐh4h4M÷Óìöó°,‹z½N§Óa}}¿õ·þñ/þE|>×®]Ãï÷³¼¼L"‘ð&ïD£Qr¹œ§` ƒ”J%¾÷½×”W3^Œ“þ„œWLÜ7)ë1Lã¢/ ãùäó§î› "KÐ BC•jñ‹q³Ïç# ŽFÉd2,¯¬°²²B$áàèˆÿç÷~H8̇?ô!J¥±X !„gÚi·q]¡üêò~×¥Ón“J¥eOë233cŒ†#ffgÿþT>ñóŸÿüŸ»à»"ÀXô©O}ŠÇ{̵mÛRY@EÍ7on]~óMfg瘙ž%‹sR*L姨Td­?](Ðjµh6›Þô]åØ ߬Œ4 ¹ºy…Jµ‚ ?îÄKã]­ðÓ¬€=.´RУ µ®@[‹96®ó,/(Œ›ˆ$û`{ìÁxqŽäcFRI8ò¾† †2èv»üʯþ ?þ8Íf“VK¶Gë…ü“ìözáF#êõ:½^sçÎñÉO~’_ù•_Áu]nÞ¼É`0`yy™\.'3¯j•d2I¡ AØJ¥ Xþà—Ù?Ø' *~~r'öN}&ÄäÿÄäxq¹€')?aˆ‰…>X4 5aW÷NõhœAŸaÊf¡h4êY‡ÏÍÍ!„àµ×^ãå—^â£25•'‰Ðl6ñûý„CaÉ ¨cƒ~ŸT*¥tA‚ÁõF]g"‰Ø‘h4þÄãÿ+ÇqüŸûÜçÜ?ÏRàáMý¨w,óä“O8B]hJ°×íõ¶¿øÅ/–MÓï{æ}n¥R¦ÕéP˜.0è¨Vªr‘ A©T"N9:>&‰HÆ)‹Þ”[ À;w¸w÷ÖÈBZ†I’­<¤úoBæëqÿ®ðÔn-A¼‰ÁQý“´à„hȶm%²ÆT =¸iÀÑ[¤r'©:4²ŽFŒìN‡P0Ìo~ö7åo0 R©¼-ðàâŸì&¬Õj }ôQ>õ©Oñ±}ŒÑhÄ7°,‹••‰ûûû”J%VVV˜››c8R*•9…9 I 6!¼A-ß}ù»8Ž#SxÍ—^BójñN8‰±>`\ës\<ºp²ð§g Gc ©H\Õ•è=·Rªì%‘HÄY\”¥@:¦ÛïóüóÏsóæM>ñ‰¿Œù€_€m;Äã jui+&î¶w? J™ù|ÞvàYe)îþyú>üð6ÇïüÎï¸@À±mûAJ° T_ýõ­ÝÝ]6Ö7hÊ9mAމÅbÄãr‘›¦I6+{d155E£Ñ¤Óé0•ÏøBŠ:®\¹B³ÕR “=æJ O/\‰b?¨Äãü]ÇUî“Vã– ¤¶@ïôã$w,$²åP’ñ‚µ%C`Ûž.À)`4ÂY†P­VøøÇ>Î{ßû¤gúvéýƒ;þp8¤V«1xâ‰'øßø >ò‘Ðëõ¸qã®ë²ººJ$áþýûœœœP(˜õÊÀ›ÀT­Vév»$ Oë÷ûÙÝÝåêÕ«ƒ!úÝîàQsÂ@†í+UŸ  búÌ1& ÏóJWu+PÏÛùQ lO6Œ±¦!˯ À¦©dÂ!¢Ñ(ٌԬ¬¬‡ÙÞÞæÿû˜ž™fmuÍ£d¥W š+Ø•s&>¿OúÖj$ ì‘E¿? –ˆc†áóùY]]ýŸòùü£¿ùç8]è]†Ã!=öŸúÔ§œ‰2@öÑ+W*׿ý­ou‘ˆ8{ö¬‹Æ8)—q\‡\.G½^§ÛéR(d?@½F>ŸÃvNJ'¤3L¿ŸJ¹B:!‘ˆsãÆ îßßõ½Lë•0ÈuÔN¢ëü‰~µèåãÆ¦¢^Àp5ﯕ†Z04f&é5­p­A°$`i0Ðg–‡#Yô}|>ûoÿm,Ë¢\.{Ón\øúZ×j5lÛæ½ï}/Ÿþô§ùð‡?L»Ýf{{Ã0X[[# ±»»KµZevvV `ipïÞ=çÏŸÇ4MÊå2½^L&C:¦ßï+%œ_M6¹téFŸ?0Ö†ÚÙ‰lÀ•^~ÚjëàðN»ƒæÐÓ©¼a TšoOp€p݉ "d * „!0L —‚1ø|&€œ,¥eÂëëëÌÎÎâ—^—ÿóK\|ò"gÏž¥ÙlÊ>„´‡Ãøý~šÍéTJ²4¶-éÀº8â3}Ôë ±²²d»B¤9{ö_9Ž#&§ ý×<ÞàmJçž{n ô(A¡(Áo}ë[·÷îßçƒü íN‡f½AaJú¸”OÈæ²ø|>JÅ"ñDœh,F©XÄð“I§©œ”±l›…ÅBá0Õr…ÍÍM•¢ oÞ¶—‹yl'î‚î8#˜Ì WK‚ÝñÏìq¯€,Té ÅAjk}mé2@ÉŽ# iB©  GP$ÕJ…|ô/ð|€ÃÃCÚí¶‡èT0P­Vq]—gžy†Ï|æ3<ûì³Ôëu¶·· ¬®®Ê†˜óóóÒ†­Raww—`0ÈÊÊ †aP­V±,‹l6륹ÚJ;“ÉÐh4¼Éº—.] O¬Ê DoÅ~¿ŸH8Âp0äòåËüÇßÿ|ñ‹_¢ßëáó™Œ%ÅB˜” Œ‰²BgŒ?ù:8E)Žñ¿ÏG0”Ó…’q–––¼R Z«ñÿþ§ß§\.óÑ~”Zµ&Å>.t:]ÒJ*l±XŒz]Š…Ç¥«Æµ[-„€L&k†a‡"‘üÂOxÓ…t÷çÛÒ?‡ã¡Õ_çÝÖïýï¿OeHJ°sogçê÷_Ýž_˜†D¢Q"‘ÇÇÇ„CaR©¥“.Ïå©Õjôú= …n—z£N>'›†Ç! pus“ã#0Æ]€®«fܨRÀQ;¹'V ßd× œ1`èÍp´Û°Ëd£ãXêùOK‹µDØYR!hIœ`dT©0RB!i Ò >ûÙÏÒï÷½ôÜu]úý¾lƒ6 >ðð™Ï|†÷½ï}”Ëe¶·· ‡Ã¬®®"„ðÆ”/,,xsöööˆF£¬¬¬œ:gmmùùyêõº¥{`«Î0âñ¸7¬åÆìÜÛ! ÊRÀ/BÀG ¤^«ñâ~‘ßûüïñüóϳ··G6›!•Nã8®êæÚXh¼°rÁ«ÏÓ8Pe‚Úé9…ÿé!¼¬Df²Äðû¥o@,#—ϳ¶¶Æòò2¡p˜·nñ¯B¡À…G/Hÿ‰fÃkŒª×ë$Si¥ÝN¥h4êCå6ÔÔbƒA#“Í27?ÿr™ÌÙßüìg­+W®ð_xèàÁCK"ƒÁ ¿ó;¿ã¶ÂË\h!ºŽãì¾ðïÖª5.^¼è¦RI*Õ ÖÈbJ1­V‹©Â£áˆJ¥B.›Ã0 ŠÅc’É$‘¨ ñx‚üT“r™«W¯b+SO×ëbÓ@ŸH©S{Ýìxz… è¾eâ8Ž'v1;`{ÊAež¡¤ÂÞã½su‹±Ê¬±0GMõÙ¶C¥ZæƒüúЇØÝÝe4y¦ùÈGøÌg>ÃÅ‹)‹Ü¹s‡X,Æêê*ŽãpïÞ=z½‹‹‹d³YŽÙÛÛ#³¼¼ŒeYܽ{—^¯ÇÒÒ™L†££#z½ñxœ©©)F£‘§DÌf³D£Q:ÕªØ"„à{¯½Æp8Âôù”ß) JÅ/¿üøGÄ+¯¼B»Ýöp„D2A4ÃÁ‘Á4 IDATÕ–c:s`,æ1„1!0:¥x÷q<À0NÓ²²0U) ‹E½ƒÓ…Žëòíï|‡^x}ìcÄ1jõ:éL†N» @"§¦†" ZjÊîÇãÔë5"‘ˆ˜™ž¶{ý~îÂ… ÿ§cÛ|îÿccˆŸ÷ñ®Ò: øä'?©³Û0 =; ê|óòå·^}åÞûÔS²=©ËçÁ•Þù©dŠP0ÄqéXš>$“œ”N ›ÍR«Õè÷ûÌÍÏŠ„±,‹·~ðJ'e¤ÖÞ\°]O˜Æƒ;¹– 3¾í °è ŠÐUŒ‚ãèÝ]ý\•R¤z =¦Lõ XÊaȶ•¿à„¹‡¢-[5)‘í8üÝ¿ûw©×ë†Á/ýÒ/ñÜsÏñØcqttÄÎÎÉd’••F£÷îÝc8²´´D:æððÃCiž´´Äp8äîÝ»J¥888ðÎ9{ö,®ëz–d…BAZh«QfºÐ¥G©XäúõëDÂrÉññ1o^~“W_}•LÓ$ÓétxóÍ7¹té’ì54Н»^¬Æ„¶<,@b„ÆiÁ”y½º\’„Ð)…ôj4 Ù' ŒDcÑ()uÝÖÖÖ¤õø˜?þ£?¶mìq|¦I8R¢rÔx¿/Áf³O9Õë5’ɤììõÕ¹#3ÚùBáãçÏû;_ûêWí?þã?6'¿þ<í<ñž|òI677'³Ýît»Ûß}ùåê'þÒ_Êd2÷èèP$â1•HøÈbvf–V«¥|ædVÏå0 “ÑpH<çèèˆk×¶˜šÊãuêºÒu¼‘—Ú¾ZÝRµŒãe–¶«úÑÕÃ])4 ©MóîW,¸=@Uèê>øS,øßn4œ?žúOÿ)³³³rÌúþ>Žãx*½J¥B©Tòv÷ápÈÁÁ®ëzT^µZ¥X,’H$X]]¥ßï{“‰òù<@@*2ÛmÂá°gÄR*•èõz²vŽÇ½òÀ¶¥×~*™b÷þ.ù©<õzF£áMî÷ûìîîrãÆ Ži4<òÈ#œ={˲PgxBC¡û¸Ê?ÀSŠ CÙŒ¹(QºF*HÈ·F=."ßuªõ¦ÏÄïú …#Dc#¦¦òl¬oP.—év:ܸqƒç¿ô%þÚ_ÿë<ýôÓܸqÇuH&”OÊ€`“\6'[¸-‹d2I¥R!’ÞGÇG,..åJ™B¡ðOöö÷¿ù›Ÿýì½d"aD"ççÍ ¼+2€·;t$JP[†µÊ¥×_ßúÁåË\|òIfff©Õët»]¦¦¦èõzÔåøf/+H¦R„BAŠÅ¢‘LR*–ðûÌÌJÏý7ß|Óã°=ŒÂ›…œ6ûx÷M4é´]gxãó]m9æzME“íÅÞC&{ ´pÈV„òÐv ú¼ÿ™÷ÓíõØÝÝ%ŸÏ³°°@§ÓáÞ½{¬¬¬FÙÛÛ£T*155Åüü<ív› Ãð¨¯ÝÝ]J¥ù|ÞÏvç΄¬®­yå@µZ%‘H0==¶)F$“I¢±GGG4šR¤¤Šb±Ýn—7ÞxƒçŸž^xýý}¯/x²^Ó@CáeRb $ƒ€PØà„pÈPâ$C;ãöc…QøM¡@X4F,c~až3gÎP(èüÉŸü ›››|øÃ–Á,Åu\Ú]9\¤©½b’.ŒÇä®Þé´ÉdÒ´;mp!‘ˆ ¿ÏgÃáÙ÷¼ç=ÿk½VãððPü¸ÉÑ?ËñðÀxAzú¬¢]N÷´´eØÁááo|ãý¥ÅE155å+šR±D,—º€“’Ì 2ióÎçó´ÛmZíSù<±X Ã0Ù¹wÏ£ÁôÐJÇC•“b“íÀzá W;ßLЍîBσp ÔòcdÇáiÂ=ž(I ™˜p¬AB[‰ˆê:…|žÅÅEšÍ&»»»˜¦yjQW*¦§§=zowwŸÏÇÊÊ ~¿Ÿ{÷îQ.—) LOOS©T¸sû6>ÓôØ‚›7oR¯×Éd2LOO{Ŷm“ËåˆF£´Ûmš iŠ2 ²··Ç—¿üe>ÿùÏóío›R©DPMðѯÿÂ… „CAÓT´¡ib Ó”‹Ô4¥°G˜ºžWɾ)0„)ù}åd`zµþd™`hë1PìÀTN†O*C¡±hœt*Åêê*kkkÄãqŽ‹E¾úå/cÛ6Ï>û,ÑXŒF£)€¡ zd*Åp4¢ßïK;±fŸ?@(¦^Ó%ÃÇuÌ™éi;“ÍþÆÆÚÚs£Ñȶ,Ëüy³?üÇsÏ=§)A=E¨ ¼ö½ïÝ®T*Þ4ád2ÉÉÉXÐh4èv:LMMÑïtK&BJ'%‰`€v«M4Åq]¾ÿúëÞü=G¹uQZvWÓò~½K«¿×Qµ¿ÐšMhª2Á–öžHîîZ_ ËŒ±3Ñ$8AIj¥¡×…¨”…¸r~;N~e\aªë÷ûÙÙÙ¡V«1;;ËÌÌ Õj•ÝÝ]+++˜¦ÉÝ»w½A,…BR©ÄÎΡPˆÕÕU ÓäÆQ(8sæ ¶m³··Çh4bjjŠH$B¥R¡^¯ …ä.Û¦R­rõêUþàþ€÷ïþß}ùe:ñxܛΫŽòùO»Óá+_ý*;;;<þØcD£QB¡¤ Û¡Ûé’Édi·Ú†A4ó4ív‹t:E·Û5ñ¸Íç.œ?ÿ¿ ñß]øQUÍ¥ÇS„v¾ño !8³qÆ=::"™L G(•ŠøRé4•rÛ–Y§ò’&¬VkÒ44™:ßã l[•Æ€k;ÊxíŸÄ\yÿxœ˜6‘çâÕ÷à‫‡ŒK׋$òù\ÒáÆU£´¼çp¤¥™Î<Á’RYØöG-¦œZ|{{{Äb1–——FlooÓétXXX “ɰ¿¿Ïîî.‰D‚••,ËâÚµk”ËeX\\¤V«qóæM9hS±FƒL&ã5 •Ë’YÉd2r¨h­†eY¤R)Âá0Ãán·‹‚`0ˆÂ39}â‰'H¥’¸®«´úáW‹Ÿ±@ ‹To€ÚñÇB-*#âZD$]ˆ—Mxå*5´6@©M±¾¾ÎÊŠìÜßßç _øÁ`§žzŠF³eË×Z«Õ†B^SPZ %ßQ^(ÌxŸ_Öø¦o<(E˜˜†©©n«ÒÀ4¼rÀ0LÐvà¸xS‚„jŠ9u …6ŽuB oðI£Ïh$J4£P(pöìYòù>æÖ­[6660 ƒk×®qrrÂüü<óóó4 ¼rÀ‚b±H·Û%™Lzˆ«ÕÂP2YY—eY|>ŸôÕO¥xäÂyÇÆ0LLUŸ¦ú2¦aà3M ŸZȦáuõ™¦ý( P¢ÿ*`&†¾rB…ã>­œd´þÀTìD($‹zYÒêÚ*áp˜F«Å[?xKJ𣒠m¨–áH$Bµ^#Ox‚étZy "}Ôäa! :ÝŽ±¸¸h‡"á s³sE©:.YÀ»>ÀiJ0 º?¢K°üÊ«¯^Ûß¿Ïc=FU¥ZÉd’r¹Œëºäò9šÍ&N—|>Ï`0 V¯‘Íf1 ƒ““ÏjÌq|¦É[—/S©T$à:ª3ÐÛz½Ýy¼Ókqô½·[OÔú®ÞÉÕóàjºq"ƒxÀ•Øêy½ç’òdÍYËE?Qãêrc¢&N&aÙ6«««.p||L¡P`qq‘z½Îææ&½^õõu …ûûûloo‰DX__Çq¶¶¶¨T*,-.R(ØÝÝåÞ½{Äb1oŒùþþ>n—L&C<§ÙlR«ÕðûýJ7ïÒjµ°-KNÙQó tÓÒ£>ÊâüöÈ’€æ¸wÀñèŽÀ …ŸâÔî®n«Ý^x” Îô5C‰†ÎÆô¡ÎÆ Di˜Ò=(“Éf8{ö,‰D!ׯ_CƒÓMÕ2<è÷©éB:ã …BÔk ³ @0•—º”H4òÓ4‡îÏCü®?ŽÚœ¤ßÿþ÷OR‚]&(Á£ããëß}ù»ƒT*-fgfI&“t»]šÍ&ù|Çv(—ˤÓiÏ©FŽ…JP.—Èesž~>—ÏS:9áÊ•+ø C¥üúoÖ4Ÿ^ðúu¨Å.Æ@ž x=*‘O ÖÏ3Î\ÇUÏ1Þï÷ÊT©¡Õ‡Z«³Dt†ëkë„B!nß¾M©Tbvf†ù¹9Êå2››› ‡C666ÈårÜ¿Ÿ;wîH$X__g8²µµE£Ñ`ee…\.ÇÎî.ÛÛÛ$ 666p‡{÷ève Õm²U%xI§ÓضíÙ—E£Qü~?ý~Ÿ~¿iJé­eY<þøã$’ jTšÚÝMÃÀTA@f \áݧA¥Â)³ÃÀ0'n{ÀßXÿ/0¼á!²e‰ž8Lí$›…b‘K‹KÌÌÌ`š&•J…íímfggi¶¤®$Q«×ˆÇc˜†A«Ù$“N«NA‹dblh4›¤Ò)F#Ë0 ƒ©Báé@ p°‡Ãá;ÎÞà¿„@€P0è9‡Ãažyæp]™® )L ßxXªÆ SjöµSv7 ©…®ûHëÇ@!Þ˜ží˜PÔ¡pôï5$- ‡äx±|žååeüÍf“J¹,k{Õô3Yôz}Òé ÍfÃ4e€Ô,€€N»íY‹9ŽK"ž Z¯Šd2igÒéh<û0€ã8â ƒzøIÿü·± Óý=Ô(1 sóæÍ«¯_ºÄÙ³g…ãÈ6X­èõzLå§è úÔj2õB¦þñDœp$ÊÉI¿ßO*•×% r°¿Ïõë×ñûýÞ.,44ä­õÉH<û„´(Hÿë}èä¶-Ñ}äN} ¼Óõ¿Ð±â+&ƧêçÅÕ¾úb<‹o 7à÷óÈÙ³œœœpõêU„œ;wŽD"ÁíÛ·¹ÿ>Ù\ŽÕÕUšÍ&›››ô{=ÖVWI$ܺu‹{;;äóyVVVh4\ÝÜd8xúøƒƒ/øæòyºÝ.GGGضM:Ƨd±Ý^Oé–½ýÁm‡µ°°À#çÎaÙ¶ÚýMoç7OÕúrQÊûMÙ`dj¡*e )ƒ‡iL¤ô»¿NñÇbJ 4¡&Ô½†WÃøL“` H8!‘ˆ3?7GH1Ñh”“r×qH¦’^·d  ®J‚áhÄ`8 ISo4½VèZ­N2™ÀqÚ­6ÙlVw½þ€mÛ›|YÀCÀO&Á@E :oöF–u÷å—^: ¬¯¯»:ݬTª¤Óiå“i–HP)W† “ÍÒTA"ŸÏÓëõèv»d³YLÓÇkßûÃá!Œqª®À;¹@õâÖš¹áj%Û˜!p=RÙÕÌ‚\özO\ ¨»&j¡QþSV×ò÷ Ø5€!Ÿ_¦°Žëâóû½”=rëÖ-ööö( ,//S«Õ¸zõ*–e±±±A$åæÍ›Ü¿Ÿééi–——)W*lªsÖ76d{윔Jd³Y¦¦¦è´Ûà8’~õ™&•r™N»M4%ÒWƦšéÙxîÜ9fgg%#à÷cj °!¦9®Ëq0^àS f É  ‚Î !Sys¢!h¼ Äx¡{ A Èàê õù ƒ„Ã2Ù,þ@Ó4†I¥Ò¸Ž¤’Édh·;à"ÍCªU"‘~Ÿ2I§å¬Ñt*E³Ñ݈ሠ„#á >Ó̸R¦úŽj€wAp◠ߘ'Ÿ|’aÖêßãÍW_y…§Ÿ~šL&£\‚å81m“•S¢V«E.›Ã¶ªÒ£Íkp ‡ÃÊæÊÏ­[·¸sç>¿Oe®·€qçômWÝïx¥ÁÄ+!”ÊÐõŠY¹ø]E Wª u&€u&cPת¨ ¼=9g_d8sæ ;;;ìíï333ÃÒÒår™­­-„lll ¹~ý:‡‡‡ÌÎͱ¸¸H©Tò2‡ ~?×®]ãèèˆùùy¨ÕjRËoš ÉÓîtˆ'D£QºŽä½}>B¡¶m{®Æ@€ÿâ/âp ý÷ù|ªKo¼k?¸£Q|MÅ囆¼T_òhJqVá­uáz‹_bòbz¿Ã4ð«× Gp]—d2I«Õ¡V«+±™\Ⱥ0¡v÷N·K6“¥Õn!Äb’ˆF£¦I«¥¨ÁA_¸®ËtazÞ4Íe×umÞá~ø`\>ÿØc üä'?  ü¡, V¯ß|饗ÙlV¤Ói·Ýn+°´¨Êd3e•úG"ÊåLÓôÆ9[–E&“‘AÇX¶Ík¯½æÕí™§»§öãô"—Ü2x»ñÄëöþ;‰ô+–AzàŸÑÑq¼|À{¼ª"$Ø¥³ P½­xÙ‰P5¬) Û&rîÜ9‰‹lmmaš&gΜÁ0 ¶¶¶(‹,,,0??ÏÑÑ[[[ø|>I ÁÕ«W)–J,--1;;Ëþþ>·¶· …BžuÖÁÁn—t&C<£­‹ýÑh”ÑhD«Õ’(w$@0ä=ïy 9w¡¼Ï0=Þ^×á>sŒ ¦ÖŒw~¹˜ÇÒa¹ë›ž¡±„’Ë/Ÿáó¨GÐÎBb ² 8ô¤a~!£Á|>OÐÇï÷áóùh6e¯DOÍH+aP@õ Ôª²Àqd Ìd2´Úm†¦E<'žHÄ×ͪ Q¼6àá€É(û“œ~š|pŠPK}?¹téÒ­»wïJqF.‡išœèÔ?.Sd3Yš&Ýžì ‡²©%›Á0 *• ©TšL&ÃÖÖûøü~¹HÕß/wðq=.ÈÕÚ›\û»¿÷£‰×?vÆ(½×í§Ò]‹zÀÖ¶0ñ8¡µìJßî(j+ “Ëå¸sç@€3gÎpus“J¹ÌÊò2333Ü¿Ÿk[[„B!Îll`Û6W¯^¥R­²²²ÂôÌ »;;ª¿?ÌÚš4ÉÜÝÝ¥ÛéÏçÇ-Áõ:ÁPˆx<Î`0 ®¬Ëc1‰Š÷z=šÍ&=öÎ_`8(OA¿'2u½n˜^Y`¨ñ^¦ ¦^}¯wøñûäõèóÌqý/ÄxÁ#&² ý¦ †º_¦O=:>ÂÍå å´¤f£ašÄâ1jÕ*ñxÔcñ$ ÐŲF5(ˆB²(‘ð²#5? ¡ÞäÿÆ3øÉS~ˆ„Ñ%¸³³sõ«_þòpyyYœ9{–b±ˆãØäsy:ííV[¢þ®tÓM§Ò^ê IU t°±™Ÿ›# Ñl4¸téu Ã8ÕÍrkjÎñ¼Ýû”ó½W&Œ‡}jXQëŒ&uTíÉzWW˜LÔÉž^Ð5>àJû­P0Èȶ¹¶uË—/377‡cÛl^¹B­Vcuu•©B{÷îqãÆ â±ë ®lnÒh4X[]%—Ëq÷î]n^¿N<g}}®b ºÓ…±XŒr¹LµZ%•‚™Ži !HL ÕèÞÀ–ÇŸx‚L&-Û SvôƘT”à©”_àa†W"˜*øéŸ 5 Ð8&ë/¨wE›•ž )f@v iHª˜–k×®‘ˆÅH%SAÁµFt:-Í[{}2™ FS ¡âqjj¸¨a2ôÕ¨7/(D"w8…b€ ¼#çà‡dÌ~Šc‚´˜4¢ëÂþK/¿|¯ßë±²²âÖëu²¹<8©”‰éÔÿ¤ŒéóK¶^c8’Ëåèö¤~ “ɪ c‰Dxë? \.ãó™xÍ80Ät‘> T?Žsô;5(dA–.¸®J!Ô)ŽëŒ?¤ˆSÁC^J¡RÆI$CH~{dsíÚ/¾ð[×¶FÜ¿ŸF³ÉúÆÙl–Û·osëÖ-’)©qït»\¹r…N§ÃÆÆ™L†[ÛÛlooK p}v»Í•+Wèõzr f,ÆáÑ•J…xÆï÷“ÍfqÇóˆF£ƒAz½½^[)/^¼Èà3M|¦Ó”àŸOazÜSü¼Oe 2#Ж߆÷ÿ1p§~Æ`2.Ôµõ¾”Iˆ1ZZ¬Ã¯Ïgò§ò'ø|>ò…¦i‰D¨Õåœ@Çue/@Vúº¶C2™¤V×Ô Ÿz½F:•Æ éõzR/Ðjb˜ñhŒz½é3 62øï ø)s€A 9Ý%Øïõû·¿ô¥/ñÌÓϸõzvGò©z„U2™$”©0$©F8Û–M6—•"¡¶lp‰ÅcCðúë¯Ë7†µ9 IDATÅ0Ôί&Ðh…žÞÕ½ÓðêsCåì§; ÔéêµMVDrŸÑÆ—’ &—ZÅ&Ü‘äḲk.c;6—ß|“/üÁð‹/P©T<žHDvœ]ÝÜdgg‡©B•ÕU*• o½õ–eqîÜ9¢Ñ([[[ÜÛÙ‘àÊ '''¼uå ŽãpæÌÁ 7nÜ T,2==M2™¤X,rtx(]‚§¦°,‹âñ1ý¾ô¿ †B´ÛmÚí6>ŸO2Ý.ëëë,--a[ò=6MS-N¹Ûëºß§¸áÇôêwC˜Ê9h žÊ I#šš%0Íq‡¡ÊLÓ7Ñ,4¡` õYL&S|ík_ãÒ¥K<þøc8¶M:­fŒ,Ò \öûýD#éš”LââÒnµ=jÐv©$Õjp8"»Õ¨±‘eÓë÷X4†ëº#¤¥ß;¢ß%ž€§QíŸäx‡–bàºî$%XûÁåË[wïÜ)œ;wޝ|å+÷|tt„Ïç'NS¯Õ‡ÌÍÏyLA.ŸÃÕr™x"A8¦Z­ŽD(‹\¹r…<ûzý¾Ûu ¨ “2“wq”÷œt± ¡9:¡¸|aÈÇ{?ãëäx©Æøº©»MÓG  ÛíðÆ÷_çû߃b±H ð&õ¤ÓizÝ.;;;ø}>ææçñû|ìP«ÕÈçr<òÈ#²'àÊ ÓdnnNú îí©ë“çì™3Ôëun\¿N0baaÑpȽ{÷(œJ&¥~¥Â±úÒ™Œÿêu,Ë"¨tðÝn׫yñÑ{Ÿ !Àç“×U ‚F£¦aÊëéÈ‹ç:.®®c L]²ŒÂ=¥¼®ÀÅ‘€¨ºpB56PgU2sÓ×W{Çuåy®À±m.™l–W_y…ÿûßþ[Î?O,– Ýn‹Å=GeÃ4h6›žUÝp4R¶j ¯AhïþÉDœV53;#³"Ë&™LS©”]¿Ï'B¡P×qÝàG–¿?óñ. úÃý“R‚ÿþßÿ{Û0Œ¡mÛ“”`ªZ¯_ÿÎw¾óÔc??{æŒ[®VD«Ý¦Ûí27;+-³k52™4~ŸŸýã}"á‰x’““®‹d šM†CIíyíÕïñäÅ'½n?)´jw0’±óÀûñâÇÞâ×  0\•çËbÍÛ{ÊCT– ~æŠqôqz] x^»ôG‡Gø|>’É$™LÆÞ©'ú,--a{{{4ÔÕGy„j­Æ[o½E `qi !{{{´šM¦ Ξ;G¥RaëÚ5"‘‹KKô{=nݺ®K:Æôù(—Ë¢]˜š¢«|Ç!‰ ‚Ÿ~¿ïiF–ÅGUýNÍ×q0}>é¸lø 9ŽÍ@Ò¦†©º y( \Y"àJÃùžøp\éõ Tšw\Bºê*ܪ ÍuB‘¸ð•çŸçwÿíï’Ÿšbcm½ý}OöÜï÷™žž¦©¿D"ÎÁÁ!ñh ŸÏôZÔƒƒá™Ù5DÅ$¦¼58ØnµÝ\>'ööJ£ÑèÄ0Œ€ú¼ÿÌÇ»¢pÝñpПæø1”`S}/¾ð ·nß¾Í?ô!p¡|R&™J )Ÿœx"¡ZMM¸Q `«Ù$›Ë®fåóy|Ê'O¶Å¥˜Gx¿ÇËËÿ ½Çà9OAèNlî^cªr¶:!àô3+4#0¾ðúíoݺI¯Û#–4ßââ"©TŠr¹L§Óaee…™ÙYööö¸ºµE8áüùó¸ŽÃ~ðJÅ¢×þ»»³Ãµ­-"‘gϞŶ,¶®^¥Ýj±²¼L4åÆõëììîzåAµVãî;Ø–Åt¡ Ün¥f¦©MN¦ï^× ¼ÐZ"Œ'Ö导Qáš:]Ç%àL¥Ø»¿Ç¿ø?þ¿û»¿K4ã‰'ž ×ïãºð«V*Ráç(|Iöt»]2Ù Íf €DBF#øý~u ê"ûÞ˜´ƒƒƒ{Žã4…¾ñ‡êg;Þ%ÀÏv­çÏf2Ôëuúƒósó*-mHé¯ÏäèèP– ±'¥"¤R)%Y•@Ô«¯¼Â…óç=ºÈ[ã*Ç×;‹‹ÞÉÕm÷Z2ø©ñÕº3P`ù<†®Œ‰Ð žÛ±6Ö78þý~jµB³ÑäöÝ»’Ïç1 ƒ»wîÐ ˜™aaq‘R±Èå7ß$²¶¶†eYܹs‡~¿ÏÌÌ sóóRý·¿O"™deu•V«Åµk×0L“Ù¹9ü~?ûûû4›Òsn~žV«ÅþÁyí´ p¯Û% ’H$èõûÒ-×çSï{ Å{OÃÐÁÞPõ–!dúî3}¸¸Ø¶áÊÉÎÂüá†)Çq‘X½£vvÅ–àà8†*Á\W¡4.8b¬À´ŸÏG,æèèˆÿð¾È׿þ ¡0 Ê„&–÷¥3R~ÞVîJNÇ‘8¤™Ã„B!Ž%8è8tÚÏÙQ*BÙ­" ptt@2•¢Ûép\,^ÒŸ–wº†Þà¡êxî¹çÜ_|QS‚ºA¨!„Ⱥ®»÷òK/íü¿ñ7Ö}ôQ·Óî˲¨Õj¤Óiü?ž¡h¹"ÓÔl.G·Ó¡Õj3==ã8^Q0(pïîì°ººÊh8TJ¹pÇH¼ZÐcÙàøu /<€ød™*¼û©èsAU¹>-éRÁ®Ò³ñùLff昙™ecãŒZåË—I¥RD¢1JÅcnܼI"‘àÌ™3 †Cnß¾Íp8dvv–h4Êññ1÷wwI¥Ó¬®®ÊÆŸ«W øý^y°¿·'[®§¦8sæ {{{ìîì`ú|är9¸êÆŸt:M§Û¥V¯#„ðÔÕj•_þ¥_ú Þm…£˜0<÷ Dz1}ÒöÛ¶ÕüÇã¸Ãñ®•«tÓÐT®t>Àt‘F¤Â$‘ŒQ¯ÕxþK_âëßø{{÷Y^^a}}££#âµ±m›L:C¹\–²àHDaI%YòÌÎÌÒív,Ò™4ÕJ•@0@$awg—dRžÛn·™™™¡×í1²,7NW77+ÅRið))ðûÀ;y“”à¿ü—ÿÒÝÜÜ´ Ã:ŽÓcL Æ6¯^Ýzñ…×?öñ³³³ÃíÛ· žÈb¤@™~¯O³Ñ$—ËaÂkeF£B03;Ãî΀^¯Çk¯¾ÊÚÚšäÛ5à½*áqú wmÉåË¿Yk÷]ÇEÕ®£AÁ¢¡21Y”êÃ+ää@ —Ñhè!Ú‘H˜XlÕµUƒÅâ1ÛÛÛ¬ª6ÞË—/Óï÷Y˜Ÿ'•JQ,¹¿»KZñü•Šž Y]‘Æ ÷ïß§ÝnS(˜™™‘ÍA››ŒF# ÓÓrP«’Ífi·Ûœ¨Ùx<ŽeÉ”x8’Ídxâ‰'ä+ù1%¡@ŠoÇAúå;Ÿd¬‘VëI´˜§DZŽe#\Cq°¨² þöÞ3Ê®ë¼ÜçÞ—s•PU@€B  EÒ –e "-Éb7-‹MIݲÝcÍÉ–Ûiµå™5#-qYìQ´%(‘Å`J Q"‰@"€È¨œ^Î9Ü{Ïü8çÞw ƒfò¬UÀ«ª—ê½wÎ÷}ûÛßÞKÝ ËìÀp9¨ÕxjçN<úè£Hg2ðz}ÆÈÈ2™´ÖÁPÝ–@Š¥"¢¶y[­zº{P,a4`wØ1;7—Ë ‘(‹Ì[ Ñ@³ÕDWwŠÅ"D~¿óóóp:9qâÄAJi’°ѻ㸒¥o r!µ%¨½µz}lÏÞ=©Þ~[(‰ÒãÇ“¥K—òÈÈê3£Ñˆùä<3 q¹I§¡Pª}p«Õ‘0(U ÉœN'Μ>ØÂ"‘0Úm‰Õœ*(GTlCø¨ÖÒÓÞ55zk uè|WW Îö\ÔÊ 0ê0:gƒÆ ìð©E¢©ƒh@?†–-ƒB)R©FFê?59…ã'Nh.?åJ§OŸ†ÍfÃвehK&¹`$FWWÒ©Nž<ÉF—,A:ÆB,†V«Î]G¡P@.›e­Æÿ—$ V«Ífë7lÀªU« Š¾üÏþc£Ãìõ4ˆ‚ÆŠcä &,Êî‹\ œ­IE‘2¬…™©|ãSv› ”Ä#?ŒS§NÃétbÓÕLäÓf³‚"Ÿ/ +ÚÅýf“y+ 0ˆ8¼ àpÂ`4 P,ÀÏÁÁf½h$Œb‰ƒN§±XLGQ=$IBµV£ÃCCÂÅ Š/^Ü”ÒÞ-À•Ì3ëoÿ¥/}‰Þÿý—k Vä?~æôéÓ¡¡á!tˆÂ`0 Ãd62B G¨Õë(–J…˜Èc6›…Ãå€ÍnC2‘„Ål%lÁìÌ :„Oüáò¬Õ"9û—.ôÔŽŸÚ ﵬÃt÷E@Ô"ô÷H8®@Tékajä"tTp@V“›D‘p}½}€ÀÄ(n¿ýʤ±g×.Äâq„‚A(”âìÙ³L¦»§}½½H&“˜žžf4à¡!´šMLLLpG\Œ&ò¹R©L&|Ú­Š…$®öl6›5E`5ú«QýÕ¿ÿ`Ó~üÕEu d ƒA;\Y×€mr‘ˆÚc"jÆ*V«Dqê…ðàÏĉÇáñzÑÛÛƒáåËa2™J§ÐÝÝ|.£Á—ˉٹY8œNFäóyü~´[mÔku,ég¦,àµ}"‘€Õf…ÉdÆüü<f™ÖÛÛË-ÝI(‹ÁépP©Ý&Ï>óÌo(°@‘(¥M°rWyÅÃòeÖÛâeRûµ·3/qÂöíÛ•K[‚ ÄJ§Ï?±cǦÿã‹_t¬]·Ž9z„´ÛmD£ÑÎÏ£h@2‘ä¾v.¤ÒiP°v`µ\EµZEww7êõ:QÄñ㣸ñƙѣ,kí½Ž¸GÑWÔÎ3Juc¦è焟”ý™À–ÖÔ-uóS¢Âêo¥fêa$ªZzZRmY‚@XÌV¸úÜX2°×nÝ‚B |îݳn·[OŸ:ŸÏ‡áåËÑl401>Î 1nX,ds9”âqXm6„ÃaÔëuäx EO&ªºY,|øÃ~ÍŸí³@¢u–DQÕoP`$"$Y†ª¤sg€Ùl‚ÙbÆÅ ñÈÃà¹çö¢V¯ãꫯ†( šyçÌô4\.‹è…Ó–lµQ«ÕÑßßÏ6:Ç‹x"«Í‹ÅŠX,—Y›U*ôt÷ V«i)Ù\‹v»333°;œh·Ûðz½Ôáp>ðÀÑ…xü !D¦”V´À*ÂkÎÞò€õ„­°Z­¯ËýÝu×]tûöíz!6À¬ÅûöíûãOzÃðð0žyúø~˜LfÄbóܹ†©Ë’ŒH$ÂÔk9ƒ‚l޵- £pÚl(–Ê8vô~ïC¿‡¶$AT7Y¼QÚIï Øè/ ùSÒÎ%¤Ó-Ôj@i§à¤!C º€jw öQ»?Î"àÏ±Ó SM®Ê#‰m8N¬]·×\s ¦g¦ñÂÉ033X,Ž}Ï?j•!Øn óóH§Ó°Ûíˆp£ÐD"J)ìºÁŸz½®‘“*• ¢‘V­ZÅ_¶+Èu·U8V2‰ò@(hµŒLÈ#ãá‡ÆÎ;™XŠÛU«W£··çÏŸG(B³ÙD­^ÇÀÀ Š…"ïnÄb1ØøgXuKV¨¢°¨jÇ>“¢3™L°;ØFg=~¥b‘HõzÅRkV¯ÁòËéüÜ%¨(ÊìÎ'ŸœÉ øÀu Ífù9zØqåŸ2Ú‡†èÏÂ'~¬p®=ÔkuŠETªUøA\{íµ¸ûî»qï½÷âÉ;ñoÿöo¸þ†`P‰:‰„†¸6C¾P@½VƒÅl†Á`è°ÿ^u™} gjFà©§žÂŸÿùŸã¾ûî‡B)֯߀ë7¢^¯ÃápÀh4 —eîÒ²$¡\*# ¡\.CQdø}~äò9˜Íf8d3l¶Dˆ&7ßl4QoÔðP,2e`—‹Í›Øì6X¬V¤3Xm tÝöÑmô†n ?öùÚ?ÿóÙÓgÎì&„ä)¥y0L«Î?Û2óôj{à·]oj >AQd©¨Ä½Þ‘\ù©Å.B÷ÜsÚljY;=gÎ;}ê…o¾åìܹ‚ ÀLJ5Zíº»ºÑlª™›L$š×}:­E)…ÅjÅÁ°qãF h#Là—GzÂe¾ ° ^º*#”Í6«›XÏÔº ÚÒðŽƒ¾ôà›˜¢s€èÞöÔV¤Ê>T¹È.%*¿‘ãü0 Úm(’ÄyEƒf ò¡}ù|gNŸÆ“;wâÐÁƒ˜_X@<C™O!z<ˆ¢ˆz½àºë®»â÷ÿ¥–šú “v?|è~úÓû0::ŠH4‚k®ÙŒ\.‹®®.Ôu”+  ¡P(‚‚ùÆâqXlVØ­VLÄãðx= „ X("j’æ]]¬…'Añl6Ì3æççñGV4)“aS§ýÈGi0ÄÉ“'É¿ï{å'ž|r´V¯ñÈŸ?ø0Ý´iÓbrÉo¹ÞÔ@3ËàŽ¶P«×Q­00èJ׫l úêõúØî]»27ÝtS`xx˜Ž‘6'y¸&`l!‹Ù —‹S„ÛÂá°¦ Z@e³Yx¼^X­VÌÎÎâܹóذaºq(çúS}+€ô¨Ù¾ºÙÕ"a‘Š˜î÷ü•Ôxþ‡ëþS¦FzŽ p@QeÒ©©C§„ ‹  £# n~@Íhøõ©…j •¡—6òz½øÀõ×ã×_v»ññqìÞ½{÷îÅùsçpþÂ4›MPJ±aì\¹’Ýÿ Ú—.UA½ÏsçÎáG?ü!N:›ÃŽeÃCذ~½6èr¹033 ·Ë³Ù‚©éif%G …CèkU´Z-ô-Y‚|!Q9-xv»&“ ¹\>ŸðëéEµR…,1«tÕƒ‚û2Òµk×¢Ùl’ï|û[ÒŽÇwLÌ-,œ!YBH‰RšPôZ ¤ÕjíV[û{Û,àM9Ô7`ýúõøÍo~Ãgé™Z®Çã‹OÛé¯ûZ×Ë´õ^‚¹Cž½qëÖ­H¥ÒˆÅØ´šÇ‹B¡ˆf»‰î®n´ZM­'k4¹ ç d2% ½}}ˆÅãhKÜ5kVC‹¹Úí€.å¦|:M}þüÅ"ñ>ô³–_'¾_ò¯šæsÌPRÔD(nö 3|ÄûˆÚ¦VMMª°VÙA&°ëë»zCƒÁ€•+WbÅŠø³?û3¤Óiœ:u O?ý4ž~úilݺ6›íõ©ÿuÍÊ3ÓÓøùÏŽgž} ›7oF­ZeZ ¦¦¦‡!µÛ¨VjX:ˆB1E–‡‘L&µ4_%û˜Œd³9AHí6*• –,YÂÙ€ |>Æ4™ÌŒø33Ç Y’Éd±qã\wÝu´\)“Ý»vãî_8~üøyYQ’„:€¥4  vÔAh+Š¢|ãß ÿøÇa4¼¶½ó¦ê¦ôx<¸õÖ[ßÐÇz‰–`[?@ñ$R©³{vï¾úÚk¯³{½záÂy2Ð?€¶ÔF!Ÿƒ[Íb1˜L&¸Ýn1¨²¶J¥³H­6œ&ÇÇ111‰åËýV'Ñ©mx=à§’„Ôè­·÷b¯ŽQ> ðúŸ^²ÙÕƒBîûU³ ö+ÂTl4E"ö˜*[YÃ(8‹N¡Ââ£FèÜ×åÌ):Ø´Ã^Ý”Á`7ß|3n¾ùf|å+_Ѳ¿+Ýü—nüd2‰|{vï†h0  âê«®†Ñl®ÚS, ü~?fggawØa³Ú0?7Ç;?r¹‘MÆþTÇx ð#‹ÁbaváðxÜ ÃmQ)ÍðJÜÞtðõHñ^i½DKðR0Ð5>>~ú©§~µú“Ÿ¼C\±b&''Q¯7D!Iò¹<.,f âñLÜ1¨X,¡ÙRKÆl ø R©Μ9ƒ¹ÙYtuuC’$MŸ_õT£n§e§RUH±ÖéP‹yþ@¸‘ÁIç~tÉ { ÚA Exí¤Ñ][¥%|0‰•ªÇ*|JøI£b@eT\¼Ñ_éýQ×koüZ­†'vìÀ/ùK´¥6\.®¿þz,Y²'Nœ@wW7JÅ"†– ¡Ï ðûˆÅ`±Zà°;095©µ3™  k÷UÊš[’"IHg2šÑéÌÌ Ì3fçfát: ( FFFè¿s#ª•*yà¾û”'žxböüùó(ãUE‡ògùW†¯Öý pâÕj¥ÕjU{¯„7ñ–·߈u™– j)®— «I²<³gמ׳£éLn· V›U“Îòú¼(—Kh4êðûP§Ý.7Ìf¦1h2™àt9!µ%ˆZ­68ƒÁÀ7 4:j:›úÍD8´]ý»øÐ hg³Bí €,ŽêZ¢¥>¨.uWOõ1ùa Þ ;~ˆú¸Ú¢–-ŠÂ6YÑÒï×ú~ý¶KáÔ]A`\…O>‰/}ñ‹øÞ÷þ Ý==¸öÚë°bù sõ'\n’©4‚¡lvÒ©ÁK׋„¸Ž?óŒ "ŸÏƒRÖPÑzÕcÒËÅbó¹Z­ê5&-ŸËåÐl6±ví:ÜxãtóæÍäÈ¡Ã䯾üåܽ÷Þ»ÿÜùó‡AH’R¦”¦(¥ æÌò¯ytêþ²  Bˆ@±ÛíÔh4^‘°~½åD 7j]¦%¨fU°tª  túì™/œ<Ù»vÝ:ø}~8.>øSå#À¹\N'*‰;¼^”J%FæèŠ¢Ý–XÛÐçƒÑ`ÀÉ'pÓM7ÁïóA’e.2 €R(DíÇóh®{Þ¼¥Ç÷„F×Ñjy¾Õ á*Bj­žêSnE´/ªÏ Ôƒƒ@3Ñ¢¸úÕ‚A{ì¬kÉ} ‰Ê-Tñ€N½¥³/·ô-=Y–ñÜsÏáç>ˆb±“ÙŒßýàïbÓ¦kpúô †‚ši4A³ÙD«ÕÄÒ¥ƒL’LVDÙ!àñy±0¿§Ó¥¹G,¥¾X IDATƒAP ”J%>Û_E«ÕF¹\€×ãa’gf ÜÙø“Ÿü$žݻÉ/{¬²k÷î‹…bq†°~ƒ§ûjÔÏñ/5â—ÔA¨BÚŠ¢È„j³Ù¨  ¿^¯ñ»ö¸LKPŸh-Ájµ:¶cǎ̺õë[·n¥'N'¹\N—Ss·%DÐæ½kµ*"a¦  ^O=Q@4E«ÙÂ\*…Çã£Û>Š–$ñ2›vPwÖ<ƒ¦ùTü¹ ÍÙ)à;Q\«¿©:Òª¢ŠT»aç0`  P)ÇZÝ¡hå‚w`O[- (dÈ`*Ä(OÉ_Ï¥ßøŠ¢àÈ‘#xì?þÇFG±fÍlºæ¤Ói\}õÕ(‹¨Õê^Ž…ùˆ‚ˆ`0„ééix½^8œNLNN" A™9lww7D"¢\.c`pùBŠ¢ #•Jk]€™6¯ÏT~òð©T+–/ÇÆ«®¢•J…Ü󵯷yäá‰X"1 LiƒÒ².Ý×oü¸± !¤ÁU®%Y–å;ï¼Ùl–>û쳯 ^¢_ïÊ@]ú–à%^‚*X;räÈÙ©ÉI¬_¿µZµq¼>T«.ãì@Ëf™š®ÝÎNBàó1‰çj­¿×Q!+2¬V+Ž=‚|!£ÁfÿÍž¥ZNÍžgç ëÿÓþUÌÚxqRÔúýê=ª¬C@ãtæät7ãLù7„°”—¤ñT¥ Pv¼È2Ù$ E–™æÞQS/8VuÿÏ;‡ÿóÿßúÖ·P(qË-·àÓŸþ4÷q Ãét`nn]]] Äã1ôöõ2mýr}}½(‹h·ÛèêŠ"LÁh4 ·¯ÉT6«6«™t†³µ1Þ6—‘ Z­@’$8T*elÙ²™~ü§¢(’øý³?ýÓ™ÿõÍÿow,‘8AÉóž~œ²ôþÒt?øåA¨‚P—e¹µ|ùrùßÿýßéý÷ßOÍÜ6ýõή޵𢖠ݾ}»*Zƒ:%¸“©ÔùçžnÓú ¬¡p˜ Bšˆ¢ˆL6 »Í'w·Qø@­VC¥RYDrØí°9ìȤÓ0™L…È-,`tt·Þz+êµ:CÓi'!×Zmº¥Îh¬~¯P3þ8iHíÅ©·ÐÀ@^ßëÛ êOµ^?:€  fÔ[3ƒÎÕyªÜDÕžëõˆþ—¶ô¦§§ñè#`ïÞçÐ×ׇÛo¿¹|k׬d³9lX¿Ùl­v}}}˜…ÑdBo_3:q»àr¹15u’É£‰"Ò™4—BjKÈq«³jµÊÚ½¡0J¥2((|>âqÆês88sö Ü1) Q¯×KvíÚ…ï}ç;Éã'N\lµÛIlüsV@'Ú«ÿ«A¨.BCI’$Éf³)wÜq¾ùÍoR•óFl~à]~è×]w݅˵ !5Jil÷³».nûȶu¸îZ<ûì.f &+ðGý¨7(–J éçâ•V+ó·ËraK¿ßz­Žb©ŒH8Œz½ÁÔƒ–-[8Y£ƒ²C``%`qw@Þ¬,пùªAÛÀ:tQ-T¬@ûRÀÎ=h×ײvŸ‹2 ÝýkóB÷äczúD!ˆWþqº\/ÿᇑ#G`0™°~ãzÜyç!ŸÏC–eD»¢8>zÁ n§ÏœAW4 AJ§¹O¡ŒB>áåèT*h¶ZD&“ÍfG(Âøø8¬V+Ün7.\¼× ‡Óñ‰qö¾ J¥2¢Q¦Úëóxñá|„:œ²ÿù}dçÎ¥_=õÔÅr¹<ËSø¦¢(%°š^Ÿî «ó !užîË’$)Û¶m£÷ÜsâŒív&“é ÃUÞõÀË´5Ý@®±ññ3{÷î]ýéÿr—xäÈQf0ûÏbÛ­12P«ÙD©T„ÏÇZFÉ»žÃá@­Vcj¸±N:…­[·2z° j™»Þ?¨ó«áZeàk[ ‚VÓCE ;·â­M0”v>Bçžy­¯òÔl\“?Ž…T¯¯â>À¦ê(X].·%.GöZÚ½ú–^.—Ã;và¿ø%ˆ@ð»¿û»P¨‚Õ«VÃa·ãÈáÃX³z ZͲÙ,6lØ€bµøV¬B2•„ÅlFOO.\¸‡Ó€?€'NÀëaÔíR©„h$Ê8ùú—ô£Ùl¢Z©`íZžMp}Äx<J)º»»áõzéÀÀr¹ùçú;vì˜H¥Ó“*„¥Tmëék}à+¡Sç7Áëü‘‘úå/™ÞqǰÙl$éÍi™¿áð¯—h ª^‚*X“dyzÿ¾}±f£‰¾¾>j4¹^`‘éÕq)'æÒÂŒD3Ù,L&³æ{×j·ðûÑh6QãvØV«û÷ïG«Õ„Zs¿ìa®õéy Ïþ ¨^‚Ú.Vx«O׫¿TkyÒ9¨>“à<µ¾ô)çà: $U:Ʀ”£*¼(Ë ¨"CVd­Èîj4„—[*1H4 <ôÐCøË¿ø <¾c|>û¹Ïaùð0L&‚Á .\¸‹Å‚hW”ûú™Qéô$º»»ár»J¥ÐßߪÈÈd2èïïG¹\F©XDÿÔêu(TA(âê»VAÄc1„B!˜ÍŒÚÕÕJ)J¥n¹åºeËj6™ÉÏx@ùÜg?;õƒíÛ÷¦ÒéS„¯óèÔù3ük¬ÎOÈ ‚P¡&ËrËl6Kwß}7=rä½ûî»a6›¡pâ7²›¢®w}¼ä”`ƒƒ*à¡ÇOž<¹oß¾Þõë×cll zÅB·&.a0àñxX OIRù|.— &³ñxF£‘»À4091sç΃}hZ à„ªâ¡:¨Ÿt"¿Öª×Áô/Žôj_ŸÚ««Ó%ൻŠúkÊC”‘~TÐP¥ª‚‚ 6 BÍ*˜œ®&CÆ ö"ªS¯ª9pÉÒ#ûívÏ<ó üÙƒˆ'âØ¼e3,f úzûDpàÀôôôfgç0²jår©T ›6mb^ƒõ®ÚpÉA@_o/.Ž]„Óá@0Äè±Q„Â!ü:|á`¢ÀJºžž4šuFðêéÑ¢´+ §ÓI?þ‰OÀïó‘ÇùKúãÿ8~âäÉ‹’$¥Ôa3]Ÿ¿ä«Ä?cUAZ‚ ´$I’m6›|ÇwàË_þ2,ËLÚ\Þ”¯®÷Äð2-Aý|€¯R©Œí|òÉìµ×]çY¹’>ñä“Ä`4Àãõ¢\*¡^¯!B–eæíæêp.|Q©TP¯×Fa6™4T}ÿ¾}X½fõe6¦ú¿;›‘@݀ܔ‚¨q™èL);->µöç?ÔÀ9,º^‡tDTÞ´ËÂÎáðV ÖJDG² ¡ €(2@HøýXÔÒÓõògff`2[ð±} Ñh/^D0ÄÔÔdYF__ÆÆÆP„‚Aœ={‡^¯@__œN'Ž=‚þþ~‚ˆT*Í€=þ¾lذ™lÍf=##ˆ'âÁ@sós°X˜9G*•ÄêÕ«±uëVZ¯ÕÉ©S§ðÿüüç…§žzê"çíëë|ýÆ¿\ßàâ4’$IÊÐнçž{è¶mÛÞ²¯®÷Ät"âKL ªhlöÄñãgg§§¯ÀÆZ)•‘Ëçátº`³1n€ t¸Õjápá–ÎNØlvd²Í&‚Aî¼;¡¡!6$¤ï³³'¨õñ5`ІÉ1c }ftµ/¯„I]ëŽ6Øét¸ÎrLÅø)aÖ\„ýÐ4x»OM÷ ]gBôس#„,žË§£££øÉO~‚ÇcåÈV¯Yƒ@ €eË–i*Ãv»ÇƉDÐh4033•+V"ŸÏca~[¶nA6›EµRÅÆ‘H$ PŠ¥K—âÂÅ 0™MˆF£8{ö,œN'Î;‡H$ ³ÅŒD<Þž^˜,&$’ ôv÷Àn³ã¸ž®]»gΜ&ßûî÷jOýú×cÙlv†°:¿M)-é¸ûú¹~~û¥ê|µ½ùV­÷Ìp™)A½—`Ym Æ“És¿ùÍo®þ¯ÿí¿Y×®]K3™4Éç ŒP«¢R©h¹\Ž·†œÈd2 ŠŸß‡z½†R±ˆh8ŒR¹‚L:ýû÷axhXÛdÚâ\°ò@©Âêq][°s[5bCóÅ#:öPGcgD'6¢†˜Àˆ¨úkžåTaû_A÷|ÙÏ©¢@Ø­™Þ¡¢• Ìž¯3—áÂÜ÷ÓŸbôøqH’„›n¾˜Ÿ@0D"™@­Vê‘UˆÅc¨Õj‡Â˜™™@àö¸16v.· ^ŸÇŽE(‚ÕjÅäÔ$!Š"bó X>4 µ÷WréóV«Õ«û‘H$`4°¤ Μ9 §Ã‰-[·R›Í†d2I¾ý­oµøÃNMNMBÊ:Þþ¥é~:€ŒÅ×$„H²,Ëú¶ž:ö¬Öùoõz׃€—[wÝuƒÐTf` Œ©U{úéß\Œ-,`Ë–-¨×(•Êðú¼DÙLV›N'sŠ‘d™K?±ñ`× oZÌfx¼^ÑdÂéS§1;7 “ÑÔ!à¨G…y‘qÔSA£ív~µ˜,R V <*z¯ ¨i?ˆÎ¸²j5®Þ˜çºLE»w-Ö~¦¨ßð¬@—ÉPEa8€ `vfÿôÕ¯â _ø&&'188ˆ7`ùò嘛ŸƒÉl„×ëC"Î,³Š©É)DÂa´¥6¦g¦˜SN£‰tš{éT ù|ƒƒƒˆ-, Ýnc`p3³³°ÚlÄì,ß CˆÅ `6™‘N¥哞ápˆÞyçÔår‘ûï»þÉç??û_ùÊÞÉ©©ã„<€"¥4©ãí«ß,€8w_„¢Á`¨)ŠÒ2›Íí»ï¾[9räݾ}»Fæy«£¾~½õGЛ¸^Q8”O NNMŸ9zôèšÿtç‚Ñh„Ñ Âåv!—eáHÍf …b^&£ :ÝÕI¸««‹óÏ[Ìw —ÃýûñŸï¼S3Ñâ¨ÉuÑ[ßÐè»Úõôõ¢ÝW§ç–š DÝäD„z~©Øî7‹³•Kô Ôôž‰hOGCõA@2‘ÀC=„gŸ}Ù|«FF0¼|9æççv¡V­¢R*cåȲ٠r¹Ö¯_D2‰f« ŸÏ‡©©)Dººº019 ›Í—Ë…“'OÂïÀj±`tt”Ù˜‹"b F«ÕF:Æš5kP¯×Q.W±aÃzäò9H²ŒáeCÔÏ4ÿÈè±c¸÷ßHîÚ³ç¢$IIBHSÇÛWûù—«ó뼟ß~;Öù/·ÞÇЛ´^aJPk ʲ<½k×®…b¡€uëÖQ·Ç©%¡X,ÀãuÃd4!›ÍÂh4Âíq£T*£Ùh"à0€0ŸƒËÍÂl6 ƒÑˆp$»ÝÑÑQ$“I¦®tÒxµ- ›ôS¯À¾½ Ç–—zŸ¾ °8‚S­f×ýn&ñ¥¶øÔk+Tá% {|ªf*  =%ö8²$3‹Ù\ßûî÷ðÙÏ|Oýú×p¹ÝX½j7nDµRÙdF @,‡ÙbÁhÔøúƒÓÓÓðûü „ “É ··õz‰D=½ ©ÏçóèííÅìì,ªÕ*1;; AÑ×ׇ™™ix<tuwarr Á`€elÍn¹õVºrÕ*rüø(ùëÿñ? ŸúÔ§Žüæ™göɲ:zrÿ¾}ظq#’é$Óp{P戲ßïgCAy6d±ZËå ‘ÏTQ­Uáõxár: >tˆ;Ö,2 ×ôø6e5»FÈéP|Y¾è[¾ù±¨"`7S·§Žv$tU=sØ·᥋êoÏS|¥óKHí6@§Ë‰jµŠÿøGøßÿü øá·Ãë÷ãºë®ƒÏëÅ’%ýP™l@ù\™l‘H¥bÕj~¿±x ’$¡§§ îÉ 055›ÕŸ×‡ñ‰q8¹ÏØøèELNN¢¿ dYF"™ÀÐÐJÅJ¥"FFFàt:éUWm¤FƒüÏ¿ÿûÚŸ|þON>üè£Ï•+•qA*”Ò¼¢(qt8ûÓ`›,ÝWyû%uã›ÍfyûöíÊåúùo—tÿrëíûÌÞ uiK,@ßl”«Õ±={öd‰F£´T,Á€*ùlN§6› ¹|l(¨^¯¡\.ÃçóB°[m°Ûmšð¤ÛãÁ¡Ã‡‘Ë1AÉ1÷ wíA' PÁÎfW#2¿?9ˆ:,D;D‹†ü{_Öß¿ý+¥Ë6è¢l#† àƒ@TËå†$Éxø¡‡ð¿ý÷ÿŽûï{ «V¯ÆÖ­[ KJ5¿ƒá0Ú­67„£Z©`vvÇŽÃ×jˆAÕNµúºV]'WkùŽN€ÚׇìÓaª`§6Þ×É.¨‘hþ‚Ú¡DthÑ¡”@†\N'šÍ&ž|òIüâ—áÌ鳈D"ؼe³¦ŽÛ–Ú˜_X@WW—ƦìïïGµZA¡TÀ²¥ËÏçÑl¶0<¼¹|R[F ÄüÂ<B¡fgg˜«ŽÝ† .ÀëõÁívctt>¯!˜žžDÿšÆ'&°f͘LFºtÙ2ø}>òðÃ+ÿôÕ¯ÎŽŽŽQ { ‘G_ç«#ºª"O•#û—åí+Šò¶­ó_n½ý¨7`]Ú ó~­¾%X‹Åãçž|â‰z0$k×­¥ÅR áEƒÙl+›(‹hµÛðûüh·ÛÌ ÓÍUƒrL](Á`d)áÁP-—! ¢–Œw:rTr&àbøŸÿ,Ú뚊֜ƒ–´kŽþ{i7A=8:væœÃ«)8ýv‡ &£ÏíÝ‹/}ñ‹øÆ½ÿŠL&‹Á¥ƒØ²u a¾^¯™tF£ ~¿ÉTF£6› ±v\.Ò©4|^ FæçæøA¤’,¨×kˆÅbˆv1»±r¹ŒÞÞ^$q‹E ô ‘H Ùl! b|b ¡Pˆ®^³šJ’DŽ>Lþø>•ºóÎ;÷= B<ÝÏpdÿrQ?VççEQ¬PJë²,·—/_.oß¾]yðÁéÐÐ$‰ùZ¾]ëü—[ïÉ @¿.ã%¨µ)¥±=»w}òŽ;Ö®^µö€ÙbÛía Á­6BÝÁEÞb1#žHÀ 83°ÌúØ&"Ò–Úp¸\ˆÅã=~×_=jõ: qQZ®öò)¥‹ä¿´Ô\ æTÙUŒCß·ç÷I%H¼\ZªM$ªÀ¡zh@Š,`·Ù@D‚£‡à±ÇÃá#Gàr:±~ý^þpùôT`” §D£Ì÷®/`É’~Ô Ôë5 £P,¢V«aIÿdÒȲ„H$ÂH=Š׃ùùyØív„‚!\¸p6› 6› gNŸF8†ÑdÄùcŒàc±X‡éàÀ ¹pî<¾úÕ¯{ì±±r¥2§cð]n`Gì¿"‘çíÔÏ­ëû̯p½ê–àôôé#‡¯ÚvÛGE¿ß@€$É÷ßl¶hÌ@¯×ƒj¥ŠZ¥Šp$€Í©ÛíLS Nà  ÑnµðüóÏcÓ¦M PT@•ž«¦úõ`íygt`]¯CþÕÛ=:(PÝø1OùµÒ€tÀDœ˜À:2sô5 8wö,}ôQœ|ሢ€®®n¬_·ív¥R ^¯éL €ÏëE2™‚(Šðx¼XXX€Õb…ÛíÂÔÔì6Ì æÆÆàñx`6›09É9eYÆÂÂúúú¹\Ë–-E¡P@6“Åú ëQ.—Q®T°lh“““0ˆFlÚ´‰Z­VÄbäÿþéÿUûþ÷0±MBª‚ ´©¢T¶ñ‹èLé-šÔ›ÏY"Ï;1Ý¿ÜzÏ—¶?ó™Ï\V2L–å™_íÜ»þ†z¯½ö:zôèQ’Ëçø†÷¡V«¡Z­jòÙ\V» »9>³îóù™@±ˆ@0ƒÁˆt:…©©)œ=sW]}š&¯ËU~­Ú›×ký«œÕâšÿ5@Whì┿Ãêl~”Td «Å³ÕЉñq<±cvíÞ BFFFÐl4àñz™ îì ¼>/@˜ob(‚"+( èîîaêÉÅ"úûûQ©TP.—1<<Œr©„V«‰Á¤Ó4[MƒA¤Ò) „CaÌÏÏC¸ÝLMOÁíqÁívãä‰`³Ù`w8`4é–­[A…ü¯{ïmÿèG?šž˜œ#„”øûZUE_çë©»êÀN]„¦ m½0Ç[=°óF®÷ì¼â” æ%xüĉ“ÇŽí½éæ›qüøq‹D#Qé·Z­p:œŒ\¿/ ˜Õó0Mp:]¨V«!0ŒØ»w/Ö­[ÕùGM¹É%›SÛ¬‹ÄÙbŸjè ~§£´x¿«K-÷ ¯ ÔÁ“Ù ‡Å‚…ù<þøã8p`?ê\.Ö®[PŠD"ŸÏ‡l6 "“Í@Dø|~$l*Òåvan~޽NN'¦¦¦àv¹aµZ166§Óƒh@2‘D$Á "L# £Þ¨#Naxh˜•Z¹<–¯Xx<ŽJµŒ[n¹….Y²©tš<µs§òÝï|g~ÿÁƒÁXy-J髸!MžÊï$"Ï•®÷ôðj§«µÚØsÏ=—ýЇ?ì„Ãt~~ž8].ä²YÍ)¨Õfè¶*Ç! ð2Hæ7 ƒRE;¬ .^ù ç±fõj4› ¨Œ¿Ž@¨º_©~ÄÿEŒ<¢Ûèør©™8» MЗ2•a4šÀ¸ð <üë§±óW;Q­Õ°|ùrȲ ¯× §Ã±±1xÜÞý`ÚùŠB‘Ëæ˜Ñ?{zzÐl4P.•100 Ù/ZÆ<š ²Ô^Vd|~Äã (TéØrY¬ðz½˜˜œ„Áh‚ÓéD½Þ Û¶mCoo/yü¿À~ðƒÔ¾.´Z­$!¤Eiè&õ.­óÕNÏ¥œÊÛq`ç\ïéxõ-ÁÇyáĉÖoØ€…X ÍFÅbnºáU¹p*Õ*þÂaP0Àf·Ánw “Í¢-IèîîÆÂÂdYÂÞ½{02²’àëFnuÏOÏóï¤óúhÄaQ„âsûT;%ø`аîUˆ¢‡ …|OìØgžy™lV‹×^w" •J2Ï»l”Røý,úƒ«&§Òz?Ál6ÁåravvŽƒvVLM±^¿ÝfÃØØ¼/ QË&ÉdÐÕÕEQL&100€R©Œd2‰Í›7chhˆ*ŠBb ø—þçâ£ÿñc•JE•âj)ŠRþm>›ÍF¿ùÍo¾«¾W³ÞÇÚo±^mKp!?ÿ«¿ªG£Q²lÙ2šL&µ; fêÁ„Èe³ÜJÜÁ"›,Áïó¡Ùl¢X(Âífé/(`±XpîìYLMNÁb±@àPëpu]šyvÀP0p. @’™aJ(B»ÕB±PD(æüû2Bá*•*êõ:¡ò… Vïçr9È|Æ"“eι>¿eQ¢‡ÃŽj­Š›nº‰Þpà tnnŽ|ýkÿRÿ£;ï<õãŸüdOµZ#„T@iAQ”—¢îÆðbEžæåyÞIDž+]ïîãí·\¯ÔjÄg>ŸG$A«Í˜Ž=Ý=h5Z(—Êè[Ò‡z½ŽJ¥‚ÁÁA”Ëe45ôô ¡X*¡Ùlb``…\’"3K­l €Ýá@"‘ÀêÕ«é?øAäóyòø/Iy䑹c££c”ÒŒÊ໤Î×›l¨_¯Èó’Ÿ:°ó^[ïø-Z‚Š2óäOÌÞpã[·n¥O?ý4)Šp¹Ý°X,Hh83©VªE ‚€l6 ›Í ‡Ã¡Ñ„=&ˆ)Ë ÃüË¿„h¡È"Ù%\¼ƒ k j <ìXÜÔ¬£ÔTŸ}o±Z §NįvîÄ©S§¡P`íºu0™ŒÈç ðûüÈd2¦s nJŸ—ùÝ«8›hd©ª…ºÓåÄüܬü™ÝngB““p:ç~~^¯—†Rðû| ”bnnápÝ]ÝtýúõˆD"äÀøÎ·¾Ý·ß¹V»P>µÖ+|m°:ÿ ð “YS©×‹uøöÿËߤõj§O9súô©S*NAáóú4@_ÍeÙ\€ËéD±P@»Ý†ÏïG»-!_(ÀåvÁl2£\.Ãjµ" azj ÷ßÌ&3 ¢È\vTB/Ÿç  ñ¼_+Ô±];P6âk±XaµÙ091‰{ÿõ¸çë÷`zf^ŸëÖ­E4ÑJB˜$·ßç(Ïç°1gõr›Gü`0ÈpR‰  ÖQ*— YÆÃÍ2Yôo"Ž X,2ºn0ˆl6 Y–áñxOÄíŠâöÛo§+GV’™éiò·ó7Åÿú¹Ïݵg÷Þ¶$Íqênö%FtÀ•wUê.ŸÔk¾S'õô«R©hŒÓÿ<©ä+Yïg|½Ú–`­VÛõ쳙߹é¦À²eËèùóç‰À#¼ÕfƒÓÉøí¶„P8ÌÞ´¼J¶0ÅZBàóøP«w8íÅ…Ý»g/‚OýñÃf³¡^o Pf0Jˆ SÜÑž=€* d „Âh4rQR`|r»ŸÝ…C‡¡V¯cÕªU0™Ì¨ÕªH§™ë‘ñ àõu"¾ÇÓ‰ønñXœE|§“³û˜IÊì,‹øv‡ÓÓÓp8Zôw¹\0™ŒŒ0äaÞzÉDNõ\ºt)]µj‰ùÆ¿þkýñ;ÆÓéô4!¤B˜§^EY,Å¥Gö_VŠëLäQŸçæÍ›‡;|>ߢ뼖õþ [¯²%˜ÛàÀ™cÇŽÞxÍ5× ›É ›ë€Úþ\n¬ É$ x½^f)V®  A4ˆÈ%r0›™¯€,Ë%V ï{þyÄb øÃ?ü$†‡‡a0 ÈÚm¦ÃmN€E{Á FˆÜ©‡RŠL&éé<ÿÜóôü…óDE˜­,_±¡`SÓS\ðTÑ"»¢°Ë¡p²$£/ h¿»« ­FKsÊm4(—ËXÒ×ÇíÒª@¹ÌæQ.•Ðh4Ù!W,B’$‚!$“ ˜-fÜrË­´·· dû÷¿ßþكΎ(¨ ¾—ÜVÉ=ïj€OÍP¾úÕ¯¾âu^ËzÿЭWð,s,ÀH&ÏízöÙ«ÿú¯ÿÆéê¢ç/\ Á`‹I.^¡nør¹Œ`0ƒ("‘ËÁl¶Àår²T¸ÑDWwÚRår=½=h¶šeS“SøÚ¿| #«F°yófô ÀïõÁî°³R€€—µFéLù\/ŽáÂùsXXX@.Ÿ‡@^“Ù©ÕF(B:ÍZmgQ”O¥R "Ün7‹ø&#\.b±Ìf3œ.æççaáîGssŒÝg³Û1;3 ¯÷§§§át:aµX1‹ÁíqÃj±bnv6›"ìïï§›6m‚Ñh$?{à塇š;{îܸ¢(*À§÷ÔÓG}ýÆÏ|*V¤_¯Çöþð를Á0ÄÁ/ĉƒØ÷üóp»Ý¨V«(sÓP›‹š-f¸\nU#‘hdiNl62™4dYBOO?jµ²™,pê…pâøq8].ƒ!8v¸\.ˆ¢ˆF½F³Z­†\6‹r¹ŒV«‹Å× ŸÏ‡¾¾>ƒAÌÌÌÀï÷CjK(ŠG‚e…b‘‰nò(ßÕE«ÕF©XBWwcñ•ËèíéÑPý¾>†ðW+U,èC­VE­^ÃÀ3ÖlÔèîêB©TD³ÕDooÉͶn½–®\µЬÑcÇðÃþ0µoß¾ m¦Á×âTlUƒïRdÿEZûx|¯f½QÇûÀ%ëe¼µ,€sl|üÌ3O?½öö?ø˜aõêÕ( Èçó°X,p¹\Z«‹1Ú˜N ÃÉtîÓ™´Æh6›(Ù(ЍT*ˆF£°X¬ˆÇbppÖ\­ZÅüÜ,Kg9‡Ú|ûÜnØì68].MIGED£Qf\£|"‘€Éd€Ûåf—,ÊÇãqå.Äb 0óº~~~V«v‡ósó°Ûì°Ûíþ¾ÕŽ™™­ÞŸšœ„Ãå„ÙbÁ|lv»…Ýnǧ>õÇ´¿¿ŸìÚµ ýüç…½Ï=7V©TæÀÄ7›”Ò2}±“î¥ ¾ºð>ƒïu[ïŽãñu\/!ªz V !5…ÒÙO>9]­VpÕUWÑb±ˆV«©²æóy¸œ.ØíväóyP…Âïó£Ñ¬3 ±Û “ÉÈDCEf7V­VQ­VáóûøápØÑÓÕ‡Ã_À®în¸ÝnD»ºÐÓӯχh4Š@ ›ÍŽ¥K—"ü~?õ:J¥‚šÍ&Êå2½WË“f³©Ù7uT*U‚~ÔêlÒ1²ˆ_«" h0D¥RÑ.—Êe4šM„C!dsYTJ -ÂæÍ›éG·}”Ê’Lþñþ¡ö_úÒÉ'wî|®Z©ŒB*„Ò<¥ô夸R`Ã=ï3ø^çõþ+u™õ-Aµ («_çΟ?súÔ) BX,VØl6¶á)…×çE³ÁÚd&£¹\£ºá+š‰(!ÌdÄjµÂaw •LÁj³aIÿìv'¢Ñ.,^‹Ù¿ßþþ%Ì 3Åò+`³Ù‡‰DÏå`4àt8‘Éæ`æ5{&“aõ»ÝÎ/[YF’N³ZÞfC*a#¶6;2ifŸm³ÛN§µž~:†Ãá€ÅbA:Öúû™tN§Š¢ ÕlaÛ¶ÐøÃh4›ä»ßù¶ôÙÏ~æâöýhw¾P8O)€™iÆéâ?&ƹ€kí7/ÕÚ_±b…¶ñ߉Šõ«_%¯»îºðºuëèùóçI£ÙD©TÒ”qâñ8Œ<—µÞx!ÈeÙ†w¹\(òhµZèîîF½^G­^Cww7ªÕ *VƒA€`ÐÛÕËÄ=IÝ=ÝPÕj•_Ÿe===¨×k¨Õª¼~¯¡Vc™µz_îA­Æ~ÞÇ‘üZ½†%}KP­VQ¯Õ±d ¿\¯¡¿•jFCCûõzzzP*Q.—±jÕ–-¢Kú—³ÙL~ú“ŸH?ýÉOæ/Žs > @í’]5åO|oåz?x‰¥o šL&E–e‰£Óú–`áÀÁƒ§Ž9‚Í›·Àãñ JÁd4±_©¢^«Áç÷„ —ËÂbµÀåt¢P*¡ÙnÁçóA’$òy†œ[­Èf3¬¦¶X‘Íå`w0åœd*‹Å³ÙŒd2 ‹•]N¥R°ÛY¤Îf3°Ûì°Ú¬Èd3°Ûí°ØlHgØe›ÕŠL:Í"»Õ†L&£Õï™L» ¿ìtÀbµ •JÁátò(ŸÓé‚ÙbF&“Ûí¥éL›7o¦·Ýv;í[ÒGöìÙCÿôóŸ}å+_Ùwalì(IB*”ÒÔKhð©DUk_%ò´_NkÿýtÿÊÖû¯ÞK¬W˜T³€z6—;¿{÷î’Ïç%¡Pˆ–K%m°%—ç)½ÃÁØ€-.*µÙ†w8u%«ÙK¥Úí6üþJåZÍ~?Ê• Ú­‚Vw·ZMT*4›ËF Õr•]Pá‘Z»~£‰`P½~ççfÁ@•JÍfÁ@år™1÷A”Ë%4M„‚A”KeT«U¸=.øý~|⟠¿ÿû¿OŽ9Bþú¯þ*ÿwû·÷ð2땦UGáé™™³OìØ±þ3Ÿý¬eýúõ˜œœB¡P„ÓÅè¿©T àñyù†/iÃ0±Xÿ{×#GufÏ­®éçô£ªz^ÝÓã1ƒ!vã³aœ`{$,Æ€±FBXÚEË]dm$~@þ@6‚hl$«‰á˜uœX–ñbkcœà€'2F Fü˜w÷L?ªú1]]{÷Ç­ênc°Å0®#]uO«5šGßï~÷ûÎwŽ(ŠH$â(•Jü}’µT¥ ’"¡T*:®Ãªª‚ÚŠ"AUUX¶ Yæ¯[–E‘¡j*lÓ‚Ò¯ \Ö`Z&ÒršV†a™H'3(«\Æ<Ýß­¬Á0L~—wÞ¯( TMƒaÈdúQ©”¡7èîîÁí·ßΆ‡‡qáüyòÂóÏë<—››;O¸‹®  Ê–Öàs©»ËšÁ÷M>KL Òöb €2!¤Æ›>räÈ'Ûï½÷¦µkײ“'OBE‚^çDEQàïðcf~†ox‡8´°PCOO¯3Z¬9”\Æ'ó’Js GVZÏ%Y€R‘?>ÀÃÉC> E$$G®«Plzí E$ ǽ¸ˆD<ÑÇçþRÜÉ. âüý…|’,Áç195…žîÜzë­,•J¡¡7ÈoG~k½òÊËgÏû-êîâßR³ù.gß+ð]exàsp áPƒRZG›pè'g?=qbͶ{(LÄ(v ›u†hâñ–G@vÛ~ÑhgÓ0#‹annŽ· ãqÌÏÏsªn<Îi»‚€DBB±P!€œP(ùsIr²I–¡–T§)£¤–@…"+Mý=EQ ilÛv^wTŒår6µíìD¹\ƺï¬c[·n…idÿþýìûöM¾÷þûSJ]wýs6þE >J©E)]ö ¾¯;¼ð9X¢%H]1 ´iX–}áÐë¯OoÞ²%ó½ø;uê©T*¨×ëMbN±XB(È•qUMC£Ñ@:†®ë¨Ukèëëktúúú`|𦧧–Åu÷»»»a[TMCOW,‡ÎÛåó¨%$•$ÀŠ¥"EãÙ‚äˆzŠùJ’AàmI—‘8;;‹D"¡¡ëÙàà ºº»ÉÉÑQ<ÿ sï¼óÎóbûì .­º»uwYšl|áýµ/—˜ÔÛ¦Ë:ÿÆ…3?ºývœ;w}t‘p‘TUmöùmÛ†Z⣡pÓSÓMòÍÔô4§ÞF"˜E0@4E6;‹@€‹nä²Ü©8êf >â±æóÜx¤™-8ôßB¡à (%ø¸¯H’ܬ9ȲŒRI#@,C.—E:ÆÝwßÍdY&£££øÏ_ýJ;|äÈ™j­6@w©»hqöÛ7¾ žêWè×ŠÉÆ7^žuho îØ±à-ÁÅÄ z­V;sìÍ7çƒÁ Éd˜®ëPºXÌt7|©Tµù,@­ZC£Á)ĵ…t½Žd2ÉI9µ(JÒ©AQ’MÚn2™lfÉdLËDY«@Q^VnÒ’UUmZ™—T²¤€1Öþ „ ŸçüD"ÛnÛÄ~øaÖh4È/žzjá±ý·÷öýñÇjµÚ'N‘¯ºëZh» ¾‹¨»—bðµWö=ßÕƒ—\&Ú³€W_}•6 sQPPzçĉ±÷Þ{ï×|{ >øàˆ>Åb?i:wÈIH ˆ>ÌÎäÖÙlÑh'&''éäÔ[×/aÆÉÂáfggœÑÜf³³ð¸ñøépNóDQäœóó<[HÄQÈÐ!ŠˆÅ㘚æßÿŽ;î`CCC$;;KþûÅÍ={öœŸ˜p7½Vo©ÝöŸA–™‹îr…—\&Ú³€ááa E r[‚*!¤ž››;½oï^- “Í›6³j…Û`%Ü¢ã,%dhªÊÕveš¦Á4-È 'ߘoÅU²Ž"+X¨.`aA‡’L6Çq“É$êN !éü,Ê”6/%©€RUSÑÙÙ †5k¾Íyä6¸bì~u·ý裎?óÌ3oŽOL¼K)(; ¾öÿZ ¾,8ÍWõù|µå袻\áe_=ô;zôh»|¸;$Tÿ¿?ýéÔ¶íÛ7oذ'NœÀ\~’,¡V­9>‚=Ž I’šp’”€Ï‘‹ÅcO`†zÈæx†\"CpÝr]‰®H$‚YGÈ#6Ç}ã±8&&' Ö}÷»lõêÕðûýäo<5Ÿ=1:ú±mÛs„ƒðÊþRâ›íœýÅ&Ÿªì{¾¯/¼ÿÈÀmM=øàƒxî¹çÚµj৤J!1ŸÏ½¶gÏš›nº©kÓæM̰ bÛ6Jj Á`±X´ÕÞ»¨HÇÉ=”Ò&¹Ç¶y»®R®À4M¤Ò)‡žÛ@oooS€#3ÁBódú3ÐëuT¡F£ÁÇz»»P©UL&±uëV–N§É›Gâµ×^+ýù/9S¯×§ !n¯Š‹7þçÍæÓp8LÏæ{éþ×Þà ÐÞ\B+  @ƒsbƒäú¨Œ±’ щ©©Ñ_<ýtø?~ùËUëÖ­c½½½äСCÐ4 C×]‡º®£Z­"•JÁh¨Tªèéméóuuw檦r~ð ÃD"( ˆ'â;Dfø P0À…l¢ˆH8‚¡ë†ØÍ7ßLDQ$###Öž={&Î9¶ZmÔ]÷ž¿¸—ß´Ïö¨»Ëd±Ò¨‡Ï¥” v¾ÿ>6lØ@ Ãè „„c2€^AHQJ»~ðýïo|êé§Óé4#Çÿ¦i`.—C‡ß¾¾ff¦A)E?²Ù,LÓÄÀÀæçæ¹HÇŠ(8Ÿ+¹AD©„•+Q®TQ,08¸ªª¡T*aË–-lýúõ0-‹¼sü¯ìåW^ž~÷Ô©—PÝmgð¹A`Iê.cÌ£î.3xà ¶mø|>ìܹ“ŒŒŒ>Ÿ/`Ûv@@<ô ‚ÐG)•7ßüäϾ2•J1Ã0pòäIrìØ±– Æü<úûÓ¦¦¦J¥ ŠbÓ.+ b||ÉdÑ(&ÆÇ!Ë2â‰8Æ/Œ# AQdøý¶yÓfd2äÈáÃØ½{wñí·ßvmµΉ߮ÆÓ®µÙÔ]Bˆ·ñ¿áðÀÄ%²€c, ­ ¡‹R½ñ†¾óï?ûÙõ›6m*• + Èæräøñã¨V*èîéÁÔô4übú3d³³°m™L¹lzCÇÀÀr¹9T*åfVP­T±á– X»v-ëêî&ãçÏ㥗^*ÿïo|\«Õ&œÊ¾áÈm—qé_ƒ3¢ûYÔ]à«Ñ¤÷põá€/%²¿mÛa2€ð РO„$¥4 SÿôÀ7=°cG|ÕªUÐu333laa“““äôéÓˆÇâ¨.T1>>Žþt?AÀää$úúú¸t×|™ ºº’»ñƱråJœ>}šìûýï^?tè¬c«U1©.Áàsƒ@ûÆ×A°,˲–²Õ¼¿Üà€/ÅY€iš>ÆX­ rV/!DbŒE2™Lÿöm÷¬øá~¿ñ[ß"ápÕJ Ó`FÃ@.—ƒeYŽF 7ÑL¥R°, þ@éT „Â(ƒ¦©øŸ½{Í=¿ûÝùÉ©©³àUýŶZ‹7þb?=€mÛö§¨»Œ1oã/SxàKÂÍFFF°sçNAEѲ¬€xèÐ^ì&„È„(¥´@0+ÃÃé7J«nXȬâ‰8!ˆD"°mº®#‰À4M‹%0F1—ËáƒN³ Îëo½õÖÜûccŸ€Óq õE÷ü¥fóë‘Çô |×.¼ð%Á!†aàþûï' >Ÿ¯Ã¶í 7èϺÁ‹„’ QaJ©€@0DW D{zz‘H$܉‚¡|>)k[¨×­jµfV*åz6›­åóù å#¹eÆeÌæ/¦îZ^ïÚ†¾¸W±±1¬_¿žX–%BDJi@'€<$ÁƒB@”!H¥89‹´=º‹`m‹bBtJ)g"òu9¶ZÔ+ðyðÀW÷*pàÀÜwß}IJ,AѶ퀀8ø¦—%9¯uBü„ðŒ@ „ÆÀ!`üŸe°cxKÏ% µ“yÚÅ7Û+ûf{eß+ðyðÀWˆÅAÀ4M"Šb‡mÛ~Ƙ› DÁ7¾»¢à" ÀÎÐô¡•­Óß/ð¹²dî$¢[ì»"Õ]¯ÀwmcÙÆØ§üÔÿpí¹÷ïßÇ{Œœ={–‚àcŒu0Æà§}ÄYÎ ;_t ÜIe¡5†ì€*Zž…5pZ²îVöcthhˆ=ûì³lÛ¶m°,ë"Îþr!Ä nŸe®&ÜÂ`£ÑÀ®]»ÈÈȪժ ‚è?øf‚÷1€V°8¸é¿å¬xÐÁ7|ÝY:!Ä$„˜”Rº~ýzöøã³»îº @ ù³yð,Ãà~À«• >üðCˆ¢xU2÷„M÷÷ã…çŸÇO>IA ”Râßàn0p—{ò·_.ú¶mËDë*àöñMðà`3ÆØ¿üô§ìŸò$$.#î^Q®B`YV¯^ÎhÔ |—À² î‡|tt?¾ÿ~Äãñf‘ëï ÆlÛF,ƒ ÈÍÍ!_(º®˲ÜJ¿Û\¼ñÛ»ÀÅ€ö@àf6&U$ }}},:Rcæ57žëóù iöìÝ‹[n¹åš ~W‚e7ì~ÈEŸ±X  ”^ÕŸÉ-¶õõö"Õ×ǪÕ*›ž™! àu]·,ËZÜúsOþÅÀÖµEQ¤¡`¿éTй¿³aˆD"×ÔÆw!cM-þ .ÿ-€Î-¹í’ŽIEND®B`‚¨% ÿÿÿÿ(0` 77$;;4'7728885A>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M;<9H99/6770%... +++771*66.BJLI‹TXVóW[YøY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øX\ZøTXVûSWUÛBDAd770%333@@@77/2*333---Y\Z˯²°ÿ¸»¹ÿ¸»¹ÿ®²°ÿw{yÿ[_]ÿdgfÿbgeÿcfdÿbedÿafcÿadbÿadbÿ`cbÿ_daÿ_caÿ^baÿ^caÿ]a_ÿ]a_ÿ]a_ÿ]a_ÿ[_]ÿ[`^ÿ[_]ÿY][ÿZ^\ÿY][ÿY][ÿY][ÿY][ÿX\ZÿVZXÿlomÿ¡£¡ÿ¸»¹ÿ»½»ÿ»½»ÿX\Yí77$VZX™¡¤£ÿ·º¸ÿµ¸¶ÿˆ‹‰ÿ…‰†ÿèëéÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿçêèÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿîñïÿª®¬ÿrusÿµ¸¶ÿ¹¼ºÿª¬«ÿZ^\ÉSWUn”’øµ¸¶ÿ±´²ÿospÿÁÄÁÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿéíêÿª®¬ÿ…Šˆÿ¨¬ªÿåèæÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿáåâÿ\`^ÿ±´²ÿ¹¼ºÿ˜œšÿW[YšSWUM~‚€ò¶¹·ÿ®±¯ÿuwvÿ¾ÁÀÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÏÒÑÿŠŽŒÿjmiÿ@>9ÿgieÿ†‹ˆÿ¼¿½ÿìïíÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿ×ÛÙÿafcÿ®±¯ÿ¹»ºÿ„ˆ†øSWUmSWU+ospó´·µÿª®«ÿ|€}ÿ«¯­ÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿåèæÿ¢¦¤ÿ…‚ÿLLGÿ:93ÿ:93ÿ:93ÿEE@ÿwzwÿ‹ÿÎÑÐÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÁÄÁÿosqÿª®«ÿ¸¼¹ÿpsrñSWUGSWU `dbû³¶´ÿ§ª¨ÿ…ƒÿ˜›šÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÆÉÈÿ‡ŒŠÿfieÿAA<ÿ??:ÿ??:ÿ??:ÿ??:ÿ??:ÿ??:ÿRTPÿ…ƒÿ™›ÿÜßÞÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿ¨¬ªÿy}{ÿ§ª¨ÿ¸º¹ÿ^b`ôSWU"]a_ò¬°®ÿ£§¥ÿˆ‹ÿ‚‡ƒÿïòðÿïòðÿïòðÿïòðÿïòðÿáäâÿ™›ÿƒÿOQMÿEFBÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿFGDÿad`ÿ…Šˆÿª®¬ÿãçåÿëïíÿëïíÿëïìÿëîìÿëîìÿŽ‘ÿ…ƒÿ¤¨¦ÿ³¶µÿTXVûSWUZ^\Õ¤¨¥ÿ ¤¡ÿ“‘ÿorpÿíðîÿìðîÿìïíÿêíëÿ½À¾ÿˆ‹ÿ…Šˆÿ†‹‰ÿ‚‡…ÿSUSÿbedÿbedÿbedÿbedÿbedÿbedÿbedÿbedÿRUSÿ…‰‡ÿ†Šˆÿ…Šˆÿ‹Žÿ¼Á¿ÿåéçÿçëéÿæëèÿæëèÿrxuÿ‹Œÿ£§¤ÿ¥¨¦ÿX\ZäX\Z°˜›™ÿž¢ ÿ•™—ÿ^a`ÿèìêÿèìêÿèìêÿèìéÿýýýÿÿÿÿÿÿÿÿÿûüûÿ~‚€ÿRUSÿVXXÿWYXÿWYXÿWYXÿWYXÿWYXÿWYXÿVXXÿW[Yÿˆ‹ÿûûûÿýýýÿýýýÿûûûÿâçäÿâçåÿâçäÿâçäÿ_baÿ“˜•ÿ¤§¦ÿ”˜–ÿX\Z¿TXV…‹Ž‹þž¡Ÿÿ˜œ™ÿVZXÿàåâÿäèæÿäèæÿãèæÿãèåÿãèåÿãçåÿâçåÿƒˆ…ÿ]`^ÿnsqÿrwtÿrwuÿrwuÿrwuÿrwuÿrwtÿmrpÿ_caÿ‘•’ÿàæãÿáçäÿáæäÿàåâÿÞäáÿÞäáÿÞäáÿ×ÛÙÿUYWÿ™šÿ£§¤ÿ†‰‡þUYWSWUa|€~õ Ÿÿ•™—ÿ`dbÿËÐÍÿàåâÿßåâÿßäâÿßäáÿßäáÿÞäáÿÞäáÿ“–”ÿ]`^ÿGGCÿ<;5ÿ>=7ÿ?>8ÿ?=8ÿ><7ÿ<:5ÿED@ÿ`daÿ ¥¡ÿÛáÞÿÛáÞÿÛáÞÿÚáÝÿÚàÝÿÚàÝÿÚàÝÿ¿ÄÂÿcfdÿ•™—ÿ£¦¥ÿvywõSWUeSWU@nqoñ žÿ’–”ÿhmiÿ·¼ºÿÛáÞÿÛáÞÿÛáÞÿÛáÞÿÚàÝÿÚàÝÿÚàÝÿ¥©¦ÿ`a_ÿGGAÿgfbÿxwsÿwwrÿwwrÿxwsÿggaÿGF@ÿdfbÿ­²­ÿ×ÞÚÿ×ÝÚÿÖÝÚÿÖÝÚÿÖÝÙÿÖÝÙÿÕÜÙÿ§­ªÿjomÿ’–”ÿ£¦¤ÿeigñSWU@SWUcfdöœŸÿ“ÿmrnÿ¤ª§ÿ×ÞÚÿ×ÝÚÿ×ÝÚÿÖÝÚÿÖÝÚÿÖÝÙÿÖÜÙÿ´¹¶ÿ^`\ÿRSNÿ……‚ÿde`ÿbc^ÿbc^ÿde`ÿ„…ÿTSOÿ`b^ÿ¹À¼ÿÓÚÖÿÒÚÖÿÒÙÖÿÒÙÖÿÒÙÖÿÑÙÕÿÑÙÕÿ’˜•ÿqtrÿ“ÿ£¦¤ÿX\ZöSWUSWU[_]ûš›ÿ‹ÿosqÿ’˜•ÿÓÚ×ÿÓÚÖÿÒÚÖÿÒÙÖÿÒÙÖÿÒÙÖÿÑÙÕÿÅÌÉÿRSOÿ_a]ÿ’”ÿtvrÿtvrÿsuqÿsuqÿ’ÿ_a]ÿVVQÿÈÏÌÿÎÖÓÿÎÖÒÿÎÖÒÿÎÖÒÿÍÕÒÿÍÕÒÿÍÕÑÿy}ÿvzxÿŽ’ÿ›žœÿTXVøX\Zè”—•ÿˆŒ‰ÿtwtÿ~„€ÿÎÖÓÿÎÖÓÿÎÖÒÿÎÖÒÿÎÖÒÿÍÕÒÿÍÕÒÿÍÕÑÿMLHÿilgÿ ¢Ÿÿ…ˆ…ÿ…ˆ…ÿ…ˆ…ÿ…ˆ…ÿž¡žÿjliÿTTPÿÊÓÏÿÊÓÏÿÊÒÏÿÊÒÎÿÉÒÎÿÉÒÎÿÉÒÎÿÉÑÎÿfliÿ|€}ÿŽ‘ŽÿŽ‘ÿW[XÝW[YÈ‹ŽŒÿ†‹ˆÿw{xÿnspÿÊÓÏÿÊÓÏÿÊÒÏÿÊÒÎÿÉÒÎÿÉÒÎÿÉÒÎÿÉÑÍÿWXRÿswsÿ­±­ÿ–›—ÿ–›—ÿ–›—ÿ–›—ÿ¬°­ÿuxvÿ^^ZÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿW\Zÿ…‚ÿŒ’ÿ€„‚ÿV[X¶UYW €ƒÿ†Šˆÿz~{ÿ`daÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿqtpÿ~‚€ÿº¿¼ÿ§®ªÿ§®ªÿ§®ªÿ§­©ÿ¹¾»ÿ~ƒ€ÿrtoÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ¼ÅÁÿX\Zÿ…ƒÿ‘ÿtxvýTXV…SWUvuxwû„‰†ÿ{€}ÿUZWÿÇÐÌÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿŽ‘ÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡ŒŠÿŠŽ‰ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿª²¯ÿadcÿ}‚ÿŒ‘ŽÿhljôSWU]SWUTkolòƒ‰†ÿz|ÿZ][ÿ¬´°ÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿœ¢ÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡ŒŠÿ—š—ÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ’™–ÿeigÿz|ÿŒ‘Žÿ]a_ñSWU8SWU2afcòƒˆ…ÿv{xÿgliÿkpmÿœ£ ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿ–šÿ‡‹‰ÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡‹‰ÿ‘—”ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿ¤¡ÿ•‘ÿZ^\ÿnspÿw|yÿŠŒÿUYWúSWUSWUX\Zú‡‹‰ÿv{xÿuzwÿfjhÿW\ZÿSWUÿSWUÿSWUÿSWUÿSWUÿSWUÿTXVÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿˆ‹ÿX\ZÿSWUÿSWUÿSWUÿSWUÿSWUÿSWUÿ_cbÿkomÿv{xÿ€„‚ÿ~ƒ€ÿTXVðUYWã|~ÿ…ƒÿy~{ÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿ‡ŒŠÿÈÎËÿ¹À½ÿ¹À½ÿ¹À½ÿ¹À½ÿÈÍËÿ‰ŽŒÿy}{ÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿx}zÿ€…ƒÿ‡Œ‰ÿafcõTXV|SWUYZ^\ôvzxÿ„ˆ…ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿˆ‹ÿËÑÎÿ½ÄÁÿ½ÄÁÿ½ÄÁÿ½ÄÁÿËÐÎÿŠÿ…‰†ÿ„‰†ÿ„‰†ÿ„‰†ÿ„‰†ÿ„Їÿ„Їÿ„Їÿ„‰†ÿ{€~ÿ_daôTXVçSWU SWUSWU_UYWÍTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñUYWñ‡ŒŠÿÏÓÒÿÂÇÅÿÂÇÅÿÂÇÅÿÂÇÅÿÏÓÑÿˆŽŒÿY][òTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñUYWéTXVsSWU€€€ˆ‹üÒÖÕÿÆËÉÿÆËÉÿÆËÉÿÆËÉÿÒÖÔÿ‹ŽöŠŠŠ€€€ˆ‹üÕÙØÿÊÏÍÿÊÏÍÿÊÏÍÿÊÏÍÿÕÙ×ÿ‹ŽöŠŠŠ€€€ˆ‹üÙÜÛÿÎÓÑÿÎÓÑÿÎÓÑÿÎÓÑÿØÜÛÿ‹ŽöŠŠŠ€€€ˆ‹üÜßÞÿÒÖÕÿÒÖÕÿÒÖÕÿÒÖÕÿÛßÞÿ‹‘ŽöŠŠŠ• ++Ї‹üßâáÿÖÚÙÿÖÚÙÿÖÚÙÿÖÚÙÿßâáÿŒ‘÷VVŒ&‰ “N”¢•Ñ“ó”ú”ø”ø”ø”ø”ø“ø†‹‹ÿâåäÿÚÞÝÿÚÞÝÿÚÞÝÿÚÞÝÿâåäÿ‡Œÿ “ù”ø”ø”ø”ø”ø“ú”õ•Ô–§“W•—˜ô ¥ÿ©ÿ­ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ®ÿ†‹‹ÿåççÿÞááÿÞááÿÞááÿÞááÿåççÿˆÿ¬ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ®ÿ©ÿ ¥ÿšô•°“ý((Âÿ¾ÿºÿºÿºÿºÿºÿºÿºÿºÿ¸ÿ†‹‹ÿèëêÿâåäÿâåäÿâåäÿâåäÿèêêÿ‡Œ‘ÿ µÿºÿºÿºÿºÿºÿºÿºÿºÿ ½ÿ))Äÿ”ùŽ“ÿKKåÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿ×ÿ†‹Œÿëîíÿæéèÿæéèÿæéèÿæéèÿëîíÿ‡’ÿ ÒÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿDDäÿšö•“û[[èÿ--éÿ åÿåÿåÿåÿåÿåÿåÿåÿ âÿ†‹Œÿïððÿêììÿêììÿêììÿêììÿîððÿˆ“ÿÜÿåÿåÿåÿåÿåÿåÿåÿ åÿ((èÿ^^ëÿ—ø™–…¢÷``Íþ~~Üÿáÿåÿåÿåÿåÿåÿåÿâÿ†‹‹ÿáäãÿÛßÞÿÚÞÝÿÚÞÝÿÛßÞÿáäãÿŠ‘ÿ€€Üÿåÿåÿåÿåÿåÿåÿáÿ~~ÜÿccÎþ##§õ–œ”9”ƒ—¸—â”ü“ÿ“ÿ“ÿ“ÿ“ÿ“ÿ‚†Šÿ¸¼»ÿÆËÊÿÇÌÊÿÇÌÊÿÆËÊÿº¿½ÿƒ‡Šÿ’ÿ“ÿ“ÿ“ÿ“ÿ“ÿ”ü˜ã™»”ˆ‘A‰‹‹a‡‹æ‡‹ñ‡‹ñ‡‹ñ‡‹ñˆŠè‰‹nÀóÃÀcLÀKÐÀ­$ÀýÀÌ@Àž+À ©À4Áà“¨ðÎ2ð•Jðý9ð‰\øÍÁøPÊøÛønöøƒƒøïïøVÙø?Žíü?"—ü?æü? `ü?fbü?ÔZü?üãÉþºèþç¶þÿͽÿÿàÿÿ›Wÿÿàÿÿ”1ÿÿàÿÿÛ‡ÿÿàÿÿܺÿøÿY}ÿ€ÿûÿÿ¹nÿâRÿtÿ‘`ÿÿÊ…ÿ€ÿ*VÿÿðÿÿcÇÿÿÿÿÿÿËÿÿÿÿÿÿ<ÿÿÿÿÿÿU$¨ ÿÿÿÿ( @ +++111;<76<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86880 @@@333AB>9ÿ>>9ÿXZVÿ„Іÿ®²¯ÿêíëÿïòðÿïòðÿïòðÿïòðÿ¾ÁÀÿptsÿ²µ³ÿ‰ŒŠöSWUkSWU?rus𯳰ÿvzwÿ²µ³ÿïòðÿïòðÿïòðÿ¾ÂÀÿ…‹‰ÿ^a^ÿSURÿ_a_ÿ_a_ÿ_a_ÿ_a_ÿUWSÿswtÿ‹ÿÍÑÎÿìðíÿìïíÿëïíÿ¥©§ÿx|zÿ°³±ÿtywðSWUFSWUcgeô­±®ÿy}{ÿ žÿëïíÿâäãÿ ¤¡ÿ‚‡…ÿ‚†„ÿrvtÿQSRÿWXXÿWXXÿWXXÿWXXÿQSRÿz~|ÿ…Šˆÿ…‹ˆÿ§¬ªÿÝâàÿåêçÿŠŒÿ€„ÿ®±®ÿcgeóSWU!SWUY][úª­«ÿ‚ÿ†‹ˆÿåêçÿåéçÿûüûÿÿÿÿÿÜÞÝÿuywÿbgdÿqvtÿqvtÿqvtÿqvtÿ^baÿx{yÿÜáÞÿÿÿÿÿÿÿÿÿàåâÿßåâÿrutÿ‡‹‰ÿ«®¬ÿX\ZúSWUW[Yç ¤£ÿƒ‡†ÿswtÿßäâÿßäáÿÞäáÿÞãáÿØÝÚÿgjfÿJKGÿ>=7ÿ>=7ÿ>=7ÿ>=7ÿMMIÿkmjÿÚáÞÿÚàÝÿÚàÝÿÙàÝÿÙßÜÿ^c`ÿŽ’ÿ £¡ÿW[YäX\ZÉ“˜•ÿŠŽŒÿbfcÿÙßÜÿØßÜÿØßÛÿØÞÛÿ×ÞÛÿdfbÿNNJÿijeÿ|ÿ|ÿjkgÿNNIÿmqlÿÔÛØÿÔÛØÿÔÛ×ÿÓÚ×ÿÎÔÒÿV[Yÿ”™–ÿ‘•“ÿX\ZÀVZX¡†ŠˆÿŽ’ÿVZXÿÒÙÖÿÒÚÖÿÒÙÖÿÒÙÖÿÑÙÕÿqsnÿ^_\ÿ”—”ÿ}|ÿ|zÿ•˜”ÿ_a]ÿz|xÿÎÖÓÿÎÖÒÿÍÖÒÿÍÕÒÿ¸¾»ÿ_daÿ’—”ÿ‚†„ÿUYW’SWUwz~|û“ÿY^[ÿÂÉÅÿÌÔÑÿÌÔÐÿÌÔÐÿËÔÐÿ…ˆƒÿmqmÿ¨¬¨ÿ‘•‘ÿ‘•‘ÿ©¬©ÿoqoÿ‰‰ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ£ª¦ÿfjiÿ’•“ÿuxvöSWUhSWUVmqnðŽÿ`dbÿ¯¸´ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿœ¡œÿ~‚€ÿ½À¾ÿ«±®ÿ«±®ÿ½Â¿ÿ„‚ÿ›¡žÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ’™–ÿjmkÿ“ÿfjhðSWUCSWU5bfdïŠÿdhgÿ¢©¦ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ·¾ºÿ„‰‡ÿÄÊÇÿ¶½ºÿ¶½ºÿÆËÉÿ†‹‰ÿ±¹µÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ}ƒ€ÿlpnÿŽ‘ÿ\`^õSWUSWUY][õ‰Šÿinkÿinkÿ”š˜ÿ•œ™ÿ•™ÿ•™ÿ•™ÿ„‰†ÿÄÊÇÿ¶½ºÿ¶½ºÿÆËÉÿ†Šˆÿ”š˜ÿ–šÿ–šÿ–šÿ‹’ŽÿZ]\ÿswtÿŠŽŒÿUZXûSWUTXV÷„ˆ†ÿ|~ÿfjgÿ\a_ÿ\`^ÿ\`^ÿ\`^ÿ\`^ÿ…ŠˆÿÄÊÇÿ¶½ºÿ¶½ºÿÆËÉÿ‡ŒŠÿ_caÿ\`^ÿ\`^ÿ\`^ÿ`edÿmspÿƒ‡„ÿz}ÿUYWÞUYWºjnký„‰†ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ†Œ‰ÿÈÍËÿ»Â¿ÿ»Â¿ÿÊÏÍÿˆ‹ÿ‡‹‰ÿ‡‹‰ÿ‡‹‰ÿ‡‹‰ÿ‡ŒŠÿ‡Œ‰ÿ|~ÿ\`^ôSWUfSWU'UYWÎSWUüTXVüTXVüTXVüTXVüTXVüTXVü…ŠˆÿÌÑÏÿÁÇÅÿÁÇÅÿÏÓÒÿ‡ŒŠÿW[YüTXVüTXVüTXVüTXVüTXVüTXVõTXVSWUSWUSWUSWUSWUSWUSWUSWU‡ŒŠ÷ÑÕÔÿÇÍËÿÇÍËÿÓØÖÿˆ‹ùuzxSWUSWUSWUSWUSWUSWU‡ŒŠ÷ÕØ×ÿÍÒÐÿÍÒÐÿØÜÚÿˆ‹ø…Šˆ““"“(“(“(†‹ŠøÙÜÛÿÓ×ÖÿÓ×ÖÿÝàßÿˆŒù()5“(“(“"““$”Á”ì“ü•ó•ò•ò•òƒˆ‹ÿÞáàÿÙÝÜÿÙÝÜÿáääÿ†ŠŒÿ ”ó•ò•ò•ó“ü”í”Ó(“”ó £ü­ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿƒˆŒÿâäãÿßâáÿßâáÿæèèÿ†‹ÿ­ÿ¯ÿ¯ÿ¯ÿ¯ÿ­ÿ ¤ý”ô“"“8žð77ÔÿÉÿÈÿÈÿÈÿÈÿÈÿƒˆÿæèèÿåççÿåççÿëììÿ†‹Žÿ ÄÿÈÿÈÿÈÿÈÿÉÿ55Óÿ ï“@“8¤ð^^ôÿðÿïÿïÿïÿïÿïÿ„‰Žÿëììÿìîîÿìîîÿïññÿ…ŠŽÿéÿïÿïÿïÿïÿðÿ]]ôÿ ¨ñ“@“–ç66½úllÚÿwwáÿwwäÿwwåÿwwåÿwwåÿ‡‹’ÿ¾ÂÀÿÒÖÔÿÒÖÔÿÄÈÆÿ‡‹ÿwxâÿwwåÿwwåÿwwåÿwwáÿmmÚÿ99¿û–ê““–™—Ζê”ü“ÿ“ÿ“ÿ23ÿ}‚‹ÿ‚‡Šÿ‚‡Šÿ„‹ÿ>Aÿ“ÿ“ÿ“ÿ”ü–ë—Ï–“À€€ÀÀÀÀÀÀÀàààààààðððü?ÿøÿÿÿøððððøh ÿÿÿÿ(  UYW¥SWUýTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüSWUüUYWÅUYWïz|ÿ„ÿ~‚ÿ~ƒ€ÿ~ƒ€ÿ~ƒ€ÿ~‚€ÿ~‚€ÿ~‚€ÿ~‚€ÿ}‚ÿ}‚ÿ~ƒ€ÿ~ƒ€ÿTXVüX\Zׇ‹‰ÿmqoÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿfigÿŽ’ÿVZXðZ^\¹’ÿ[`]ÿÉÏËÿ×ÞÙÿ×ÞÙÿ½Â½ÿGF@ÿvxsÿÕÜ×ÿ×ÞÙÿ×ÞÙÿÓÚÕÿVZXÿ—š˜ÿY][ÚX\Z”‹ŽŒÿmroÿ³ºµÿ×ÞÙÿµ»¶ÿ@>9ÿHJEÿDD@ÿZZUÿÌÒÍÿ×ÞÙÿ¾ÅÁÿehfÿ—›™ÿ[_]¼SWUq{}ú|~ÿœ¢žÿ­²­ÿ=<6ÿIKHÿSWUÿSWUÿHIDÿGGAÿ½Â½ÿ§­©ÿtxvÿ‡‹‰ÿX\Z™SWUTlpnñ†‰‡ÿ‚‡„ÿprmÿ<:4ÿ=;6ÿSWUÿSWUÿ@>9ÿ64.ÿmnhÿ“ÿƒÿy}{úSWUtSWU8`dbÿjokÿ×ÞÙÿ×ÞÙÿ64.ÿTXVÿSWUÿ971ÿÒÙÓÿ×ÞÙÿsxtÿ‰‹ÿkomñSWUXSWUX\Zô”˜–ÿVZXÿÓÚÕÿ×ÞÙÿ[]ZÿeigÿeigÿZ\XÿÒÙÓÿÕÜ×ÿ[_]ÿ‘”’ÿ`dbïSWU;SWUTXVû‘ŽÿmqoÿW[YÿY][ÿehfÿµ¼¹ÿ´»¸ÿdhfÿX\YÿX\Zÿknlÿ“‘ÿW[YõSWUVZXŸTXVýTXVüTXVüUXWüeigÿ¶½ºÿ¶½ºÿdhfÿSVTüTXVüTXVüTXVüVZX¿SWUSWUSWUSWU\`^eigÿ¶½ºÿ¶½ºÿdhfÿIK:SWUSWUSWU”d•à”ö”÷“÷dhhÿ¶½ºÿ¶½ºÿdhfÿ“÷”÷”ö•Ü“R“ü­ÿ³ÿ³ÿ±ÿdhiÿÏÔÒÿÎÓÑÿdhfÿ³ÿ³ÿ³ÿ«ÿ”ñ”ýEEåÿEEíÿEEíÿFFéÿeiiÿäææÿâääÿdhfÿEEíÿEEíÿEEíÿAAàÿ–ó–v–ï•ü•ü•üILxþdhfÿdhfÿEHzþ•ü•ü•ü—ì•bV‚–ýµÿfÿ×ÿðÿðÿðÿ€ðÿÀðÿ€íÿ€²ÿ€†ÿ€OÿTransGUI/rpc-spec.txt0000644000000000000000000012731112207071063013470 0ustar rootroot1. Introduction This document describes a protocol for interacting with Transmission sessions remotely. 1.1 Terminology The JSON terminology in RFC 4627 is used. JSON is fairly common now, but for the benefit of torrent developers familiar with benc: a JSON array is equivalent to a benc list, a JSON object is equivalent to a benc dictionary, and a JSON object's keys are the dictionary's string keys. 1.2 Resources The command-line utility "transmission-remote" uses this RPC API. Several developers have reported using its --debug JSON output as a reference when developing/debugging their own code. 2. Message Format Messages are formatted as objects. There are two types: requests (described in 2.1) and responses (described in 2.2). All text MUST be UTF-8 encoded. 2.1. Requests Requests support three keys: (1) A required "method" string telling the name of the method to invoke (2) An optional "arguments" object of key/value pairs (3) An optional "tag" number used by clients to track responses. If provided by a request, the response MUST include the same tag. 2.2. Responses Reponses support three keys: (1) A required "result" string whose value MUST be "success" on success, or an error string on failure. (2) An optional "arguments" object of key/value pairs (3) An optional "tag" number as described in 2.1. 2.3. Transport Mechanism HTTP POSTing a JSON-encoded request is the preferred way of communicating with a Transmission RPC server. The current Transmission implementation has the default URL as http://host:9091/transmission/rpc. Clients may use this as a default, but should allow the URL to be reconfigured, since the port and path may be changed to allow mapping and/or multiple daemons to run on a single server. 2.3.1. CSRF Protection Most Transmission RPC servers require a X-Transmission-Session-Id header to be sent with requests, to prevent CSRF attacks. When your request has the wrong id -- such as when you send your first request, or when the server expires the CSRF token -- the Transmission RPC server will return an HTTP 409 error with the right X-Transmission-Session-Id in its own headers. So, the correct way to handle a 409 response is to update your X-Transmission-Session-Id and to resend the previous request. 3. Torrent Requests 3.1. Torrent Action Requests Method name | libtransmission function ---------------------+------------------------------------------------- "torrent-start" | tr_torrentStart "torrent-start-now" | tr_torrentStartNow "torrent-stop" | tr_torrentStop "torrent-verify" | tr_torrentVerify "torrent-reannounce" | tr_torrentManualUpdate ("ask tracker for more peers") Request arguments: "ids", which specifies which torrents to use. All torrents are used if the "ids" argument is omitted. "ids" should be one of the following: (1) an integer referring to a torrent id (2) a list of torrent id numbers, sha1 hash strings, or both (3) a string, "recently-active", for recently-active torrents Response arguments: none 3.2. Torrent Mutators Method name: "torrent-set" Request arguments: string | value type & description ----------------------+------------------------------------------------- "bandwidthPriority" | number this torrent's bandwidth tr_priority_t "downloadLimit" | number maximum download speed (KBps) "downloadLimited" | boolean true if "downloadLimit" is honored "files-wanted" | array indices of file(s) to download "files-unwanted" | array indices of file(s) to not download "honorsSessionLimits" | boolean true if session upload limits are honored "ids" | array torrent list, as described in 3.1 "location" | string new location of the torrent's content "peer-limit" | number maximum number of peers "priority-high" | array indices of high-priority file(s) "priority-low" | array indices of low-priority file(s) "priority-normal" | array indices of normal-priority file(s) "queuePosition" | number position of this torrent in its queue [0...n) "seedIdleLimit" | number torrent-level number of minutes of seeding inactivity "seedIdleMode" | number which seeding inactivity to use. See tr_idlelimit "seedRatioLimit" | double torrent-level seeding ratio "seedRatioMode" | number which ratio to use. See tr_ratiolimit "trackerAdd" | array strings of announce URLs to add "trackerRemove" | array ids of trackers to remove "trackerReplace" | array pairs of "uploadLimit" | number maximum upload speed (KBps) "uploadLimited" | boolean true if "uploadLimit" is honored Just as an empty "ids" value is shorthand for "all ids", using an empty array for "files-wanted", "files-unwanted", "priority-high", "priority-low", or "priority-normal" is shorthand for saying "all files". Response arguments: none 3.3. Torrent Accessors Method name: "torrent-get". Request arguments: (1) An optional "ids" array as described in 3.1. (2) A required "fields" array of keys. (see list below) Response arguments: (1) A "torrents" array of objects, each of which contains the key/value pairs matching the request's "fields" argument. (2) If the request's "ids" field was "recently-active", a "removed" array of torrent-id numbers of recently-removed torrents. Note: For more information on what these fields mean, see the comments in libtransmission/transmission.h. The "source" column here corresponds to the data structure there. key | type | source ----------------------------+-----------------------------+--------- activityDate | number | tr_stat addedDate | number | tr_stat bandwidthPriority | number | tr_priority_t comment | string | tr_info corruptEver | number | tr_stat creator | string | tr_info dateCreated | number | tr_info desiredAvailable | number | tr_stat doneDate | number | tr_stat downloadDir | string | tr_torrent downloadedEver | number | tr_stat downloadLimit | number | tr_torrent downloadLimited | boolean | tr_torrent error | number | tr_stat errorString | string | tr_stat eta | number | tr_stat etaIdle | number | tr_stat files | array (see below) | n/a fileStats | array (see below) | n/a hashString | string | tr_info haveUnchecked | number | tr_stat haveValid | number | tr_stat honorsSessionLimits | boolean | tr_torrent id | number | tr_torrent isFinished | boolean | tr_stat isPrivate | boolean | tr_torrent isStalled | boolean | tr_stat leftUntilDone | number | tr_stat magnetLink | number | n/a manualAnnounceTime | number | tr_stat maxConnectedPeers | number | tr_torrent metadataPercentComplete | double | tr_stat name | string | tr_info peer-limit | number | tr_torrent peers | array (see below) | n/a peersConnected | number | tr_stat peersFrom | object (see below) | n/a peersGettingFromUs | number | tr_stat peersSendingToUs | number | tr_stat percentDone | double | tr_stat pieces | string (see below) | tr_torrent pieceCount | number | tr_info pieceSize | number | tr_info priorities | array (see below) | n/a queuePosition | number | tr_stat rateDownload (B/s) | number | tr_stat rateUpload (B/s) | number | tr_stat recheckProgress | double | tr_stat secondsDownloading | number | tr_stat secondsSeeding | number | tr_stat seedIdleLimit | number | tr_torrent seedIdleMode | number | tr_inactvelimit seedRatioLimit | double | tr_torrent seedRatioMode | number | tr_ratiolimit sizeWhenDone | number | tr_stat startDate | number | tr_stat status | number | tr_stat trackers | array (see below) | n/a trackerStats | array (see below) | n/a totalSize | number | tr_info torrentFile | string | tr_info uploadedEver | number | tr_stat uploadLimit | number | tr_torrent uploadLimited | boolean | tr_torrent uploadRatio | double | tr_stat wanted | array (see below) | n/a webseeds | array (see below) | n/a webseedsSendingToUs | number | tr_stat | | | | -------------------+--------+-----------------------------+ files | array of objects, each containing: | +-------------------------+------------+ | bytesCompleted | number | tr_torrent | length | number | tr_info | name | string | tr_info -------------------+--------------------------------------+ fileStats | a file's non-constant properties. | | array of tr_info.filecount objects, | | each containing: | +-------------------------+------------+ | bytesCompleted | number | tr_torrent | wanted | boolean | tr_info | priority | number | tr_info -------------------+--------------------------------------+ peers | array of objects, each containing: | +-------------------------+------------+ | address | string | tr_peer_stat | clientName | string | tr_peer_stat | clientIsChoked | boolean | tr_peer_stat | clientIsInterested | boolean | tr_peer_stat | flagStr | string | tr_peer_stat | isDownloadingFrom | boolean | tr_peer_stat | isEncrypted | boolean | tr_peer_stat | isIncoming | boolean | tr_peer_stat | isUploadingTo | boolean | tr_peer_stat | isUTP | boolean | tr_peer_stat | peerIsChoked | boolean | tr_peer_stat | peerIsInterested | boolean | tr_peer_stat | port | number | tr_peer_stat | progress | double | tr_peer_stat | rateToClient (B/s) | number | tr_peer_stat | rateToPeer (B/s) | number | tr_peer_stat -------------------+--------------------------------------+ peersFrom | an object containing: | +-------------------------+------------+ | fromCache | number | tr_stat | fromDht | number | tr_stat | fromIncoming | number | tr_stat | fromLpd | number | tr_stat | fromLtep | number | tr_stat | fromPex | number | tr_stat | fromTracker | number | tr_stat -------------------+--------------------------------------+ pieces | A bitfield holding pieceCount flags | tr_torrent | which are set to 'true' if we have | | the piece matching that position. | | JSON doesn't allow raw binary data, | | so this is a base64-encoded string. | -------------------+--------------------------------------+ priorities | an array of tr_info.filecount | tr_info | numbers. each is the tr_priority_t | | mode for the corresponding file. | -------------------+--------------------------------------+ trackers | array of objects, each containing: | +-------------------------+------------+ | announce | string | tr_tracker_info | id | number | tr_tracker_info | scrape | string | tr_tracker_info | tier | number | tr_tracker_info -------------------+--------------------------------------+ trackerStats | array of objects, each containing: | +-------------------------+------------+ | announce | string | tr_tracker_stat | announceState | number | tr_tracker_stat | downloadCount | number | tr_tracker_stat | hasAnnounced | boolean | tr_tracker_stat | hasScraped | boolean | tr_tracker_stat | host | string | tr_tracker_stat | id | number | tr_tracker_stat | isBackup | boolean | tr_tracker_stat | lastAnnouncePeerCount | number | tr_tracker_stat | lastAnnounceResult | string | tr_tracker_stat | lastAnnounceStartTime | number | tr_tracker_stat | lastAnnounceSucceeded | boolean | tr_tracker_stat | lastAnnounceTime | number | tr_tracker_stat | lastAnnounceTimedOut | boolean | tr_tracker_stat | lastScrapeResult | string | tr_tracker_stat | lastScrapeStartTime | number | tr_tracker_stat | lastScrapeSucceeded | boolean | tr_tracker_stat | lastScrapeTime | number | tr_tracker_stat | lastScrapeTimedOut | boolean | tr_tracker_stat | leecherCount | number | tr_tracker_stat | nextAnnounceTime | number | tr_tracker_stat | nextScrapeTime | number | tr_tracker_stat | scrape | string | tr_tracker_stat | scrapeState | number | tr_tracker_stat | seederCount | number | tr_tracker_stat | tier | number | tr_tracker_stat -------------------+-------------------------+------------+ wanted | an array of tr_info.fileCount | tr_info | 'booleans' true if the corresponding | | file is to be downloaded. | -------------------+--------------------------------------+ webseeds | an array of strings: | +-------------------------+------------+ | webseed | string | tr_info +-------------------------+------------+ Example: Say we want to get the name and total size of torrents #7 and #10. Request: { "arguments": { "fields": [ "id", "name", "totalSize" ], "ids": [ 7, 10 ] }, "method": "torrent-get", "tag": 39693 } Response: { "arguments": { "torrents": [ { "id": 10, "name": "Fedora x86_64 DVD", "totalSize": 34983493932, }, { "id": 7, "name": "Ubuntu x86_64 DVD", "totalSize", 9923890123, } ] }, "result": "success", "tag": 39693 } 3.4. Adding a Torrent Method name: "torrent-add" Request arguments: key | value type & description ---------------------+------------------------------------------------- "cookies" | string pointer to a string of one or more cookies. "download-dir" | string path to download the torrent to "filename" | string filename or URL of the .torrent file "metainfo" | string base64-encoded .torrent content "paused" | boolean if true, don't start the torrent "peer-limit" | number maximum number of peers "bandwidthPriority" | number torrent's bandwidth tr_priority_t "files-wanted" | array indices of file(s) to download "files-unwanted" | array indices of file(s) to not download "priority-high" | array indices of high-priority file(s) "priority-low" | array indices of low-priority file(s) "priority-normal" | array indices of normal-priority file(s) Either "filename" OR "metainfo" MUST be included. All other arguments are optional. The format of the "cookies" should be NAME=CONTENTS, where NAME is the cookie name and CONTENTS is what the cookie should contain. Set multiple cookies like this: "name1=content1; name2=content2;" etc. Response arguments: On success, a "torrent-added" object in the form of one of 3.3's tr_info objects with the fields for id, name, and hashString. On failure due to a duplicate torrent existing, a "torrent-duplicate" object in the same form. 3.5. Removing a Torrent Method name: "torrent-remove" Request arguments: string | value type & description ---------------------------+------------------------------------------------- "ids" | array torrent list, as described in 3.1 "delete-local-data" | boolean delete local data. (default: false) Response arguments: none 3.6. Moving a Torrent Method name: "torrent-set-location" Request arguments: string | value type & description ---------------------------------+------------------------------------------------- "ids" | array torrent list, as described in 3.1 "location" | string the new torrent location "move" | boolean if true, move from previous location. | otherwise, search "location" for files | (default: false) Response arguments: none 3.7. Renaming a Torrent's Path Method name: "torrent-rename-path" For more information on the use of this function, see the transmission.h documentation of tr_torrentRenamePath(). In particular, note that if this call succeeds you'll want to update the torrent's "files" and "name" field with torrent-get. Request arguments: string | value type & description ---------------------------------+------------------------------------------------- "ids" | array the torrent torrent list, as described in 3.1 | (must only be 1 torrent) "path" | string the path to the file or folder that will be renamed "name" | string the file or folder's new name Response arguments: "path", "name", and "id", holding the torrent ID integer 4. Session Requests 4.1. Session Arguments string | value type | description ---------------------------------+------------+------------------------------------- "alt-speed-down" | number | max global download speed (KBps) "alt-speed-enabled" | boolean | true means use the alt speeds "alt-speed-time-begin" | number | when to turn on alt speeds (units: minutes after midnight) "alt-speed-time-enabled" | boolean | true means the scheduled on/off times are used "alt-speed-time-end" | number | when to turn off alt speeds (units: same) "alt-speed-time-day" | number | what day(s) to turn on alt speeds (look at tr_sched_day) "alt-speed-up" | number | max global upload speed (KBps) "blocklist-url" | string | location of the blocklist to use for "blocklist-update" "blocklist-enabled" | boolean | true means enabled "blocklist-size" | number | number of rules in the blocklist "cache-size-mb" | number | maximum size of the disk cache (MB) "config-dir" | string | location of transmission's configuration directory "download-dir" | string | default path to download torrents "download-queue-size" | number | max number of torrents to download at once (see download-queue-enabled) "download-queue-enabled" | boolean | if true, limit how many torrents can be downloaded at once "dht-enabled" | boolean | true means allow dht in public torrents "encryption" | string | "required", "preferred", "tolerated" "idle-seeding-limit" | number | torrents we're seeding will be stopped if they're idle for this long "idle-seeding-limit-enabled" | boolean | true if the seeding inactivity limit is honored by default "incomplete-dir" | string | path for incomplete torrents, when enabled "incomplete-dir-enabled" | boolean | true means keep torrents in incomplete-dir until done "lpd-enabled" | boolean | true means allow Local Peer Discovery in public torrents "peer-limit-global" | number | maximum global number of peers "peer-limit-per-torrent" | number | maximum global number of peers "pex-enabled" | boolean | true means allow pex in public torrents "peer-port" | number | port number "peer-port-random-on-start" | boolean | true means pick a random peer port on launch "port-forwarding-enabled" | boolean | true means enabled "queue-stalled-enabled" | boolean | whether or not to consider idle torrents as stalled "queue-stalled-minutes" | number | torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size "rename-partial-files" | boolean | true means append ".part" to incomplete files "rpc-version" | number | the current RPC API version "rpc-version-minimum" | number | the minimum RPC API version supported "script-torrent-done-filename" | string | filename of the script to run "script-torrent-done-enabled" | boolean | whether or not to call the "done" script "seedRatioLimit" | double | the default seed ratio for torrents to use "seedRatioLimited" | boolean | true if seedRatioLimit is honored by default "seed-queue-size" | number | max number of torrents to uploaded at once (see seed-queue-enabled) "seed-queue-enabled" | boolean | if true, limit how many torrents can be uploaded at once "speed-limit-down" | number | max global download speed (KBps) "speed-limit-down-enabled" | boolean | true means enabled "speed-limit-up" | number | max global upload speed (KBps) "speed-limit-up-enabled" | boolean | true means enabled "start-added-torrents" | boolean | true means added torrents will be started right away "trash-original-torrent-files" | boolean | true means the .torrent file of added torrents will be deleted "units" | object | see below "utp-enabled" | boolean | true means allow utp "version" | string | long version string "$version ($revision)" ---------------------------------+------------+-----------------------------+ units | object containing: | +--------------+--------+------------------+ | speed-units | array | 4 strings: KB/s, MB/s, GB/s, TB/s | speed-bytes | number | number of bytes in a KB (1000 for kB; 1024 for KiB) | size-units | array | 4 strings: KB/s, MB/s, GB/s, TB/s | size-bytes | number | number of bytes in a KB (1000 for kB; 1024 for KiB) | memory-units | array | 4 strings: KB/s, MB/s, GB/s, TB/s | memory-bytes | number | number of bytes in a KB (1000 for kB; 1024 for KiB) +--------------+--------+------------------+ "rpc-version" indicates the RPC interface version supported by the RPC server. It is incremented when a new version of Transmission changes the RPC interface. "rpc-version-minimum" indicates the oldest API supported by the RPC server. It is changes when a new version of Transmission changes the RPC interface in a way that is not backwards compatible. There are no plans for this to be common behavior. 4.1.1. Mutators Method name: "session-set" Request arguments: one or more of 4.1's arguments, except: "blocklist-size", "config-dir", "rpc-version", "rpc-version-minimum", and "version" Response arguments: none 4.1.2. Accessors Method name: "session-get" Request arguments: none Response arguments: all of 4.1's arguments 4.2. Session Statistics Method name: "session-stats" Request arguments: none Response arguments: string | value type ---------------------------+------------------------------------------------- "activeTorrentCount" | number "downloadSpeed" | number "pausedTorrentCount" | number "torrentCount" | number "uploadSpeed" | number ---------------------------+-------------------------------+ "cumulative-stats" | object, containing: | +------------------+------------+ | uploadedBytes | number | tr_session_stats | downloadedBytes | number | tr_session_stats | filesAdded | number | tr_session_stats | sessionCount | number | tr_session_stats | secondsActive | number | tr_session_stats ---------------------------+-------------------------------+ "current-stats" | object, containing: | +------------------+------------+ | uploadedBytes | number | tr_session_stats | downloadedBytes | number | tr_session_stats | filesAdded | number | tr_session_stats | sessionCount | number | tr_session_stats | secondsActive | number | tr_session_stats 4.3. Blocklist Method name: "blocklist-update" Request arguments: none Response arguments: a number "blocklist-size" 4.4. Port Checking This method tests to see if your incoming peer port is accessible from the outside world. Method name: "port-test" Request arguments: none Response arguments: a bool, "port-is-open" 4.5. Session shutdown This method tells the transmission session to shut down. Method name: "session-close" Request arguments: none Response arguments: none 4.6. Queue Movement Requests Method name | libtransmission function ---------------------+------------------------------------------------- "queue-move-top" | tr_torrentQueueMoveTop() "queue-move-up" | tr_torrentQueueMoveUp() "queue-move-down" | tr_torrentQueueMoveDown() "queue-move-bottom" | tr_torrentQueueMoveBottom() Request arguments: string | value type & description ------------+---------------------------------------------------------- "ids" | array torrent list, as described in 3.1. Response arguments: none 4.7. Free Space This method tests how much free space is available in a client-specified folder. Method name: "free-space" Request arguments: string | value type & description ------------+---------------------------------------------------------- "path" | string the directory to query Response arguments: string | value type & description ------------+---------------------------------------------------------- "path" | string same as the Request argument "size-bytes"| number the size, in bytes, of the free space in that directory 5.0. Protocol Versions The following changes have been made to the RPC interface: RPC | Release | Backwards | | Vers. | Version | Compat? | Method | Description ------+---------+-----------+----------------------+------------------------------- 1 | 1.30 | n/a | n/a | Initial version ------+---------+-----------+----------------------+------------------------------- 2 | 1.34 | yes | torrent-get | new arg "peers" ------+---------+-----------+----------------------+------------------------------- 3 | 1.41 | yes | torrent-get | added "port" to "peers" | | yes | torrent-get | new arg "downloaders" | | yes | session-get | new arg "version" | | yes | torrent-remove | new method ------+---------+-----------+----------------------+------------------------------- 4 | 1.50 | yes | session-get | new arg "rpc-version" | | yes | session-get | new arg "rpc-version-minimum" | | yes | session-stats | added "cumulative-stats" | | yes | session-stats | added "current-stats" | | yes | torrent-get | new arg "downloadDir" ------+---------+-----------+----------------------+------------------------------- 5 | 1.60 | yes | | new method "torrent-reannounce" | | yes | | new method "blocklist-update" | | yes | | new method "port-test" | | | | | | yes | session-get | new arg "alt-speed-begin" | | yes | session-get | new arg "alt-speed-down" | | yes | session-get | new arg "alt-speed-enabled" | | yes | session-get | new arg "alt-speed-end" | | yes | session-get | new arg "alt-speed-time-enabled" | | yes | session-get | new arg "alt-speed-up" | | yes | session-get | new arg "blocklist-enabled" | | yes | session-get | new arg "blocklist-size" | | yes | session-get | new arg "peer-limit-per-torrent" | | yes | session-get | new arg "seedRatioLimit" | | yes | session-get | new arg "seedRatioLimited" | | NO | session-get | renamed "pex-allowed" to "pex-enabled" | | NO | session-get | renamed "port" to "peer-port" | | NO | session-get | renamed "peer-limit" to "peer-limit-global" | | | | | | yes | torrent-add | new arg "files-unwanted" | | yes | torrent-add | new arg "files-wanted" | | yes | torrent-add | new arg "priority-high" | | yes | torrent-add | new arg "priority-low" | | yes | torrent-add | new arg "priority-normal" | | | | | | yes | torrent-set | new arg "bandwidthPriority" | | yes | torrent-set | new arg "honorsSessionLimits" | | yes | torrent-set | new arg "seedRatioLimit" | | yes | torrent-set | new arg "seedRatioLimited" | | NO | torrent-set | renamed "speed-limit-down" to "downloadLimit" | | NO | torrent-set | renamed "speed-limit-down-enabled" to "downloadLimited" | | NO | torrent-set | renamed "speed-limit-up" to "uploadLimit" | | NO | torrent-set | renamed "speed-limit-up-enabled" to "uploadLimited" | | | | | | yes | torrent-get | new arg "bandwidthPriority" | | yes | torrent-get | new arg "fileStats" | | yes | torrent-get | new arg "honorsSessionLimits" | | yes | torrent-get | new arg "percentDone" | | yes | torrent-get | new arg "pieces" | | yes | torrent-get | new arg "seedRatioLimit" | | yes | torrent-get | new arg "seedRatioMode" | | yes | torrent-get | new arg "torrentFile" | | yes | torrent-get | new ids option "recently-active" | | NO | torrent-get | removed arg "downloadLimitMode" | | NO | torrent-get | removed arg "uploadLimitMode" ------+---------+-----------+----------------------+------------------------------- 6 | 1.70 | yes | | new "method torrent-set-location" ------+---------+-----------+----------------------+------------------------------- 7 | 1.80 | NO | torrent-get | removed arg "announceResponse" | | NO | torrent-get | removed arg "announceURL" | | NO | torrent-get | removed arg "downloaders" | | NO | torrent-get | removed arg "lastAnnounceTime" | | NO | torrent-get | removed arg "lastScrapeTime" | | NO | torrent-get | removed arg "leechers" | | NO | torrent-get | removed arg "nextAnnounceTime" | | NO | torrent-get | removed arg "nextScrapeTime" | | NO | torrent-get | removed arg "scrapeResponse" | | NO | torrent-get | removed arg "scrapeURL" | | NO | torrent-get | removed arg "seeders" | | NO | torrent-get | removed arg "timesCompleted" | | NO | torrent-get | removed arg "swarmSpeed" | | yes | torrent-get | new arg "magnetLink" | | yes | torrent-get | new arg "metadataPercentComplete" | | yes | torrent-get | new arg "trackerStats" | | yes | session-set | new arg "incomplete-dir" | | yes | session-set | new arg "incomplete-dir-enabled" ------+---------+-----------+----------------------+------------------------------- 8 | 1.90 | yes | session-set | new arg "rename-partial-files" | | yes | session-get | new arg "rename-partial-files" | | yes | session-get | new arg "config-dir" | | yes | torrent-add | new arg "bandwidthPriority" | | yes | torrent-get | new trackerStats arg "lastAnnounceTimedOut" ------+---------+-----------+----------------------+------------------------------- 8 | 1.92 | yes | torrent-get | new trackerStats arg "lastScrapeTimedOut" ------+---------+-----------+----------------------+------------------------------- 9 | 2.00 | yes | session-set | new arg "start-added-torrents" | | yes | session-set | new arg "trash-original-torrent-files" | | yes | session-get | new arg "start-added-torrents" | | yes | session-get | new arg "trash-original-torrent-files" | | yes | torrent-get | new arg "isFinished" ------+---------+-----------+----------------------+------------------------------- 10 | 2.10 | yes | session-get | new arg "cache-size-mb" | | yes | torrent-set | new arg "trackerAdd" | | yes | torrent-set | new arg "trackerRemove" | | yes | torrent-set | new arg "trackerReplace" | | yes | session-set | new arg "idle-seeding-limit" | | yes | session-set | new arg "idle-seeding-limit-enabled" | | yes | session-get | new arg "units" | | yes | torrent-set | new arg "seedIdleLimit" | | yes | torrent-set | new arg "seedIdleMode" ------+---------+-----------+----------------------+------------------------------- 11 | 2.12 | yes | session-get | new arg "blocklist-url" | | yes | session-set | new arg "blocklist-url" ------+---------+-----------+----------------------+------------------------------- 12 | 2.20 | yes | session-get | new arg "download-dir-free-space" | | yes | session-close | new method ------+---------+-----------+----------------------+------------------------------- 13 | 2.30 | yes | session-get | new arg "isUTP" to the "peers" list | | yes | torrent-add | new arg "cookies" | | NO | torrent-get | removed arg "peersKnown" ------+---------+-----------+--------------------------+------------------------------- 14 | 2.40 | NO | torrent-get | values of "status" field changed | | yes | torrent-get | new arg "queuePosition" | | yes | torrent-get | new arg "isStalled" | | yes | torrent-get | new arg "fromLpd" in peersFrom | | yes | torrent-set | new arg "queuePosition" | | yes | session-set | new arg "download-queue-size" | | yes | session-set | new arg "download-queue-enabled" | | yes | session-set | new arg "seed-queue-size" | | yes | session-set | new arg "seed-queue-enabled" | | yes | session-set | new arg "queue-stalled-enabled" | | yes | session-set | new arg "queue-stalled-minutes" | | yes | | new method "queue-move-top" | | yes | | new method "queue-move-up" | | yes | | new method "queue-move-down" | | yes | | new method "queue-move-bottom" | | yes | | new method "torrent-start-now" ------+---------+-----------+--------------------------+------------------------------- 15 | 2.80 | yes | torrent-get | new arg "etaIdle" | | yes | torrent-rename-path | new method | | yes | free-space | new method | | yes | torrent-add | new return return arg "torrent-duplicate" 5.1. Upcoming Breakage These features will be removed three months after 2.80's release: 1. session-get's 'download-dir-free-space' argument will be removed. Its functionality has been superceded by the 'free-space' method. 2. HTTP POSTs to http://server:port/transmission/upload will fail. This was an undocumented hack to allow web clients to add files without client-side access to the file. This functionality is superceded by using HTML5's FileReader object + the documented 'torrent-add' method. TransGUI/download.pas0000644000000000000000000001445712261763702013545 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit download; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, synsock, ExtCtrls, BaseForm, utils; resourcestring SDownloadProgress = '%s of %s downloaded'; SDownloadProgress2 = '%s downloaded'; type TDownloadThread = class; { TDownloadForm } TDownloadForm = class(TBaseForm) btCancel: TButton; UpdateTimer: TTimer; txPercent: TLabel; txBytes: TLabel; txFileName: TLabel; pbDownload: TProgressBar; procedure btCancelClick(Sender: TObject); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormResize(Sender: TObject); procedure FormShow(Sender: TObject); procedure UpdateTimerTimer(Sender: TObject); private FThread: TDownloadThread; FTotalSize: Int64; FDownloaded: Int64; FError: string; procedure UpdateStatus(Data: PtrInt); public { public declarations } end; { TDownloadThread } TDownloadThread = class(TThread) private FHttp: THTTPSend; FForm: TDownloadForm; FUrl: string; FDestFileName: string; FOut: TFileStreamUTF8; procedure DoMonitor(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer); procedure WriteToFile; protected procedure Execute; override; end; function DownloadFile(const URL, DestFolder: string; const DestFileName: string = ''; const DisplayName: string = ''): boolean; implementation uses Main, rpc; function DownloadFile(const URL, DestFolder: string; const DestFileName, DisplayName: string): boolean; var s: string; begin with TDownloadForm.Create(Application) do try s:=ExtractFileName(StringReplace(URL, '/', DirectorySeparator, [rfReplaceAll])); if DisplayName <> '' then txFileName.Caption:=DisplayName else txFileName.Caption:=s; if DestFileName <> '' then s:=DestFileName; FThread.FUrl:=URL; FThread.FDestFileName:=IncludeTrailingPathDelimiter(DestFolder) + s; FThread.Suspended:=False; Result:=ShowModal = mrOk; finally Free; end; end; { TDownloadThread } procedure TDownloadThread.DoMonitor(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer); begin if Terminated then begin FHttp.Abort; exit; end; if FHttp.DownloadSize <> 0 then begin FForm.FTotalSize:=FHttp.DownloadSize; Inc(FForm.FDownloaded, Len); WriteToFile; end; end; procedure TDownloadThread.WriteToFile; begin if FOut = nil then FOut:=TFileStreamUTF8.Create(FDestFileName, fmCreate); FHttp.Document.Position:=0; FOut.CopyFrom(FHttp.Document, FHttp.Document.Size); FHttp.Document.Clear; end; procedure TDownloadThread.Execute; var res: PtrInt; begin res:=1; try FHttp:=THTTPSend.Create; try if RpcObj.Http.ProxyHost <> '' then begin FHttp.ProxyHost:=RpcObj.Http.ProxyHost; FHttp.ProxyPort:=RpcObj.Http.ProxyPort; FHttp.ProxyUser:=RpcObj.Http.ProxyUser; FHttp.ProxyPass:=RpcObj.Http.ProxyPass; end; FHttp.Sock.OnMonitor:=@DoMonitor; if FHttp.HTTPMethod('GET', FUrl) then begin if FHttp.ResultCode = 200 then begin FForm.FDownloaded:=FHttp.DownloadSize; WriteToFile; res:=2; end else if not Terminated then FForm.FError:=Format('HTTP error: %d', [FHttp.ResultCode]); end else if not Terminated then FForm.FError:=FHttp.Sock.LastErrorDesc; finally FHttp.Free; end; except FForm.FError:=Exception(ExceptObject).Message; end; FOut.Free; if res = 1 then DeleteFile(FDestFileName); Application.QueueAsyncCall(@FForm.UpdateStatus, res); FForm.FThread:=nil; end; { TDownloadForm } procedure TDownloadForm.FormCreate(Sender: TObject); begin FThread:=TDownloadThread.Create(True); FThread.FreeOnTerminate:=True; FThread.FForm:=Self; UpdateTimerTimer(nil); end; procedure TDownloadForm.btCancelClick(Sender: TObject); begin btCancel.Enabled:=False; FThread.Terminate; end; procedure TDownloadForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin if FThread <> nil then begin CloseAction:=caNone; btCancel.Click; end; end; procedure TDownloadForm.FormDestroy(Sender: TObject); begin end; procedure TDownloadForm.FormResize(Sender: TObject); begin btCancel.Left:=(ClientWidth - btCancel.Width) div 2; end; procedure TDownloadForm.FormShow(Sender: TObject); begin FormResize(nil); end; procedure TDownloadForm.UpdateTimerTimer(Sender: TObject); begin if FTotalSize <> 0 then begin pbDownload.Max:=FTotalSize; pbDownload.Position:=FDownloaded; txPercent.Caption:=Format('%.1f%%', [FDownloaded*100/FTotalSize]); txBytes.Caption:=Format(SDownloadProgress, [GetHumanSize(FDownloaded), GetHumanSize(FTotalSize)]); txPercent.Show; end else begin txBytes.Caption:=Format(SDownloadProgress2, [GetHumanSize(FDownloaded)]); txPercent.Hide; end; end; procedure TDownloadForm.UpdateStatus(Data: PtrInt); begin if Data <> 0 then begin if Data = 2 then ModalResult:=mrOk else begin if FError <> '' then MessageDlg(FError, mtError, [mbOK], 0); ModalResult:=mrCancel; end; exit; end; end; initialization {$I download.lrs} end. TransGUI/GeoIP.pas0000644000000000000000000004764011733110656012676 0ustar rootroot{ * Copyright (C) 2005 MaxMind LLC All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ChangeLog * 2003-04-09 Translation of C# class to Pascal provided by W. Tracz * 2005-07-20 Added support for GeoIP Region, City, ISP and Organization (Yvan Schwab/esoftys) } { Thanks to W. Tracz/Yvan Schwab for contributing this class } {$mode delphi} unit GeoIP; interface uses Classes, SysUtils, utils, {$ifdef FPC} sockets {$else} WinSock {$endif}; type TGeoIPResult = ( GEOIP_SUCCESS = 0, GEOIP_NODATA = 1, GEOIP_ERROR_IPADDR = 2, GEOIP_ERROR_DBTYPE = 3, GEOIP_ERROR_IO = 4 ); TGeoIPDBTypes = ( GEOIP_COUNTRY_EDITION = 1, GEOIP_CITY_EDITION_REV1 = 2, GEOIP_REGION_EDITION_REV1 = 3, GEOIP_ISP_EDITION = 4, GEOIP_ORG_EDITION = 5, GEOIP_CITY_EDITION_REV0 = 6, GEOIP_REGION_EDITION_REV0 = 7, GEOIP_PROXY_EDITION = 8, GEOIP_ASNUM_EDITION = 9 ); TGeoIPCountry = record CountryCode: string; CountryName: string; end; TGeoIPRegion = record CountryCode: string; Region: string; end; TGeoIPCity = record CountryCode: string; CountryName: string; Region: string; City: string; PostalCode: string; Latitude: Double; Longitude: Double; DmaCode: Integer; AreaCode: Integer; end; TGeoIPOrg = record Name: string; end; TGeoIP = class private FInputFile: TFileStreamUTF8; FDatabaseType: TGeoIPDBTypes; FDatabaseSegments: array of Cardinal; FDatabaseInfo: string; FRecordLength: Cardinal; function _GetCity(IPNum: Cardinal; var GeoIPCity: TGeoIPCity): TGeoIPResult; function _GetCountry(IPNum: Cardinal; var GeoIPCountry: TGeoIPCountry): TGeoIPResult; function _GetOrg(IPNum: Cardinal; var GeoIPOrg: TGeoIPOrg): TGeoIPResult; function _GetRegion(IPNum: Cardinal; var GeoIPRegion: TGeoIPRegion): TGeoIPResult; function AddrToNum(const IPAddr: string): Cardinal; procedure InitDBFile; function SeekRecord(IPNum: Cardinal): Cardinal; public constructor Create(const FileName: string); destructor Destroy; override; function GetCity(const IPAddr: string; var GeoIPCity: TGeoIPCity): TGeoIPResult; function GetCountry(const IPAddr: string; var GeoIPCountry: TGeoIPCountry): TGeoIPResult; function GetDatabaseInfo: string; function GetOrg(const IPAddr: string; var GeoIPOrg: TGeoIPOrg): TGeoIPResult; function GetRegion(const IPAddr: string; var GeoIPRegion: TGeoIPRegion): TGeoIPResult; end; const CountryCodes:array [0..252] of string = ('--','AP','EU','AD','AE','AF','AG','AI','AL','AM','AN','AO','AQ','AR','AS','AT','AU','AW','AZ','BA','BB','BD','BE','BF','BG','BH','BI','BJ','BM','BN','BO','BR','BS','BT','BV','BW','BY','BZ','CA','CC','CD','CF','CG','CH','CI','CK','CL','CM','CN','CO','CR','CU','CV','CX','CY','CZ','DE','DJ','DK','DM','DO','DZ','EC','EE','EG','EH','ER','ES','ET','FI','FJ','FK','FM','FO','FR','FX','GA','GB','GD','GE','GF','GH','GI','GL','GM','GN','GP','GQ','GR','GS','GT','GU','GW', 'GY','HK','HM','HN','HR','HT','HU','ID','IE','IL','IN','IO','IQ','IR','IS','IT','JM','JO','JP','KE','KG','KH','KI','KM','KN','KP','KR','KW','KY','KZ','LA','LB','LC','LI','LK','LR','LS','LT','LU','LV','LY','MA','MC','MD','MG','MH','MK','ML','MM','MN','MO','MP','MQ','MR','MS','MT','MU','MV','MW','MX','MY','MZ','NA','NC','NE','NF','NG','NI','NL','NO','NP','NR','NU','NZ','OM','PA','PE','PF','PG','PH','PK','PL','PM','PN','PR','PS','PT','PW','PY','QA','RE','RO','RU', 'RW','SA','SB','SC','SD','SE','SG','SH','SI','SJ','SK','SL','SM','SN','SO','SR','ST','SV','SY','SZ','TC','TD','TF','TG','TH','TJ','TK','TM','TN','TO','TL','TR','TT','TV','TW','TZ','UA','UG','UM','US','UY','UZ','VA','VC','VE','VG','VI','VN','VU','WF','WS','YE','YT','RS','ZA','ZM','ME','ZW','A1','A2','O1','AX','GG','IM','JE','BL','MF'); CountryNames:array [0..252] of string = ('N/A','Asia/Pacific Region','Europe','Andorra','United Arab Emirates','Afghanistan','Antigua and Barbuda','Anguilla','Albania','Armenia','Netherlands Antilles','Angola','Antarctica','Argentina','American Samoa','Austria','Australia','Aruba','Azerbaijan','Bosnia and Herzegovina','Barbados','Bangladesh','Belgium','Burkina Faso','Bulgaria','Bahrain','Burundi','Benin','Bermuda','Brunei Darussalam','Bolivia','Brazil','Bahamas','Bhutan','Bouvet Island','Botswana', 'Belarus','Belize','Canada','Cocos (Keeling) Islands','Congo, The Democratic Republic of the','Central African Republic','Congo','Switzerland','Cote D''Ivoire','Cook Islands','Chile','Cameroon','China','Colombia','Costa Rica','Cuba','Cape Verde','Christmas Island','Cyprus','Czech Republic','Germany','Djibouti','Denmark','Dominica','Dominican Republic','Algeria','Ecuador','Estonia','Egypt','Western Sahara','Eritrea','Spain','Ethiopia','Finland','Fiji', 'Falkland Islands (Malvinas)','Micronesia, Federated States of','Faroe Islands','France','France, Metropolitan','Gabon','United Kingdom','Grenada','Georgia','French Guiana','Ghana','Gibraltar','Greenland','Gambia','Guinea','Guadeloupe','Equatorial Guinea','Greece','South Georgia and the South Sandwich Islands','Guatemala','Guam','Guinea-Bissau','Guyana','Hong Kong','Heard Island and McDonald Islands','Honduras','Croatia','Haiti','Hungary','Indonesia','Ireland', 'Israel','India','British Indian Ocean Territory','Iraq','Iran, Islamic Republic of','Iceland','Italy','Jamaica','Jordan','Japan','Kenya','Kyrgyzstan','Cambodia','Kiribati','Comoros','Saint Kitts and Nevis','Korea, Democratic People''s Republic of','Korea, Republic of','Kuwait','Cayman Islands','Kazakstan','Lao People''s Democratic Republic','Lebanon','Saint Lucia','Liechtenstein','Sri Lanka','Liberia','Lesotho','Lithuania','Luxembourg','Latvia', 'Libyan Arab Jamahiriya','Morocco','Monaco','Moldova, Republic of','Madagascar','Marshall Islands','Macedonia, the Former Yugoslav Republic of','Mali','Myanmar','Mongolia','Macao','Northern Mariana Islands','Martinique','Mauritania','Montserrat','Malta','Mauritius','Maldives','Malawi','Mexico','Malaysia','Mozambique','Namibia','New Caledonia','Niger','Norfolk Island','Nigeria','Nicaragua','Netherlands','Norway','Nepal','Nauru','Niue','New Zealand','Oman', 'Panama','Peru','French Polynesia','Papua New Guinea','Philippines','Pakistan','Poland','Saint Pierre and Miquelon','Pitcairn','Puerto Rico','Palestinian Territory, Occupied','Portugal','Palau','Paraguay','Qatar','Reunion','Romania','Russian Federation','Rwanda','Saudi Arabia','Solomon Islands','Seychelles','Sudan','Sweden','Singapore','Saint Helena','Slovenia','Svalbard and Jan Mayen','Slovakia','Sierra Leone','San Marino','Senegal','Somalia','Suriname', 'Sao Tome and Principe','El Salvador','Syrian Arab Republic','Swaziland','Turks and Caicos Islands','Chad','French Southern Territories','Togo','Thailand','Tajikistan','Tokelau','Turkmenistan','Tunisia','Tonga','Timor-Leste','Turkey','Trinidad and Tobago','Tuvalu','Taiwan','Tanzania, United Republic of','Ukraine','Uganda','United States Minor Outlying Islands','United States','Uruguay','Uzbekistan','Holy See (Vatican City State)', 'Saint Vincent and the Grenadines','Venezuela','Virgin Islands, British','Virgin Islands, U.S.','Vietnam','Vanuatu','Wallis and Futuna','Samoa','Yemen','Mayotte','Serbia','South Africa','Zambia','Montenegro','Zimbabwe','Anonymous Proxy','Satellite Provider','Other','Aland Islands','Guernsey','Isle of Man','Jersey','Saint Barthelemy','Saint Martin'); implementation const COUNTRY_BEGIN = 16776960; STATE_BEGIN_REV0 = 16700000; STATE_BEGIN_REV1 = 16000000; STRUCTURE_INFO_MAX_SIZE = 20; DATABASE_INFO_MAX_SIZE = 100; SEGMENT_RECORD_LENGTH = 3; STANDARD_RECORD_LENGTH = 3; ORG_RECORD_LENGTH = 4; MAX_RECORD_LENGTH = 4; MAX_ORG_RECORD_LENGTH = 300; FULL_RECORD_LENGTH = 50; US_OFFSET = 1; CANADA_OFFSET = 677; WORLD_OFFSET = 1353; FIPS_RANGE = 360; { TGeoIP } constructor TGeoIP.Create(const FileName: string); begin inherited Create; FInputFile := TFileStreamUTF8.Create(FileName, fmOpenRead or fmShareDenyNone); InitDBFile; end; destructor TGeoIP.Destroy; begin if Assigned(FInputFile) then FInputFile.Free; inherited Destroy; end; function TGeoIP._GetCity(IPNum: Cardinal; var GeoIPCity: TGeoIPCity): TGeoIPResult; var SeekCity: Cardinal; RecordPointer: Cardinal; StrLen: Cardinal; buf: array[0..FULL_RECORD_LENGTH-1] of Byte; p: PChar; i: Integer; DmaAreaCombo: Integer; begin if (FDatabaseType <> GEOIP_CITY_EDITION_REV0) and (FDatabaseType <> GEOIP_CITY_EDITION_REV1) then begin Result := GEOIP_ERROR_DBTYPE; Exit; end; SeekCity := SeekRecord(IPNum); if SeekCity = FDatabaseSegments[0] then begin Result := GEOIP_NODATA; Exit; end; RecordPointer := SeekCity + (2 * FRecordLength - 1) * FDatabaseSegments[0]; FInputFile.Seek(RecordPointer, soFromBeginning); FInputFile.Read(buf, FULL_RECORD_LENGTH); // get country GeoIPCity.CountryCode := CountryCodes[buf[0]]; GeoIPCity.CountryName := CountryNames[buf[0]]; // get region p := @buf[1]; StrLen := 0; while (p[StrLen] <> #0) do Inc(StrLen); GeoIPCity.Region := Copy(p, 0, StrLen); // get city Inc(p, StrLen + 1); StrLen := 0; while (p[StrLen] <> #0) do Inc(StrLen); GeoIPCity.City := Copy(p, 0, StrLen); // get postal code Inc(p, StrLen + 1); StrLen := 0; while (p[StrLen] <> #0) do Inc(StrLen); GeoIPCity.PostalCode := Copy(p, 0, StrLen); // get latitude Inc(p, StrLen + 1); GeoIPCity.Latitude := 0.0; for i:=0 to 2 do begin GeoIPCity.Latitude := GeoIPCity.Latitude + (Integer(p[i]) shl (i*8)); end; GeoIPCity.Latitude := GeoIPCity.Latitude/10000 - 180; // get longitude Inc(p, 3); GeoIPCity.Longitude := 0.0; for i:=0 to 2 do begin GeoIPCity.Longitude := GeoIPCity.Longitude + (Integer(p[i]) shl (i*8)); end; GeoIPCity.Longitude := GeoIPCity.Longitude/10000 - 180; // get area code and dma code for post April 2002 databases and for US locations GeoIPCity.DmaCode := 0; GeoIPCity.AreaCode := 0; if FDatabaseType = GEOIP_CITY_EDITION_REV1 then begin if GeoIPCity.CountryCode = 'US' then begin Inc(p, 3); DmaAreaCombo := 0; for i:=0 to 2 do begin DmaAreaCombo := DmaAreaCombo + (Integer(p[i]) shl (i*8)); end; GeoIPCity.DmaCode := DmaAreaCombo div 1000; GeoIPCity.AreaCode := DmaAreaCombo mod 1000; end; end; Result := GEOIP_SUCCESS; end; function TGeoIP._GetCountry(IPNum: Cardinal; var GeoIPCountry: TGeoIPCountry): TGeoIPResult; var ret: Cardinal; begin if (FDatabaseType <> GEOIP_COUNTRY_EDITION) and (FDatabaseType <> GEOIP_PROXY_EDITION) then begin Result := GEOIP_ERROR_DBTYPE; Exit; end; ret := SeekRecord(IPNum) - COUNTRY_BEGIN; if ret > 0 then begin GeoIPCountry.CountryCode := CountryCodes[ret]; GeoIPCountry.CountryName := CountryNames[ret]; Result := GEOIP_SUCCESS; end else begin Result := GEOIP_NODATA; end; end; function TGeoIP._GetOrg(IPNum: Cardinal; var GeoIPOrg: TGeoIPOrg): TGeoIPResult; var SeekOrg: Cardinal; RecordPointer: Cardinal; StrLen: Cardinal; buf: array[0..MAX_ORG_RECORD_LENGTH-1] of Byte; p: PChar; begin if (FDatabaseType <> GEOIP_ORG_EDITION) and (FDatabaseType <> GEOIP_ISP_EDITION) and (FDatabaseType <> GEOIP_ASNUM_EDITION) then begin Result := GEOIP_ERROR_DBTYPE; Exit; end; SeekOrg := SeekRecord(IPNum); if SeekOrg = FDatabaseSegments[0] then begin Result := GEOIP_NODATA; Exit; end; RecordPointer := SeekOrg + (2 * FRecordLength - 1) * FDatabaseSegments[0]; FInputFile.Seek(RecordPointer, soFromBeginning); FInputFile.Read(buf, FULL_RECORD_LENGTH); p := @buf[0]; StrLen := 0; while (p[StrLen] <> #0) do Inc(StrLen); GeoIPOrg.Name := Copy(p, 0, StrLen); Result := GEOIP_SUCCESS; end; function TGeoIP._GetRegion(IPNum: Cardinal; var GeoIPRegion: TGeoIPRegion): TGeoIPResult; var SeekRegion: Cardinal; begin if (FDatabaseType <> GEOIP_REGION_EDITION_REV0) and (FDatabaseType <> GEOIP_REGION_EDITION_REV1) then begin Result := GEOIP_ERROR_DBTYPE; Exit; end; SeekRegion := SeekRecord(IPNum); if FDatabaseType = GEOIP_REGION_EDITION_REV0 then begin // Region Edition, pre June 2003 Dec(SeekRegion, STATE_BEGIN_REV0); if SeekRegion >= 1000 then begin GeoIPRegion.CountryCode := 'US'; GeoIPRegion.Region := Chr((SeekRegion - 1000) div 26 + 65) + Chr((SeekRegion - 1000) mod 26 + 65); end else begin GeoIPRegion.CountryCode := CountryCodes[SeekRegion]; GeoIPRegion.Region := ''; end; end else if FDatabaseType = GEOIP_REGION_EDITION_REV1 then begin // Region Edition, post June 2003 Dec(SeekRegion, STATE_BEGIN_REV1); if SeekRegion < US_OFFSET then begin // Unknown GeoIPRegion.CountryCode := ''; GeoIPRegion.Region := ''; end else if SeekRegion < CANADA_OFFSET then begin // USA State GeoIPRegion.CountryCode := 'US'; GeoIPRegion.Region := Chr((SeekRegion - US_OFFSET) div 26 + 65) + Chr((SeekRegion - US_OFFSET) mod 26 + 65); end else if SeekRegion < WORLD_OFFSET then begin // Canada Province GeoIPRegion.CountryCode := 'CA'; GeoIPRegion.Region := Chr((SeekRegion - CANADA_OFFSET) div 26 + 65) + Chr((SeekRegion - CANADA_OFFSET) mod 26 + 65); end else begin // Not US or Canada GeoIPRegion.CountryCode := CountryCodes[(SeekRegion - WORLD_OFFSET) div FIPS_RANGE]; GeoIPRegion.Region := ''; end; end; Result := GEOIP_SUCCESS; end; function TGeoIP.AddrToNum(const IPAddr: string): Cardinal; {$ifdef FPC} begin Result:=StrToHostAddr(IPAddr).s_addr; end; {$else} var netlong: LongInt; begin netlong := inet_addr(PChar(IPAddr)); if netlong <> INADDR_NONE then Result := ntohl(netlong) else Result := 0; end; {$endif} function TGeoIP.GetCity(const IPAddr: string; var GeoIPCity: TGeoIPCity): TGeoIPResult; var IPNum: Cardinal; begin IPNum := AddrToNum(IPAddr); if IPNum = 0 then begin Result := GEOIP_ERROR_IPADDR; Exit; end; Result := _GetCity(IPNum, GeoIPCity); end; function TGeoIP.GetCountry(const IPAddr: string; var GeoIPCountry: TGeoIPCountry): TGeoIPResult; var IPNum: Cardinal; begin IPNum := AddrToNum(IPAddr); if IPNum = 0 then begin Result := GEOIP_ERROR_IPADDR; Exit; end; Result := _GetCountry(IPNum, GeoIPCountry); end; function TGeoIP.GetDatabaseInfo: string; var i: Integer; delim: array[0..2] of Byte; HasStructureInfo: Boolean; begin FDatabaseInfo := ''; HasStructureInfo := False; FInputFile.Seek(-3, soFromEnd); for i:=0 to STRUCTURE_INFO_MAX_SIZE-1 do begin FInputFile.Read(delim, 3); if (delim[0] = 255) and (delim[1] = 255) and (delim[2] = 255) then begin HasStructureInfo := True; Break; end; FInputFile.Seek(-4, soFromCurrent); end; if HasStructureInfo then FInputFile.Seek(-3, soFromCurrent) else // no structure info, must be pre Sep 2002 database, go back to end FInputFile.Seek(-3, soFromEnd); for i:=0 to DATABASE_INFO_MAX_SIZE-1 do begin FInputFile.Read(delim, 3); if (delim[0] = 0) and (delim[1] = 0) and (delim[2] = 0) then begin SetLength(FDatabaseInfo, i); FInputFile.Read(PChar(FDatabaseInfo)^, i); Break; end; FInputFile.Seek(-4, soFromCurrent); end; Result := FDatabaseInfo; end; function TGeoIP.GetOrg(const IPAddr: string; var GeoIPOrg: TGeoIPOrg): TGeoIPResult; var IPNum: Cardinal; begin IPNum := AddrToNum(IPAddr); if IPNum = 0 then begin Result := GEOIP_ERROR_IPADDR; Exit; end; Result := _GetOrg(IPNum, GeoIPOrg); end; function TGeoIP.GetRegion(const IPAddr: string; var GeoIPRegion: TGeoIPRegion): TGeoIPResult; var IPNum: Cardinal; begin IPNum := AddrToNum(IPAddr); if IPNum = 0 then begin Result := GEOIP_ERROR_IPADDR; Exit; end; Result := _GetRegion(IPNum, GeoIPRegion); end; procedure TGeoIP.InitDBFile; var i,j: Integer; delim: array[0..2] of Byte; buf: array[0..SEGMENT_RECORD_LENGTH-1] of Byte; begin // default to GeoIP Country Edition FDatabaseType := GEOIP_COUNTRY_EDITION; FRecordLength := STANDARD_RECORD_LENGTH; FInputFile.Seek(-3, soFromEnd); for i:=0 to STRUCTURE_INFO_MAX_SIZE-1 do begin FInputFile.Read(delim, 3); if (delim[0] = 255) and (delim[1] = 255) and (delim[2] = 255) then begin FInputFile.Read(FDatabaseType, 1); if Byte(FDatabaseType) >= 106 then begin // Backward compatibility with databases from April 2003 and earlier Dec(FDatabaseType, 105); end; if FDatabaseType = GEOIP_REGION_EDITION_REV0 then begin // Region Edition, pre June 2003 SetLength(FDatabaseSegments, 1); FDatabaseSegments[0] := STATE_BEGIN_REV0; end else if FDatabaseType = GEOIP_REGION_EDITION_REV1 then begin // Region Edition, post June 2003 SetLength(FDatabaseSegments, 1); FDatabaseSegments[0] := STATE_BEGIN_REV1; end else if (FDatabaseType = GEOIP_CITY_EDITION_REV0) or (FDatabaseType = GEOIP_CITY_EDITION_REV1) or (FDatabaseType = GEOIP_ORG_EDITION) or (FDatabaseType = GEOIP_ISP_EDITION) or (FDatabaseType = GEOIP_ASNUM_EDITION) then begin // City/Org Editions have two segments, read offset of second segment SetLength(FDatabaseSegments, 1); FDatabaseSegments[0] := 0; FInputFile.Read(buf, SEGMENT_RECORD_LENGTH); for j:=0 to SEGMENT_RECORD_LENGTH-1 do begin Inc(FDatabaseSegments[0], Integer(buf[j]) shl (j*8)); end; if (FDatabaseType = GEOIP_ORG_EDITION) or (FDatabaseType = GEOIP_ISP_EDITION) then FRecordLength := ORG_RECORD_LENGTH; end; Break; end else begin FInputFile.Seek(-4, soFromCurrent); end; end; if (FDatabaseType = GEOIP_COUNTRY_EDITION) or (FDatabaseType = GEOIP_PROXY_EDITION) then begin SetLength(FDatabaseSegments, 1); FDatabaseSegments[0] := COUNTRY_BEGIN; end; end; function TGeoIP.SeekRecord(IPNum: Cardinal): Cardinal; var depth: Cardinal; offset: Cardinal; i,j: Cardinal; x: array[0..1] of Cardinal; y: Cardinal; buf: array[0..2*MAX_RECORD_LENGTH-1] of Byte; begin offset := 0; for depth:=31 downto 0 do begin FInputFile.Seek(2 * FRecordLength * offset, soFromBeginning); FInputFile.Read(buf, 2 * FRecordLength); for i:=0 to 1 do begin x[i] := 0; for j:=0 to FRecordLength-1 do begin y := buf[i*FRecordLength+j]; x[i] := x[i] + (y shl (j*8)); end; end; if (IPNum and (1 shl depth)) <> 0 then begin if x[1] >= FDatabaseSegments[0] then begin Result := x[1]; Exit; end else begin Offset := x[1]; end; end else begin if x[0] >= FDatabaseSegments[0] then begin Result := x[0]; Exit; end else begin Offset := x[0]; end; end; end; Result := 0; end; end. TransGUI/transgui.lpr0000644000000000000000000000313112231246521013557 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2013 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} {$ifdef windows} {$apptype gui} {$endif windows} program transgui; {$mode objfpc}{$H+} uses {$ifdef UNIX} cthreads, {$ifdef darwin} maclocale, {$else} clocale, {$endif} {$endif} Interfaces, // this includes the LCL widgetset Forms { you can add units after this }, BaseForm, Main, rpc, AddTorrent, ConnOptions, varlist, TorrProps, DaemonOptions, About, IpResolver, download, ColSetup, utils, ResTranslator, AddLink, MoveTorrent, AddTracker, Options; {$R *.res} begin if not CheckAppParams then exit; Application.Initialize; Application.CreateForm(TMainForm, MainForm); Application.Run; end. TransGUI/bencode.pas0000644000000000000000000001625012226603273013323 0ustar rootrootunit BEncode; interface uses Classes, Contnrs, SysUtils; type TBEncodedFormat = (befEmpty, befString, befInteger, befList, befDictionary); TBEncodedData = class(TObject) public Header: AnsiString; Data: TObject; // actually TBEncoded destructor Destroy; override; constructor Create(bData: TObject); end; { TBEncodedDataList } TBEncodedDataList = class(TObjectList) protected function GetItems(Index: Integer): TBEncodedData; procedure SetItems(Index: Integer; AClass: TBEncodedData); public function FindElement(Header: AnsiString; RaiseException: boolean = True): TObject; function Add(AClass: TBEncodedData): Integer; function Extract(Item: TBEncodedData): TBEncodedData; function Remove(AClass: TBEncodedData): Integer; function IndexOf(AClass: TBEncodedData): Integer; function First: TBEncodedData; function Last: TBEncodedData; procedure Insert(Index: Integer; AClass: TBEncodedData); property Items[Index: Integer]: TBEncodedData read GetItems write SetItems; default; end; TBEncoded = class(TObject) private FFormat: TBEncodedFormat; procedure SetFormat(Format: TBEncodedFormat); public StringData: AnsiString; IntegerData: int64; ListData: TBEncodedDataList; property Format: TBEncodedFormat read FFormat write SetFormat; class procedure Encode(Encoded: TObject; var Output: AnsiString); destructor Destroy; override; constructor Create(Stream: TStream); end; implementation destructor TBEncodedData.Destroy; begin Data.Free; inherited Destroy; end; constructor TBEncodedData.Create(bData: TObject); begin inherited Create; Self.Data := bData; end; destructor TBEncoded.Destroy; begin if ListData <> nil then ListData.Free; inherited Destroy; end; constructor TBEncoded.Create(Stream: TStream); procedure InvalidInput; begin raise Exception.Create('Invalid torrent file.'); end; function GetString(Buffer: AnsiString): AnsiString; var X: char; lngth:Integer; begin // loop until we come across it repeat Stream.ReadBuffer(X, 1); if not ((X in ['0'..'9']) or (x = ':')) then InvalidInput; if X = ':' then begin if Buffer = '' then InvalidInput; if Length(Buffer) > 6 then InvalidInput; lngth:=StrToInt(Buffer); SetLength(Result, lngth); Stream.ReadBuffer(Result[1], lngth); Break; end else Buffer := Buffer + X; until False; end; var X: char; Buffer: AnsiString; Data: TBEncodedData; Encoded: TBEncoded; begin inherited Create; // get first character to determine the format of the proceeding data Stream.ReadBuffer(X, 1); // is it an integer? if X = 'i' then begin // yes it is, let's read until we come across e Buffer := ''; repeat Stream.ReadBuffer(X, 1); if not ((X in ['0'..'9']) or (X = 'e')) then InvalidInput; if X = 'e' then begin if Buffer = '' then InvalidInput else begin Format := befInteger; IntegerData := StrToInt64(Buffer); Break; end; end else Buffer := Buffer + X; until False; end // is it a list? else if X = 'l' then begin // its a list Format := befList; // loop until we come across e repeat // have a peek around and see if theres an e Stream.ReadBuffer(X, 1); // is it an e? if X = 'e' then Break; // otherwise move the cursor back Stream.Seek(-1, soFromCurrent); // create the element Encoded := TBEncoded.Create(Stream); // add it to the list ListData.Add(TBEncodedData.Create(Encoded)); until False; end // is it a dictionary? else if X = 'd' then begin // its a dictionary :> Format := befDictionary; // loop until we come across e repeat // have a peek around and see if theres an e Stream.ReadBuffer(X, 1); // is it an e? if X = 'e' then Break; // if it isnt an e it has to be numerical! if not (X in ['0'..'9']) then begin InvalidInput; end; // now read the string data Buffer := GetString(AnsiString(X)); // create the element Encoded := TBEncoded.Create(Stream); // create the data element Data := TBEncodedData.Create(Encoded); Data.Header := Buffer; // add it to the list ListData.Add(Data); until False; end // is it a string? else if X in ['0'..'9'] then begin StringData := GetString(AnsiString(X)); Format := befString; end else InvalidInput; end; class procedure TBEncoded.Encode(Encoded: TObject; var Output: AnsiString); var i: integer; begin with TBEncoded(Encoded) do begin // what type of member is it? case Format of befString: Output := Output + IntToStr(Length(StringData)) + ':' + StringData; befInteger: Output := Output + 'i' + IntToStr(IntegerData) + 'e'; befList: begin Output := Output + 'l'; for i := 0 to ListData.Count - 1 do Encode(TBEncoded(ListData[i].Data), Output); Output := Output + 'e'; end; befDictionary: begin Output := Output + 'd'; for i := 0 to ListData.Count - 1 do begin Output := Output + IntToStr(Length(ListData[i].Header)) + ':' + ListData[i].Header; Encode(TBEncoded(ListData[i].Data), Output); end; Output := Output + 'e'; end; end; end; end; procedure TBEncoded.SetFormat(Format: TBEncodedFormat); begin if Format in [befList, befDictionary] then ListData := TBEncodedDataList.Create; FFormat := Format; end; function TBEncodedDataList.FindElement(Header: AnsiString; RaiseException: boolean): TObject; var i: integer; begin Header := LowerCase(Header); for i := 0 to Count - 1 do if LowerCase(Items[i].Header) = Header then begin Result := Items[i].Data; Exit; end; if RaiseException then raise Exception.CreateFmt('Element ''%s'' not found.', [Header]) else Result:=nil; end; function TBEncodedDataList.Add(AClass: TBEncodedData): Integer; begin Result := inherited Add(AClass); end; function TBEncodedDataList.Extract(Item: TBEncodedData): TBEncodedData; begin Result := TBEncodedData(inherited Extract(Item)); end; function TBEncodedDataList.First: TBEncodedData; begin Result := TBEncodedData(inherited First); end; function TBEncodedDataList.GetItems(Index: Integer): TBEncodedData; begin Result := TBEncodedData(inherited Items[Index]); end; function TBEncodedDataList.IndexOf(AClass: TBEncodedData): Integer; begin Result := inherited IndexOf(AClass); end; procedure TBEncodedDataList.Insert(Index: Integer; AClass: TBEncodedData); begin inherited Insert(Index, AClass); end; function TBEncodedDataList.Last: TBEncodedData; begin Result := TBEncodedData(inherited First); end; function TBEncodedDataList.Remove(AClass: TBEncodedData): Integer; begin Result := inherited Remove(AClass); end; procedure TBEncodedDataList.SetItems(Index: Integer; AClass: TBEncodedData); begin inherited Items[Index] := AClass; end; end. TransGUI/json/0000755000000000000000000000000012261774330012166 5ustar rootrootTransGUI/json/fpjson.pp0000644000000000000000000011606212003350220014012 0ustar rootroot{ This file is part of the Free Component Library JSON Data structures Copyright (c) 2007 by Michael Van Canneyt michael@freepascal.org See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} {$mode objfpc} {$h+} unit fpjson; interface uses variants, SysUtils, classes, contnrs; type TJSONtype = (jtUnknown, jtNumber, jtString, jtBoolean, jtNull, jtArray, jtObject); TJSONFloat = Extended; TJSONStringType = WideString; TJSONCharType = widechar; PJSONCharType = ^TJSONCharType; { TJSONData } TJSONData = class(TObject) protected function GetAsBoolean: Boolean; virtual; abstract; function GetAsFloat: TJSONFloat; virtual; abstract; function GetAsInteger: Integer; virtual; abstract; function GetIsNull: Boolean; virtual; procedure SetAsBoolean(const AValue: Boolean); virtual; abstract; procedure SetAsFloat(const AValue: TJSONFloat); virtual; abstract; procedure SetAsInteger(const AValue: Integer); virtual; abstract; function GetAsJSON: TJSONStringType; virtual; abstract; function GetAsString: TJSONStringType; virtual; abstract; procedure SetAsString(const AValue: TJSONStringType); virtual; abstract; function GetValue: variant; virtual; abstract; procedure SetValue(const AValue: variant); virtual; abstract; function GetItem(Index : Integer): TJSONData; virtual; procedure SetItem(Index : Integer; const AValue: TJSONData); virtual; function GetCount: Integer; virtual; public Constructor Create; virtual; Class function JSONType: TJSONType; virtual; Procedure Clear; virtual; Abstract; property Count: Integer read GetCount; property Items[Index: Integer]: TJSONData read GetItem write SetItem; property Value: variant read GetValue write SetValue; Property AsString : TJSONStringType Read GetAsString Write SetAsString; Property AsFloat : TJSONFloat Read GetAsFloat Write SetAsFloat; Property AsInteger : Integer Read GetAsInteger Write SetAsInteger; Property AsBoolean : Boolean Read GetAsBoolean Write SetAsBoolean; Property IsNull : Boolean Read GetIsNull; Property AsJSON : TJSONStringType Read GetAsJSON; end; TJSONDataClass = Class of TJSONData; TJSONNumberType = (ntFloat,ntInteger); TJSONNumber = class(TJSONData) protected public class function JSONType: TJSONType; override; class function NumberType : TJSONNumberType; virtual; abstract; end; { TJSONFloatNumber } TJSONFloatNumber = class(TJSONNumber) Private FValue : TJSONFloat; protected function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; function GetValue: variant; override; procedure SetValue(const AValue: variant); override; public Constructor Create(AValue : TJSONFloat); reintroduce; class function NumberType : TJSONNumberType; override; Procedure Clear; override; end; { TJSONIntegerNumber } TJSONIntegerNumber = class(TJSONNumber) Private FValue : Integer; protected function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; function GetValue: variant; override; procedure SetValue(const AValue: variant); override; public Constructor Create(AValue : Integer); reintroduce; class function NumberType : TJSONNumberType; override; Procedure Clear; override; end; { TJSONString } TJSONString = class(TJSONData) Private FValue: TJSONStringType; protected function GetValue: Variant; override; procedure SetValue(const AValue: Variant); override; function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; public Constructor Create(AValue : TJSONStringType); reintroduce; class function JSONType: TJSONType; override; Procedure Clear; override; end; { TJSONboolean } TJSONBoolean = class(TJSONData) Private FValue: Boolean; protected function GetValue: Variant; override; procedure SetValue(const AValue: Variant); override; function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; public Constructor Create(AValue : Boolean); reintroduce; class function JSONType: TJSONType; override; Procedure Clear; override; end; { TJSONnull } TJSONNull = class(TJSONData) protected Procedure Converterror(From : Boolean); function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; function GetIsNull: Boolean; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; function GetValue: variant; override; procedure SetValue(const AValue: variant); override; public class function JSONType: TJSONType; override; Procedure Clear; override; end; TJSONArrayIterator = procedure(Item: TJSONData; Data: TObject; var Continue: Boolean) of object; { TJSONArray } TJSONObject = Class; TJSONArray = class(TJSONData) Private FList : TFPObjectList; function GetArrays(Index : Integer): TJSONArray; function GetBooleans(Index : Integer): Boolean; function GetFloats(Index : Integer): TJSONFloat; function GetIntegers(Index : Integer): Integer; function GetNulls(Index : Integer): Boolean; function GetObjects(Index : Integer): TJSONObject; function GetStrings(Index : Integer): TJSONStringType; function GetTypes(Index : Integer): TJSONType; procedure SetArrays(Index : Integer; const AValue: TJSONArray); procedure SetBooleans(Index : Integer; const AValue: Boolean); procedure SetFloats(Index : Integer; const AValue: TJSONFloat); procedure SetIntegers(Index : Integer; const AValue: Integer); procedure SetObjects(Index : Integer; const AValue: TJSONObject); procedure SetStrings(Index : Integer; const AValue: TJSONStringType); protected Procedure Converterror(From : Boolean); function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; function GetValue: variant; override; procedure SetValue(const AValue: variant); override; function GetCount: Integer; override; function GetItem(Index : Integer): TJSONData; override; procedure SetItem(Index : Integer; const AValue: TJSONData); override; public Constructor Create; overload; reintroduce; Constructor Create(const Elements : Array of Const); overload; Destructor Destroy; override; class function JSONType: TJSONType; override; // Examine procedure Iterate(Iterator : TJSONArrayIterator; Data: TObject); function IndexOf(obj: TJSONData): Integer; // Manipulate Procedure Clear; override; function Add(Item : TJSONData): Integer; function Add(I : Integer): Integer; function Add(S : TJSONStringType): Integer; function Add: Integer; function Add(F : TJSONFloat): Integer; function Add(B : Boolean): Integer; function Add(AnArray : TJSONArray): Integer; function Add(AnObject: TJSONObject): Integer; Procedure Delete(Index : Integer); Procedure Remove(Item : TJSONData); // Easy Access Properties. property Items;default; Property Types[Index : Integer] : TJSONType Read GetTypes; Property Nulls[Index : Integer] : Boolean Read GetNulls; Property Integers[Index : Integer] : Integer Read GetIntegers Write SetIntegers; Property Strings[Index : Integer] : TJSONStringType Read GetStrings Write SetStrings; Property Floats[Index : Integer] : TJSONFloat Read GetFloats Write SetFloats; Property Booleans[Index : Integer] : Boolean Read GetBooleans Write SetBooleans; Property Arrays[Index : Integer] : TJSONArray Read GetArrays Write SetArrays; Property Objects[Index : Integer] : TJSONObject Read GetObjects Write SetObjects; end; TJSONObjectIterator = procedure(Const AName : TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean) of object; { TJSONObject } TJSONObject = class(TJSONData) private FHash : TFPHashObjectList; // Careful : Names limited to 255 chars. function GetArrays(AName : String): TJSONArray; function GetBooleans(AName : String): Boolean; function GetElements(AName: string): TJSONData; function GetFloats(AName : String): TJSONFloat; function GetIntegers(AName : String): Integer; function GetIsNull(AName : String): Boolean; reintroduce; function GetNameOf(Index : Integer): TJSONStringType; function GetObjects(AName : String): TJSONObject; function GetStrings(AName : String): TJSONStringType; function GetTypes(AName : String): TJSONType; procedure SetArrays(AName : String; const AValue: TJSONArray); procedure SetBooleans(AName : String; const AValue: Boolean); procedure SetElements(AName: string; const AValue: TJSONData); procedure SetFloats(AName : String; const AValue: TJSONFloat); procedure SetIntegers(AName : String; const AValue: Integer); procedure SetIsNull(AName : String; const AValue: Boolean); procedure SetObjects(AName : String; const AValue: TJSONObject); procedure SetStrings(AName : String; const AValue: TJSONStringType); protected Procedure Converterror(From : Boolean); function GetAsBoolean: Boolean; override; function GetAsFloat: TJSONFloat; override; function GetAsInteger: Integer; override; procedure SetAsBoolean(const AValue: Boolean); override; procedure SetAsFloat(const AValue: TJSONFloat); override; procedure SetAsInteger(const AValue: Integer); override; function GetAsJSON: TJSONStringType; override; function GetAsString: TJSONStringType; override; procedure SetAsString(const AValue: TJSONStringType); override; function GetValue: variant; override; procedure SetValue(const AValue: variant); override; function GetCount: Integer; override; function GetItem(Index : Integer): TJSONData; override; procedure SetItem(Index : Integer; const AValue: TJSONData); override; public constructor Create; reintroduce; Constructor Create(const Elements : Array of Const); overload; destructor Destroy; override; class function JSONType: TJSONType; override; // Examine procedure Iterate(Iterator : TJSONObjectIterator; Data: TObject); function IndexOf(Item: TJSONData): Integer; Function IndexOfName(const AName: TJSONStringType): Integer; // Manipulate Procedure Clear; override; function Add(const AName: TJSONStringType; AValue: TJSONData): Integer; overload; function Add(const AName: TJSONStringType; AValue: Boolean): Integer; overload; function Add(const AName: TJSONStringType; AValue: TJSONFloat): Integer; overload; function Add(const AName: TJSONStringType; AValue: TJSONStringType): Integer; overload; function Add(const AName: TJSONStringType; Avalue: Integer): Integer; overload; function Add(const AName: TJSONStringType): Integer; overload; function Add(const AName: TJSONStringType; AValue : TJSONArray): Integer; overload; procedure Delete(Index : Integer); procedure Remove(Item : TJSONData); procedure Extract(Item : TJSONData); // Remove child element without destroying it // Easy access properties. property Names[Index : Integer] : TJSONStringType read GetNameOf; property Elements[AName: string] : TJSONData read GetElements write SetElements; default; Property Types[AName : String] : TJSONType Read GetTypes; Property Nulls[AName : String] : Boolean Read GetIsNull Write SetIsNull; Property Floats[AName : String] : TJSONFloat Read GetFloats Write SetFloats; Property Integers[AName : String] : Integer Read GetIntegers Write SetIntegers; Property Strings[AName : String] : TJSONStringType Read GetStrings Write SetStrings; Property Booleans[AName : String] : Boolean Read GetBooleans Write SetBooleans; Property Arrays[AName : String] : TJSONArray Read GetArrays Write SetArrays; Property Objects[AName : String] : TJSONObject Read GetObjects Write SetObjects; end; EJSON = Class(Exception); Function StringToJSONString(const S : TJSONStringType) : TJSONStringType; Function JSONStringToString(const S : TJSONStringType) : TJSONStringType; implementation Resourcestring SErrCannotConvertFromNull = 'Cannot convert data from Null value'; SErrCannotConvertToNull = 'Cannot convert data to Null value'; SErrCannotConvertFromArray = 'Cannot convert data from array value'; SErrCannotConvertToArray = 'Cannot convert data to array value'; SErrCannotConvertFromObject = 'Cannot convert data from object value'; SErrCannotConvertToObject = 'Cannot convert data to object value'; SErrInvalidFloat = 'Invalid float value : %s'; SErrInvalidInteger = 'Invalid float value : %s'; SErrCannotSetNotIsNull = 'IsNull cannot be set to False'; SErrCannotAddArrayTwice = 'Adding an array object to an array twice is not allowed'; SErrCannotAddObjectTwice = 'Adding an object to an array twice is not allowed'; SErrUnknownTypeInConstructor = 'Unknown type in JSON%s constructor: %d'; SErrNotJSONData = 'Cannot add object of type %s to TJSON%s'; SErrPointerNotNil = 'Cannot add non-nil pointer to JSON%s'; SErrOddNumber = 'TJSONObject must be constructed with name,value pairs'; SErrNameMustBeString = 'TJSONObject constructor element name at pos %d is not a string'; SErrElementNotFound = 'JSON element ''%s'' not found.'; Function StringToJSONString(const S : TJSONStringType) : TJSONStringType; var ms: TMemoryStream; procedure _Add(const s: TJSONStringType); begin if s <> '' then ms.WriteBuffer(s[1], Length(s)*SizeOf(TJSONCharType)); end; Var I,L : Integer; P : PJSONCharType; begin ms:=TMemoryStream.Create; try I:=1; L:=Length(S); P:=PJSONCharType(S); While I<=L do begin if (Ord(P^) < $80) and (AnsiChar(P^) in ['"','/','\',#8,#9,#10,#12,#13]) then begin Case P^ of '\' : _Add('\\'); '/' : _Add('\/'); '"' : _Add('\"'); #8 : _Add('\b'); #9 : _Add('\t'); #10 : _Add('\n'); #12 : _Add('\f'); #13 : _Add('\r'); end; end else if Ord(P^) >= $80 then begin _Add('\u'); _Add(hexStr(Ord(P^), 4)); end else ms.WriteBuffer(P^, SizeOf(P^)); Inc(I); Inc(P); end; SetString(Result, PJSONCharType(ms.Memory), ms.Size div SizeOf(TJSONCharType)); finally ms.Free; end; end; Function JSONStringToString(const S : TJSONStringType) : TJSONStringType; var ms: TMemoryStream; procedure _Add(const c: TJSONCharType); inline; begin ms.Write(c, SizeOf(c)); end; Var I,L : Integer; P : PJSONCharType; w : String; begin ms:=TMemoryStream.Create; try I:=1; L:=Length(S); P:=PJSONCharType(S); While (I<=L) do begin if (P^='\') then begin Inc(P); If (P^<>#0) then begin Inc(I); Case P^ of '\','"','/' : _Add(P^); 'b' : _Add(#8); 't' : _Add(#9); 'n' : _Add(#10); 'f' : _Add(#12); 'r' : _Add(#13); 'u' : begin W:=Copy(S,I+1,4); Inc(I,4); Inc(P,4); _Add(WideChar(StrToInt('$'+W))); end; end; end; end else _Add(P^); Inc(I); Inc(P); end; SetString(Result, PJSONCharType(ms.Memory), ms.Size div SizeOf(TJSONCharType)); finally ms.Free; end; end; { TJSONData } function TJSONData.GetItem(Index : Integer): TJSONData; begin Result:=nil; end; function TJSONData.GetCount: Integer; begin Result:=0; end; constructor TJSONData.Create; begin Clear; end; function TJSONData.GetIsNull: Boolean; begin Result:=False; end; class function TJSONData.JSONType: TJSONType; begin JSONType:=jtUnknown; end; procedure TJSONData.SetItem(Index : Integer; const AValue: TJSONData); begin // Do Nothing end; { TJSONnumber } class function TJSONnumber.JSONType: TJSONType; begin Result:=jtNumber; end; { TJSONstring } class function TJSONstring.JSONType: TJSONType; begin Result:=jtString; end; procedure TJSONString.Clear; begin FValue:=''; end; function TJSONstring.GetValue: Variant; begin Result:=FValue; end; procedure TJSONstring.SetValue(const AValue: Variant); begin FValue:=AValue; end; function TJSONstring.GetAsBoolean: Boolean; begin Result:=StrToBool(FValue); end; function TJSONstring.GetAsFloat: TJSONFloat; Var C : Integer; begin Val(FValue,Result,C); If (C<>0) then If Not TryStrToFloat(FValue,Result) then Raise EConvertError.CreateFmt(SErrInvalidFloat,[FValue]); end; function TJSONstring.GetAsInteger: Integer; begin Result:=StrToInt(FValue); end; procedure TJSONstring.SetAsBoolean(const AValue: Boolean); begin FValue:=BoolToStr(AValue); end; procedure TJSONstring.SetAsFloat(const AValue: TJSONFloat); begin FValue:=FloatToStr(AValue); end; procedure TJSONstring.SetAsInteger(const AValue: Integer); begin FValue:=IntToStr(AValue); end; function TJSONstring.GetAsJSON: TJSONStringType; begin Result:='"'+StringToJSONString(FValue)+'"'; end; function TJSONstring.GetAsString: TJSONStringType; begin Result:=FValue; end; procedure TJSONstring.SetAsString(const AValue: TJSONStringType); begin FValue:=AValue; end; constructor TJSONstring.Create(AValue: TJSONStringType); begin FValue:=AValue; end; { TJSONboolean } function TJSONboolean.GetValue: Variant; begin Result:=FValue; end; class function TJSONboolean.JSONType: TJSONType; begin Result:=jtBoolean; end; procedure TJSONBoolean.Clear; begin FValue:=False; end; procedure TJSONboolean.SetValue(const AValue: Variant); begin FValue:=boolean(AValue); end; function TJSONboolean.GetAsBoolean: Boolean; begin Result:=FValue; end; function TJSONboolean.GetAsFloat: TJSONFloat; begin Result:=Ord(FValue); end; function TJSONboolean.GetAsInteger: Integer; begin Result:=Ord(FValue); end; procedure TJSONboolean.SetAsBoolean(const AValue: Boolean); begin FValue:=AValue; end; procedure TJSONboolean.SetAsFloat(const AValue: TJSONFloat); begin FValue:=(AValue<>0) end; procedure TJSONboolean.SetAsInteger(const AValue: Integer); begin FValue:=(AValue<>0) end; function TJSONboolean.GetAsJSON: TJSONStringType; begin If FValue then Result:='True' else Result:='False'; end; function TJSONboolean.GetAsString: TJSONStringType; begin Result:=BoolToStr(FValue); end; procedure TJSONboolean.SetAsString(const AValue: TJSONStringType); begin FValue:=StrToBool(AValue); end; constructor TJSONboolean.Create(AValue: Boolean); begin FValue:=AValue; end; { TJSONnull } procedure TJSONnull.Converterror(From : Boolean); begin If From then Raise EJSON.Create(SErrCannotConvertFromNull) else Raise EJSON.Create(SErrCannotConvertToNull); end; {$warnings off} function TJSONnull.GetAsBoolean: Boolean; begin ConvertError(True); end; function TJSONnull.GetAsFloat: TJSONFloat; begin ConvertError(True); end; function TJSONnull.GetAsInteger: Integer; begin ConvertError(True); end; function TJSONnull.GetIsNull: Boolean; begin Result:=True; end; procedure TJSONnull.SetAsBoolean(const AValue: Boolean); begin ConvertError(False); end; procedure TJSONnull.SetAsFloat(const AValue: TJSONFloat); begin ConvertError(False); end; procedure TJSONnull.SetAsInteger(const AValue: Integer); begin ConvertError(False); end; function TJSONnull.GetAsJSON: TJSONStringType; begin Result:='Null'; end; function TJSONnull.GetAsString: TJSONStringType; begin ConvertError(True); end; procedure TJSONnull.SetAsString(const AValue: TJSONStringType); begin ConvertError(True); end; function TJSONnull.GetValue: Variant; begin Result:=variants.Null; end; procedure TJSONnull.SetValue(const AValue: variant); begin ConvertError(False); end; class function TJSONnull.JSONType: TJSONType; begin Result:=jtNull; end; procedure TJSONNull.Clear; begin // Do nothing end; {$warnings on} { TJSONFloatNumber } function TJSONFloatNumber.GetAsBoolean: Boolean; begin Result:=(FValue<>0); end; function TJSONFloatNumber.GetAsFloat: TJSONFloat; begin Result:=FValue; end; function TJSONFloatNumber.GetAsInteger: Integer; begin Result:=Round(FValue); end; procedure TJSONFloatNumber.SetAsBoolean(const AValue: Boolean); begin FValue:=Ord(AValue); end; procedure TJSONFloatNumber.SetAsFloat(const AValue: TJSONFloat); begin FValue:=AValue; end; procedure TJSONFloatNumber.SetAsInteger(const AValue: Integer); begin FValue:=AValue; end; function TJSONFloatNumber.GetAsJSON: TJSONStringType; begin Result:=AsString; end; function TJSONFloatNumber.GetAsString: TJSONStringType; begin Str(FValue,Result); end; procedure TJSONFloatNumber.SetAsString(const AValue: TJSONStringType); Var C : Integer; begin Val(AValue,FValue,C); If (C<>0) then Raise EConvertError.CreateFmt(SErrInvalidFloat,[AValue]); end; function TJSONFloatNumber.GetValue: variant; begin Result:=FValue; end; procedure TJSONFloatNumber.SetValue(const AValue: variant); begin FValue:=AValue; end; constructor TJSONFloatNumber.Create(AValue: TJSONFloat); begin FValue:=AValue; end; class function TJSONFloatNumber.NumberType: TJSONNumberType; begin Result:=ntFloat; end; procedure TJSONFloatNumber.Clear; begin FValue:=0; end; { TJSONIntegerNumber } function TJSONIntegerNumber.GetAsBoolean: Boolean; begin Result:=FValue<>0; end; function TJSONIntegerNumber.GetAsFloat: TJSONFloat; begin Result:=Ord(FValue); end; function TJSONIntegerNumber.GetAsInteger: Integer; begin Result:=FValue; end; procedure TJSONIntegerNumber.SetAsBoolean(const AValue: Boolean); begin FValue:=Ord(AValue); end; procedure TJSONIntegerNumber.SetAsFloat(const AValue: TJSONFloat); begin FValue:=Round(AValue); end; procedure TJSONIntegerNumber.SetAsInteger(const AValue: Integer); begin FValue:=AValue; end; function TJSONIntegerNumber.GetAsJSON: TJSONStringType; begin Result:=AsString; end; function TJSONIntegerNumber.GetAsString: TJSONStringType; begin Result:=IntToStr(FValue) end; procedure TJSONIntegerNumber.SetAsString(const AValue: TJSONStringType); begin FValue:=StrToInt(AValue); end; function TJSONIntegerNumber.GetValue: variant; begin Result:=FValue; end; procedure TJSONIntegerNumber.SetValue(const AValue: variant); begin FValue:=AValue; end; constructor TJSONIntegerNumber.Create(AValue: Integer); begin FValue:=AValue; end; class function TJSONIntegerNumber.NumberType: TJSONNumberType; begin Result:=ntInteger; end; procedure TJSONIntegerNumber.Clear; begin FValue:=0; end; { TJSONArray } function TJSONArray.GetBooleans(Index : Integer): Boolean; begin Result:=Items[Index].AsBoolean; end; function TJSONArray.GetArrays(Index : Integer): TJSONArray; begin Result:=Items[Index] as TJSONArray; end; function TJSONArray.GetFloats(Index : Integer): TJSONFloat; begin Result:=Items[Index].AsFloat; end; function TJSONArray.GetIntegers(Index : Integer): Integer; begin Result:=Items[Index].AsInteger; end; function TJSONArray.GetNulls(Index : Integer): Boolean; begin Result:=Items[Index].IsNull; end; function TJSONArray.GetObjects(Index : Integer): TJSONObject; begin Result:=Items[Index] as TJSONObject; end; function TJSONArray.GetStrings(Index : Integer): TJSONStringType; begin Result:=Items[Index].AsString; end; function TJSONArray.GetTypes(Index : Integer): TJSONType; begin Result:=Items[Index].JSONType; end; procedure TJSONArray.SetArrays(Index : Integer; const AValue: TJSONArray); begin Items[Index]:=AValue; end; procedure TJSONArray.SetBooleans(Index : Integer; const AValue: Boolean); begin Items[Index]:=TJSonBoolean.Create(AValue); end; procedure TJSONArray.SetFloats(Index : Integer; const AValue: TJSONFloat); begin Items[Index]:=TJSONFloatNumber.Create(AValue); end; procedure TJSONArray.SetIntegers(Index : Integer; const AValue: Integer); begin Items[Index]:=TJSONIntegerNumber.Create(AValue); end; procedure TJSONArray.SetObjects(Index : Integer; const AValue: TJSONObject); begin Items[Index]:=AValue; end; procedure TJSONArray.SetStrings(Index : Integer; const AValue: TJSONStringType); begin Items[Index]:=TJSONString.Create(AValue); end; procedure TJSONArray.Converterror(From: Boolean); begin If From then Raise EJSON.Create(SErrCannotConvertFromArray) else Raise EJSON.Create(SErrCannotConvertToArray); end; {$warnings off} function TJSONArray.GetAsBoolean: Boolean; begin ConvertError(True); end; function TJSONArray.GetAsFloat: TJSONFloat; begin ConvertError(True); end; function TJSONArray.GetAsInteger: Integer; begin ConvertError(True); end; procedure TJSONArray.SetAsBoolean(const AValue: Boolean); begin ConvertError(False); end; procedure TJSONArray.SetAsFloat(const AValue: TJSONFloat); begin ConvertError(False); end; procedure TJSONArray.SetAsInteger(const AValue: Integer); begin ConvertError(False); end; {$warnings on} function TJSONArray.GetAsJSON: TJSONStringType; var ms: TMemoryStream; procedure _Add(const s: TJSONStringType); begin if s <> '' then ms.WriteBuffer(s[1], Length(s)*SizeOf(TJSONCharType)); end; Var I : Integer; begin ms:=TMemoryStream.Create; try _Add('['); For I:=0 to Count-1 do begin if I > 0 then _Add(', '); _Add(Items[i].AsJSON); end; _Add(']'); SetString(Result, PJSONCharType(ms.Memory), ms.Size div SizeOf(TJSONCharType)); finally ms.Free; end; end; {$warnings off} function TJSONArray.GetAsString: TJSONStringType; begin ConvertError(True); end; procedure TJSONArray.SetAsString(const AValue: TJSONStringType); begin ConvertError(False); end; function TJSONArray.GetValue: variant; begin ConvertError(True); end; procedure TJSONArray.SetValue(const AValue: variant); begin ConvertError(False); end; {$warnings on} function TJSONArray.GetCount: Integer; begin Result:=Flist.Count; end; function TJSONArray.GetItem(Index: Integer): TJSONData; begin Result:=FList[Index] as TJSONData; end; procedure TJSONArray.SetItem(Index: Integer; const AValue: TJSONData); begin If (Index=FList.Count) then FList.Add(AValue) else FList[Index]:=AValue; end; constructor TJSONArray.Create; begin Flist:=TFPObjectList.Create(True); end; Function VarRecToJSON(Const Element : TVarRec; SourceType : String) : TJSONData; begin Result:=Nil; With Element do case VType of vtInteger : Result:=TJSONIntegerNumber.Create(VInteger); vtBoolean : Result:=TJSONBoolean.Create(VBoolean); vtChar : Result:=TJSONString.Create(VChar); vtExtended : Result:=TJSONFloatNumber.Create(VExtended^); vtString : Result:=TJSONString.Create(vString^); vtAnsiString : Result:=TJSONString.Create(AnsiString(vAnsiString)); vtWideString : Result:=TJSONString.Create(WideString(VWideString)); {$ifndef VER2_4} vtUnicodeString : Result:=TJSONString.Create(UnicodeString(VUnicodeString)); {$endif VER2_4} vtPChar : Result:=TJSONString.Create(StrPas(VPChar)); vtPointer : If (VPointer<>Nil) then Raise EJSON.CreateFmt(SErrPointerNotNil,[SourceType]) else Result:=TJSONNull.Create; vtCurrency : Result:=TJSONFloatNumber.Create(vCurrency^); vtInt64 : Result:=TJSONFloatNumber.Create(vInt64^); vtQWord : Result:=TJSONFloatNumber.Create(VQWord^); vtObject : if (VObject is TJSONData) then Result:=TJSONData(VObject) else Raise EJSON.CreateFmt(SErrNotJSONData,[SourceType,VObject.ClassName]); //vtVariant : else Raise EJSON.CreateFmt(SErrUnknownTypeInConstructor,[SourceType,VType]) end; end; constructor TJSONArray.Create(Const Elements: array of const); Var I : integer; J : TJSONData; begin Create; For I:=Low(Elements) to High(Elements) do begin J:=VarRecToJSON(Elements[i],'Array'); Add(J); end; end; Destructor TJSONArray.Destroy; begin FreeAndNil(FList); inherited Destroy; end; class function TJSONArray.JSONType: TJSONType; begin Result:=jtArray; end; procedure TJSONArray.Iterate(Iterator: TJSONArrayIterator; Data: TObject); Var I : Integer; Cont : Boolean; begin I:=0; Cont:=True; While (I-1) then Raise EJSON.Create(SErrCannotAddArrayTwice); Result:=Add(TJSONData(AnArray)); end; function TJSONArray.Add(AnObject: TJSONObject): Integer; begin If (IndexOf(AnObject)<>-1) then Raise EJSON.Create(SErrCannotAddObjectTwice); Result:=Add(TJSONData(AnObject)); end; procedure TJSONArray.Delete(Index: Integer); begin FList.Delete(Index); end; procedure TJSONArray.Remove(Item: TJSONData); begin FList.Remove(Item); end; { TJSONObject } function TJSONObject.GetArrays(AName : String): TJSONArray; begin Result:=GetElements(AName) as TJSONArray; end; function TJSONObject.GetBooleans(AName : String): Boolean; begin Result:=GetElements(AName).AsBoolean; end; function TJSONObject.GetElements(AName: string): TJSONData; begin Result:=TJSONData(FHash.Find(AName)); if Result = nil then Raise EJSON.CreateFmt(SErrElementNotFound, [AName]); end; function TJSONObject.GetFloats(AName : String): TJSONFloat; begin Result:=GetElements(AName).AsFloat; end; function TJSONObject.GetIntegers(AName : String): Integer; begin Result:=GetElements(AName).AsInteger; end; function TJSONObject.GetIsNull(AName : String): Boolean; begin Result:=GetElements(AName).IsNull; end; function TJSONObject.GetNameOf(Index: Integer): TJSONStringType; begin Result:=FHash.NameOfIndex(Index); end; function TJSONObject.GetObjects(AName : String): TJSONObject; begin Result:=GetElements(AName) as TJSONObject; end; function TJSONObject.GetStrings(AName : String): TJSONStringType; begin Result:=GetElements(AName).AsString; end; function TJSONObject.GetTypes(AName : String): TJSONType; begin Result:=Getelements(Aname).JSONType; end; procedure TJSONObject.SetArrays(AName : String; const AValue: TJSONArray); begin SetElements(AName,AVAlue); end; procedure TJSONObject.SetBooleans(AName : String; const AValue: Boolean); begin SetElements(AName,TJSONBoolean.Create(AVAlue)); end; procedure TJSONObject.SetElements(AName: string; const AValue: TJSONData); Var Index : Integer; begin Index:=FHash.FindIndexOf(AName); If (Index=-1) then FHash.Add(AName,AValue) else FHash.Items[Index]:=AValue; // Will free the previous value. end; procedure TJSONObject.SetFloats(AName : String; const AValue: TJSONFloat); begin SetElements(AName,TJSONFloatNumber.Create(AVAlue)); end; procedure TJSONObject.SetIntegers(AName : String; const AValue: Integer); begin SetElements(AName,TJSONIntegerNumber.Create(AVAlue)); end; procedure TJSONObject.SetIsNull(AName : String; const AValue: Boolean); begin If Not AValue then Raise EJSON.Create(SErrCannotSetNotIsNull); SetElements(AName,TJSONNull.Create); end; procedure TJSONObject.SetObjects(AName : String; const AValue: TJSONObject); begin SetElements(AName,AValue); end; procedure TJSONObject.SetStrings(AName : String; const AValue: TJSONStringType); begin SetElements(AName,TJSONString.Create(AVAlue)); end; procedure TJSONObject.Converterror(From: Boolean); begin If From then Raise EJSON.Create(SErrCannotConvertFromObject) else Raise EJSON.Create(SErrCannotConvertToObject); end; {$warnings off} function TJSONObject.GetAsBoolean: Boolean; begin ConvertError(True); end; function TJSONObject.GetAsFloat: TJSONFloat; begin ConvertError(True); end; function TJSONObject.GetAsInteger: Integer; begin ConvertError(True); end; procedure TJSONObject.SetAsBoolean(const AValue: Boolean); begin ConvertError(False); end; procedure TJSONObject.SetAsFloat(const AValue: TJSONFloat); begin ConvertError(False); end; procedure TJSONObject.SetAsInteger(const AValue: Integer); begin ConvertError(False); end; {$warnings on} function TJSONObject.GetAsJSON: TJSONStringType; var ms: TMemoryStream; procedure _Add(const s: TJSONStringType); begin if s <> '' then ms.WriteBuffer(s[1], Length(s)*SizeOf(TJSONCharType)); end; Var I : Integer; begin ms:=TMemoryStream.Create; try For I:=0 to Count-1 do begin If ms.Size = 0 then _Add('{ ') else _Add(', '); _Add('"'); _Add(StringToJSONString(Names[i])); _Add('" : '); _Add(Items[I].AsJSON); end; If ms.Size = 0 then _Add('{}') else _Add(' }'); SetString(Result, PJSONCharType(ms.Memory), ms.Size div SizeOf(TJSONCharType)); finally ms.Free; end; end; {$warnings off} function TJSONObject.GetAsString: TJSONStringType; begin ConvertError(True); end; procedure TJSONObject.SetAsString(const AValue: TJSONStringType); begin ConvertError(False); end; function TJSONObject.GetValue: variant; begin ConvertError(True); end; procedure TJSONObject.SetValue(const AValue: variant); begin ConvertError(False); end; {$warnings on} function TJSONObject.GetCount: Integer; begin Result:=FHash.Count; end; function TJSONObject.GetItem(Index: Integer): TJSONData; begin Result:=TJSONData(FHash.Items[Index]); end; procedure TJSONObject.SetItem(Index: Integer; const AValue: TJSONData); begin FHash.Items[Index]:=AValue; end; constructor TJSONObject.Create; begin FHash:=TFPHashObjectList.Create(True); end; constructor TJSONObject.Create(const Elements: array of const); Var I : integer; AName : String; J : TJSONData; begin Create; If ((High(Elements)-Low(Elements)) mod 2)=0 then Raise EJSON.Create(SErrOddNumber); I:=Low(Elements); While I<=High(Elements) do begin With Elements[i] do Case VType of vtChar : AName:=VChar; vtString : AName:=vString^; vtAnsiString : AName:=(AnsiString(vAnsiString)); vtPChar : AName:=StrPas(VPChar); else Raise EJSON.CreateFmt(SErrNameMustBeString,[I+1]); end; If (ANAme='') then Raise EJSON.CreateFmt(SErrNameMustBeString,[I+1]); Inc(I); J:=VarRecToJSON(Elements[i],'Object'); Add(AName,J); Inc(I); end; end; destructor TJSONObject.Destroy; begin FreeAndNil(FHash); inherited Destroy; end; class function TJSONObject.JSONType: TJSONType; begin Result:=jtObject; end; procedure TJSONObject.Iterate(Iterator: TJSONObjectIterator; Data: TObject); Var I : Integer; Cont : Boolean; begin I:=0; Cont:=True; While (I Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! TransGUI/json/jsonscanner.pp0000644000000000000000000002004311366572451015056 0ustar rootroot{ This file is part of the Free Component Library JSON source lexical scanner Copyright (c) 2007 by Michael Van Canneyt michael@freepascal.org See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} {$mode objfpc} {$h+} unit jsonscanner; interface uses SysUtils, Classes; resourcestring SErrInvalidCharacter = 'Invalid character ''%s'''; SErrOpenString = 'string exceeds end of line'; type TJSONToken = ( tkEOF, tkWhitespace, tkString, tkNumber, tkTrue, tkFalse, tkNull, // Simple (one-character) tokens tkComma, // ',' tkColon, // ':' tkCurlyBraceOpen, // '{' tkCurlyBraceClose, // '}' tkSquaredBraceOpen, // '[' tkSquaredBraceClose, // ']' tkUnknown ); EScannerError = class(Exception); TJSONScanner = class private FSource : TStringList; FCurRow: Integer; FCurToken: TJSONToken; FCurTokenString: widestring; FCurLine: string; TokenStr: PChar; function GetCurColumn: Integer; protected procedure Error(const Msg: string);overload; procedure Error(const Msg: string; Args: array of Const);overload; function DoFetchToken: TJSONToken; public constructor Create(Source : TStream); overload; constructor Create(Source : String); overload; destructor Destroy; override; function FetchToken: TJSONToken; property CurLine: string read FCurLine; property CurRow: Integer read FCurRow; property CurColumn: Integer read GetCurColumn; property CurToken: TJSONToken read FCurToken; property CurTokenString: widestring read FCurTokenString; end; const TokenInfos: array[TJSONToken] of string = ( 'EOF', 'Whitespace', 'String', 'Number', 'True', 'False', 'Null', ',', ':', '{', '}', '[', ']', '' ); implementation constructor TJSONScanner.Create(Source : TStream); begin FSource:=TStringList.Create; FSource.LoadFromStream(Source); end; constructor TJSONScanner.Create(Source : String); begin FSource:=TStringList.Create; FSource.Text:=Source; end; destructor TJSONScanner.Destroy; begin FreeAndNil(FSource); Inherited; end; function TJSONScanner.FetchToken: TJSONToken; begin Result:=DoFetchToken; end; procedure TJSONScanner.Error(const Msg: string); begin raise EScannerError.Create(Msg); end; procedure TJSONScanner.Error(const Msg: string; Args: array of Const); begin raise EScannerError.CreateFmt(Msg, Args); end; function TJSONScanner.DoFetchToken: TJSONToken; function FetchLine: Boolean; begin Result:=FCurRow= Low(LongInt)) and (I <= High(LongInt)) then Result:=TJSONIntegerNumber.Create(I) else begin I:=0; Val(S,F,Error); If (Error<>0) then DoError(SErrInvalidNumber); Result:=TJSONFloatNumber.Create(F); end; end; // Current token is {, on exit current token is } Function TJSONParser.ParseObject : TJSONObject; Var T : TJSONtoken; E : TJSONData; N : TJSONStringType; begin Result:=TJSONObject.Create; Try T:=GetNextToken; While T<>tkCurlyBraceClose do begin If T<>tkString then DoError(SErrExpectedElementName); N:=CurrentTokenString; T:=GetNextToken; If (T<>tkColon) then DoError(SErrExpectedColon); E:=DoParse(False,False); Result.Add(N,E); T:=GetNextToken; If Not (T in [tkComma,tkCurlyBraceClose]) then DoError(SExpectedCommaorBraceClose); If T=tkComma then T:=GetNextToken; end; Except FreeAndNil(Result); Raise; end; end; // Current token is [, on exit current token is ] Function TJSONParser.ParseArray : TJSONArray; Var T : TJSONtoken; E : TJSONData; LastComma : Boolean; begin Result:=TJSONArray.Create; LastComma:=False; Try Repeat T:=GetNextToken; If (T<>tkSquaredBraceClose) then begin E:=DoParse(True,False); If (E<>Nil) then Result.Add(E) else if (Result.Count>0) then DoError(SErrEmptyElement); T:=GetNextToken; If Not (T in [tkComma,tkSquaredBraceClose]) then DoError(SExpectedCommaorBraceClose); LastComma:=(t=TkComma); end; Until (T=tkSquaredBraceClose); If LastComma then // Test for ,] case DoError(SErrUnExpectedToken); Except FreeAndNil(Result); Raise; end; end; // Get next token, discarding whitespace Function TJSONParser.GetNextToken : TJSONToken ; begin Repeat Result:=FScanner.FetchToken; Until (Result<>tkWhiteSpace); end; Procedure TJSONParser.DoError(Msg : String); Var S : TJSONStringType; begin S:=Format(Msg,[CurrentTokenString]); S:=Format('Error at line %d, Pos %d:',[FScanner.CurRow,FSCanner.CurColumn])+S; Raise EJSONScanner.Create(S); end; constructor TJSONParser.Create(Source: TStream); begin Inherited Create; FScanner:=TJSONScanner.Create(Source); end; constructor TJSONParser.Create(Source: TJSONStringType); begin Inherited Create; FScanner:=TJSONScanner.Create(Source); end; destructor TJSONParser.Destroy(); begin FreeAndNil(FScanner); inherited Destroy(); end; end. TransGUI/json/COPYING.FPC0000644000000000000000000000227511366572451013643 0ustar rootrootThis is the file COPYING.FPC, it applies to the Free Pascal Run-Time Library (RTL) and packages (packages) distributed by members of the Free Pascal Development Team. The source code of the Free Pascal Runtime Libraries and packages are distributed under the Library GNU General Public License (see the file COPYING) with the following modification: As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you didn't receive a copy of the file COPYING, contact: Free Software Foundation 675 Mass Ave Cambridge, MA 02139 USA TransGUI/readme.txt0000644000000000000000000000615012261763702013216 0ustar rootrootTransmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Transmission Remote GUI is feature rich cross platform front-end to remotely control Transmission daemon via its RPC protocol. It is faster and has more functionality than builtin Transmission web interface. Transmission Remote GUI is developed using Lazarus RAD and Free Pascal compiler. Features: * Native application for Windows, Linux and MacOS X * uTorrent-like interface * Select files to download * Choose files priority * View details about connected peers * Full information about each torrent * Per torrent options Project home: http://code.google.com/p/transmisson-remote-gui/ INSTALLATION LINUX: Easy way (recommended). There are precompiled program's binaries for i386 and x86_64 Linux architectures. - Download a .zip archive for your architecture. - Unzip it to your home dir. - Create a desktop or menu shortcut to the transgui executable. * (If needed, change the transgui file permissions to executable). - Run the program using the created shortcut. Harder way. Build the program by yourself. - Make sure you have working Lazarus and Free Pascal compiler installed. * Free Pascal Compiler 2.6.2 and Lazarus 1.0 is used to develop Transmission Remote GUI. You may use different versions of FPC and Lazarus at your own risk. - Download the sources archive and extract it to some folder or perform svn checkout. - Open terminal/command line prompt and cd to the sources folder; - Execute "make" command to build the application; - Execute "make zipdist" command to create a release .zip archive in the "Release" sub-folder. More information about building here: http://code.google.com/p/transmisson-remote-gui/wiki/Building COMMAND LINE PARAMETERS You can specify path to a .torrent file or a magnet link as a command line parameter. The program will add the specified torrent. -hidden : Start the program hidden. Only the program's tray icon will be visible. --home= : Specifies a home directory for the program. All program's settings are stored in the home directory. You can run multiple instances of the program by specifying different home directories. PORTABLE MODE If the program finds the transgui.ini file in the same folder as the binary file, then it will store all configuration and data files in the program's folder, instead of the folder in a user profile. ADVANCED PARAMETERS There are some parameters in the tramsgui.ini file, that can not be modified via the GUI. [Interface] ; Maximum number of elements in the folder history list MaxFoldersHistory=10 TransGUI/main.pas0000644000000000000000000061717612261763702012671 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit Main; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, zstream, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls, Menus, ActnList, httpsend, StdCtrls, fpjson, jsonparser, ExtCtrls, rpc, syncobjs, variants, varlist, IpResolver, zipper, ResTranslator, VarGrid, StrUtils, LCLProc, Grids, BaseForm, utils, AddTorrent; const AppName = 'Transmission Remote GUI'; AppVersion = '5.0.1'; resourcestring sAll = 'All torrents'; sWaiting = 'Waiting'; sVerifying = 'Verifying'; sDownloading = 'Downloading'; sSeeding = 'Seeding'; sFinished = 'Finished'; sStopped = 'Stopped'; sUnknown = 'Unknown'; sCompleted = 'Completed'; sConnected = 'connected'; sActive = 'Active'; sInactive = 'Inactive'; sErrorState = 'Error'; sUpdating = 'Updating...'; sFinishedDownload = '''%s'' has finished downloading'; sDownloadComplete = 'Download complete'; sUpdateComplete = 'Update complete.'; sTorrentVerification = 'Torrent verification may take a long time.' + LineEnding + 'Are you sure to start verification of torrent ''%s''?'; sTorrentsVerification = 'Torrents verification may take a long time.' + LineEnding + 'Are you sure to start verification of %d torrents?'; sReconnect = 'Reconnect in %d seconds.'; sDisconnected = 'Disconnected'; sConnectingToDaemon = 'Connecting to daemon...'; sSecs = '%ds'; sMins = '%dm'; sHours = '%dh'; sDays = '%dd'; sDownloadingSeeding = '%s%s%d downloading, %d seeding%s%s, %s'; sDownSpeed = 'D: %s/s'; sUpSpeed = 'U: %s/s'; SFreeSpace = 'Free: %s'; sNoPathMapping = 'Unable to find path mapping.'+LineEnding+'Use the application''s options to setup path mappings.'; sGeoIPConfirm = 'Geo IP database is needed to resolve country by IP address.' + LineEnding + 'Download this database now?'; sFlagArchiveConfirm = 'Flag images archive is needed to display country flags.' + LineEnding + 'Download this archive now?'; sInSwarm = 'in swarm'; sHashfails = '%s (%d hashfails)'; sDone = '%s (%s done)'; sHave = '%d x %s (have %d)'; sUnableExtractFlag = 'Unable to extract flag image.'; sTrackerWorking = 'Working'; sTrackerUpdating = 'Updating'; sRestartRequired = 'You need to restart the application to apply changes.'; sRemoveTorrentData = 'Are you sure to remove torrent ''%s'' and all associated DATA?'; sRemoveTorrentDataMulti = 'Are you sure to remove %d selected torrents and all their associated DATA?'; sRemoveTorrent = 'Are you sure to remove torrent ''%s''?'; sRemoveTorrentMulti = 'Are you sure to remove %d selected torrents?'; sUnableGetFilesList = 'Unable to get files list'; sTrackerError = 'Tracker'; sSkip = 'skip'; sLow = 'low'; sNormal = 'normal'; sHigh = 'high'; sByte = 'b'; sKByte = 'KB'; sMByte = 'MB'; sGByte = 'GB'; sTByte = 'TB'; sPerSecond = '/s'; sOf = 'of'; sNoTracker = 'No tracker'; sTorrents = 'Torrents'; sBlocklistUpdateComplete = 'The block list has been updated successfully.' + LineEnding + 'The list entries count: %d.'; sSeveralTorrents = '%d torrents'; sUnableToExecute = 'Unable to execute "%s".'; sSSLLoadError = 'Unable to load OpenSSL library files: %s and %s'; SRemoveTracker = 'Are you sure to remove tracker ''%s''?'; SUnlimited = 'Unlimited'; SAverage = 'average'; SCheckNewVersion = 'Do you wish to enable automatic checking for a new version of %s?'; SDuplicateTorrent = 'Torrent already exists in the list'; SUpdateTrackers = 'Update trackers for the existing torrent?'; SDownloadingTorrent = 'Downloading torrent file...'; SConnectTo = 'Connect to %s'; SEnterPassword = 'Please enter a password to connect to %s:'; SDownloaded = 'Downloaded'; SUploaded = 'Uploaded'; SFilesAdded = 'Files added'; SActiveTime = 'Active time'; type { TProgressImage } TProgressImage = class(TGraphicControl) private FBmp: TBitmap; FBorderColor: TColor; FEndIndex: integer; FImageIndex: integer; FImages: TImageList; FStartIndex: integer; FTimer: TTimer; function GetFrameDelay: integer; procedure SetBorderColor(const AValue: TColor); procedure SetEndIndex(const AValue: integer); procedure SetFrameDelay(const AValue: integer); procedure SetImageIndex(const AValue: integer); procedure SetImages(const AValue: TImageList); procedure SetStartIndex(const AValue: integer); procedure UpdateIndex; procedure DoTimer(Sender: TObject); protected procedure Paint; override; procedure VisibleChanged; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Images: TImageList read FImages write SetImages; property ImageIndex: integer read FImageIndex write SetImageIndex; property StartIndex: integer read FStartIndex write SetStartIndex; property EndIndex: integer read FEndIndex write SetEndIndex; property FrameDelay: integer read GetFrameDelay write SetFrameDelay; property BorderColor: TColor read FBorderColor write SetBorderColor; end; { TMainForm } TMainForm = class(TBaseForm) acConnect: TAction; acAddTorrent: TAction; acStopTorrent: TAction; acRemoveTorrent: TAction; acStartTorrent: TAction; acSetHighPriority: TAction; acSetNormalPriority: TAction; acSetLowPriority: TAction; acSetNotDownload: TAction; acOptions: TAction; acDaemonOptions: TAction; acStartAllTorrents: TAction; acStopAllTorrents: TAction; acExit: TAction; acResolveHost: TAction; acResolveCountry: TAction; acShowCountryFlag: TAction; acSetupColumns: TAction; acRemoveTorrentAndData: TAction; acOpenFile: TAction; acOpenContainingFolder: TAction; acAddLink: TAction; acReannounceTorrent: TAction; acMoveTorrent: TAction; acSelectAll: TAction; acShowApp: TAction; acHideApp: TAction; acAddTracker: TAction; acEditTracker: TAction; acDelTracker: TAction; acConnOptions: TAction; acNewConnection: TAction; acDisconnect: TAction; acAltSpeed: TAction; acForceStartTorrent: TAction; acQMoveTop: TAction; acQMoveUp: TAction; acQMoveDown: TAction; acQMoveBottom: TAction; acCheckNewVersion: TAction; acFolderGrouping: TAction; acAdvEditTrackers: TAction; acFilterPane: TAction; acInfoPane: TAction; acStatusBar: TAction; acCopyPath: TAction; acRename: TAction; acTrackerGrouping: TAction; acUpdateBlocklist: TAction; acUpdateGeoIP: TAction; acTorrentProps: TAction; acVerifyTorrent: TAction; ActionList: TActionList; ApplicationProperties: TApplicationProperties; edSearch: TEdit; imgSearch: TImage; imgFlags: TImageList; ImageList16: TImageList; FilterTimer: TTimer; MenuItem100: TMenuItem; MenuItem68: TMenuItem; MenuItem93: TMenuItem; MenuItem94: TMenuItem; MenuItem95: TMenuItem; MenuItem96: TMenuItem; MenuItem97: TMenuItem; MenuItem98: TMenuItem; MenuItem99: TMenuItem; panDetailsWait: TPanel; txGlobalStats: TLabel; lvFilter: TVarGrid; lvTrackers: TVarGrid; MenuItem25: TMenuItem; MenuItem34: TMenuItem; MenuItem35: TMenuItem; MenuItem40: TMenuItem; MenuItem41: TMenuItem; MenuItem43: TMenuItem; MenuItem63: TMenuItem; MenuItem64: TMenuItem; MenuItem77: TMenuItem; MenuItem78: TMenuItem; MenuItem79: TMenuItem; MenuItem80: TMenuItem; MenuItem81: TMenuItem; MenuItem82: TMenuItem; MenuItem83: TMenuItem; MenuItem84: TMenuItem; MenuItem85: TMenuItem; MenuItem86: TMenuItem; MenuItem87: TMenuItem; MenuItem88: TMenuItem; MenuItem89: TMenuItem; MenuItem90: TMenuItem; MenuItem91: TMenuItem; MenuItem92: TMenuItem; miView: TMenuItem; miDonate: TMenuItem; miHomePage: TMenuItem; pmiQueue: TMenuItem; miQueue: TMenuItem; pmFilter: TPopupMenu; sepTrackers: TMenuItem; MenuItem65: TMenuItem; MenuItem66: TMenuItem; MenuItem67: TMenuItem; MenuItem69: TMenuItem; MenuItem70: TMenuItem; MenuItem72: TMenuItem; MenuItem76: TMenuItem; pmiUpSpeedLimit: TMenuItem; pmiDownSpeedLimit: TMenuItem; pmDownSpeeds: TPopupMenu; pmUpSpeeds: TPopupMenu; sepCon2: TMenuItem; MenuItem71: TMenuItem; sepCon1: TMenuItem; MenuItem73: TMenuItem; MenuItem74: TMenuItem; MenuItem75: TMenuItem; pmSepOpen2: TMenuItem; MenuItem42: TMenuItem; pmSepOpen1: TMenuItem; MenuItem44: TMenuItem; MenuItem45: TMenuItem; MenuItem46: TMenuItem; MenuItem47: TMenuItem; MenuItem48: TMenuItem; MenuItem49: TMenuItem; MenuItem50: TMenuItem; MenuItem51: TMenuItem; MenuItem52: TMenuItem; MenuItem53: TMenuItem; MenuItem54: TMenuItem; MenuItem55: TMenuItem; MenuItem56: TMenuItem; MenuItem58: TMenuItem; MenuItem59: TMenuItem; MenuItem60: TMenuItem; MenuItem61: TMenuItem; MenuItem62: TMenuItem; miPriority: TMenuItem; pmiPriority: TMenuItem; MenuItem57: TMenuItem; pbDownloaded: TPaintBox; pmTrackers: TPopupMenu; pmConnections: TPopupMenu; tabStats: TTabSheet; tabTrackers: TTabSheet; tbConnect: TToolButton; tbtAltSpeed: TToolButton; sepAltSpeed: TToolButton; sepQueue: TToolButton; tbQMoveUp: TToolButton; tbQMoveDown: TToolButton; ToolButton9: TToolButton; txConnErrorLabel: TLabel; panSearch: TPanel; panFilter: TPanel; panReconnectFrame: TShape; txReconnectSecs: TLabel; txConnError: TLabel; MenuItem38: TMenuItem; MenuItem39: TMenuItem; panReconnect: TPanel; txLastActive: TLabel; txLastActiveLabel: TLabel; txTracker: TLabel; txTrackerLabel: TLabel; txCompletedOn: TLabel; txCompletedOnLabel: TLabel; txAddedOn: TLabel; txAddedOnLabel: TLabel; MenuItem19: TMenuItem; MenuItem20: TMenuItem; MenuItem21: TMenuItem; MenuItem22: TMenuItem; MenuItem23: TMenuItem; MenuItem24: TMenuItem; miTSep1: TMenuItem; MenuItem26: TMenuItem; MenuItem27: TMenuItem; MenuItem28: TMenuItem; MenuItem29: TMenuItem; MenuItem30: TMenuItem; MenuItem31: TMenuItem; MenuItem32: TMenuItem; MenuItem33: TMenuItem; MenuItem36: TMenuItem; MenuItem37: TMenuItem; miAbout: TMenuItem; miHelp: TMenuItem; panTop: TPanel; pmTray: TPopupMenu; HSplitter: TSplitter; pmPeers: TPopupMenu; TrayIcon: TTrayIcon; txCreated: TLabel; txCreatedLabel: TLabel; txTorrentHeader: TPanel; txTorrentName: TLabel; txTorrentNameLabel: TLabel; txDownProgress: TLabel; txDownProgressLabel: TLabel; panProgress: TPanel; txMaxPeers: TLabel; txMaxPeersLabel: TLabel; txPeers: TLabel; txPeersLabel: TLabel; txSeeds: TLabel; txSeedsLabel: TLabel; txDummy2: TLabel; txTrackerUpdate: TLabel; txDummy1: TLabel; txRemaining: TLabel; txRemainingLabel: TLabel; txStatus: TLabel; txStatusLabel: TLabel; txRatio: TLabel; txRatioLabel: TLabel; txDownLimit: TLabel; txDownLimitLabel: TLabel; txTrackerUpdateLabel: TLabel; txTransferHeader: TPanel; txUpSpeed: TLabel; txUpLimit: TLabel; txUpSpeedLabel: TLabel; txDownSpeed: TLabel; txDownSpeedLabel: TLabel; txUploaded: TLabel; txUploadedLabel: TLabel; txDownloaded: TLabel; txDownloadedLabel: TLabel; txUpLimitLabel: TLabel; txWasted: TLabel; txWastedLabel: TLabel; miCopyLabel: TMenuItem; pmLabels: TPopupMenu; txError: TLabel; txErrorLabel: TLabel; MenuItem17: TMenuItem; MenuItem18: TMenuItem; miTools: TMenuItem; TickTimer: TTimer; MainToolBar: TToolBar; panTransfer: TPanel; ToolButton1: TToolButton; ToolButton2: TToolButton; ToolButton3: TToolButton; ToolButton4: TToolButton; tbStopTorrent: TToolButton; ToolButton6: TToolButton; ToolButton7: TToolButton; ToolButton8: TToolButton; txComment: TLabel; txCommentLabel: TLabel; txHash: TLabel; txHashLabel: TLabel; panGeneralInfo: TPanel; lvFiles: TVarGrid; lvPeers: TVarGrid; MainMenu: TMainMenu; MenuItem1: TMenuItem; MenuItem10: TMenuItem; MenuItem11: TMenuItem; MenuItem12: TMenuItem; MenuItem13: TMenuItem; MenuItem14: TMenuItem; MenuItem15: TMenuItem; MenuItem16: TMenuItem; MenuItem2: TMenuItem; MenuItem3: TMenuItem; MenuItem4: TMenuItem; MenuItem5: TMenuItem; MenuItem6: TMenuItem; MenuItem7: TMenuItem; MenuItem8: TMenuItem; MenuItem9: TMenuItem; miConnect: TMenuItem; miExit: TMenuItem; miTorrent: TMenuItem; OpenTorrentDlg: TOpenDialog; PageInfo: TPageControl; pmTorrents: TPopupMenu; pmFiles: TPopupMenu; sbGenInfo: TScrollBox; txPieces: TLabel; txPiecesLabel: TLabel; txTotalSize: TLabel; txTotalSizeLabel: TLabel; gTorrents: TVarGrid; gStats: TVarGrid; VSplitter: TSplitter; StatusBar: TStatusBar; tabPeers: TTabSheet; tabGeneral: TTabSheet; TorrentsListTimer: TTimer; tabFiles: TTabSheet; procedure acAddLinkExecute(Sender: TObject); procedure acAddTorrentExecute(Sender: TObject); procedure acAddTrackerExecute(Sender: TObject); procedure acAdvEditTrackersExecute(Sender: TObject); procedure acAltSpeedExecute(Sender: TObject); procedure acCheckNewVersionExecute(Sender: TObject); procedure acConnectExecute(Sender: TObject); procedure acConnOptionsExecute(Sender: TObject); procedure acCopyPathExecute(Sender: TObject); procedure acDelTrackerExecute(Sender: TObject); procedure acEditTrackerExecute(Sender: TObject); procedure acFilterPaneExecute(Sender: TObject); procedure acFolderGroupingExecute(Sender: TObject); procedure acForceStartTorrentExecute(Sender: TObject); procedure acHideAppExecute(Sender: TObject); procedure acInfoPaneExecute(Sender: TObject); procedure acMoveTorrentExecute(Sender: TObject); procedure acNewConnectionExecute(Sender: TObject); procedure acOpenContainingFolderExecute(Sender: TObject); procedure acOpenFileExecute(Sender: TObject); procedure acOptionsExecute(Sender: TObject); procedure acDisconnectExecute(Sender: TObject); procedure acExitExecute(Sender: TObject); procedure acDaemonOptionsExecute(Sender: TObject); procedure acQMoveBottomExecute(Sender: TObject); procedure acQMoveDownExecute(Sender: TObject); procedure acQMoveTopExecute(Sender: TObject); procedure acQMoveUpExecute(Sender: TObject); procedure acReannounceTorrentExecute(Sender: TObject); procedure acRemoveTorrentAndDataExecute(Sender: TObject); procedure acRemoveTorrentExecute(Sender: TObject); procedure acRenameExecute(Sender: TObject); procedure acResolveCountryExecute(Sender: TObject); procedure acResolveHostExecute(Sender: TObject); procedure acSelectAllExecute(Sender: TObject); procedure acSetHighPriorityExecute(Sender: TObject); procedure acSetLowPriorityExecute(Sender: TObject); procedure acSetNormalPriorityExecute(Sender: TObject); procedure acSetNotDownloadExecute(Sender: TObject); procedure acSetupColumnsExecute(Sender: TObject); procedure acShowAppExecute(Sender: TObject); procedure acShowCountryFlagExecute(Sender: TObject); procedure acStartAllTorrentsExecute(Sender: TObject); procedure acStartTorrentExecute(Sender: TObject); procedure acStatusBarExecute(Sender: TObject); procedure acStopAllTorrentsExecute(Sender: TObject); procedure acStopTorrentExecute(Sender: TObject); procedure acTorrentPropsExecute(Sender: TObject); procedure acTrackerGroupingExecute(Sender: TObject); procedure acUpdateBlocklistExecute(Sender: TObject); procedure acUpdateGeoIPExecute(Sender: TObject); procedure acVerifyTorrentExecute(Sender: TObject); procedure ApplicationPropertiesEndSession(Sender: TObject); procedure ApplicationPropertiesException(Sender: TObject; E: Exception); procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); procedure ApplicationPropertiesMinimize(Sender: TObject); procedure ApplicationPropertiesRestore(Sender: TObject); procedure edSearchChange(Sender: TObject); procedure FormActivate(Sender: TObject); procedure FormDropFiles(Sender: TObject; const FileNames: array of String); procedure FormWindowStateChange(Sender: TObject); procedure gTorrentsCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); procedure gTorrentsClick(Sender: TObject); procedure gTorrentsDblClick(Sender: TObject); procedure gTorrentsDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean); procedure gTorrentsEditorHide(Sender: TObject); procedure gTorrentsEditorShow(Sender: TObject); procedure gTorrentsQuickSearch(Sender: TVarGrid; var SearchText: string; var ARow: integer); procedure gTorrentsResize(Sender: TObject); procedure gTorrentsSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string); procedure gTorrentsSortColumn(Sender: TVarGrid; var ASortCol: integer); procedure HSplitterChangeBounds(Sender: TObject); procedure lvFilesDblClick(Sender: TObject); procedure lvFilesEditorHide(Sender: TObject); procedure lvFilesEditorShow(Sender: TObject); procedure lvFilesSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string); procedure lvFilterCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); procedure lvFilterClick(Sender: TObject); procedure lvFilterDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean); procedure lvPeersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); procedure lvTrackersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); procedure lvTrackersDblClick(Sender: TObject); procedure lvTrackersKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure miDonateClick(Sender: TObject); procedure miHomePageClick(Sender: TObject); procedure PageInfoResize(Sender: TObject); procedure panReconnectResize(Sender: TObject); procedure pbDownloadedPaint(Sender: TObject); procedure StatusBarMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure TickTimerTimer(Sender: TObject); procedure FilterTimerTimer(Sender: TObject); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormResize(Sender: TObject); procedure FormShow(Sender: TObject); procedure lvFilterResize(Sender: TObject); procedure miAboutClick(Sender: TObject); procedure miCopyLabelClick(Sender: TObject); procedure PageInfoChange(Sender: TObject); procedure TorrentsListTimerTimer(Sender: TObject); procedure pmFilesPopup(Sender: TObject); procedure pmTorrentsPopup(Sender: TObject); procedure TrayIconDblClick(Sender: TObject); procedure VSplitterChangeBounds(Sender: TObject); private FStarted: boolean; FTorrents: TVarList; FFiles: TVarList; FTrackers: TStringList; FResolver: TIpResolver; FUnZip: TUnZipper; FReconnectWaitStart: TDateTime; FReconnectTimeOut: integer; FTorrentProgress: TBitmap; FLastPieces: string; FLastPieceCount: integer; FLastDone: double; FCurConn: string; FPathMap: TStringList; FLastFilerIndex: integer; FFilterChanged: boolean; FCurDownSpeedLimit: integer; FCurUpSpeedLimit: integer; FFlagsPath: string; FAddingTorrent: integer; FPendingTorrents: TStringList; FLinksFromClipboard: boolean; FLastClipboardLink: string; {$ifdef LCLcarbon} FFormActive: boolean; {$endif LCLcarbon} FSlowResponse: TProgressImage; FDetailsWait: TProgressImage; FDetailsWaitStart: TDateTime; FMainFormShown: boolean; FFilesTree: TFilesTree; FFilesCapt: string; FCalcAvg: boolean; FPasswords: TStringList; procedure UpdateUI; procedure UpdateUIRpcVersion(RpcVersion: integer); function DoConnect: boolean; procedure DoCreateOutZipStream(Sender: TObject; var AStream: TStream; AItem: TFullZipFileEntry); procedure DoDisconnect; procedure DoOpenFlagsZip(Sender: TObject; var AStream: TStream); procedure TorrentProps(PageNo: integer); procedure ShowConnOptions(NewConnection: boolean); procedure SaveColumns(LV: TVarGrid; const AName: string; FullInfo: boolean = True); procedure LoadColumns(LV: TVarGrid; const AName: string; FullInfo: boolean = True); function GetTorrentError(t: TJSONObject; Status: integer): string; function SecondsToString(j: integer): string; function DoAddTorrent(const FileName: Utf8String): boolean; procedure UpdateTray; procedure HideApp; procedure ShowApp; procedure DownloadFinished(const TorrentName: string); function GetFlagImage(const CountryCode: string): integer; procedure BeforeCloseApp; function GetGeoIpDatabase: string; function GetFlagsArchive: string; function DownloadGeoIpDatabase(AUpdate: boolean): boolean; procedure TorrentColumnsChanged; function EtaToString(ETA: integer): string; function GetTorrentStatus(TorrentIdx: integer): string; function GetSeedsText(Seeds, SeedsTotal: integer): string; function GetPeersText(Peers, PeersTotal, Leechers: integer): string; function RatioToString(Ratio: double): string; function TorrentDateTimeToString(d: Int64): string; procedure DoRefresh(All: boolean = False); function GetFilesCommonPath(files: TJSONArray): string; procedure InternalRemoveTorrent(const Msg, MsgMulti: string; RemoveLocalData: boolean); function IncludeProperTrailingPathDelimiter(const s: string): string; procedure UrlLabelClick(Sender: TObject); procedure CenterReconnectWindow; procedure ProcessPieces(const Pieces: string; PieceCount: integer; const Done: double); function ExecRemoteFile(const FileName: string; SelectFile: boolean): boolean; function GetSelectedTorrents: variant; procedure FillDownloadDirs(CB: TComboBox; const CurFolderParam: string); procedure SaveDownloadDirs(CB: TComboBox; const CurFolderParam: string); procedure SetRefreshInterval; procedure AddTracker(EditMode: boolean); procedure UpdateConnections; procedure DoConnectToHost(Sender: TObject); procedure FillSpeedsMenu; procedure DoSetDownloadSpeed(Sender: TObject); procedure DoSetUploadSpeed(Sender: TObject); procedure SetSpeedLimit(const Dir: string; Speed: integer); function FixSeparators(const p: string): string; function MapRemoteToLocal(const RemotePath: string): string; procedure CheckAddTorrents; procedure CheckClipboardLink; procedure CenterDetailsWait; function GetPageInfoType(pg: TTabSheet): TAdvInfoType; procedure DetailsUpdated; function RenameTorrent(TorrentId: integer; const OldPath, NewName: string): boolean; procedure FilesTreeStateChanged(Sender: TObject); function SelectTorrent(TorrentId, TimeOut: integer): integer; procedure OpenCurrentTorrent(OpenFolderOnly: boolean); public procedure FillTorrentsList(list: TJSONArray); procedure FillPeersList(list: TJSONArray); procedure FillFilesList(ATorrentId: integer; list, priorities, wanted: TJSONArray; const DownloadDir: WideString); procedure FillGeneralInfo(t: TJSONObject); procedure FillTrackersList(TrackersData: TJSONObject); procedure FillSessionInfo(s: TJSONObject); procedure FillStatistics(s: TJSONObject); procedure CheckStatus(Fatal: boolean = True); function TorrentAction(const TorrentIds: variant; const AAction: string; args: TJSONObject = nil): boolean; function SetFilePriority(TorrentId: integer; const Files: array of integer; const APriority: string): boolean; function SetCurrentFilePriority(const APriority: string): boolean; procedure SetTorrentPriority(APriority: integer); procedure ClearDetailsInfo(Skip: TAdvInfoType = aiNone); function SelectRemoteFolder(const CurFolder, DialogTitle: string): string; procedure ConnectionSettingsChanged(const ActiveConnection: string; ForceReconnect: boolean); end; function CheckAppParams: boolean; function GetHumanSize(sz: double; RoundTo: integer = 0; const EmptyStr: string = '-'): string; function PriorityToStr(p: integer; var ImageIndex: integer): string; procedure DrawProgressCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const ACellRect: TRect); var MainForm: TMainForm; RpcObj: TRpc; FTranslationFileName: string; FTranslationLanguage: string; FAlterColor: TColor; IsUnity: boolean; Ini: TIniFileUtf8; const // Torrents list idxName = 0; idxSize = 1; idxDone = 2; idxStatus = 3; idxSeeds = 4; idxPeers = 5; idxDownSpeed = 6; idxUpSpeed = 7; idxETA = 8; idxRatio = 9; idxDownloaded = 10; idxUploaded = 11; idxTracker = 12; idxTrackerStatus = 13; idxAddedOn = 14; idxCompletedOn = 15; idxLastActive = 16; idxPath = 17; idxPriority = 18; idxSizeToDowload = 19; idxTorrentId = 20; idxQueuePos = 21; idxSeedingTime = 22; idxTag = -1; idxSeedsTotal = -2; idxLeechersTotal = -3; idxStateImg = -4; idxDeleted = -5; idxDownSpeedHistory = -6; idxUpSpeedHistory = -7; TorrentsExtraColumns = 7; // Peers list idxPeerHost = 0; idxPeerPort = 1; idxPeerCountry = 2; idxPeerClient = 3; idxPeerFlags = 4; idxPeerDone = 5; idxPeerUpSpeed = 6; idxPeerDownSpeed = 7; idxPeerTag = -1; idxPeerIP = -2; idxPeerCountryImage = -3; PeersExtraColumns = 3; // Trackers list idxTrackersListName = 0; idxTrackersListStatus = 1; idxTrackersListUpdateIn = 2; idxTrackersListSeeds = 3; idxTrackerTag = -1; idxTrackerID = -2; TrackersExtraColumns = 2; // Filter idices fltAll = 0; fltDown = 1; fltDone = 2; fltActive = 3; fltInactive = 4; fltStopped = 5; fltError = 6; // Status images imgDown = 9; imgSeed = 10; imgDownError = 11; imgSeedError = 12; imgError = 13; imgDone = 14; imgStopped = 29; imgDownQueue = 16; imgSeedQueue = 17; imgAll = 19; imgActive = 20; StatusFiltersCount = 7; TorrentFieldsMap: array[idxName..idxSeedingTime] of string = ('', 'totalSize', '', 'status', 'peersSendingToUs,seeders', 'peersGettingFromUs,leechers', '', '', 'eta', 'uploadRatio', 'downloadedEver', 'uploadedEver', '', '', 'addedDate', 'doneDate', 'activityDate', '', 'bandwidthPriority', '', '', 'queuePosition', 'secondsSeeding'); FinishedQueue = 1000000; TR_PRI_SKIP = -1000; // psedudo priority TR_PRI_LOW = -1; TR_PRI_NORMAL = 0; TR_PRI_HIGH = 1; implementation uses {$ifdef linux} process, dynlibs, {$endif linux} {$ifdef darwin} urllistenerosx, {$endif darwin} synacode, ConnOptions, clipbrd, DateUtils, TorrProps, DaemonOptions, About, ToolWin, download, ColSetup, types, AddLink, MoveTorrent, ssl_openssl_lib, AddTracker, lcltype, Options, ButtonPanel, BEncode, synautil; const TR_STATUS_CHECK_WAIT_1 = ( 1 shl 0 ); // Waiting in queue to check files TR_STATUS_CHECK_1 = ( 1 shl 1 ); // Checking files TR_STATUS_DOWNLOAD_1 = ( 1 shl 2 ); // Downloading TR_STATUS_SEED_1 = ( 1 shl 3 ); // Seeding TR_STATUS_STOPPED_1 = ( 1 shl 4 ); // Torrent is stopped TR_STATUS_STOPPED_2 = 0; // Torrent is stopped TR_STATUS_CHECK_WAIT_2 = 1; // Queued to check files TR_STATUS_CHECK_2 = 2; // Checking files TR_STATUS_DOWNLOAD_WAIT_2 = 3; // Queued to download TR_STATUS_DOWNLOAD_2 = 4; // Downloading TR_STATUS_SEED_WAIT_2 = 5; // Queued to seed TR_STATUS_SEED_2 = 6; // Seeding TR_STATUS_FINISHED = $100; // Torrent is finished (pseudo status) TR_SPEEDLIMIT_GLOBAL = 0; // only follow the overall speed limit TR_SPEEDLIMIT_SINGLE = 1; // only follow the per-torrent limit TR_SPEEDLIMIT_UNLIMITED = 2; // no limits at all SpeedHistorySize = 20; const SizeNames: array[1..5] of string = (sByte, sKByte, sMByte, sGByte, sTByte); var TR_STATUS_STOPPED, TR_STATUS_CHECK_WAIT, TR_STATUS_CHECK, TR_STATUS_DOWNLOAD_WAIT, TR_STATUS_DOWNLOAD, TR_STATUS_SEED_WAIT, TR_STATUS_SEED: integer; function GetHumanSize(sz: double; RoundTo: integer; const EmptyStr: string): string; var i: integer; begin if sz < 0 then begin Result:=EmptyStr; exit; end; i:=Low(SizeNames); if RoundTo > 0 then begin Inc(i); sz:=sz/1024; end; while i <= High(SizeNames) do begin if sz < 1024 then break; sz:=sz/1024; Inc(i); end; if (RoundTo = 0) and (i > 3) then RoundTo:=i - 2; Result:=Format('%.' + IntToStr(RoundTo) + 'f %s', [sz, SizeNames[i]]); end; function AddToChannel(Clr: TColor; Value: integer; Position: byte): TColor; var i: integer; begin i:=(Clr shr (Position*8)) and $FF; i:=i + Value; if i < 0 then i:=0; if i > $FF then i:=$FF; Result:=Clr and (not (Cardinal($FF) shl (Position*8))) or (Cardinal(i) shl (Position*8)); end; function AddToColor(Color: TColor; R, G, B: integer): TColor; begin Result:=ColorToRGB(Color); Result:=AddToChannel(Result, R, 0); Result:=AddToChannel(Result, G, 1); Result:=AddToChannel(Result, B, 2); end; function GetLikeColor(Color: TColor; Delta: integer): TColor; var i, j: integer; begin Result:=ColorToRGB(Color); j:=Result and $FF; //red i:=(Result shr 8) and $FF; // green if i > j then j:=i; i:=((Result shr 16) and $FF) shr 1; // blue if i > j then j:=i; if j < $80 then i:=(($80 - j) div $20 + 1)*Delta else i:=Delta; if (i + j > 255) or (i + j < 0) then i:=-Delta; Result:=AddToColor(Result, i, i, i); end; function LocateFile(const FileName: string; const Paths: array of string): string; var i: integer; begin for i:=Low(Paths) to High(Paths) do begin Result:=IncludeTrailingPathDelimiter(Paths[i]) + FileName; if FileExistsUTF8(Result) then exit; end; Result:=''; end; procedure OnTranslate(Sender: TResTranslator; const ResourceName: AnsiString; var Accept: boolean); const IgnoreUnits: array[0..12] of string = ('fpjson','jsonparser','jsonscanner','lclstrconsts','math', 'rtlconsts','sysconst','variants','zbase','zipper','zstream', 'xmlcfg', 'registry'); IgnoreControls: array[0..3] of string = ('AboutForm.txAuthor', 'MainForm.miLn', 'ConnOptionsForm.cbUseSocks5', 'ConnOptionsForm.tabConnection'); var i: integer; begin Accept := not AnsiMatchText(Copy2Symb(ResourceName, '.'), IgnoreUnits) or AnsiStartsText('lclstrconsts.rsMb', ResourceName) //<-- dialog buttons or AnsiStartsText('lclstrconsts.rsMt', ResourceName); //<-- dialog message if Accept then for i:=Low(IgnoreControls) to High(IgnoreControls) do if AnsiStartsText(IgnoreControls[i], ResourceName) then begin Accept:=False; exit; end; if Accept and (Copy(ResourceName, Length(ResourceName) - 8, MaxInt) = '.Category') then Accept:=False; end; var FHomeDir: string; FIPCFileName: string; FRunFileName: string; function IsProtocolSupported(const url: string): boolean; const Protocols: array [1..3] of string = ('http:', 'https:', 'magnet:'); var i: integer; s: string; begin s:=AnsiLowerCase(url); for i:=Low(Protocols) to High(Protocols) do if Copy(s, 1, Length(Protocols[i])) = Protocols[i] then begin Result:=True; exit; end; Result:=False; end; procedure AddTorrentFile(const FileName: string); var h: System.THandle; t: TDateTime; s: string; begin if not IsProtocolSupported(FileName) and not FileExistsUTF8(FileName) then exit; t:=Now; repeat if FileExistsUTF8(FIPCFileName) then h:=FileOpenUTF8(FIPCFileName, fmOpenWrite or fmShareDenyRead or fmShareDenyWrite) else h:=FileCreateUTF8(FIPCFileName); if h <> System.THandle(-1) then begin s:=FileName + LineEnding; FileSeek(h, 0, soFromEnd); FileWrite(h, s[1], Length(s)); FileClose(h); break; end; Sleep(20); until Now - t >= 3/SecsPerDay; end; procedure LoadTranslation; begin FTranslationFileName := Ini.ReadString('Interface', 'TranslationFile', ''); if FTranslationFileName <> '-' then if (FTranslationFileName = '') or not IsTranslationFileValid(DefaultLangDir + FTranslationFileName) then FTranslationLanguage := LoadDefaultTranslationFile(@OnTranslate) else FTranslationLanguage := LoadTranslationFile(DefaultLangDir + FTranslationFileName, @OnTranslate); if FTranslationLanguage = '' then FTranslationLanguage := 'English' end; function CheckAppParams: boolean; var i: integer; s: string; h: System.THandle; pid: SizeUInt; {$ifdef linux} proc: TProcess; sr: TSearchRec; hLib: TLibHandle; {$endif linux} begin Application.Title:=AppName; {$ifdef linux} IsUnity:=CompareText(GetEnvironmentVariable('XDG_CURRENT_DESKTOP'), 'unity') = 0; if GetEnvironmentVariable('LIBOVERLAY_SCROLLBAR') <> '0' then begin i:=FindFirstUTF8('/usr/lib/liboverlay-scrollbar*', faAnyFile, sr); FindClose(sr); hLib:=LoadLibrary('liboverlay-scrollbar.so'); if hLib <> 0 then FreeLibrary(hLib); if (i = 0) or (hLib <> 0) then begin // Turn off overlay scrollbars, since they are not supported yet. // Restart the app with the LIBOVERLAY_SCROLLBAR=0 env var. proc:=TProcess.Create(nil); try s:=''; for i:=0 to ParamCount do s:=s + '"' + ParamStrUTF8(i) + '" '; proc.CommandLine:=s; for i:=0 to GetEnvironmentVariableCount - 1 do proc.Environment.Add(GetEnvironmentString(i)); proc.Environment.Values['LIBOVERLAY_SCROLLBAR']:='0'; proc.Execute; finally proc.Free; end; Result:=False; exit; end; end; {$endif linux} FHomeDir:=GetCmdSwitchValue('home'); if FHomeDir = '' then begin if FileExistsUTF8(ChangeFileExt(ParamStrUTF8(0), '.ini')) then FHomeDir:=ExtractFilePath(ParamStrUTF8(0)) // Portable mode else FHomeDir:=IncludeTrailingPathDelimiter(GetAppConfigDirUTF8(False)); end else FHomeDir:=IncludeTrailingPathDelimiter(FHomeDir); ForceDirectoriesUTF8(FHomeDir); FIPCFileName:=FHomeDir + 'ipc.txt'; FRunFileName:=FHomeDir + 'run'; Ini:=TIniFileUtf8.Create(FHomeDir+ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.ini')); Ini.CacheUpdates:=True; // Check for outdated IPC file if FileExistsUTF8(FIPCFileName) then begin h:=FileOpenUTF8(FIPCFileName, fmOpenRead or fmShareDenyNone); if h <> INVALID_HANDLE_VALUE then begin i:=FileGetDate(h); FileClose(h); if (i > 0) and (Abs(Now - FileDateToDateTime(i)) > 1/MinsPerDay) then DeleteFileUTF8(FIPCFileName); end; end; for i:=1 to ParamCount do begin s:=ParamStrUTF8(i); if IsProtocolSupported(s) or FileExistsUTF8(s) then AddTorrentFile(s); end; if FileExistsUTF8(FRunFileName) then begin // Another process is running h:=FileOpenUTF8(FRunFileName, fmOpenRead or fmShareDenyNone); if FileRead(h, pid, SizeOf(pid)) = SizeOf(pid) then begin {$ifdef mswindows} AllowSetForegroundWindow(pid); {$endif mswindows} end; FileClose(h); if not FileExistsUTF8(FIPCFileName) then FileClose(FileCreateUTF8(FIPCFileName)); for i:=1 to 50 do if not FileExistsUTF8(FIPCFileName) then begin // The running process works normally. Exit application. Result:=False; exit; end else Sleep(200); // The running process is not responding DeleteFileUTF8(FRunFileName); // Delete IPC file if it is empty h:=FileOpenUTF8(FIPCFileName, fmOpenRead or fmShareDenyNone); i:=FileSeek(h, 0, soFromEnd); FileClose(h); if i = 0 then DeleteFileUTF8(FIPCFileName); end; // Create a new run file h:=FileCreateUTF8(FRunFileName); pid:=GetProcessID; FileWrite(h, pid, SizeOf(pid)); FileClose(h); LoadTranslation; SizeNames[1]:=sByte; SizeNames[2]:=sKByte; SizeNames[3]:=sMByte; SizeNames[4]:=sGByte; SizeNames[5]:=sTByte; IntfScale:=Ini.ReadInteger('Interface', 'Scaling', 100); Result:=True; end; function PriorityToStr(p: integer; var ImageIndex: integer): string; begin case p of TR_PRI_SKIP: begin Result:=sSkip; ImageIndex:=23; end; TR_PRI_LOW: begin Result:=sLow; ImageIndex:=24; end; TR_PRI_NORMAL: begin Result:=sNormal; ImageIndex:=25; end; TR_PRI_HIGH: begin Result:=sHigh; ImageIndex:=26; end; else Result:='???'; end; end; procedure DrawProgressCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const ACellRect: TRect); var R, RR: TRect; i, j, h: integer; s: string; cl: TColor; Progress: double; sz: TSize; ts: TTextStyle; begin Progress:=double(Sender.Items[ADataCol, ARow]); with Sender.Canvas do begin R:=ACellRect; FrameRect(R); s:=Format('%.1f%%', [Progress]); sz:=TextExtent(s); InflateRect(R, -1, -1); Pen.Color:=clBtnFace; Rectangle(R); InflateRect(R, -1, -1); i:=R.Left + Round(Progress*(R.Right - R.Left)/100.0); j:=(R.Top + R.Bottom) div 2; h:=(R.Top + R.Bottom - sz.cy) div 2; cl:=GetLikeColor(clHighlight, 70); GradientFill(Rect(R.Left, R.Top, i, j), cl, clHighlight, gdVertical); GradientFill(Rect(R.Left, j, i, R.Bottom), clHighlight, cl, gdVertical); ts:=TextStyle; ts.Layout:=tlTop; ts.Alignment:=taLeftJustify; ts.Wordbreak:=False; TextStyle:=ts; j:=(R.Left + R.Right - sz.cx) div 2; if i > R.Left then begin RR:=Rect(R.Left, R.Top, i, R.Bottom); Font.Color:=clHighlightText; TextRect(RR, j, h, s); end; if i < R.Right then begin RR:=Rect(i, R.Top, R.Right, R.Bottom); Brush.Color:=Sender.Color; FillRect(RR); Font.Color:=clWindowText; TextRect(RR, j, h, s); end; end; end; { TProgressImage } procedure TProgressImage.SetImages(const AValue: TImageList); begin if FImages=AValue then exit; FImages:=AValue; Width:=FImages.Width; Height:=FImages.Height; end; procedure TProgressImage.SetStartIndex(const AValue: integer); begin if FStartIndex=AValue then exit; FStartIndex:=AValue; UpdateIndex; end; procedure TProgressImage.UpdateIndex; begin if (FImageIndex < FStartIndex) or (FImageIndex > FEndIndex) then FImageIndex:=FStartIndex; Invalidate; end; procedure TProgressImage.DoTimer(Sender: TObject); begin ImageIndex:=ImageIndex + 1; end; procedure TProgressImage.SetImageIndex(const AValue: integer); begin if FImageIndex=AValue then exit; FImageIndex:=AValue; UpdateIndex; end; procedure TProgressImage.SetEndIndex(const AValue: integer); begin if FEndIndex=AValue then exit; FEndIndex:=AValue; UpdateIndex; end; function TProgressImage.GetFrameDelay: integer; begin Result:=FTimer.Interval; end; procedure TProgressImage.SetBorderColor(const AValue: TColor); begin if FBorderColor=AValue then exit; FBorderColor:=AValue; end; procedure TProgressImage.SetFrameDelay(const AValue: integer); begin FTimer.Interval:=AValue; end; procedure TProgressImage.Paint; begin if FBmp = nil then begin FBmp:=TBitmap.Create; FBmp.Width:=Width; FBmp.Height:=Height; end; with FBmp.Canvas do begin Brush.Color:=clBtnFace; if FBorderColor <> clNone then begin Pen.Color:=FBorderColor; Rectangle(0, 0, FBmp.Width, FBmp.Height); end else FillRect(0, 0, FBmp.Width, FBmp.Height); if FImages <> nil then FImages.Draw(FBmp.Canvas, (Self.Width - FImages.Width) div 2, (Self.Height - FImages.Height) div 2, ImageIndex); end; Canvas.Draw(0, 0, FBmp); end; procedure TProgressImage.VisibleChanged; begin inherited VisibleChanged; if Visible then begin ImageIndex:=StartIndex; FTimer.Enabled:=True; end else FTimer.Enabled:=False; end; constructor TProgressImage.Create(AOwner: TComponent); begin inherited Create(AOwner); FTimer:=TTimer.Create(Self); FTimer.Enabled:=False; FTimer.Interval:=100; FTimer.OnTimer:=@DoTimer; FBorderColor:=clNone; Visible:=False; end; destructor TProgressImage.Destroy; begin FBmp.Free; inherited Destroy; end; { TMainForm } procedure TMainForm.FormCreate(Sender: TObject); var ws: TWindowState; i, j: integer; R: TRect; {$ifdef darwin} s: string; pic: TPicture; {$endif darwin} begin {$ifdef darwin} // Load better icon if possible s:=ExtractFilePath(ParamStrUTF8(0)) + '..' + DirectorySeparator + 'Resources' + DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.icns'); if FileExistsUTF8(s) then begin pic:=TPicture.Create; try pic.LoadFromFile(s); try Application.Icon.Assign(pic.Graphic); except end; finally pic.Free; end; end; RegisterURLHandler(@AddTorrentFile); {$endif darwin} Application.Title:=AppName; Caption:=Application.Title; txTransferHeader.Font.Size:=Font.Size + 2; txTorrentHeader.Font.Size:=txTransferHeader.Font.Size; TrayIcon.Icon.Assign(Application.Icon); RpcObj:=TRpc.Create; FTorrents:=TVarList.Create(gTorrents.Columns.Count, 0); FTorrents.ExtraColumns:=TorrentsExtraColumns; gTorrents.Items.ExtraColumns:=TorrentsExtraColumns; lvFiles.Items.ExtraColumns:=FilesExtraColumns; FFiles:=lvFiles.Items; FFilesTree:=TFilesTree.Create(lvFiles); FFilesTree.Checkboxes:=True; FFilesTree.OnStateChange:=@FilesTreeStateChanged; lvPeers.Items.ExtraColumns:=PeersExtraColumns; lvTrackers.Items.ExtraColumns:=TrackersExtraColumns; FTrackers:=TStringList.Create; FTrackers.Sorted:=True; FReconnectTimeOut:=-1; FAlterColor:=GetLikeColor(gTorrents.Color, -$10); lvFilter.Items.ExtraColumns:=1; gTorrents.AlternateColor:=FAlterColor; lvPeers.AlternateColor:=FAlterColor; lvTrackers.AlternateColor:=FAlterColor; gStats.AlternateColor:=FAlterColor; FPendingTorrents:=TStringList.Create; FFilesCapt:=tabFiles.Caption; FPasswords:=TStringList.Create; FSlowResponse:=TProgressImage.Create(MainToolBar); with FSlowResponse do begin Align:=alRight; Images:=ImageList16; StartIndex:=30; EndIndex:=37; Width:=ScaleInt(24); Left:=MainToolBar.ClientWidth; Parent:=MainToolBar; end; FDetailsWait:=TProgressImage.Create(panDetailsWait); with FDetailsWait do begin Images:=ImageList16; StartIndex:=FSlowResponse.StartIndex; EndIndex:=FSlowResponse.EndIndex; Width:=Images.Width*2; Height:=Width; BorderColor:=clBtnShadow; panDetailsWait.Width:=Width; panDetailsWait.Height:=Height; Parent:=panDetailsWait; end; DoDisconnect; PageInfo.ActivePageIndex:=0; PageInfoChange(nil); {$ifdef LCLgtk2} with MainToolBar do begin EdgeBorders:=[ebLeft, ebTop, ebRight, ebBottom]; EdgeInner:=esNone; EdgeOuter:=esRaised; Flat:=True; end; i:=acAltSpeed.ImageIndex; acAltSpeed.ImageIndex:=-1; tbtAltSpeed.ImageIndex:=i; {$endif} txTransferHeader.Color:=GetLikeColor(clBtnFace, -15); txTorrentHeader.Color:=txTransferHeader.Color; txTransferHeader.Caption:=' ' + txTransferHeader.Caption; txTorrentHeader.Caption:=' ' + txTorrentHeader.Caption; txTransferHeader.Height:=txTransferHeader.Canvas.TextHeight(txTransferHeader.Caption) + 2; txTorrentHeader.Height:=txTorrentHeader.Canvas.TextHeight(txTorrentHeader.Caption) + 2; with gStats do begin BeginUpdate; try Items[0, 0]:=UTF8Decode(SDownloaded); Items[0, 1]:=UTF8Decode(SUploaded); Items[0, 2]:=UTF8Decode(SFilesAdded); Items[0, 3]:=UTF8Decode(SActiveTime); finally EndUpdate; end; end; if Ini.ReadInteger('MainForm', 'State', -1) = -1 then begin R:=Screen.MonitorFromRect(BoundsRect).WorkareaRect; if R.Right - R.Left < 300 then R:=Rect(0, 0, Screen.Width, Screen.Height); j:=R.Right - R.Left; i:=j*3 div 4; j:=j*95 div 100; if i > Width then Width:=i; if Width > j then Width:=j; Left:=(R.Right - R.Left - Width) div 2; j:=R.Bottom - R.Top; i:=j*3 div 4; j:=j*8 div 10; if i > Height then Height:=i; if Height > j then Height:=j; Top:=(R.Bottom - R.Top - Height) div 2; end else begin ws:=TWindowState(Ini.ReadInteger('MainForm', 'State', integer(WindowState))); Left:=Ini.ReadInteger('MainForm', 'Left', Left); Top:=Ini.ReadInteger('MainForm', 'Top', Top); Width:=Ini.ReadInteger('MainForm', 'Width', Width); Height:=Ini.ReadInteger('MainForm', 'Height', Height); if ws = wsMaximized then WindowState:=wsMaximized; end; if Ini.ReadBool('MainForm', 'FilterPane', acFilterPane.Checked) <> acFilterPane.Checked then acFilterPane.Execute; if Ini.ReadBool('MainForm', 'InfoPane', acInfoPane.Checked) <> acInfoPane.Checked then acInfoPane.Execute; if Ini.ReadBool('MainForm', 'StatusBar', acStatusBar.Checked) <> acStatusBar.Checked then acStatusBar.Execute; LoadColumns(gTorrents, 'TorrentsList'); TorrentColumnsChanged; LoadColumns(lvFiles, 'FilesList'); LoadColumns(lvPeers, 'PeerList'); LoadColumns(lvTrackers, 'TrackersList'); acResolveHost.Checked:=Ini.ReadBool('PeersList', 'ResolveHost', True); acResolveCountry.Checked:=Ini.ReadBool('PeersList', 'ResolveCountry', True) and (GetGeoIpDatabase <> ''); acShowCountryFlag.Checked:=Ini.ReadBool('PeersList', 'ShowCountryFlag', True) and (GetFlagsArchive <> ''); acShowCountryFlag.Enabled:=acResolveCountry.Checked; FCurConn:=Ini.ReadString('Hosts', 'CurHost', ''); if FCurConn = '' then FCurConn:=Ini.ReadString('Connection', 'Host', ''); FPathMap:=TStringList.Create; if Application.HasOption('hidden') then begin ApplicationProperties.ShowMainForm:=False; TickTimer.Enabled:=True; UpdateTray; end; UpdateConnections; i:=Ini.ReadInteger('Interface', 'LastRpcVersion', -1); if i >= 0 then UpdateUIRpcVersion(i); acFolderGrouping.Checked:=Ini.ReadBool('Interface', 'FolderGrouping', True); acTrackerGrouping.Checked:=Ini.ReadBool('Interface', 'TrackerGrouping', True); FLinksFromClipboard:=Ini.ReadBool('Interface', 'LinksFromClipboard', True); Application.OnActivate:=@FormActivate; Application.OnException:=@ApplicationPropertiesException; end; procedure TMainForm.FormDestroy(Sender: TObject); procedure _CreateAllForms; begin // Create all application forms to properly update language files TAboutForm.Create(Self).Free; TAddLinkForm.Create(Self).Free; TAddTorrentForm.Create(Self).Free; TAddTrackerForm.Create(Self).Free; TColSetupForm.Create(Self).Free; TConnOptionsForm.Create(Self).Free; TDaemonOptionsForm.Create(Self).Free; TDownloadForm.Create(Self).Free; TMoveTorrentForm.Create(Self).Free; TOptionsForm.Create(Self).Free; TTorrPropsForm.Create(Self).Free; end; begin if Application.HasOption('updatelang') then begin _CreateAllForms; SupplementTranslationFiles; end; if Application.HasOption('makelang') then begin _CreateAllForms; MakeTranslationFile; end; DeleteFileUTF8(FRunFileName); FPasswords.Free; FResolver.Free; FTrackers.Free; FUnZip.Free; RpcObj.Free; FTorrentProgress.Free; FPathMap.Free; FTorrents.Free; FPendingTorrents.Free; try Ini.UpdateFile; except end; end; procedure TMainForm.FormResize(Sender: TObject); begin if panReconnect.Visible then CenterReconnectWindow; end; procedure TMainForm.FormShow(Sender: TObject); begin if not FMainFormShown then begin FMainFormShown:=True; VSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'VSplitter', VSplitter.GetSplitterPosition)); HSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'HSplitter', HSplitter.GetSplitterPosition)); MakeFullyVisible; end; if not FStarted then TickTimer.Enabled:=True; UpdateTray; end; procedure TMainForm.lvFilterResize(Sender: TObject); begin lvFilter.Columns[0].Width:=lvFilter.ClientWidth; end; procedure TMainForm.miAboutClick(Sender: TObject); begin with TAboutForm.Create(Self) do try ShowModal; finally Free; end; end; procedure TMainForm.miCopyLabelClick(Sender: TObject); begin with TLabel(pmLabels.PopupComponent) do if (Length(Name) > 5) and (Copy(Name, Length(Name) - 4, 5) = 'Label') then Clipboard.AsText:=TLabel(Parent.FindChildControl(Copy(Name, 1, Length(Name) - 5))).Caption else Clipboard.AsText:=Caption; end; procedure TMainForm.acConnectExecute(Sender: TObject); begin if RpcObj.Connected then begin tbConnect.CheckMenuDropdown; exit; end; if FCurConn = '' then ShowConnOptions(True) else DoConnect; end; procedure TMainForm.acConnOptionsExecute(Sender: TObject); begin ShowConnOptions(False); end; procedure TMainForm.acCopyPathExecute(Sender: TObject); begin if lvFiles.Items.Count > 0 then Clipboard.AsText:='"' + FFilesTree.GetFullPath(lvFiles.Row) + '"'; end; procedure TMainForm.acDelTrackerExecute(Sender: TObject); var req, args: TJSONObject; id, torid: integer; begin id:=lvTrackers.Items[idxTrackerID, lvTrackers.Row]; torid:=RpcObj.CurTorrentId; if MessageDlg('', Format(SRemoveTracker, [UTF8Encode(widestring(lvTrackers.Items[idxTrackersListName, lvTrackers.Row]))]), mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then exit; AppBusy; Self.Update; req:=TJSONObject.Create; try req.Add('method', 'torrent-set'); args:=TJSONObject.Create; args.Add('ids', TJSONArray.Create([torid])); args.Add('trackerRemove', TJSONArray.Create([id])); req.Add('arguments', args); args:=nil; args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; DoRefresh; AppNormal; end; procedure TMainForm.acEditTrackerExecute(Sender: TObject); begin AddTracker(True); end; procedure TMainForm.acFilterPaneExecute(Sender: TObject); begin acFilterPane.Checked:=not acFilterPane.Checked; panFilter.Visible:=acFilterPane.Checked; HSplitter.Visible:=acFilterPane.Checked; HSplitter.Left:=panFilter.Width; if lvFilter.Items.Count > 0 then lvFilter.Row:=0; end; procedure TMainForm.acFolderGroupingExecute(Sender: TObject); begin acFolderGrouping.Checked:=not acFolderGrouping.Checked; Ini.WriteBool('Interface', 'FolderGrouping', acFolderGrouping.Checked); RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtTorrents]; end; procedure TMainForm.acForceStartTorrentExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'torrent-start-now'); end; procedure TMainForm.acHideAppExecute(Sender: TObject); begin HideApp; end; procedure TMainForm.acInfoPaneExecute(Sender: TObject); begin acInfoPane.Checked:=not acInfoPane.Checked; PageInfo.Visible:=acInfoPane.Checked; VSplitter.Top:=PageInfo.Top - VSplitter.Height; VSplitter.Visible:=acInfoPane.Checked; if VSplitter.Visible then PageInfoChange(nil) else RpcObj.AdvInfo:=aiNone; end; procedure TMainForm.acMoveTorrentExecute(Sender: TObject); var ids: variant; i: integer; s: string; req: TJSONObject; aids: TJSONArray; args: TJSONObject; ok: boolean; t: TDateTime; begin if gTorrents.Items.Count = 0 then exit; AppBusy; with TMoveTorrentForm.Create(Self) do try gTorrents.Tag:=1; gTorrents.EnsureSelectionVisible; FillDownloadDirs(edTorrentDir, 'LastMoveDir'); if gTorrents.SelCount = 0 then gTorrents.RowSelected[gTorrents.Row]:=True; ids:=GetSelectedTorrents; i:=gTorrents.Items.IndexOf(idxTorrentId, ids[0]); if VarIsEmpty(gTorrents.Items[idxPath, i]) then exit; edTorrentDir.Text:=UTF8Encode(widestring(gTorrents.Items[idxPath, i])); if gTorrents.SelCount > 1 then s:=Format(sSeveralTorrents, [gTorrents.SelCount]) else s:=UTF8Encode(widestring(gTorrents.Items[idxName, i])); Caption:=Caption + ' - ' + s; AppNormal; if ShowModal = mrOk then begin Application.ProcessMessages; AppBusy; req:=TJSONObject.Create; try req.Add('method', 'torrent-set-location'); args:=TJSONObject.Create; aids:=TJSONArray.Create; for i:=VarArrayLowBound(ids, 1) to VarArrayHighBound(ids, 1) do aids.Add(integer(ids[i])); args.Add('ids', aids); args.Add('location', TJSONString.Create(UTF8Decode(edTorrentDir.Text))); args.Add('move', TJSONIntegerNumber.Create(integer(cbMoveData.Checked) and 1)); req.Add('arguments', args); args:=RpcObj.SendRequest(req, False); args.Free; finally req.Free; end; gTorrents.Tag:=0; AppNormal; if args = nil then CheckStatus(False) else begin SaveDownloadDirs(edTorrentDir, 'LastMoveDir'); ok:=False; t:=Now; with gTorrents do while not ok and not Application.Terminated and (Now - t < 20/SecsPerDay) do begin RpcObj.RequestFullInfo:=True; DoRefresh(True); Sleep(200); Application.ProcessMessages; ok:=True; for i:=0 to Items.Count - 1 do if RowSelected[i] then begin if VarIsEmpty(Items[idxPath, i]) or (AnsiCompareText(UTF8Encode(widestring(Items[idxPath, i])), edTorrentDir.Text) <> 0) then begin ok:=False; break; end; end; end; end; end; finally gTorrents.Tag:=0; Free; end; end; procedure TMainForm.acNewConnectionExecute(Sender: TObject); begin ShowConnOptions(True); end; procedure TMainForm.acOpenContainingFolderExecute(Sender: TObject); begin if gTorrents.Items.Count = 0 then exit; Application.ProcessMessages; if lvFiles.Focused and (lvFiles.Items.Count > 0) then begin AppBusy; ExecRemoteFile(FFilesTree.GetFullPath(lvFiles.Row), not FFilesTree.IsFolder(lvFiles.Row)); AppNormal; end else OpenCurrentTorrent(True); end; procedure TMainForm.acOpenFileExecute(Sender: TObject); begin if gTorrents.Items.Count = 0 then exit; Application.ProcessMessages; if lvFiles.Focused then begin if lvFiles.Items.Count = 0 then exit; ExecRemoteFile(FFilesTree.GetFullPath(lvFiles.Row), False); end else OpenCurrentTorrent(False); end; procedure TMainForm.acOptionsExecute(Sender: TObject); var OldCheckVer: boolean; begin AppBusy; with TOptionsForm.Create(Self) do try ConnForm.ActiveConnection:=FCurConn; edRefreshInterval.Value:=Ini.ReadInteger('Interface', 'RefreshInterval', 5); edRefreshIntervalMin.Value:=Ini.ReadInteger('Interface', 'RefreshIntervalMin', 20); cbCalcAvg.Checked:=FCalcAvg; {$ifndef darwin} cbTrayMinimize.Checked:=Ini.ReadBool('Interface', 'TrayMinimize', True); {$else} cbTrayMinimize.Enabled:=False; {$endif} cbTrayClose.Checked:=Ini.ReadBool('Interface', 'TrayClose', False); cbTrayIconAlways.Checked:=Ini.ReadBool('Interface', 'TrayIconAlways', True); cbTrayNotify.Checked:=Ini.ReadBool('Interface', 'TrayNotify', True); cbShowAddTorrentWindow.Checked:=Ini.ReadBool('Interface', 'ShowAddTorrentWindow', True); cbDeleteTorrentFile.Checked:=Ini.ReadBool('Interface', 'DeleteTorrentFile', False); cbLinksFromClipboard.Checked:=Ini.ReadBool('Interface', 'LinksFromClipboard', True); edIntfScale.Value:=Ini.ReadInteger('Interface', 'Scaling', 100); cbCheckNewVersion.Checked:=Ini.ReadBool('Interface', 'CheckNewVersion', False); edCheckVersionDays.Value:=Ini.ReadInteger('Interface', 'CheckNewVersionDays', 5); cbCheckNewVersionClick(nil); OldCheckVer:=cbCheckNewVersion.Checked; {$ifdef linux} if IsUnity then begin cbTrayIconAlways.Enabled:=False; cbTrayIconAlways.Checked:=False; cbTrayMinimize.Enabled:=False; cbTrayMinimize.Checked:=False; cbTrayNotify.Enabled:=False; cbTrayNotify.Checked:=False; end; {$endif linux} AppNormal; if ShowModal = mrOk then begin AppBusy; Ini.WriteInteger('Interface', 'RefreshInterval', edRefreshInterval.Value); Ini.WriteInteger('Interface', 'RefreshIntervalMin', edRefreshIntervalMin.Value); Ini.WriteBool('Interface', 'CalcAvg', cbCalcAvg.Checked); {$ifndef darwin} Ini.WriteBool('Interface', 'TrayMinimize', cbTrayMinimize.Checked); {$endif} Ini.WriteBool('Interface', 'TrayClose', cbTrayClose.Checked); Ini.WriteBool('Interface', 'TrayIconAlways', cbTrayIconAlways.Checked); Ini.WriteBool('Interface', 'TrayNotify', cbTrayNotify.Checked); Ini.WriteBool('Interface', 'ShowAddTorrentWindow', cbShowAddTorrentWindow.Checked); Ini.WriteBool('Interface', 'DeleteTorrentFile', cbDeleteTorrentFile.Checked); Ini.WriteBool('Interface', 'LinksFromClipboard', cbLinksFromClipboard.Checked); FLinksFromClipboard:=cbLinksFromClipboard.Checked; Ini.WriteInteger('Interface', 'Scaling', edIntfScale.Value); Ini.WriteBool('Interface', 'CheckNewVersion', cbCheckNewVersion.Checked); Ini.WriteInteger('Interface', 'CheckNewVersionDays', edCheckVersionDays.Value); if cbCheckNewVersion.Checked and not OldCheckVer then CheckNewVersion; Ini.UpdateFile; UpdateTray; AppNormal; with ConnForm do ConnectionSettingsChanged(ActiveConnection, ActiveSettingChanged); end; finally Free; end; end; procedure TMainForm.acAddTorrentExecute(Sender: TObject); begin if not OpenTorrentDlg.Execute then exit; FPendingTorrents.AddStrings(OpenTorrentDlg.Files); TickTimerTimer(nil); end; procedure TMainForm.acAddTrackerExecute(Sender: TObject); begin AddTracker(False); end; procedure TMainForm.acAdvEditTrackersExecute(Sender: TObject); begin gTorrents.RemoveSelection; TorrentProps(1); end; procedure TMainForm.acAltSpeedExecute(Sender: TObject); var req, args: TJSONObject; begin AppBusy; req:=TJSONObject.Create; try req.Add('method', 'session-set'); args:=TJSONObject.Create; args.Add('alt-speed-enabled', integer(not acAltSpeed.Checked) and 1); req.Add('arguments', args); args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession]; AppNormal; end; procedure TMainForm.acCheckNewVersionExecute(Sender: TObject); begin Application.ProcessMessages; AppBusy; CheckNewVersion(False); AppNormal; end; procedure TMainForm.acAddLinkExecute(Sender: TObject); begin AppBusy; with TAddLinkForm.Create(Self) do try AppNormal; if ShowModal = mrOk then DoAddTorrent(edLink.Text); finally Free; end; end; function TMainForm.DoAddTorrent(const FileName: Utf8String): boolean; var torrent: string; WaitForm: TBaseForm; IsAppHidden: boolean; Procedure _AddTrackers(TorrentId: integer); var req, args: TJSONObject; fs: TFileStreamUTF8; TorData, AnnData, LData, LLData: TBEncoded; t: TJSONArray; tt: TJSONObject; trackers: TJSONArray; i, j: Integer; s, tfn, TorrentHash: string; TrackersList: TStringList; begin RpcObj.Status:=''; s:=''; if TorrentId <> 0 then begin i:=SelectTorrent(TorrentId, 2000); if i >= 0 then s:=Format(': %s', [UTF8Encode(widestring(gTorrents.Items[idxName, i]))]); end; ForceAppNormal; s:=SDuplicateTorrent + s + '.'; if RpcObj.RPCVersion < 10 then begin MessageDlg(s, mtError, [mbOK], 0); exit; end; if MessageDlg(s + LineEnding + LineEnding + SUpdateTrackers, mtConfirmation, [mbYes, mbNo], 0) <> mrYes then exit; Application.ProcessMessages; TrackersList:=TStringList.Create; try TorrentHash:=''; if AnsiCompareText('magnet:?', Copy(FileName, 1, 8)) = 0 then begin // Get trackers from the magnet link TrackersList.Delimiter:='&'; TrackersList.DelimitedText:=Copy(FileName, 9, MaxInt); i:=0; while i < TrackersList.Count do begin s:=TrackersList.Names[i]; if (TorrentId = 0) and (CompareText(s, 'xt') = 0) then begin s:=LowerCase(TrackersList.ValueFromIndex[i]); if (TorrentHash = '') and (Pos('urn:btih:', s) = 1) then begin s:=Copy(s, 10, MaxInt); if Length(s) = 32 then // base32 encoded hash TorrentHash:=StrToHex(Base32Decode(UpperCase(s))) else TorrentHash:=s; end; end else if CompareText(s, 'tr') = 0 then begin TrackersList[i]:=DecodeURL(TrackersList.ValueFromIndex[i]); Inc(i); continue; end; TrackersList.Delete(i); end; end else try if IsProtocolSupported(FileName) then begin // Downloading torrent file tfn:=SysToUTF8(GetTempDir(True)) + 'remote-torrent.torrent'; if not DownloadFile(FileName, ExtractFilePath(tfn), ExtractFileName(tfn), SDownloadingTorrent) then exit; end else tfn:=FileName; // Read trackers from the torrent file... AppBusy; TorData:=nil; fs:=TFileStreamUTF8.Create(tfn, fmOpenRead or fmShareDenyNone); try TorData:=TBEncoded.Create(fs); if TorrentId = 0 then begin // Calculate torrent hash LData:=(TorData.ListData.FindElement('info') as TBEncoded); s:=''; LData.Encode(LData, s); TorrentHash:=StrToHex(SHA1(s)); end; AnnData:=(TorData.ListData.FindElement('announce-list', False) as TBEncoded); if AnnData <> nil then for i:=0 to AnnData.ListData.Count - 1 do begin LData:=AnnData.ListData.Items[i].Data as TBEncoded; for j:=0 to LData.ListData.Count - 1 do begin LLData:=LData.ListData.Items[j].Data as TBEncoded; TrackersList.Add(LLData.StringData); end; end else begin AnnData:=(TorData.ListData.FindElement('announce', False) as TBEncoded); if AnnData <> nil then TrackersList.Add(AnnData.StringData); end; finally TorData.Free; fs.Free; end; finally // Delete temp file if (tfn <> '') and (tfn <> FileName) then DeleteFileUTF8(tfn); end; // Request trackers from the existing torrent req:=TJSONObject.Create; try req.Add('method', 'torrent-get'); args:=TJSONObject.Create; if TorrentId = 0 then args.Add('ids', TJSONArray.Create([TorrentHash])) else args.Add('ids', TJSONArray.Create([TorrentId])); args.Add('fields', TJSONArray.Create(['id', 'trackers'])); req.Add('arguments', args); args:=RpcObj.SendRequest(req); if args = nil then begin CheckStatus(False); exit; end; try t:=args.Arrays['torrents']; if t.Count = 0 then raise Exception.Create('Torrent not found.'); tt:=t.Objects[0] as TJSONObject; i:=tt.Integers['id']; if TorrentId = 0 then SelectTorrent(i, 2000); TorrentId:=i; trackers:=tt.Arrays['trackers']; // Deleting existing trackers from the list for i:=0 to trackers.Count - 1 do begin s:=UTF8Encode((Trackers.Items[i] as TJSONObject).Strings['announce']); j:=TrackersList.IndexOf(s); if j >= 0 then TrackersList.Delete(j); end; finally args.Free; end; finally req.Free; end; if TrackersList.Count > 0 then begin trackers:=TJSONArray.Create; for i:=0 to TrackersList.Count - 1 do trackers.Add(TrackersList[i]); args:=TJSONObject.Create; args.Add('ids', TJSONArray.Create([TorrentId])); args.Add('trackerAdd', trackers); req:=TJSONObject.Create; try req.Add('method', 'torrent-set'); req.Add('arguments', args); args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; DoRefresh; end; finally TrackersList.Free; AppNormal; end; end; function _AddTorrent(args: TJSONObject): integer; var req: TJSONObject; begin Result:=0; req:=TJSONObject.Create; try req.Add('method', 'torrent-add'); if torrent = '-' then args.Add('filename', TJSONString.Create(FileName)) else args.Add('metainfo', TJSONString.Create(torrent)); req.Add('arguments', args); args:=RpcObj.SendRequest(req); if args <> nil then try if args.IndexOfName('torrent-duplicate') >= 0 then begin _AddTrackers(args.Objects['torrent-duplicate'].Integers['id']); exit; end; Result:=args.Objects['torrent-added'].Integers['id']; finally args.Free; end else if RpcObj.Status='duplicate torrent' then begin _AddTrackers(0); exit; end; finally req.Free; end; if Result = 0 then CheckStatus(False); end; procedure ShowWaitMsg(const AText: string); begin if WaitForm = nil then begin WaitForm:=TBaseForm.CreateNew(Self); with WaitForm do begin {$ifndef windows} if IsAppHidden then ShowInTaskBar:=stAlways; {$endif windows} Caption:=AppName; BorderStyle:=bsToolWindow; BorderIcons:=[]; Position:=poScreenCenter; Constraints.MinWidth:=300; AutoSize:=True; BorderWidth:=ScaleInt(16); with TLabel.Create(WaitForm) do begin Alignment:=taCenter; Align:=alClient; Parent:=WaitForm; end; end; end; with WaitForm do begin TLabel(Controls[0]).Caption:=AText + '...'; Show; BringToFront; {$ifdef lclgtk2} Application.ProcessMessages; {$endif lclgtk2} Update; {$ifdef lclgtk2} sleep(100); Application.ProcessMessages; {$endif lclgtk2} end; end; procedure HideWaitMsg; begin if WaitForm <> nil then FreeAndNil(WaitForm); end; var req, args: TJSONObject; id: integer; t, files: TJSONArray; i: integer; fs: TFileStreamUTF8; s, OldDownloadDir, IniSec, OldName: string; ok: boolean; begin Result:=False; if not RpcObj.Connected and not RpcObj.Connecting then if not DoConnect then exit; WaitForm:=nil; id:=0; Inc(FAddingTorrent); try AppBusy; try IsAppHidden:=not Self.Visible or (Self.WindowState = wsMinimized); with TAddTorrentForm.Create(Self) do try if IsAppHidden then begin ShowWaitMsg(Caption); {$ifndef windows} ShowInTaskBar:=stAlways; {$endif windows} end; Application.ProcessMessages; if IsProtocolSupported(FileName) then torrent:='-' else begin fs:=TFileStreamUTF8.Create(FileName, fmOpenRead or fmShareDenyNone); try SetLength(torrent, fs.Size); fs.ReadBuffer(PChar(torrent)^, Length(torrent)); finally fs.Free; end; torrent:=EncodeBase64(torrent); end; IniSec:='AddTorrent.' + FCurConn; FillDownloadDirs(cbDestFolder, 'LastDownloadDir'); req:=TJSONObject.Create; try req.Add('method', 'session-get'); args:=RpcObj.SendRequest(req); if args = nil then begin CheckStatus(False); exit; end; s:=UTF8Encode(args.Strings['download-dir']); if cbDestFolder.Items.IndexOf(s) < 0 then begin cbDestFolder.Items.Insert(0, s); cbDestFolder.ItemIndex:=0; end; if RpcObj.RPCVersion < 15 then if args.IndexOfName('download-dir-free-space') >= 0 then txDiskSpace.Caption:=txDiskSpace.Caption + ' ' + GetHumanSize(args.Floats['download-dir-free-space']) else begin txDiskSpace.Hide; txSize.Top:=(txSize.Top + txDiskSpace.Top) div 2; end; args.Free; finally req.Free; end; lvFilter.Row:=0; edSearch.Text:=''; args:=TJSONObject.Create; args.Add('paused', TJSONIntegerNumber.Create(1)); i:=Ini.ReadInteger(IniSec, 'PeerLimit', 0); if i <> 0 then args.Add('peer-limit', TJSONIntegerNumber.Create(i)); args.Add('download-dir', TJSONString.Create(UTF8Decode(cbDestFolder.Text))); id:=_AddTorrent(args); if id = 0 then exit; DoRefresh(True); args:=RpcObj.RequestInfo(id, ['files','maxConnectedPeers','name','metadataPercentComplete']); if args = nil then begin CheckStatus(False); exit; end; try t:=args.Arrays['torrents']; if t.Count = 0 then raise Exception.Create(sUnableGetFilesList); OldName:=UTF8Encode(t.Objects[0].Strings['name']); edSaveAs.Caption:=OldName; if RpcObj.RPCVersion < 15 then begin edSaveAs.Enabled:=False; edSaveAs.ParentColor:=True; end; edPeerLimit.Value:=t.Objects[0].Integers['maxConnectedPeers']; FilesTree.FillTree(id, t.Objects[0].Arrays['files'], nil, nil); Width:=Ini.ReadInteger('AddTorrent', 'Width', Width); if (RpcObj.RPCVersion >= 7) and (lvFiles.Items.Count = 0) and (t.Objects[0].Floats['metadataPercentComplete'] <> 1.0) then begin // Magnet link gbContents.Hide; gbSaveAs.BorderSpacing.Bottom:=gbSaveAs.BorderSpacing.Top; BorderStyle:=bsDialog; AutoSizeForm(TCustomForm(gbContents.Parent)); edSaveAs.Enabled:=False; edSaveAs.ParentColor:=True; end else // Torrent file Height:=Ini.ReadInteger('AddTorrent', 'Height', Height); finally args.Free; end; OldDownloadDir:=cbDestFolder.Text; AppNormal; ok:=not Ini.ReadBool('Interface', 'ShowAddTorrentWindow', True); if ok then btSelectAllClick(nil) else begin HideWaitMsg; ok:=ShowModal = mrOk; if BorderStyle = bsSizeable then begin Ini.WriteInteger('AddTorrent', 'Width', Width); Ini.WriteInteger('AddTorrent', 'Height', Height); end; end; if ok then begin if IsAppHidden then ShowWaitMsg(Caption); AppBusy; Self.Update; if OldDownloadDir <> cbDestFolder.Text then begin TorrentAction(id, 'torrent-remove'); id:=0; args:=TJSONObject.Create; args.Add('paused', TJSONIntegerNumber.Create(1)); args.Add('peer-limit', TJSONIntegerNumber.Create(edPeerLimit.Value)); args.Add('download-dir', TJSONString.Create(UTF8Decode(cbDestFolder.Text))); id:=_AddTorrent(args); if id = 0 then exit; DoRefresh(True); Application.ProcessMessages; end; req:=TJSONObject.Create; try req.Add('method', 'torrent-set'); args:=TJSONObject.Create; args.Add('ids', TJSONArray.Create([id])); args.Add('peer-limit', TJSONIntegerNumber.Create(edPeerLimit.Value)); files:=TJSONArray.Create; for i:=0 to lvFiles.Items.Count - 1 do if not FilesTree.IsFolder(i) and (FilesTree.Checked[i] = cbChecked) then files.Add(integer(lvFiles.Items[idxFileId, i])); if files.Count > 0 then args.Add('files-wanted', files) else files.Free; files:=TJSONArray.Create; for i:=0 to lvFiles.Items.Count - 1 do if not FilesTree.IsFolder(i) and (FilesTree.Checked[i] <> cbChecked) then files.Add(integer(lvFiles.Items[idxFileId, i])); if files.Count > 0 then args.Add('files-unwanted', files) else files.Free; req.Add('arguments', args); args:=nil; args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; edSaveAs.Text:=Trim(edSaveAs.Text); if OldName <> edSaveAs.Text then begin // Changing torrent name req.Free; req:=TJSONObject.Create; req.Add('method', 'torrent-rename-path'); args:=TJSONObject.Create; args.Add('ids', TJSONArray.Create([id])); args.Add('path', UTF8Decode(OldName)); args.Add('name', UTF8Decode(edSaveAs.Text)); req.Add('arguments', args); args:=nil; args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; end; finally req.Free; end; if cbStartTorrent.Checked then TorrentAction(id, 'torrent-start'); SelectTorrent(id, 2000); id:=0; if Ini.ReadBool('Interface', 'DeleteTorrentFile', False) and not IsProtocolSupported(FileName) then DeleteFileUTF8(FileName); Ini.WriteInteger(IniSec, 'PeerLimit', edPeerLimit.Value); SaveDownloadDirs(cbDestFolder, 'LastDownloadDir'); Result:=True; AppNormal; end; finally Free; end; finally if id <> 0 then TorrentAction(id, 'torrent-remove'); end; finally HideWaitMsg; Dec(FAddingTorrent); end; end; procedure TMainForm.UpdateTray; begin TrayIcon.Visible:=not IsUnity and ( Ini.ReadBool('Interface', 'TrayIconAlways', True) or ( (WindowState = wsMinimized) and Ini.ReadBool('Interface', 'TrayMinimize', True) ) or ( not Self.Visible and Ini.ReadBool('Interface', 'TrayClose', False) ) ); {$ifdef darwin} acShowApp.Visible:=False; acHideApp.Visible:=False; miTSep1.Visible:=False; {$else} acHideApp.Visible:=Visible and (WindowState <> wsMinimized); {$endif darwin} SetRefreshInterval; FCalcAvg:=Ini.ReadBool('Interface', 'CalcAvg', True); end; procedure TMainForm.HideApp; begin if WindowState <> wsMinimized then Hide; HideTaskbarButton; UpdateTray; end; procedure TMainForm.ShowApp; var i: integer; begin ShowTaskbarButton; if WindowState = wsMinimized then Application.Restore; Application.ProcessMessages; Show; Application.BringToFront; BringToFront; for i:=0 to Screen.FormCount - 1 do with Screen.Forms[i] do if fsModal in FormState then BringToFront; UpdateTray; end; procedure TMainForm.DownloadFinished(const TorrentName: string); begin if not TrayIcon.Visible or not Ini.ReadBool('Interface', 'TrayNotify', True) then exit; TrayIcon.BalloonHint:=Format(sFinishedDownload, [TorrentName]); TrayIcon.BalloonTitle:=sDownloadComplete; TrayIcon.ShowBalloonHint; end; Procedure TMainForm.DoOpenFlagsZip(Sender: TObject; var AStream: TStream); begin AStream:=TFileStreamUTF8.Create(TUnZipper(Sender).FileName, fmOpenRead or fmShareDenyWrite); end; Procedure TMainForm.DoCreateOutZipStream(Sender : TObject; var AStream : TStream; AItem : TFullZipFileEntry); begin ForceDirectoriesUTF8(FFlagsPath); AStream:=TFileStreamUTF8.Create(FFlagsPath + AItem.DiskFileName, fmCreate); end; function TMainForm.GetFlagImage(const CountryCode: string): integer; var s, ImageName: string; pic: TPicture; fs: TFileStreamUTF8; begin Result:=0; if CountryCode = '' then exit; try ImageName:=CountryCode + '.png'; if FFlagsPath = '' then FFlagsPath:=FHomeDir + 'flags' + DirectorySeparator; if not FileExistsUTF8(FFlagsPath + ImageName) then begin // Unzipping flag image if FUnZip = nil then begin s:=GetFlagsArchive; if s <> '' then begin FUnZip:=TUnZipper.Create; FUnZip.FileName:=s; FUnZip.OnOpenInputStream:=@DoOpenFlagsZip; FUnZip.OnCreateStream:=@DoCreateOutZipStream; end else exit; end; FUnZip.Files.Clear; FUnZip.Files.Add(ImageName); try FUnZip.UnZipAllFiles; except FreeAndNil(FUnZip); DeleteFileUTF8(GetFlagsArchive); acShowCountryFlag.Checked:=False; MessageDlg(sUnableExtractFlag + LineEnding + Exception(ExceptObject).Message, mtError, [mbOK], 0); exit; end; if not FileExistsUTF8(FFlagsPath + ImageName) then exit; end; fs:=nil; pic:=TPicture.Create; try fs:=TFileStreamUTF8.Create(FFlagsPath + ImageName, fmOpenRead or fmShareDenyWrite); pic.LoadFromStream(fs); if imgFlags.Count = 1 then begin imgFlags.Width:=pic.Width; imgFlags.Height:=pic.Height; end; Result:=imgFlags.AddMasked(pic.Bitmap, clNone); finally pic.Free; fs.Free; end; except end; end; procedure TMainForm.BeforeCloseApp; begin if WindowState = wsNormal then begin Ini.WriteInteger('MainForm', 'Left', Left); Ini.WriteInteger('MainForm', 'Top', Top); Ini.WriteInteger('MainForm', 'Width', Width); Ini.WriteInteger('MainForm', 'Height', Height); end; if WindowState <> wsMinimized then Ini.WriteInteger('MainForm', 'State', integer(WindowState)); if VSplitter.Visible then Ini.WriteInteger('MainForm', 'VSplitter', VSplitter.GetSplitterPosition); if HSplitter.Visible then Ini.WriteInteger('MainForm', 'HSplitter', HSplitter.GetSplitterPosition); Ini.WriteBool('MainForm', 'FilterPane', acFilterPane.Checked); Ini.WriteBool('MainForm', 'InfoPane', acInfoPane.Checked); Ini.WriteBool('MainForm', 'StatusBar', acStatusBar.Checked); SaveColumns(gTorrents, 'TorrentsList'); SaveColumns(lvFiles, 'FilesList'); SaveColumns(lvPeers, 'PeerList'); SaveColumns(lvTrackers, 'TrackersList'); Ini.WriteBool('PeersList', 'ResolveHost', acResolveHost.Checked); Ini.WriteBool('PeersList', 'ResolveCountry', acResolveCountry.Checked); Ini.WriteBool('PeersList', 'ShowCountryFlag', acShowCountryFlag.Checked); if RpcObj.Connected then Ini.WriteInteger('Interface', 'LastRpcVersion', RpcObj.RPCVersion); try Ini.UpdateFile; except Application.HandleException(nil); end; DoDisconnect; Application.ProcessMessages; end; function TMainForm.GetGeoIpDatabase: string; begin Result:=LocateFile('GeoIP.dat', [FHomeDir, ExtractFilePath(ParamStrUTF8(0))]); end; function TMainForm.GetFlagsArchive: string; begin Result:=LocateFile('flags.zip', [FHomeDir, ExtractFilePath(ParamStrUTF8(0))]); end; function TMainForm.DownloadGeoIpDatabase(AUpdate: boolean): boolean; const GeoLiteURL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz'; var tmp: string; gz: TGZFileStream; fs: TFileStreamUTF8; buf: array[0..65535] of byte; i: integer; begin Result:=False; tmp:=SysToUTF8(GetTempDir(True)) + 'GeoIP.dat.gz'; if not FileExistsUTF8(tmp) or AUpdate then begin if MessageDlg('', sGeoIPConfirm, mtConfirmation, mbYesNo, 0, mbYes) <> mrYes then exit; if not DownloadFile(GeoLiteURL, ExtractFilePath(tmp), ExtractFileName(tmp)) then exit; end; try FreeAndNil(FResolver); gz:=TGZFileStream.Create(tmp, gzopenread); try fs:=TFileStreamUTF8.Create(FHomeDir + 'GeoIP.dat', fmCreate); try repeat i:=gz.read(buf, SizeOf(buf)); fs.WriteBuffer(buf, i); until i < SizeOf(buf); finally fs.Free; end; finally gz.Free; end; DeleteFileUTF8(tmp); except DeleteFileUTF8(FHomeDir + 'GeoIP.dat'); DeleteFileUTF8(tmp); raise; end; Result:=True; end; procedure TMainForm.TorrentColumnsChanged; var i: integer; s: string; begin s:=''; for i:=0 to gTorrents.Columns.Count - 1 do with gTorrents.Columns[i] do if Visible and (Width > 0) then begin if TorrentFieldsMap[ID - 1] <> '' then begin if s <> '' then s:=s + ','; s:=s + TorrentFieldsMap[ID - 1]; end; end; RpcObj.TorrentFields:=s; DoRefresh(True); end; function TMainForm.EtaToString(ETA: integer): string; const r1 = 60; r2 = 5*60; r3 = 30*60; r4 = 60*60; begin if (ETA < 0) or (ETA = MaxInt) then Result:='' else begin if ETA > 2*60*60 then // > 5 hours - round to 1 hour ETA:=(ETA + r4 div 2) div r4 * r4 else if ETA > 2*60*60 then // > 2 hours - round to 30 mins ETA:=(ETA + r3 div 2) div r3 * r3 else if ETA > 30*60 then // > 30 mins - round to 5 mins ETA:=(ETA + r2 div 2) div r2 * r2 else if ETA > 2*60 then // > 2 mins - round to 1 min ETA:=(ETA + r1 div 2) div r1 * r1; Result:=SecondsToString(ETA); end; end; function TMainForm.GetTorrentStatus(TorrentIdx: integer): string; var i: integer; begin i:=gTorrents.Items[idxStatus, TorrentIdx]; if i = TR_STATUS_CHECK_WAIT then Result:=sWaiting else if i = TR_STATUS_CHECK then Result:=sVerifying else if i = TR_STATUS_DOWNLOAD_WAIT then Result:=sWaiting else if i = TR_STATUS_DOWNLOAD then Result:=sDownloading else if i = TR_STATUS_SEED_WAIT then Result:=sWaiting else if i = TR_STATUS_SEED then Result:=sSeeding else if i = TR_STATUS_STOPPED then Result:=sStopped else if i = TR_STATUS_FINISHED then Result:=sFinished else Result:=sUnknown; end; function TMainForm.GetSeedsText(Seeds, SeedsTotal: integer): string; begin if SeedsTotal <> -1 then Result:=Format('%d/%d', [Seeds, SeedsTotal]) else Result:=Format('%d', [Seeds]); end; function TMainForm.GetPeersText(Peers, PeersTotal, Leechers: integer): string; begin Result:=Format('%d', [Peers]); if Leechers <> -1 then Result:=Format('%s/%d', [Result, Leechers]); Dec(PeersTotal); if PeersTotal >= 0 then Result:=Format('%s (%d)', [Result, PeersTotal]); end; function TMainForm.RatioToString(Ratio: double): string; begin if (Ratio = MaxInt) or (Ratio = -2) then Result:=Utf8Encode(WideString(WideChar($221E))) else if Ratio = -1 then Result:='' else Result:=Format('%.3f', [Ratio]); end; function TMainForm.TorrentDateTimeToString(d: Int64): string; begin if d = 0 then Result:='' else Result:=DateTimeToStr(UnixToDateTime(d) + GetTimeZoneDelta); end; procedure TMainForm.DoRefresh(All: boolean); begin if All then RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtTorrents, rtDetails] else RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtDetails]; end; procedure TMainForm.acDisconnectExecute(Sender: TObject); begin DoDisconnect; end; procedure TMainForm.acExitExecute(Sender: TObject); begin BeforeCloseApp; Application.Terminate; end; procedure TMainForm.acDaemonOptionsExecute(Sender: TObject); var req, args: TJSONObject; s: string; i, j: integer; begin with TDaemonOptionsForm.Create(Self) do try AppBusy; req:=TJSONObject.Create; try req.Add('method', 'session-get'); args:=RpcObj.SendRequest(req); if args <> nil then try edDownloadDir.Text:=UTF8Encode(args.Strings['download-dir']); if RpcObj.RPCVersion >= 5 then begin // RPC version 5 edPort.Value:=args.Integers['peer-port']; cbPEX.Checked:=args.Integers['pex-enabled'] <> 0; edMaxPeers.Value:=args.Integers['peer-limit-global']; cbRandomPort.Checked:=args.Integers['peer-port-random-on-start'] <> 0; cbDHT.Checked:=args.Integers['dht-enabled'] <> 0; cbSeedRatio.Checked:=args.Integers['seedRatioLimited'] <> 0; edSeedRatio.Value:=args.Floats['seedRatioLimit']; cbBlocklist.Checked:=args.Integers['blocklist-enabled'] <> 0; cbAltEnabled.Checked:=args.Integers['alt-speed-enabled'] <> 0; edAltDown.Value:=args.Integers['alt-speed-down']; edAltUp.Value:=args.Integers['alt-speed-up']; cbAutoAlt.Checked:=args.Integers['alt-speed-time-enabled'] <> 0; edAltTimeBegin.Text:=FormatDateTime('hh:nn', args.Integers['alt-speed-time-begin']/MinsPerDay); edAltTimeEnd.Text:=FormatDateTime('hh:nn', args.Integers['alt-speed-time-end']/MinsPerDay); j:=args.Integers['alt-speed-time-day']; for i:=1 to 7 do begin TCheckBox(gbAltSpeed.FindChildControl(Format('cbDay%d', [i]))).Checked:=LongBool(j and 1); j:=j shr 1; end; cbAutoAltClick(nil); end else begin // RPC versions prior to v5 cbPortForwarding.Top:=cbRandomPort.Top; edPort.Value:=args.Integers['port']; cbPEX.Checked:=args.Integers['pex-allowed'] <> 0; edMaxPeers.Value:=args.Integers['peer-limit']; cbRandomPort.Visible:=False; cbDHT.Visible:=False; cbSeedRatio.Visible:=False; edSeedRatio.Visible:=False; btTestPort.Visible:=False; cbBlocklist.Visible:=False; gbAltSpeed.Visible:=False; end; if RpcObj.RPCVersion >= 7 then begin cbIncompleteDir.Checked:=args.Integers['incomplete-dir-enabled'] <> 0; edIncompleteDir.Text:=UTF8Encode(args.Strings['incomplete-dir']); cbIncompleteDirClick(nil); end else begin cbIncompleteDir.Visible:=False; edIncompleteDir.Visible:=False; end; if RpcObj.RPCVersion >= 8 then cbPartExt.Checked:=args.Integers['rename-partial-files'] <> 0 else cbPartExt.Visible:=False; if RpcObj.RPCVersion >= 9 then cbLPD.Checked:=args.Integers['lpd-enabled'] <> 0 else cbLPD.Visible:=False; if RpcObj.RPCVersion >= 10 then begin edCacheSize.Value:=args.Integers['cache-size-mb']; cbIdleSeedLimit.Checked:=args.Integers['idle-seeding-limit-enabled'] <> 0; edIdleSeedLimit.Value:=args.Integers['idle-seeding-limit']; cbIdleSeedLimitClick(nil); end else begin edCacheSize.Visible:=False; txCacheSize.Visible:=False; txMB.Visible:=False; cbIdleSeedLimit.Visible:=False; edIdleSeedLimit.Visible:=False; txMinutes.Visible:=False; end; if args.IndexOfName('blocklist-url') >= 0 then edBlocklistURL.Text:=UTF8Encode(args.Strings['blocklist-url']) else begin edBlocklistURL.Visible:=False; cbBlocklist.Left:=cbPEX.Left; cbBlocklist.Caption:=StringReplace(cbBlocklist.Caption, ':', '', [rfReplaceAll]); end; cbBlocklistClick(nil); if RpcObj.RPCVersion >= 13 then cbUTP.Checked:=args.Integers['utp-enabled'] <> 0 else cbUTP.Visible:=False; if RpcObj.RPCVersion >= 14 then begin tabQueue.TabVisible:=True; cbDownQueue.Checked:=args.Integers['download-queue-enabled'] <> 0; edDownQueue.Value:=args.Integers['download-queue-size']; cbUpQueue.Checked:=args.Integers['seed-queue-enabled'] <> 0; edUpQueue.Value:=args.Integers['seed-queue-size']; cbStalled.Checked:=args.Integers['queue-stalled-enabled'] <> 0; edStalledTime.Value:=args.Integers['queue-stalled-minutes']; end else tabQueue.TabVisible:=False; cbPortForwarding.Checked:=args.Integers['port-forwarding-enabled'] <> 0; s:=args.Strings['encryption']; if s = 'preferred' then cbEncryption.ItemIndex:=1 else if s = 'required' then cbEncryption.ItemIndex:=2 else cbEncryption.ItemIndex:=0; cbMaxDown.Checked:=args.Integers['speed-limit-down-enabled'] <> 0; edMaxDown.Value:=args.Integers['speed-limit-down']; cbMaxUp.Checked:=args.Integers['speed-limit-up-enabled'] <> 0; edMaxUp.Value:=args.Integers['speed-limit-up']; finally args.Free; end else begin CheckStatus(False); exit; end; finally req.Free; end; cbMaxDownClick(nil); cbMaxUpClick(nil); cbRandomPortClick(nil); cbSeedRatioClick(nil); AppNormal; if ShowModal = mrOK then begin AppBusy; Self.Update; req:=TJSONObject.Create; try req.Add('method', 'session-set'); args:=TJSONObject.Create; args.Add('download-dir', UTF8Decode(edDownloadDir.Text)); args.Add('port-forwarding-enabled', integer(cbPortForwarding.Checked) and 1); case cbEncryption.ItemIndex of 1: s:='preferred'; 2: s:='required'; else s:='tolerated'; end; args.Add('encryption', s); args.Add('speed-limit-down-enabled', integer(cbMaxDown.Checked) and 1); if cbMaxDown.Checked then args.Add('speed-limit-down', edMaxDown.Value); args.Add('speed-limit-up-enabled', integer(cbMaxUp.Checked) and 1); if cbMaxUp.Checked then args.Add('speed-limit-up', edMaxUp.Value); if RpcObj.RPCVersion >= 5 then begin args.Add('peer-limit-global', edMaxPeers.Value); args.Add('peer-port', edPort.Value); args.Add('pex-enabled', integer(cbPEX.Checked) and 1); args.Add('peer-port-random-on-start', integer(cbRandomPort.Checked) and 1); args.Add('dht-enabled', integer(cbDHT.Checked) and 1); args.Add('seedRatioLimited', integer(cbSeedRatio.Checked) and 1); if cbSeedRatio.Checked then args.Add('seedRatioLimit', edSeedRatio.Value); args.Add('blocklist-enabled', integer(cbBlocklist.Checked) and 1); args.Add('alt-speed-enabled', integer(cbAltEnabled.Checked) and 1); args.Add('alt-speed-down', edAltDown.Value); args.Add('alt-speed-up', edAltUp.Value); args.Add('alt-speed-time-enabled', integer(cbAutoAlt.Checked) and 1); if cbAutoAlt.Checked then begin args.Add('alt-speed-time-begin', Round(Frac(StrToTime(edAltTimeBegin.Text))*MinsPerDay)); args.Add('alt-speed-time-end', Round(Frac(StrToTime(edAltTimeEnd.Text))*MinsPerDay)); j:=0; for i:=7 downto 1 do begin j:=j shl 1; j:=j or (integer(TCheckBox(gbAltSpeed.FindChildControl(Format('cbDay%d', [i]))).Checked) and 1); end; args.Add('alt-speed-time-day', j); end; end else begin args.Add('peer-limit', edMaxPeers.Value); args.Add('port', edPort.Value); args.Add('pex-allowed', integer(cbPEX.Checked) and 1); end; if RpcObj.RPCVersion >= 7 then begin args.Add('incomplete-dir-enabled', integer(cbIncompleteDir.Checked) and 1); if cbIncompleteDir.Checked then args.Add('incomplete-dir', UTF8Decode(edIncompleteDir.Text)); end; if RpcObj.RPCVersion >= 8 then args.Add('rename-partial-files', integer(cbPartExt.Checked) and 1); if RpcObj.RPCVersion >= 9 then args.Add('lpd-enabled', integer(cbLPD.Checked) and 1); if RpcObj.RPCVersion >= 10 then begin args.Add('cache-size-mb', edCacheSize.Value); args.Add('idle-seeding-limit-enabled', integer(cbIdleSeedLimit.Checked) and 1); args.Add('idle-seeding-limit', edIdleSeedLimit.Value); end; if edBlocklistURL.Visible then if cbBlocklist.Checked then args.Add('blocklist-url', UTF8Decode(edBlocklistURL.Text)); if RpcObj.RPCVersion >= 13 then args.Add('utp-enabled', integer(cbUTP.Checked) and 1); if RpcObj.RPCVersion >= 14 then begin args.Add('download-queue-enabled', integer(cbDownQueue.Checked) and 1); args.Add('download-queue-size', edDownQueue.Value); args.Add('seed-queue-enabled', integer(cbUpQueue.Checked) and 1); args.Add('seed-queue-size', edUpQueue.Value); args.Add('queue-stalled-enabled', integer(cbStalled.Checked) and 1); args.Add('queue-stalled-minutes', edStalledTime.Value); end; req.Add('arguments', args); args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession]; AppNormal; end; finally Free; end; end; procedure TMainForm.acQMoveBottomExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'queue-move-bottom'); end; procedure TMainForm.acQMoveDownExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'queue-move-down'); end; procedure TMainForm.acQMoveTopExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'queue-move-top'); end; procedure TMainForm.acQMoveUpExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'queue-move-up'); end; procedure TMainForm.acReannounceTorrentExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'torrent-reannounce'); end; procedure TMainForm.acRemoveTorrentAndDataExecute(Sender: TObject); begin InternalRemoveTorrent(sRemoveTorrentData, sRemoveTorrentDataMulti, True); end; procedure TMainForm.acRemoveTorrentExecute(Sender: TObject); begin InternalRemoveTorrent(sRemoveTorrent, sRemoveTorrentMulti, False); end; procedure TMainForm.acRenameExecute(Sender: TObject); begin if lvFiles.Focused then lvFiles.EditCell(idxFileName, lvFiles.Row) else gTorrents.EditCell(idxName, gTorrents.Row); end; procedure TMainForm.acResolveCountryExecute(Sender: TObject); begin if not acResolveCountry.Checked then if GetGeoIpDatabase = '' then if not DownloadGeoIpDatabase(False) then exit; acResolveCountry.Checked:=not acResolveCountry.Checked; FreeAndNil(FResolver); DoRefresh; acShowCountryFlag.Enabled:=acResolveCountry.Checked; end; procedure TMainForm.acResolveHostExecute(Sender: TObject); begin acResolveHost.Checked:=not acResolveHost.Checked; FreeAndNil(FResolver); DoRefresh; end; procedure TMainForm.acSelectAllExecute(Sender: TObject); begin Application.ProcessMessages; if lvFiles.Focused then lvFiles.SelectAll else gTorrents.SelectAll; end; procedure TMainForm.acSetHighPriorityExecute(Sender: TObject); begin Application.ProcessMessages; if lvFiles.Focused then SetCurrentFilePriority('high') else SetTorrentPriority(TR_PRI_HIGH); end; procedure TMainForm.acSetLowPriorityExecute(Sender: TObject); begin Application.ProcessMessages; if lvFiles.Focused then SetCurrentFilePriority('low') else SetTorrentPriority(TR_PRI_LOW); end; procedure TMainForm.acSetNormalPriorityExecute(Sender: TObject); begin Application.ProcessMessages; if lvFiles.Focused then SetCurrentFilePriority('normal') else SetTorrentPriority(TR_PRI_NORMAL); end; procedure TMainForm.acSetNotDownloadExecute(Sender: TObject); begin SetCurrentFilePriority('skip'); end; procedure TMainForm.acSetupColumnsExecute(Sender: TObject); var g: TVarGrid; s: string; begin Application.ProcessMessages; if lvTrackers.Focused then g:=lvTrackers else if lvPeers.Focused then g:=lvPeers else if lvFiles.Focused then g:=lvFiles else g:=gTorrents; if g = gTorrents then s:=sTorrents else if PageInfo.ActivePage = tabFiles then s:=FFilesCapt else s:=PageInfo.ActivePage.Caption; if not SetupColumns(g, 0, s) then exit; if g = gTorrents then TorrentColumnsChanged; end; procedure TMainForm.acShowAppExecute(Sender: TObject); begin ShowApp; end; procedure TMainForm.acShowCountryFlagExecute(Sender: TObject); const FlagsURL = 'http://transmisson-remote-gui.googlecode.com/files/flags.zip'; begin if not acShowCountryFlag.Checked then if GetFlagsArchive = '' then begin if MessageDlg('', sFlagArchiveConfirm, mtConfirmation, mbYesNo, 0, mbYes) <> mrYes then exit; if not DownloadFile(FlagsURL, FHomeDir) then exit; end; acShowCountryFlag.Checked:=not acShowCountryFlag.Checked; DoRefresh; end; procedure TMainForm.acStartAllTorrentsExecute(Sender: TObject); begin TorrentAction(NULL, 'torrent-start'); end; procedure TMainForm.acStartTorrentExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'torrent-start'); end; procedure TMainForm.acStatusBarExecute(Sender: TObject); begin acStatusBar.Checked:=not acStatusBar.Checked; StatusBar.Visible:=acStatusBar.Checked; if StatusBar.Visible then StatusBar.Top:=ClientHeight; end; procedure TMainForm.acStopAllTorrentsExecute(Sender: TObject); begin TorrentAction(NULL, 'torrent-stop'); end; procedure TMainForm.acStopTorrentExecute(Sender: TObject); begin TorrentAction(GetSelectedTorrents, 'torrent-stop'); end; procedure TMainForm.acTorrentPropsExecute(Sender: TObject); begin TorrentProps(0); end; procedure TMainForm.TorrentProps(PageNo: integer); const TR_RATIOLIMIT_GLOBAL = 0; // follow the global settings TR_RATIOLIMIT_SINGLE = 1; // override the global settings, seeding until a certain ratio TR_RATIOLIMIT_UNLIMITED = 2; // override the global settings, seeding regardless of ratio TR_IDLELIMIT_GLOBAL = 0; // follow the global settings TR_IDLELIMIT_SINGLE = 1; // override the global settings, seeding until a certain idle time TR_IDLELIMIT_UNLIMITED = 2; // override the global settings, seeding regardless of activity var req, args, t, tr: TJSONObject; i, j, id: integer; ids, Trackers, AddT, EditT, DelT: TJSONArray; TorrentIds: variant; s: string; trlist, sl: TStringList; begin gTorrentsClick(nil); id:=RpcObj.CurTorrentId; if id = 0 then exit; AppBusy; trlist:=nil; with TTorrPropsForm.Create(Self) do try Page.ActivePageIndex:=PageNo; gTorrents.Tag:=1; gTorrents.EnsureSelectionVisible; TorrentIds:=GetSelectedTorrents; args:=RpcObj.RequestInfo(id, ['downloadLimit', 'downloadLimitMode', 'downloadLimited', 'uploadLimit', 'uploadLimitMode', 'uploadLimited', 'name', 'maxConnectedPeers', 'seedRatioMode', 'seedRatioLimit', 'seedIdleLimit', 'seedIdleMode', 'trackers']); if args = nil then begin CheckStatus(False); exit; end; try t:=args.Arrays['torrents'].Objects[0]; if gTorrents.SelCount > 1 then s:=Format(sSeveralTorrents, [gTorrents.SelCount]) else s:=UTF8Encode(t.Strings['name']); txName.Caption:=txName.Caption + ' ' + s; Caption:=Caption + ' - ' + s; if RpcObj.RPCVersion < 5 then begin // RPC versions prior to v5 j:=t.Integers['downloadLimitMode']; cbMaxDown.Checked:=j = TR_SPEEDLIMIT_SINGLE; i:=t.Integers['downloadLimit']; if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then edMaxDown.ValueEmpty:=True else edMaxDown.Value:=i; j:=t.Integers['uploadLimitMode']; cbMaxUp.Checked:=j = TR_SPEEDLIMIT_SINGLE; i:=t.Integers['uploadLimit']; if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then edMaxUp.ValueEmpty:=True else edMaxUp.Value:=i; cbSeedRatio.Visible:=False; edSeedRatio.Visible:=False; end else begin // RPC version 5 cbMaxDown.Checked:=t.Booleans['downloadLimited']; i:=t.Integers['downloadLimit']; if i < 0 then edMaxDown.ValueEmpty:=True else edMaxDown.Value:=i; cbMaxUp.Checked:=t.Booleans['uploadLimited']; i:=t.Integers['uploadLimit']; if i < 0 then edMaxUp.ValueEmpty:=True else edMaxUp.Value:=i; case t.Integers['seedRatioMode'] of TR_RATIOLIMIT_SINGLE: cbSeedRatio.State:=cbChecked; TR_RATIOLIMIT_UNLIMITED: cbSeedRatio.State:=cbUnchecked; else cbSeedRatio.State:=cbGrayed; end; edSeedRatio.Value:=t.Floats['seedRatioLimit']; end; if RpcObj.RPCVersion >= 10 then begin case t.Integers['seedIdleMode'] of TR_IDLELIMIT_SINGLE: cbIdleSeedLimit.State:=cbChecked; TR_IDLELIMIT_UNLIMITED: cbIdleSeedLimit.State:=cbUnchecked; else cbIdleSeedLimit.State:=cbGrayed; end; edIdleSeedLimit.Value:=t.Integers['seedIdleLimit']; cbIdleSeedLimitClick(nil); trlist:=TStringList.Create; Trackers:=t.Arrays['trackers']; for i:=0 to Trackers.Count - 1 do begin tr:=Trackers[i] as TJSONObject; trlist.AddObject(UTF8Decode(tr.Strings['announce']), TObject(PtrUInt(tr.Integers['id']))); end; edTrackers.Lines.Assign(trlist); end else begin cbIdleSeedLimit.Visible:=False; edIdleSeedLimit.Visible:=False; txMinutes.Visible:=False; tabAdvanced.TabVisible:=False; end; edPeerLimit.Value:=t.Integers['maxConnectedPeers']; finally args.Free; end; cbMaxDownClick(nil); cbMaxUpClick(nil); cbSeedRatioClick(nil); AppNormal; if ShowModal = mrOk then begin AppBusy; Self.Update; req:=TJSONObject.Create; try req.Add('method', 'torrent-set'); args:=TJSONObject.Create; ids:=TJSONArray.Create; for i:=VarArrayLowBound(TorrentIds, 1) to VarArrayHighBound(TorrentIds, 1) do ids.Add(integer(TorrentIds[i])); args.Add('ids', ids); if RpcObj.RPCVersion < 5 then begin // RPC versions prior to v5 args.Add('speed-limit-down-enabled', integer(cbMaxDown.Checked) and 1); args.Add('speed-limit-up-enabled', integer(cbMaxUp.Checked) and 1); if cbMaxDown.Checked then args.Add('speed-limit-down', edMaxDown.Value); if cbMaxUp.Checked then args.Add('speed-limit-up', edMaxUp.Value); end else begin // RPC version 5 args.Add('downloadLimited', integer(cbMaxDown.Checked) and 1); args.Add('uploadLimited', integer(cbMaxUp.Checked) and 1); if cbMaxDown.Checked then args.Add('downloadLimit', edMaxDown.Value); if cbMaxUp.Checked then args.Add('uploadLimit', edMaxUp.Value); case cbSeedRatio.State of cbChecked: i:=TR_RATIOLIMIT_SINGLE; cbUnchecked: i:=TR_RATIOLIMIT_UNLIMITED; else i:=TR_RATIOLIMIT_GLOBAL; end; args.Add('seedRatioMode', i); if cbSeedRatio.State = cbChecked then args.Add('seedRatioLimit', edSeedRatio.Value); end; if RpcObj.RPCVersion >= 10 then begin case cbIdleSeedLimit.State of cbChecked: i:=TR_IDLELIMIT_SINGLE; cbUnchecked: i:=TR_IDLELIMIT_UNLIMITED; else i:=TR_IDLELIMIT_GLOBAL; end; args.Add('seedIdleMode', i); if cbIdleSeedLimit.State = cbChecked then args.Add('seedIdleLimit', edIdleSeedLimit.Value); sl:=TStringList.Create; try sl.Assign(edTrackers.Lines); // Removing unchanged trackers i:=0; while i < sl.Count do begin s:=Trim(sl[i]); if s = '' then begin sl.Delete(i); continue; end; j:=trlist.IndexOf(s); if j >= 0 then begin trlist.Delete(j); sl.Delete(i); continue; end; Inc(i); end; AddT:=TJSONArray.Create; EditT:=TJSONArray.Create; DelT:=TJSONArray.Create; try for i:=0 to sl.Count - 1 do begin s:=Trim(sl[i]); if trlist.Count > 0 then begin EditT.Add(PtrUInt(trlist.Objects[0])); EditT.Add(UTF8Decode(s)); trlist.Delete(0); end else AddT.Add(UTF8Decode(s)); end; for i:=0 to trlist.Count - 1 do DelT.Add(PtrUInt(trlist.Objects[i])); if AddT.Count > 0 then begin args.Add('trackerAdd', AddT); AddT:=nil; end; if EditT.Count > 0 then begin args.Add('trackerReplace', EditT); EditT:=nil; end; if DelT.Count > 0 then begin args.Add('trackerRemove', DelT); DelT:=nil; end; finally DelT.Free; EditT.Free; AddT.Free; end; finally sl.Free; end; end; args.Add('peer-limit', edPeerLimit.Value); req.Add('arguments', args); args:=nil; args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; DoRefresh; AppNormal; end; finally gTorrents.Tag:=0; Free; trlist.Free; end; end; procedure TMainForm.acTrackerGroupingExecute(Sender: TObject); begin acTrackerGrouping.Checked:=not acTrackerGrouping.Checked; Ini.WriteBool('Interface', 'TrackerGrouping', acTrackerGrouping.Checked); RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtTorrents]; end; procedure TMainForm.acUpdateBlocklistExecute(Sender: TObject); var req: TJSONObject; res: TJSONObject; begin Application.ProcessMessages; AppBusy; req:=TJSONObject.Create; try req.Add('method', 'blocklist-update'); res:=RpcObj.SendRequest(req, True, 3*60000); AppNormal; if res = nil then begin CheckStatus(False); exit; end; MessageDlg(Format(sBlocklistUpdateComplete, [res.Integers[('blocklist-size')]]), mtInformation, [mbOK], 0); res.Free; finally req.Free; end; end; procedure TMainForm.acUpdateGeoIPExecute(Sender: TObject); begin if DownloadGeoIpDatabase(True) then MessageDlg(sUpdateComplete, mtInformation, [mbOK], 0); end; procedure TMainForm.acVerifyTorrentExecute(Sender: TObject); var ids: variant; s: string; begin if gTorrents.Items.Count = 0 then exit; gTorrents.Tag:=1; try gTorrents.EnsureSelectionVisible; ids:=GetSelectedTorrents; if gTorrents.SelCount < 2 then s:=Format(sTorrentVerification, [UTF8Encode(widestring(gTorrents.Items[idxName, gTorrents.Items.IndexOf(idxTorrentId, ids[0])]))]) else s:=Format(sTorrentsVerification, [gTorrents.SelCount]); if MessageDlg('', s, mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then exit; finally gTorrents.Tag:=0; end; TorrentAction(ids, 'torrent-verify'); end; procedure TMainForm.ApplicationPropertiesEndSession(Sender: TObject); begin DeleteFileUTF8(FRunFileName); BeforeCloseApp; end; procedure TMainForm.ApplicationPropertiesException(Sender: TObject; E: Exception); var msg: string; {$ifdef CALLSTACK} sl: TStringList; {$endif CALLSTACK} begin ForceAppNormal; msg:=E.Message; {$ifdef CALLSTACK} try sl:=TStringList.Create; try sl.Text:=GetLastExceptionCallStack; Clipboard.AsText:=msg + LineEnding + sl.Text; DebugLn(msg + LineEnding + sl.Text); if sl.Count > 20 then begin while sl.Count > 20 do sl.Delete(20); sl.Add('...'); end; msg:=msg + LineEnding + '---' + LineEnding + 'The error details has been copied to the clipboard.' + LineEnding + '---'; msg:=msg + LineEnding + sl.Text; finally sl.Free; end; except ; // suppress exception end; {$endif CALLSTACK} MessageDlg(msg, mtError, [mbOK], 0); end; procedure TMainForm.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean); begin UpdateUI; {$ifdef LCLcarbon} CheckSynchronize; {$endif LCLcarbon} Done:=True; end; procedure TMainForm.ApplicationPropertiesMinimize(Sender: TObject); begin {$ifndef darwin} if not IsUnity and Ini.ReadBool('Interface', 'TrayMinimize', True) then HideApp; {$endif darwin} UpdateTray; end; procedure TMainForm.ApplicationPropertiesRestore(Sender: TObject); begin UpdateTray; CheckClipboardLink; end; procedure TMainForm.edSearchChange(Sender: TObject); begin DoRefresh(True); end; procedure TMainForm.FormActivate(Sender: TObject); begin CheckClipboardLink; end; procedure TMainForm.FormDropFiles(Sender: TObject; const FileNames: array of String); var i: integer; begin for i:=Low(FileNames) to High(FileNames) do AddTorrentFile(FileNames[i]); end; procedure TMainForm.FormWindowStateChange(Sender: TObject); begin {$ifdef lclgtk2} if WindowState = wsMinimized then ApplicationPropertiesMinimize(nil) else ApplicationPropertiesRestore(nil); {$endif lclgtk2} end; procedure TMainForm.gTorrentsCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); var j: integer; begin if ARow < 0 then exit; with CellAttribs do begin if ACol = gTorrents.FirstVisibleColumn then ImageIndex:=integer(Sender.Items[idxStateImg, ARow]); if Text = '' then exit; if not VarIsEmpty(Sender.Items[idxDeleted, ARow]) then with Sender.Canvas.Font do Style:=Style + [fsStrikeOut]; case ADataCol of idxStatus: Text:=GetTorrentStatus(ARow); idxSize, idxDownloaded, idxUploaded, idxSizeToDowload: Text:=GetHumanSize(Sender.Items[ADataCol, ARow], 0, '?'); idxDone: Text:=Format('%.1f%%', [double(Sender.Items[idxDone, ARow])]); idxSeeds: if not VarIsNull(Sender.Items[idxSeedsTotal, ARow]) then Text:=GetSeedsText(Sender.Items[idxSeeds, ARow], Sender.Items[idxSeedsTotal, ARow]); idxPeers: Text:=GetPeersText(Sender.Items[idxPeers, ARow], -1, Sender.Items[idxLeechersTotal, ARow]); idxDownSpeed, idxUpSpeed: begin j:=Sender.Items[ADataCol, ARow]; if j > 0 then Text:=GetHumanSize(j, 1) + sPerSecond else Text:=''; end; idxETA: Text:=EtaToString(Sender.Items[idxETA, ARow]); idxRatio: Text:=RatioToString(Sender.Items[idxRatio, ARow]); idxAddedOn, idxCompletedOn, idxLastActive: Text:=TorrentDateTimeToString(Sender.Items[ADataCol, ARow]); idxPriority: Text:=PriorityToStr(Sender.Items[ADataCol, ARow], ImageIndex); idxQueuePos: begin j:=Sender.Items[ADataCol, ARow]; if j >= FinishedQueue then Dec(j, FinishedQueue); Text:=IntToStr(j); end; idxSeedingTime: begin j:=Sender.Items[idxSeedingTime, ARow]; if j > 0 then Text:=EtaToString(j) else Text:=''; end; end; end; end; procedure TMainForm.gTorrentsClick(Sender: TObject); var i: integer; begin if gTorrents.Tag <> 0 then exit; RpcObj.Lock; try if gTorrents.Items.Count > 0 then i:=gTorrents.Items[idxTorrentId, gTorrents.Row] else i:=0; if RpcObj.CurTorrentId = i then exit; RpcObj.CurTorrentId:=i; finally RpcObj.Unlock; end; ClearDetailsInfo(GetPageInfoType(PageInfo.ActivePage)); TorrentsListTimer.Enabled:=False; TorrentsListTimer.Enabled:=True; end; procedure TMainForm.gTorrentsDblClick(Sender: TObject); var res: TJSONObject; s, n: string; begin if gTorrents.Items.Count = 0 then exit; if gTorrents.Items[idxDone, gTorrents.Row] = 100.0 then begin // The torrent is finished. Check if it is possible to open its file/folder AppBusy; try res:=RpcObj.RequestInfo(gTorrents.Items[idxTorrentId, gTorrents.Row], ['downloadDir']); if res = nil then CheckStatus(False); with res.Arrays['torrents'].Objects[0] do n:=IncludeProperTrailingPathDelimiter(UTF8Encode(Strings['downloadDir'])) + UTF8Encode(widestring(gTorrents.Items[idxName, gTorrents.Row])); s:=MapRemoteToLocal(n); if s = '' then s:=n; if FileExistsUTF8(s) or DirectoryExistsUTF8(s) then begin // File/folder exists - open it OpenCurrentTorrent(False); exit; end; finally AppNormal; end; end; acTorrentProps.Execute; end; procedure TMainForm.gTorrentsDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean); begin if ARow < 0 then exit; if ADataCol = idxDone then begin ADefaultDrawing:=False; DrawProgressCell(Sender, ACol, ARow, ADataCol, AState, R); end; end; procedure TMainForm.gTorrentsEditorHide(Sender: TObject); begin gTorrents.Tag:=0; end; procedure TMainForm.gTorrentsEditorShow(Sender: TObject); begin gTorrents.Tag:=1; gTorrents.RemoveSelection; end; procedure TMainForm.gTorrentsQuickSearch(Sender: TVarGrid; var SearchText: string; var ARow: integer); var i: integer; s: string; v: variant; begin s:=UTF8UpperCase(SearchText); for i:=ARow to gTorrents.Items.Count - 1 do begin v:=gTorrents.Items[idxName, i]; if VarIsEmpty(v) or VarIsNull(v) then continue; if Pos(s, Trim(UTF8UpperCase(UTF8Encode(widestring(v))))) > 0 then begin ARow:=i; break; end; end; end; procedure TMainForm.gTorrentsResize(Sender: TObject); begin if not FStarted then begin VSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'VSplitter', VSplitter.GetSplitterPosition)); HSplitter.SetSplitterPosition(Ini.ReadInteger('MainForm', 'HSplitter', HSplitter.GetSplitterPosition)); end; end; procedure TMainForm.gTorrentsSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string); begin if RenameTorrent(gTorrents.Items[idxTorrentId, ARow], UTF8Encode(widestring(gTorrents.Items[idxName, ARow])), Trim(Value)) then begin gTorrents.Items[idxName, ARow]:=UTF8Decode(Trim(Value)); FFilesTree.Clear; end; end; procedure TMainForm.gTorrentsSortColumn(Sender: TVarGrid; var ASortCol: integer); begin if ASortCol = idxSeeds then ASortCol:=idxSeedsTotal; if ASortCol = idxPeers then ASortCol:=idxLeechersTotal; end; procedure TMainForm.HSplitterChangeBounds(Sender: TObject); begin {$ifdef windows} Update; {$endif windows} end; procedure TMainForm.lvFilesDblClick(Sender: TObject); begin acOpenFile.Execute; end; procedure TMainForm.lvFilesEditorHide(Sender: TObject); begin gTorrents.Tag:=0; lvFiles.Tag:=0; lvFiles.HideSelection:=True; end; procedure TMainForm.lvFilesEditorShow(Sender: TObject); begin gTorrents.Tag:=1; lvFiles.Tag:=1; lvFiles.RemoveSelection; lvFiles.HideSelection:=False; end; procedure TMainForm.lvFilesSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: string); var p: string; i, lvl, len: integer; begin p:=FFilesTree.GetFullPath(ARow, False); if RenameTorrent(gTorrents.Items[idxTorrentId, gTorrents.Row], p, Trim(Value)) then begin FFiles[idxFileName, ARow]:=UTF8Decode(Trim(Value)); if FFilesTree.IsFolder(ARow) then begin // Updating path for child elements len:=Length(p); p:=ExtractFilePath(p) + Trim(Value); lvl:=FFilesTree.RowLevel[ARow]; FFiles.BeginUpdate; try FFiles[idxFileFullPath, ARow]:=UTF8Decode(p + RemotePathDelimiter); for i:=ARow + 1 to FFiles.Count - 1 do if FFilesTree.RowLevel[i] > lvl then FFiles[idxFileFullPath, i]:=UTF8Decode(p + Copy(UTF8Encode(widestring(FFiles[idxFileFullPath, i])), len + 1, MaxInt)) else break; finally FFiles.EndUpdate; end; end; end; end; procedure TMainForm.lvFilterCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); begin if ARow < 0 then exit; with CellAttribs do begin case ARow of 0: ImageIndex:=imgAll; 1: ImageIndex:=imgDown; 2: ImageIndex:=imgSeed; 3: ImageIndex:=imgActive; 4: ImageIndex:=15; 5: ImageIndex:=imgStopped; 6: ImageIndex:=imgError; else if Text <> '' then if VarIsNull(Sender.Items[-1, ARow]) then ImageIndex:=5 else ImageIndex:=22; end; end; end; procedure TMainForm.lvFilterClick(Sender: TObject); begin if VarIsNull(lvFilter.Items[0, lvFilter.Row]) then if (FLastFilerIndex > lvFilter.Row) or (lvFilter.Row = lvFilter.Items.Count - 1) then lvFilter.Row:=lvFilter.Row - 1 else lvFilter.Row:=lvFilter.Row + 1; FLastFilerIndex:=lvFilter.Row; FilterTimer.Enabled:=False; FilterTimer.Enabled:=True; end; procedure TMainForm.lvFilterDrawCell(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; const R: TRect; var ADefaultDrawing: boolean); var i: integer; RR: TRect; begin ADefaultDrawing:=not VarIsNull(Sender.Items[0, ARow]); if ADefaultDrawing then exit; with lvFilter.Canvas do begin Brush.Color:=lvFilter.Color; FillRect(R); i:=(R.Bottom + R.Top) div 2; Brush.Color:=clBtnFace; RR:=R; InflateRect(RR, -4, 0); RR.Top:=i - 1; RR.Bottom:=i + 1; FillRect(RR); end; end; procedure TMainForm.lvPeersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); var i: integer; begin if ARow < 0 then exit; with CellAttribs do begin if Text = '' then exit; if ACol = 0 then begin ImageIndex:=Sender.Items[idxPeerCountryImage, ARow]; if ImageIndex = 0 then ImageIndex:=-1; end; case ADataCol of idxPeerDone: Text:=Format('%.1f%%', [double(Sender.Items[ADataCol, ARow])*100.0]); idxPeerDownSpeed, idxPeerUpSpeed: begin i:=Sender.Items[ADataCol, ARow]; if i > 0 then Text:=GetHumanSize(i, 1) + sPerSecond else Text:=''; end; end; end; end; procedure TMainForm.lvTrackersCellAttributes(Sender: TVarGrid; ACol, ARow, ADataCol: integer; AState: TGridDrawState; var CellAttribs: TCellAttributes); var f: double; begin if ARow < 0 then exit; with CellAttribs do begin if Text = '' then exit; case ADataCol of idxTrackersListSeeds: if lvTrackers.Items[ADataCol, ARow] < 0 then Text:=''; idxTrackersListUpdateIn: begin f:=double(lvTrackers.Items[ADataCol, ARow]); if f = 0 then Text:='-' else if f = 1 then Text:=sUpdating else Text:=SecondsToString(Trunc(f)); end; end; end; end; procedure TMainForm.lvTrackersDblClick(Sender: TObject); begin acEditTracker.Execute; end; procedure TMainForm.lvTrackersKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_DELETE then begin Key:=0; acDelTracker.Execute; end; end; procedure TMainForm.miDonateClick(Sender: TObject); begin GoDonate; end; procedure TMainForm.miHomePageClick(Sender: TObject); begin GoHomePage; end; procedure TMainForm.PageInfoResize(Sender: TObject); begin if FDetailsWait.Visible then CenterDetailsWait; end; procedure TMainForm.panReconnectResize(Sender: TObject); begin panReconnectFrame.BoundsRect:=panReconnect.ClientRect; end; procedure TMainForm.pbDownloadedPaint(Sender: TObject); begin if FTorrentProgress <> nil then pbDownloaded.Canvas.StretchDraw(pbDownloaded.ClientRect, FTorrentProgress); end; procedure TMainForm.StatusBarMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var pt: TPoint; rb: boolean; begin rb:=(Button = mbRight) and RpcObj.Connected; pt:=StatusBar.ClientToScreen(Point(X, Y)); case StatusBar.GetPanelIndexAt(X, Y) of 0: if Button = mbLeft then acConnOptions.Execute; 1: if rb then pmDownSpeeds.PopUp(pt.X, pt.Y); 2: if rb then pmUpSpeeds.PopUp(pt.X, pt.Y); end; end; {$ifdef LCLcarbon} type THackApplication = class(TApplication) end; {$endif LCLcarbon} procedure TMainForm.TickTimerTimer(Sender: TObject); var i: integer; begin TickTimer.Enabled:=False; try if not FStarted then begin Application.ProcessMessages; FStarted:=True; acConnect.Execute; Application.ProcessMessages; panTransfer.ChildSizing.Layout:=cclLeftToRightThenTopToBottom; panGeneralInfo.ChildSizing.Layout:=cclLeftToRightThenTopToBottom; panTransfer.ChildSizing.Layout:=cclNone; panGeneralInfo.ChildSizing.Layout:=cclNone; with panTransfer do ClientHeight:=Controls[ControlCount - 1].BoundsRect.Bottom + ChildSizing.TopBottomSpacing; with panGeneralInfo do ClientHeight:=Controls[ControlCount - 1].BoundsRect.Bottom + ChildSizing.TopBottomSpacing; panSearch.AutoSize:=False; if Ini.ReadBool('MainForm', 'FirstRun', True) then begin if not acResolveCountry.Checked then acResolveCountry.Execute; if acResolveCountry.Checked and not acShowCountryFlag.Checked then acShowCountryFlag.Execute; Ini.WriteBool('MainForm', 'FirstRun', False); end; i:=Ini.ReadInteger('Interface', 'LastNewVersionCheck', Trunc(Now)); if i + Ini.ReadInteger('Interface', 'CheckNewVersionDays', 5) <= Trunc(Now) then begin if Ini.ReadBool('Interface', 'AskCheckNewVersion', True) then begin Ini.WriteBool('Interface', 'AskCheckNewVersion', False); if not Ini.ReadBool('Interface', 'CheckNewVersion', False) then if MessageDlg(Format(SCheckNewVersion, [AppName]), mtConfirmation, mbYesNo, 0) = mrYes then Ini.WriteBool('Interface', 'CheckNewVersion', True); end; if Ini.ReadBool('Interface', 'CheckNewVersion', False) then CheckNewVersion; end; end; CheckAddTorrents; if RpcObj.Connected then FReconnectTimeOut:=0 else if panReconnect.Visible then if Now - FReconnectWaitStart >= FReconnectTimeOut/SecsPerDay then DoConnect else txReconnectSecs.Caption:=Format(sReconnect, [FReconnectTimeOut - Round(SecsPerDay*(Now - FReconnectWaitStart))]); if FSlowResponse.Visible then begin if RpcObj.RequestStartTime = 0 then FSlowResponse.Visible:=False; end else if (RpcObj.RequestStartTime <> 0) and (Now - RpcObj.RequestStartTime >= 1/SecsPerDay) then FSlowResponse.Visible:=True; if FDetailsWait.Visible then begin if (FDetailsWaitStart = 0) or not (rtDetails in RpcObj.RefreshNow) then begin FDetailsWaitStart:=0; FDetailsWait.Visible:=False; panDetailsWait.Visible:=False; end; end else if (FDetailsWaitStart <> 0) and (Now - FDetailsWaitStart >= 300/MSecsPerDay) then begin CenterDetailsWait; FDetailsWait.Visible:=True; panDetailsWait.Visible:=True; panDetailsWait.BringToFront; end; {$ifdef LCLcarbon} THackApplication(Application).ProcessAsyncCallQueue; if Active and (WindowState <> wsMinimized) then begin if not FFormActive then begin FFormActive:=True; CheckClipboardLink; end; end else FFormActive:=False; {$endif LCLcarbon} finally TickTimer.Enabled:=True; end; end; procedure TMainForm.FilterTimerTimer(Sender: TObject); begin FilterTimer.Enabled:=False; FFilterChanged:=True; DoRefresh(True); end; procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin if Ini.ReadBool('Interface', 'TrayClose', False) then begin {$ifdef darwin} CloseAction:=caMinimize; {$else} {$ifdef linux} if IsUnity then CloseAction:=caMinimize else {$endif linux} begin CloseAction:=caHide; HideApp; UpdateTray; end; {$endif darwin} exit; end; BeforeCloseApp; end; procedure TMainForm.PageInfoChange(Sender: TObject); begin if PageInfo.ActivePage.Tag <> 0 then FDetailsWaitStart:=Now; RpcObj.Lock; try RpcObj.AdvInfo:=GetPageInfoType(PageInfo.ActivePage); DoRefresh; finally RpcObj.Unlock; end; end; procedure TMainForm.TorrentsListTimerTimer(Sender: TObject); begin TorrentsListTimer.Enabled:=False; if RpcObj.CurTorrentId <> 0 then FDetailsWaitStart:=Now; DoRefresh; end; procedure TMainForm.pmFilesPopup(Sender: TObject); begin UpdateUI; end; procedure TMainForm.pmTorrentsPopup(Sender: TObject); begin UpdateUI; end; procedure TMainForm.TrayIconDblClick(Sender: TObject); begin {$ifndef darwin} acShowApp.Execute; {$endif darwin} end; procedure TMainForm.VSplitterChangeBounds(Sender: TObject); begin {$ifdef windows} Update; {$endif windows} end; procedure TMainForm.UrlLabelClick(Sender: TObject); begin AppBusy; OpenURL((Sender as TLabel).Caption); AppNormal; end; procedure TMainForm.CenterReconnectWindow; begin CenterOnParent(panReconnect); end; function TMainForm.DoConnect: boolean; var Sec, pwd: string; i, j: integer; begin Result:=True; panReconnect.Hide; DoDisconnect; Sec:='Connection.' + FCurConn; if not Ini.SectionExists(Sec) then Sec:='Connection'; i:=FPasswords.IndexOfName(FCurConn); pwd:=Ini.ReadString(Sec, 'Password', ''); if pwd = '-' then begin if i >= 0 then pwd:=FPasswords.ValueFromIndex[i] else begin pwd:=''; if not InputQuery(Format(SConnectTo, [FCurConn]), Format(SEnterPassword, [FCurConn]), pwd) then begin RpcObj.Url:='-'; Result:=False; exit; end; end; end else pwd:=DecodeBase64(pwd); if i >= 0 then FPasswords.Delete(i); if Ini.ReadBool(Sec, 'UseSSL', False) then begin RpcObj.InitSSL; if not IsSSLloaded then begin MessageDlg(Format(sSSLLoadError, [DLLSSLName, DLLUtilName]), mtError, [mbOK], 0); exit; end; RpcObj.Url:='https'; end else RpcObj.Url:='http'; RpcObj.Http.UserName:=Ini.ReadString(Sec, 'UserName', ''); RpcObj.Http.Password:=pwd; RpcObj.Http.ProxyHost:=''; RpcObj.Http.ProxyPort:=''; RpcObj.Http.ProxyUser:=''; RpcObj.Http.ProxyPass:=''; RpcObj.Http.Sock.SocksIP:=''; RpcObj.Http.Sock.SocksPort:=''; RpcObj.Http.Sock.SocksUsername:=''; RpcObj.Http.Sock.SocksPassword:=''; if Ini.ReadBool(Sec, 'UseProxy', False) then begin if Ini.ReadBool(Sec, 'UseSockProxy', False) then begin RpcObj.Http.Sock.SocksIP := Ini.ReadString(Sec, 'ProxyHost', ''); RpcObj.Http.Sock.SocksPort := IntToStr(Ini.ReadInteger(Sec, 'ProxyPort', 8080)); RpcObj.Http.Sock.SocksUsername := Ini.ReadString(Sec, 'ProxyUser', ''); RpcObj.Http.Sock.SocksPassword := DecodeBase64(Ini.ReadString(Sec, 'ProxyPass', '')); end else begin RpcObj.Http.ProxyHost:=Ini.ReadString(Sec, 'ProxyHost', ''); RpcObj.Http.ProxyPort:=IntToStr(Ini.ReadInteger(Sec, 'ProxyPort', 8080)); RpcObj.Http.ProxyUser:=Ini.ReadString(Sec, 'ProxyUser', ''); RpcObj.Http.ProxyPass:=DecodeBase64(Ini.ReadString(Sec, 'ProxyPass', '')); end; end; RpcObj.RpcPath:=Ini.ReadString(Sec, 'RpcPath', ''); RpcObj.Url:=Format('%s://%s:%d', [RpcObj.Url, Ini.ReadString(Sec, 'Host', ''), Ini.ReadInteger(Sec, 'Port', 9091)]); SetRefreshInterval; RpcObj.InfoStatus:=sConnectingToDaemon; CheckStatus; TrayIcon.Hint:=RpcObj.InfoStatus; RpcObj.Connect; FPathMap.Text:=StringReplace(Ini.ReadString(Sec, 'PathMap', ''), '|', LineEnding, [rfReplaceAll]); i:=0; while i < FPathMap.Count do if Trim(FPathMap.ValueFromIndex[i]) = '' then FPathMap.Delete(i) else Inc(i); Ini.WriteString('Hosts', 'CurHost', FCurConn); if FCurConn <> Ini.ReadString('Hosts', 'Host1', '') then begin Ini.WriteString('Hosts', 'Host1', FCurConn); j:=2; for i:=0 to pmConnections.Items.Count - 1 do with pmConnections.Items[i] do if (Tag = 0) and (Caption <> FCurConn) then begin Ini.WriteString('Hosts', Format('Host%d', [j]), Caption); Inc(j); end; Ini.UpdateFile; UpdateConnections; end else if pmConnections.Items[0].Tag = 0 then begin pmConnections.Items[0].Checked:=True; miConnect.Items[0].Checked:=True; end; end; procedure TMainForm.DoDisconnect; var i: integer; begin TorrentsListTimer.Enabled:=False; FilterTimer.Enabled:=False; ClearDetailsInfo; gTorrents.Items.Clear; gTorrents.Enabled:=False; gTorrents.Color:=clBtnFace; lvPeers.Enabled:=False; lvPeers.Color:=gTorrents.Color; lvFiles.Enabled:=False; lvFiles.Color:=gTorrents.Color; lvTrackers.Enabled:=False; lvTrackers.Color:=gTorrents.Color; lvFilter.Enabled:=False; lvFilter.Color:=gTorrents.Color; with lvFilter do begin Items[0, 0]:=UTF8Decode(SAll); Items[0, 1]:=UTF8Decode(SDownloading); Items[0, 2]:=UTF8Decode(SCompleted); Items[0, 3]:=UTF8Decode(SActive); Items[0, 4]:=UTF8Decode(SInactive); Items[0, 5]:=UTF8Decode(sStopped); Items[0, 6]:=UTF8Decode(sErrorState); end; edSearch.Enabled:=False; edSearch.Color:=gTorrents.Color; edSearch.Text:=''; with gStats do begin BeginUpdate; try for i:=0 to Items.Count - 1 do begin Items[1, i]:=NULL; Items[2, i]:=NULL; end; finally EndUpdate; end; Enabled:=False; Color:=gTorrents.Color; end; RpcObj.Disconnect; RpcObj.InfoStatus:=sDisconnected; CheckStatus; UpdateUI; TrayIcon.Hint:=RpcObj.InfoStatus; gTorrents.Items.RowCnt:=0; FTorrents.RowCnt:=0; lvFilter.Row:=0; lvFilter.Items.RowCnt:=StatusFiltersCount; TorrentsListTimer.Enabled:=False; FilterTimer.Enabled:=False; pmConnections.Items[0].Checked:=False; miConnect.Items[0].Checked:=False; FCurDownSpeedLimit:=-2; FCurUpSpeedLimit:=-2; FillSpeedsMenu; end; procedure TMainForm.ClearDetailsInfo(Skip: TAdvInfoType); procedure ClearChildren(AParent: TPanel); var i: integer; begin AParent.AutoSize:=False; AParent.ChildSizing.Layout:=cclNone; for i:=0 to AParent.ControlCount - 1 do begin if AParent.Controls[i] is TLabel then with AParent.Controls[i] as TLabel do begin if (Length(Name) < 5) or (Copy(Name, Length(Name) - 4, 5) <> 'Label') then Caption:=''; PopupMenu:=pmLabels; end; end; end; var i, t: integer; begin if RpcObj.CurTorrentId = 0 then begin Skip:=aiNone; t:=0; end else t:=1; FDetailsWaitStart:=0; if Skip <> aiFiles then begin FFiles.Clear; tabFiles.Caption:=FFilesCapt; end; if Skip <> aiPeers then lvPeers.Items.Clear; if Skip <> aiTrackers then lvTrackers.Items.Clear; if Skip <> aiGeneral then begin ClearChildren(panGeneralInfo); ClearChildren(panTransfer); ProcessPieces('', 0, 0); txDownProgress.AutoSize:=False; txDownProgress.Caption:=''; end; for i:=0 to PageInfo.PageCount - 1 do PageInfo.Pages[i].Tag:=t; end; function TMainForm.SelectRemoteFolder(const CurFolder, DialogTitle: string): string; var i, j: integer; s, ss, sss, fn: string; dlg: TSelectDirectoryDialog; d: char; begin Result:=''; if Trim(FPathMap.Text) = '' then begin MessageDlg(sNoPathMapping, mtInformation, [mbOK], 0); exit; end; s:=MapRemoteToLocal(CurFolder); if (s = '') or not DirectoryExistsUTF8(s) then s:=FPathMap.ValueFromIndex[0]; if not DirectoryExistsUTF8(s) then begin MessageDlg(sNoPathMapping, mtInformation, [mbOK], 0); exit; end; dlg:=TSelectDirectoryDialog.Create(nil); try dlg.Title:=DialogTitle; dlg.InitialDir:=s; if not dlg.Execute then exit; fn:=dlg.FileName; for i:=0 to FPathMap.Count - 1 do begin s:=FPathMap[i]; j:=Pos('=', s); if j > 0 then begin ss:=FixSeparators(Copy(s, j + 1, MaxInt)); sss:=IncludeTrailingPathDelimiter(ss); if (CompareFilePath(ss, fn) = 0) or (CompareFilePath(sss, Copy(fn, 1, Length(sss))) = 0) then begin Result:=Copy(s, 1, j - 1); d:='/'; for j:=1 to Length(Result) do if Result[j] in ['/','\'] then begin d:=Result[j]; break; end; if CompareFilePath(ss, fn) <> 0 then begin if (Result <> '') and (Copy(Result, Length(Result), 1) <> d) then Result:=Result + d; ss:=IncludeProperTrailingPathDelimiter(ss); Result:=Result + Copy(fn, Length(ss) + 1, MaxInt); end; Result:=StringReplace(Result, DirectorySeparator, d, [rfReplaceAll]); if Copy(Result, Length(Result), 1) = d then SetLength(Result, Length(Result) - 1); end; end; end; finally dlg.Free; end; if Result = '' then MessageDlg(sNoPathMapping, mtError, [mbOK], 0); end; procedure TMainForm.ConnectionSettingsChanged(const ActiveConnection: string; ForceReconnect: boolean); begin UpdateConnections; if (FCurConn <> ActiveConnection) or ForceReconnect then begin DoDisconnect; FReconnectTimeOut:=-1; FCurConn:=ActiveConnection; if FCurConn <> '' then DoConnect; end; end; procedure TMainForm.UpdateUI; var e: boolean; begin e:=((Screen.ActiveForm = Self) or not Visible or (WindowState = wsMinimized)) and not gTorrents.EditorMode and not lvFiles.EditorMode; acConnect.Enabled:=e; acOptions.Enabled:=e; acConnOptions.Enabled:=e; e:=RpcObj.Connected and e; acDisconnect.Enabled:=e; acSelectAll.Enabled:=e; acAddTorrent.Enabled:=e; acAddLink.Enabled:=e; acDaemonOptions.Enabled:=e; acStartAllTorrents.Enabled:=e and RpcObj.Connected; acStopAllTorrents.Enabled:=acStartAllTorrents.Enabled; acStartTorrent.Enabled:=e and (gTorrents.Items.Count > 0); acForceStartTorrent.Enabled:=acStartTorrent.Enabled and (RpcObj.RPCVersion >= 14); acStopTorrent.Enabled:=e and (gTorrents.Items.Count > 0); acVerifyTorrent.Enabled:=e and (gTorrents.Items.Count > 0); acRemoveTorrent.Enabled:=e and (gTorrents.Items.Count > 0) and not edSearch.Focused; acRemoveTorrentAndData.Enabled:=acRemoveTorrent.Enabled and (RpcObj.RPCVersion >= 4); acReannounceTorrent.Enabled:=acVerifyTorrent.Enabled and (RpcObj.RPCVersion >= 5); acMoveTorrent.Enabled:=acVerifyTorrent.Enabled and (RpcObj.RPCVersion >= 6); acTorrentProps.Enabled:=acVerifyTorrent.Enabled; acOpenContainingFolder.Enabled:=acTorrentProps.Enabled and (RpcObj.RPCVersion >= 4); pmiPriority.Enabled:=e and (gTorrents.Items.Count > 0); miPriority.Enabled:=pmiPriority.Enabled; acSetHighPriority.Enabled:=e and (gTorrents.Items.Count > 0) and ( ( not lvFiles.Focused and (RpcObj.RPCVersion >= 5) ) or ((lvFiles.Items.Count > 0) and (PageInfo.ActivePage = tabFiles)) ); acSetNormalPriority.Enabled:=acSetHighPriority.Enabled; acSetLowPriority.Enabled:=acSetHighPriority.Enabled; miQueue.Enabled:=e and (gTorrents.Items.Count > 0) and (RpcObj.RPCVersion >= 14); pmiQueue.Enabled:=miQueue.Enabled; acQMoveTop.Enabled:=miQueue.Enabled; acQMoveUp.Enabled:=miQueue.Enabled; acQMoveDown.Enabled:=miQueue.Enabled; acQMoveBottom.Enabled:=miQueue.Enabled; acOpenFile.Enabled:=acSetHighPriority.Enabled and (lvFiles.SelCount < 2) and (RpcObj.RPCVersion >= 4); acCopyPath.Enabled:=acOpenFile.Enabled; acSetNotDownload.Enabled:=acSetHighPriority.Enabled; acRename.Enabled:=(RpcObj.RPCVersion >= 15) and acSetHighPriority.Enabled; acSetupColumns.Enabled:=e; acUpdateBlocklist.Enabled:=(acUpdateBlocklist.Tag <> 0) and e and (RpcObj.RPCVersion >= 5); acAddTracker.Enabled:=acTorrentProps.Enabled and (RpcObj.RPCVersion >= 10); acAdvEditTrackers.Enabled:=acAddTracker.Enabled; acEditTracker.Enabled:=acAddTracker.Enabled and (lvTrackers.Items.Count > 0); acDelTracker.Enabled:=acEditTracker.Enabled; acAltSpeed.Enabled:=e and (RpcObj.RPCVersion >= 5); pmiDownSpeedLimit.Enabled:=RpcObj.Connected; pmiUpSpeedLimit.Enabled:=RpcObj.Connected; end; procedure TMainForm.ShowConnOptions(NewConnection: boolean); var frm: TConnOptionsForm; begin AppBusy; frm:=TConnOptionsForm.Create(Self); with frm do try ActiveConnection:=FCurConn; if NewConnection then begin Caption:=SNewConnection; btNewClick(nil); if Ini.ReadInteger('Hosts', 'Count', 0) = 0 then begin panTop.Visible:=False; {$ifdef LCLgtk2} panTop.Height:=0; {$endif LCLgtk2} with Page.BorderSpacing do Top:=Left; tabPaths.TabVisible:=False; tabMisc.TabVisible:=False; end else begin btNew.Hide; btRename.Hide; btDel.Hide; panTop.ClientHeight:=btNew.Top; end; cbShowAdvancedClick(nil); AutoSizeForm(frm); end; AppNormal; ShowModal; ConnectionSettingsChanged(ActiveConnection, ActiveSettingChanged); finally Free; end; end; procedure TMainForm.SaveColumns(LV: TVarGrid; const AName: string; FullInfo: boolean); var i: integer; begin for i:=0 to LV.Columns.Count - 1 do with LV.Columns[i] do begin Ini.WriteInteger(AName, Format('Id%d', [i]), ID - 1); Ini.WriteInteger(AName, Format('Width%d', [i]), Width); if FullInfo then begin Ini.WriteInteger(AName, Format('Index%d', [i]), Index); Ini.WriteBool(AName, Format('Visible%d', [i]), Visible); end; end; if LV.SortColumn >= 0 then begin Ini.WriteInteger(AName, 'SortColumn', LV.SortColumn); Ini.WriteInteger(AName, 'SortOrder', integer(LV.SortOrder)); end; end; procedure TMainForm.LoadColumns(LV: TVarGrid; const AName: string; FullInfo: boolean); var i, j, ColId: integer; begin LV.Columns.BeginUpdate; try for i:=0 to LV.Columns.Count - 1 do begin ColId:=Ini.ReadInteger(AName, Format('Id%d', [i]), -1); if ColId = -1 then continue; for j:=0 to LV.Columns.Count - 1 do with LV.Columns[j] do if ID - 1 = ColId then begin if FullInfo then begin Index:=Ini.ReadInteger(AName, Format('Index%d', [i]), Index); Visible:=Ini.ReadBool(AName, Format('Visible%d', [i]), Visible); end; Width:=Ini.ReadInteger(AName, Format('Width%d', [i]), Width); break; end; end; finally LV.Columns.EndUpdate; end; LV.SortColumn:=Ini.ReadInteger(AName, 'SortColumn', LV.SortColumn); LV.SortOrder:=TSortOrder(Ini.ReadInteger(AName, 'SortOrder', integer(LV.SortOrder))); end; function TMainForm.GetTorrentError(t: TJSONObject; Status: integer): string; var i: integer; stats: TJSONArray; err, gerr: widestring; NoTrackerError: boolean; begin Result:=''; gerr:=t.Strings['errorString']; if RpcObj.RPCVersion >= 7 then begin NoTrackerError:=False; stats:=t.Arrays['trackerStats']; for i:=0 to stats.Count - 1 do with stats.Objects[i] do begin err:=''; if Booleans['hasAnnounced'] and not Booleans['lastAnnounceSucceeded'] then err:=Strings['lastAnnounceResult']; if err = 'Success' then err:=''; if err = '' then begin // If at least one tracker is working, then report no error NoTrackerError:=True; Result:=''; end else begin if not NoTrackerError and (Result = '') then Result:=sTrackerError + ': ' + UTF8Encode(err); // Workaround for transmission bug // If the global error string is equal to some tracker error string, // then igonore the global error string if gerr = err then gerr:=''; end; end; end else begin Result:=UTF8Encode(t.Strings['announceResponse']); if Result = 'Success' then Result:='' else if Result <> '' then begin i:=Pos('(', Result); if i <> 0 then if Copy(Result, i, 5) = '(200)' then Result:='' else Result:=sTrackerError + ': ' + Copy(Result, 1, i - 1); end; end; if (Result = '') or (Status = TR_STATUS_STOPPED) or (Status = TR_STATUS_FINISHED) then Result:=UTF8Encode(gerr); end; function TMainForm.SecondsToString(j: integer): string; begin if j < 60 then Result:=Format(sSecs, [j]) else if j < 60*60 then begin Result:=Format(sMins, [j div 60]); j:=j mod 60; if j > 0 then Result:=Format('%s, %s', [Result, Format(sSecs, [j])]); end else begin j:=(j + 30) div 60; if j < 60*24 then begin Result:=Format(sHours, [j div 60]); j:=j mod 60; if j > 0 then Result:=Format('%s, %s', [Result, Format(sMins, [j])]); end else begin j:=(j + 30) div 60; Result:=Format(sDays, [j div 24]); j:=j mod 24; if j > 0 then Result:=Format('%s, %s', [Result, Format(sHours, [j])]); end; end; end; procedure TMainForm.FillTorrentsList(list: TJSONArray); var i, j, row, crow, id, StateImg: integer; t: TJSONObject; f: double; ExistingRow: boolean; s, ss: string; function GetTorrentValue(AIndex: integer; const AName: string; AType: integer): boolean; var res: variant; i: integer; begin i:=t.IndexOfName(AName); Result:=i >= 0; if Result then case AType of vtInteger: res:=t.Items[i].AsInteger; vtExtended: res:=t.Items[i].AsFloat; else res:=t.Items[i].AsString; end else res:=NULL; FTorrents[AIndex, row]:=res; end; function StoreSpeed(var History: variant; Speed: integer): integer; var j, cnt: integer; p: PInteger; IsNew: boolean; res: Int64; begin IsNew:=VarIsEmpty(History); if IsNew then begin if Speed = 0 then begin Result:=0; exit; end; History:=VarArrayCreate([0, SpeedHistorySize], varInteger); end; p:=VarArrayLock(History); try if IsNew then begin for j:=1 to SpeedHistorySize do p[j]:=-1; j:=1; end else begin j:=Round((Now - cardinal(p[0])/SecsPerDay)/RpcObj.RefreshInterval); if j = 0 then j:=1; end; p[0]:=integer(cardinal(Round(Now*SecsPerDay))); // Shift speed array if j < SpeedHistorySize then Move(p[1], p[j + 1], (SpeedHistorySize - j)*SizeOf(integer)) else j:=SpeedHistorySize; while j > 0 do begin p[j]:=Speed; Dec(j); end; // Calc average speed res:=Speed; cnt:=1; for j:=2 to SpeedHistorySize do if p[j] < 0 then break else begin Inc(res, p[j]); Inc(cnt); end; Result:=res div cnt; finally VarArrayUnlock(History); end; if Result = 0 then VarClear(History); end; var FilterIdx, OldId: integer; TrackerFilter, PathFilter: string; UpSpeed, DownSpeed: double; DownCnt, SeedCnt, CompletedCnt, ActiveCnt, StoppedCnt, ErrorCnt: integer; IsActive: boolean; Paths: TStringList; v: variant; FieldExists: array of boolean; begin if gTorrents.Tag <> 0 then exit; if list = nil then begin ClearDetailsInfo; exit; end; { for i:=1 to 1000 do begin t:=TJSONObject.Create; t.Integers['id']:=i + 10000; t.Strings['name']:=Format('ZName %d', [i]); t.Integers['status']:=TR_STATUS_STOPPED; t.Arrays['trackerStats']:=TJSONArray.Create; t.Floats['sizeWhenDone']:=0; t.Floats['leftUntilDone']:=0; t.Integers['rateDownload']:=0; t.Integers['rateUpload']:=0; list.Add(t); end; } Paths:=TStringList.Create; try Paths.Sorted:=True; OldId:=RpcObj.CurTorrentId; IsActive:=gTorrents.Enabled; gTorrents.Enabled:=True; lvFilter.Enabled:=True; gTorrents.Color:=clWindow; lvFilter.Color:=clWindow; edSearch.Enabled:=True; edSearch.Color:=clWindow; if not IsActive then ActiveControl:=gTorrents; for i:=0 to FTrackers.Count - 1 do FTrackers.Objects[i]:=nil; // Check fields' existence SetLength(FieldExists, FTorrents.ColCnt); if list.Count > 0 then begin t:=list[0] as TJSONObject; FieldExists[idxName]:=t.IndexOfName('name') >= 0; FieldExists[idxRatio]:=t.IndexOfName('uploadRatio') >= 0; FieldExists[idxTracker]:=t.IndexOfName('trackers') >= 0; FieldExists[idxPath]:=t.IndexOfName('downloadDir') >= 0; FieldExists[idxPriority]:=t.IndexOfName('bandwidthPriority') >= 0; FieldExists[idxQueuePos]:=t.IndexOfName('queuePosition') >= 0; FieldExists[idxSeedingTime]:=t.IndexOfName('secondsSeeding') >= 0; end; UpSpeed:=0; DownSpeed:=0; DownCnt:=0; SeedCnt:=0; CompletedCnt:=0; ActiveCnt:=0; StoppedCnt:=0; ErrorCnt:=0; FilterIdx:=lvFilter.Row; if VarIsNull(lvFilter.Items[0, FilterIdx]) then Dec(FilterIdx); if FilterIdx >= StatusFiltersCount then if not VarIsNull(lvFilter.Items[-1, FilterIdx]) then begin PathFilter:=UTF8Encode(widestring(lvFilter.Items[-1, FilterIdx])); FilterIdx:=fltAll; end else begin TrackerFilter:=UTF8Encode(widestring(lvFilter.Items[0, FilterIdx])); FilterIdx:=fltAll; i:=RPos('(', TrackerFilter); if i > 0 then TrackerFilter:=Trim(Copy(TrackerFilter, 1, i - 1)); end; for i:=0 to FTorrents.Count - 1 do FTorrents[idxTag, i]:=0; for i:=0 to list.Count - 1 do begin StateImg:=-1; t:=list[i] as TJSONObject; id:=t.Integers['id']; ExistingRow:=FTorrents.Find(idxTorrentId, id, row); if not ExistingRow then FTorrents.InsertRow(row); FTorrents[idxTorrentId, row]:=t.Integers['id']; if FieldExists[idxName] then FTorrents[idxName, row]:=t.Strings['name']; j:=t.Integers['status']; if ExistingRow and (j = TR_STATUS_SEED) and (FTorrents[idxStatus, row] = TR_STATUS_DOWNLOAD) then DownloadFinished(UTF8Encode(widestring(FTorrents[idxName, row]))); FTorrents[idxStatus, row]:=j; if j = TR_STATUS_CHECK_WAIT then StateImg:=imgDownQueue else if j = TR_STATUS_CHECK then StateImg:=imgDownQueue else if j = TR_STATUS_DOWNLOAD_WAIT then StateImg:=imgDownQueue else if j = TR_STATUS_DOWNLOAD then StateImg:=imgDown else if j = TR_STATUS_SEED_WAIT then StateImg:=imgSeedQueue else if j = TR_STATUS_SEED then StateImg:=imgSeed else if j = TR_STATUS_STOPPED then StateImg:=imgDone; if GetTorrentError(t, j) <> '' then if t.Strings['errorString'] <> '' then StateImg:=imgError else if StateImg in [imgDown,imgSeed] then Inc(StateImg, 2); if j <> TR_STATUS_STOPPED then begin s:=GetTorrentError(t, j); if s <> '' then if t.Strings['errorString'] <> '' then StateImg:=imgError else if StateImg in [imgDown,imgSeed] then Inc(StateImg, 2); if RpcObj.RPCVersion >= 7 then begin s:=''; if t.Arrays['trackerStats'].Count > 0 then with t.Arrays['trackerStats'].Objects[0] do begin if integer(Integers['announceState']) in [2, 3] then s:=sTrackerUpdating else if Booleans['hasAnnounced'] then if Booleans['lastAnnounceSucceeded'] then s:=sTrackerWorking else s:=TranslateString(UTF8Encode(Strings['lastAnnounceResult']), True); if s = 'Success' then s:=sTrackerWorking; end; end else s:=t.Strings['announceResponse']; end else s:=''; FTorrents[idxTrackerStatus, row]:=UTF8Decode(s); if FTorrents[idxStatus, row] = TR_STATUS_CHECK then f:=t.Floats['recheckProgress']*100.0 else begin f:=t.Floats['sizeWhenDone']; if f <> 0 then f:=(f - t.Floats['leftUntilDone'])*100.0/f; if StateImg = imgDone then if (t.Floats['leftUntilDone'] <> 0) or (t.Floats['sizeWhenDone'] = 0) then StateImg:=imgStopped else FTorrents[idxStatus, row]:=TR_STATUS_FINISHED; end; if f < 0 then f:=0; FTorrents[idxDone, row]:=Int(f*10.0)/10.0; FTorrents[idxStateImg, row]:=StateImg; GetTorrentValue(idxDownSpeed, 'rateDownload', vtInteger); j:=StoreSpeed(FTorrents.ItemPtrs[idxDownSpeedHistory, row]^, FTorrents[idxDownSpeed, row]); if FCalcAvg and (StateImg in [imgDown, imgDownError]) then FTorrents[idxDownSpeed, row]:=j; GetTorrentValue(idxUpSpeed, 'rateUpload', vtInteger); j:=StoreSpeed(FTorrents.ItemPtrs[idxUpSpeedHistory, row]^, FTorrents[idxUpSpeed, row]); if FCalcAvg and (StateImg in [imgSeed, imgSeedError]) then FTorrents[idxUpSpeed, row]:=j; GetTorrentValue(idxSize, 'totalSize', vtExtended); GetTorrentValue(idxSizeToDowload, 'sizeWhenDone', vtExtended); GetTorrentValue(idxSeeds, 'peersSendingToUs', vtInteger); GetTorrentValue(idxPeers, 'peersGettingFromUs', vtInteger); GetTorrentValue(idxETA, 'eta', vtInteger); v:=FTorrents[idxETA, row]; if not VarIsNull(v) then if v < 0 then FTorrents[idxETA, row]:=MaxInt else begin f:=FTorrents[idxDownSpeed, row]; if f > 0 then FTorrents[idxETA, row]:=Round(t.Floats['leftUntilDone']/f); end; GetTorrentValue(idxDownloaded, 'downloadedEver', vtExtended); GetTorrentValue(idxUploaded, 'uploadedEver', vtExtended); GetTorrentValue(idxAddedOn, 'addedDate', vtExtended); GetTorrentValue(idxCompletedOn, 'doneDate', vtExtended); GetTorrentValue(idxLastActive, 'activityDate', vtExtended); if RpcObj.RPCVersion >= 7 then begin if t.Arrays['trackerStats'].Count > 0 then with t.Arrays['trackerStats'].Objects[0] do begin FTorrents[idxSeedsTotal, row]:=Integers['seederCount']; FTorrents[idxLeechersTotal, row]:=Integers['leecherCount']; end else begin FTorrents[idxSeedsTotal, row]:=-1; FTorrents[idxLeechersTotal, row]:=-1; end; if t.Floats['metadataPercentComplete'] <> 1.0 then begin FTorrents[idxSize, row]:=-1; FTorrents[idxSizeToDowload, row]:=-1; end; end else begin GetTorrentValue(idxSeedsTotal, 'seeders', vtInteger); GetTorrentValue(idxLeechersTotal, 'leechers', vtInteger); end; if FieldExists[idxRatio] then begin f:=t.Floats['uploadRatio']; if f = -2 then f:=MaxInt; FTorrents[idxRatio, row]:=f; end else FTorrents[idxRatio, row]:=NULL; if FieldExists[idxSeedingTime] then FTorrents[idxSeedingTime, row]:=t.Integers['secondsSeeding'] else FTorrents[idxSeedingTime, row]:=NULL; if RpcObj.RPCVersion >= 7 then begin if t.Arrays['trackerStats'].Count > 0 then s:=t.Arrays['trackerStats'].Objects[0].Strings['announce'] else s:=sNoTracker; end else if FieldExists[idxTracker] then s:=UTF8Encode(t.Arrays['trackers'].Objects[0].Strings['announce']) else begin s:=''; if VarIsEmpty(FTorrents[idxTracker, row]) then RpcObj.RequestFullInfo:=True; end; if s <> '' then begin j:=Pos('://', s); if j > 0 then s:=Copy(s, j + 3, MaxInt); j:=Pos('/', s); if j > 0 then s:=Copy(s, 1, j - 1); j:=Pos('.', s); if j > 0 then begin ss:=Copy(s, 1, j - 1); if AnsiCompareText(ss, 'bt') = 0 then System.Delete(s, 1, 3) else if (Length(ss) = 3) and (AnsiCompareText(Copy(ss, 1, 2), 'bt') = 0) and (ss[3] in ['1'..'9']) then System.Delete(s, 1, 4); end; j:=Pos(':', s); if j > 0 then System.Delete(s, j, MaxInt); FTorrents[idxTracker, row]:=UTF8Decode(s); end; if FieldExists[idxPath] then FTorrents[idxPath, row]:=UTF8Decode(ExcludeTrailingPathDelimiter(UTF8Encode(t.Strings['downloadDir']))) else if VarIsEmpty(FTorrents[idxPath, row]) then RpcObj.RequestFullInfo:=True; if not VarIsEmpty(FTorrents[idxPath, row]) then begin s:=UTF8Encode(widestring(FTorrents[idxPath, row])); j:=Paths.IndexOf(s); if j < 0 then Paths.AddObject(s, TObject(1)) else Paths.Objects[j]:=TObject(PtrInt(Paths.Objects[j]) + 1); end; if FieldExists[idxPriority] then FTorrents[idxPriority, row]:=t.Integers['bandwidthPriority']; if FieldExists[idxQueuePos] then begin j:=t.Integers['queuePosition']; if FTorrents[idxStatus, row] = TR_STATUS_FINISHED then Inc(j, FinishedQueue); FTorrents[idxQueuePos, row]:=j; end; DownSpeed:=DownSpeed + FTorrents[idxDownSpeed, row]; UpSpeed:=UpSpeed + FTorrents[idxUpSpeed, row]; FTorrents[idxTag, row]:=1; end; i:=0; while i < FTorrents.Count do if FTorrents[idxTag, i] = 0 then FTorrents.Delete(i) else Inc(i); gTorrents.Items.BeginUpdate; try for i:=0 to gTorrents.Items.Count - 1 do gTorrents.Items[idxTag, i]:=0; gTorrents.Items.Sort(idxTorrentId); for i:=0 to FTorrents.Count - 1 do begin IsActive:=(FTorrents[idxDownSpeed, i] <> 0) or (FTorrents[idxUpSpeed, i] <> 0); if IsActive then Inc(ActiveCnt); j:=FTorrents[idxStatus, i]; if j = TR_STATUS_DOWNLOAD then Inc(DownCnt) else if j = TR_STATUS_SEED then begin Inc(SeedCnt); Inc(CompletedCnt); end else if j = TR_STATUS_FINISHED then Inc(CompletedCnt); StateImg:=FTorrents[idxStateImg, i]; if StateImg in [imgStopped, imgDone] then Inc(StoppedCnt) else if StateImg in [imgDownError, imgSeedError, imgError] then Inc(ErrorCnt); if not VarIsEmpty(FTorrents[idxTracker, i]) then begin s:=UTF8Encode(widestring(FTorrents[idxTracker, i])); j:=FTrackers.IndexOf(s); if j < 0 then j:=FTrackers.Add(s); FTrackers.Objects[j]:=TObject(ptruint(FTrackers.Objects[j]) + 1); if (TrackerFilter <> '') and (TrackerFilter <> s) then continue; end; if (PathFilter <> '') and not VarIsEmpty(FTorrents[idxPath, i]) and (UTF8Decode(PathFilter) <> FTorrents[idxPath, i]) then continue; case FilterIdx of fltActive: if not IsActive then continue; fltInactive: if IsActive then continue; fltDown: if FTorrents[idxStatus, i] <> TR_STATUS_DOWNLOAD then continue; fltDone: if (StateImg <> imgDone) and (FTorrents[idxStatus, i] <> TR_STATUS_SEED) then continue; fltStopped: if not (StateImg in [imgStopped, imgDone]) then continue; fltError: if not (StateImg in [imgDownError, imgSeedError, imgError]) then continue; end; if edSearch.Text <> '' then if UTF8Pos(UTF8UpperCase(edSearch.Text), UTF8UpperCase(UTF8Encode(widestring(FTorrents[idxName, i])))) = 0 then continue; if not gTorrents.Items.Find(idxTorrentId, FTorrents[idxTorrentId, i], row) then gTorrents.Items.InsertRow(row); for j:=-TorrentsExtraColumns to FTorrents.ColCnt - 1 do if (j <> idxDownSpeedHistory) and (j <> idxUpSpeedHistory) then gTorrents.Items[j, row]:=FTorrents[j, i]; gTorrents.Items[idxTag, row]:=1; end; i:=0; while i < gTorrents.Items.Count do if gTorrents.Items[idxTag, i] = 0 then gTorrents.Items.Delete(i) else Inc(i); gTorrents.Sort; if gTorrents.Items.Count > 0 then begin if OldId <> 0 then begin i:=gTorrents.Items.IndexOf(idxTorrentId, OldId); if i >= 0 then gTorrents.Row:=i else if FFilterChanged then OldId:=0; end; if OldId = 0 then gTorrents.Row:=0; end; FFilterChanged:=False; finally gTorrents.Items.EndUpdate; end; gTorrentsClick(nil); crow:=-1; lvFilter.Items.BeginUpdate; try lvFilter.Items[0, 0]:=UTF8Decode(Format('%s (%d)', [SAll, list.Count])); lvFilter.Items[0, 1]:=UTF8Decode(Format('%s (%d)', [SDownloading, DownCnt])); lvFilter.Items[0, 2]:=UTF8Decode(Format('%s (%d)', [SCompleted, CompletedCnt])); lvFilter.Items[0, 3]:=UTF8Decode(Format('%s (%d)', [SActive, ActiveCnt])); lvFilter.Items[0, 4]:=UTF8Decode(Format('%s (%d)', [SInactive, FTorrents.Count - ActiveCnt])); lvFilter.Items[0, 5]:=UTF8Decode(Format('%s (%d)', [sStopped, StoppedCnt])); lvFilter.Items[0, 6]:=UTF8Decode(Format('%s (%d)', [sErrorState, ErrorCnt])); j:=StatusFiltersCount; if acFolderGrouping.Checked then begin lvFilter.Items[0, j]:=NULL; Inc(j); for i:=0 to Paths.Count - 1 do begin s:=ExtractFileName(Paths[i]); for row:=StatusFiltersCount + 1 to j - 1 do if ExtractFileName(UTF8Encode(widestring(lvFilter.Items[-1, row]))) = s then begin s:=Paths[i]; lvFilter.Items[0, row]:=UTF8Decode(Format('%s (%d)', [UTF8Encode(widestring(lvFilter.Items[-1, row])), ptruint(Paths.Objects[row - StatusFiltersCount - 1])])); end; lvFilter.Items[0, j]:=UTF8Decode(Format('%s (%d)', [s, ptruint(Paths.Objects[i])])); lvFilter.Items[-1, j]:=UTF8Decode(Paths[i]); if Paths[i] = PathFilter then crow:=j; Inc(j); end; end; row:=j; if acTrackerGrouping.Checked then begin if not VarIsNull(lvFilter.Items[0, row - 1]) then begin lvFilter.Items[0, row]:=NULL; Inc(row); end; i:=0; while i < FTrackers.Count do begin j:=ptruint(FTrackers.Objects[i]); if j > 0 then begin lvFilter.Items[0, row]:=UTF8Decode(Format('%s (%d)', [FTrackers[i], j])); lvFilter.Items[-1, row]:=NULL; if FTrackers[i] = TrackerFilter then crow:=row; Inc(i); Inc(row); end else FTrackers.Delete(i); end; end; lvFilter.Items.RowCnt:=row; finally lvFilter.Items.EndUpdate; end; if crow >= 0 then lvFilter.Row:=crow else if lvFilter.Row >= StatusFiltersCount then lvFilterClick(nil); CheckStatus; StatusBar.Panels[1].Text:=Format(sDownSpeed, [GetHumanSize(DownSpeed, 1)]); StatusBar.Panels[2].Text:=Format(sUpSpeed, [GetHumanSize(UpSpeed, 1)]); {$ifndef LCLcarbon} // There is memory leak in TTrayIcon implementation for Mac. // Disable tray icon update for Mac. TrayIcon.Hint:=Format(sDownloadingSeeding, [RpcObj.InfoStatus, LineEnding, DownCnt, SeedCnt, LineEnding, StatusBar.Panels[1].Text, StatusBar.Panels[2].Text]); {$endif LCLcarbon} finally Paths.Free; end; DetailsUpdated; end; procedure TMainForm.FillPeersList(list: TJSONArray); var i, j, row: integer; port: integer; d: TJSONData; p: TJSONObject; ip, s: string; hostinfo: PHostEntry; opt: TResolverOptions; WasEmpty: boolean; begin if list = nil then begin ClearDetailsInfo; exit; end; WasEmpty:=lvPeers.Items.Count = 0; lvPeers.Items.BeginUpdate; try lvPeers.Enabled:=True; lvPeers.Color:=clWindow; if FResolver = nil then begin opt:=[]; if acResolveHost.Checked then Include(opt, roResolveIP); if acResolveCountry.Checked then Include(opt, roResolveCountry); if opt <> [] then FResolver:=TIpResolver.Create(GetGeoIpDatabase, opt); end; for i:=0 to lvPeers.Items.Count - 1 do lvPeers.Items[idxPeerTag, i]:=0; lvPeers.Items.Sort(idxPeerIP); for i:=0 to list.Count - 1 do begin d:=list[i]; if not (d is TJSONObject) then continue; p:=d as TJSONObject; ip:=p.Strings['address']; if p.IndexOfName('port') >= 0 then port:=p.Integers['port'] else port:=0; s:=ip + ':' + IntToStr(port); if not lvPeers.Items.Find(idxPeerIP, s, row) then lvPeers.Items.InsertRow(row); lvPeers.Items[idxPeerIP, row]:=s; lvPeers.Items[idxPeerPort, row]:=port; if FResolver <> nil then hostinfo:=FResolver.Resolve(ip) else hostinfo:=nil; if hostinfo <> nil then lvPeers.Items[idxPeerHost, row]:=hostinfo^.HostName else lvPeers.Items[idxPeerHost, row]:=ip; if hostinfo <> nil then lvPeers.Items[idxPeerCountry, row]:=hostinfo^.CountryName else lvPeers.Items[idxPeerCountry, row]:=''; if acShowCountryFlag.Checked and (hostinfo <> nil) then begin if hostinfo^.ImageIndex = 0 then hostinfo^.ImageIndex:=GetFlagImage(hostinfo^.CountryCode); j:=hostinfo^.ImageIndex end else j:=0; lvPeers.Items[idxPeerCountryImage, row]:=j; lvPeers.Items[idxPeerClient, row]:=p.Strings['clientName']; lvPeers.Items[idxPeerFlags, row]:=p.Strings['flagStr']; lvPeers.Items[idxPeerDone, row]:=p.Floats['progress']; if p.IndexOfName('rateToClient') >= 0 then lvPeers.Items[idxPeerDownSpeed, row]:=p.Integers['rateToClient']; if p.IndexOfName('rateToPeer') >= 0 then lvPeers.Items[idxPeerUpSpeed, row]:=p.Integers['rateToPeer']; lvPeers.Items[idxPeerTag, row]:=1; end; i:=0; while i < lvPeers.Items.Count do if lvPeers.Items[idxPeerTag, i] = 0 then lvPeers.Items.Delete(i) else Inc(i); lvPeers.Sort; if WasEmpty and (lvPeers.Items.Count > 0) then lvPeers.Row:=0; finally lvPeers.Items.EndUpdate; end; DetailsUpdated; end; function TMainForm.GetFilesCommonPath(files: TJSONArray): string; var i: integer; d: TJSONData; f: TJSONObject; s: string; begin Result:=''; for i:=0 to files.Count - 1 do begin d:=files[i]; if not (d is TJSONObject) then continue; f:=d as TJSONObject; s:=UTF8Encode(f.Strings['name']); if i = 0 then Result:=ExtractFilePath(s) else begin while True do begin if Result = '' then exit; if Copy(s, 1, Length(Result)) <> Result then begin SetLength(Result, Length(Result) - 1); Result:=ExtractFilePath(Result); end else break; end; end; end; end; procedure TMainForm.InternalRemoveTorrent(const Msg, MsgMulti: string; RemoveLocalData: boolean); var args: TJSONObject; ids: variant; s: string; i, j, id: integer; begin if gTorrents.Items.Count = 0 then exit; gTorrents.Tag:=1; try gTorrents.EnsureSelectionVisible; ids:=GetSelectedTorrents; if gTorrents.SelCount < 2 then s:=Format(Msg, [UTF8Encode(widestring(gTorrents.Items[idxName, gTorrents.Items.IndexOf(idxTorrentId, ids[0])]))]) else s:=Format(MsgMulti, [gTorrents.SelCount]); if MessageDlg('', s, mtConfirmation, mbYesNo, 0, mbNo) <> mrYes then exit; finally gTorrents.Tag:=0; end; args:=TJSONObject.Create; if RemoveLocalData then args.Add('delete-local-data', TJSONIntegerNumber.Create(1)); if TorrentAction(ids, 'torrent-remove', args) then begin with gTorrents do begin BeginUpdate; try i:=0; while i < Items.Count do begin id:=Items[idxTorrentId, i]; for j:=0 to VarArrayHighBound(ids, 1) do if id = ids[j] then begin Items.Items[idxDeleted, i]:=1; break; end; Inc(i); end; finally EndUpdate; end; end; if RemoveLocalData then RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession]; end; end; function TMainForm.IncludeProperTrailingPathDelimiter(const s: string): string; var i: integer; d: char; begin Result:=s; if Result = '' then exit; d:='/'; for i:=1 to Length(Result) do if Result[i] in ['/','\'] then begin d:=Result[i]; break; end; if Result[Length(Result)] <> d then Result:=Result + d; end; procedure TMainForm.FillFilesList(ATorrentId: integer; list, priorities, wanted: TJSONArray; const DownloadDir: WideString); begin if lvFiles.Tag <> 0 then exit; if (list = nil) or (priorities = nil) or (wanted = nil) then begin ClearDetailsInfo; exit; end; lvFiles.Enabled:=True; lvFiles.Color:=clWindow; FFilesTree.DownloadDir:=UTF8Encode(DownloadDir); FFilesTree.FillTree(ATorrentId, list, priorities, wanted); tabFiles.Caption:=Format('%s (%d)', [FFilesCapt, list.Count]); DetailsUpdated; end; procedure TMainForm.FillGeneralInfo(t: TJSONObject); var i, j, idx: integer; s: string; f: double; begin if (gTorrents.Items.Count = 0) or (t = nil) then begin ClearDetailsInfo; exit; end; idx:=gTorrents.Items.IndexOf(idxTorrentId, t.Integers['id']); if idx = -1 then begin ClearDetailsInfo; exit; end; txDownProgress.Caption:=Format('%.1f%%', [double(gTorrents.Items[idxDone, idx])]); txDownProgress.AutoSize:=True; if RpcObj.RPCVersion >= 5 then s:=t.Strings['pieces'] else s:=''; ProcessPieces(s, t.Integers['pieceCount'], gTorrents.Items[idxDone, idx]); panTransfer.ChildSizing.Layout:=cclNone; txStatus.Caption:=GetTorrentStatus(idx); txError.Caption:=GetTorrentError(t, gTorrents.Items[idxStatus, idx]); i:=t.Integers['eta']; f:=gTorrents.Items[idxDownSpeed, idx]; if f > 0 then i:=Round(t.Floats['leftUntilDone']/f); txRemaining.Caption:=EtaToString(i); txDownloaded.Caption:=GetHumanSize(t.Floats['downloadedEver']); txUploaded.Caption:=GetHumanSize(t.Floats['uploadedEver']); f:=t.Floats['pieceSize']; if f > 0 then i:=Round(t.Floats['corruptEver']/f) else i:=0; txWasted.Caption:=Format(sHashfails, [GetHumanSize(t.Floats['corruptEver']), i]); s:=GetHumanSize(gTorrents.Items[idxDownSpeed, idx], 1)+sPerSecond; if t.IndexOfName('secondsDownloading') >= 0 then begin f:=t.Integers['secondsDownloading']; if f > 0 then s:=Format('%s (%s: %s)', [s, SAverage, GetHumanSize(t.Floats['downloadedEver']/f, 1) + sPerSecond]); end; txDownSpeed.Caption:=s; txUpSpeed.Caption:=GetHumanSize(gTorrents.Items[idxUpSpeed, idx], 1)+sPerSecond; s:=RatioToString(t.Floats['uploadRatio']); if t.IndexOfName('secondsSeeding') >= 0 then begin i:=t.Integers['secondsSeeding']; if i > 0 then s:=Format('%s (%s)', [s, EtaToString(i)]); end; txRatio.Caption:=s; if RpcObj.RPCVersion < 5 then begin // RPC versions prior to v5 j:=t.Integers['downloadLimitMode']; if j = TR_SPEEDLIMIT_GLOBAL then s:='-' else begin i:=t.Integers['downloadLimit']; if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then s:=Utf8Encode(WideString(WideChar($221E))) else s:=GetHumanSize(i*1024)+sPerSecond; end; txDownLimit.Caption:=s; j:=t.Integers['uploadLimitMode']; if j = TR_SPEEDLIMIT_GLOBAL then s:='-' else begin i:=t.Integers['uploadLimit']; if (i < 0) or (j = TR_SPEEDLIMIT_UNLIMITED) then s:=Utf8Encode(WideString(WideChar($221E))) else s:=GetHumanSize(i*1024)+sPerSecond; end; txUpLimit.Caption:=s; end else begin // RPC version 5 if t.Booleans['downloadLimited'] then begin i:=t.Integers['downloadLimit']; if i < 0 then s:=Utf8Encode(WideString(WideChar($221E))) else s:=GetHumanSize(i*1024)+sPerSecond; end else s:='-'; txDownLimit.Caption:=s; if t.Booleans['uploadLimited'] then begin i:=t.Integers['uploadLimit']; if i < 0 then s:=Utf8Encode(WideString(WideChar($221E))) else s:=GetHumanSize(i*1024)+sPerSecond; end else s:='-'; txUpLimit.Caption:=s; end; if RpcObj.RPCVersion >= 7 then with t.Arrays['trackerStats'] do begin if Count > 0 then begin if integer(Objects[0].Integers['announceState']) in [2, 3] then f:=1 else f:=Objects[0].Floats['nextAnnounceTime']; end else f:=0; end else f:=t.Floats['nextAnnounceTime']; if f = 0 then s:='-' else if f = 1 then s:=sUpdating else s:=DateTimeToStr(UnixToDateTime(Trunc(f)) + GetTimeZoneDelta); txTrackerUpdate.Caption:=s; txTracker.Caption:=UTF8Encode(widestring(gTorrents.Items[idxTracker, idx])); if RpcObj.RPCVersion >= 7 then if t.Arrays['trackerStats'].Count > 0 then i:=t.Arrays['trackerStats'].Objects[0].Integers['seederCount'] else i:=-1 else i:=t.Integers['seeders']; s:=GetSeedsText(t.Integers['peersSendingToUs'], i); txSeeds.Caption:=StringReplace(s, '/', ' ' + sOf + ' ', []) + ' '+ sConnected; if RpcObj.RPCVersion >= 7 then if t.Arrays['trackerStats'].Count > 0 then i:=t.Arrays['trackerStats'].Objects[0].Integers['leecherCount'] else i:=-1 else i:=t.Integers['leechers']; s:=GetPeersText(t.Integers['peersGettingFromUs'], -1, i); s:=StringReplace(s, ' ', ' '+ sConnected +' ', []); s:=StringReplace(s, '/', ' ' + sOf + ' ', []); txPeers.Caption:=StringReplace(s, ')', ' '+ sInSwarm+ ')', []); txMaxPeers.Caption:=t.Strings['maxConnectedPeers']; txLastActive.Caption:=TorrentDateTimeToString(Trunc(t.Floats['activityDate'])); panTransfer.ChildSizing.Layout:=cclLeftToRightThenTopToBottom; panGeneralInfo.ChildSizing.Layout:=cclNone; s:=UTF8Encode(widestring(gTorrents.Items[idxName, idx])); if RpcObj.RPCVersion >= 4 then s:=IncludeProperTrailingPathDelimiter(UTF8Encode(t.Strings['downloadDir'])) + s; txTorrentName.Caption:=s; s:=Trim(UTF8Encode(t.Strings['creator'])); if s <> '' then s:=' by ' + s; txCreated.Caption:=TorrentDateTimeToString(Trunc(t.Floats['dateCreated'])) + s; if gTorrents.Items[idxSize, idx] >= 0 then begin txTotalSize.Caption:=Format(sDone, [GetHumanSize(t.Floats['totalSize']), GetHumanSize(t.Floats['sizeWhenDone'] - t.Floats['leftUntilDone'])]); if t.Floats['totalSize'] = t.Floats['haveValid'] then i:=t.Integers['pieceCount'] else i:=Trunc(t.Floats['haveValid']/t.Floats['pieceSize']); txPieces.Caption:=Format(sHave, [t.Integers['pieceCount'], GetHumanSize(t.Floats['pieceSize']), i]); end else begin txTotalSize.Caption:='?'; txPieces.Caption:='?'; end; txHash.Caption:=t.Strings['hashString']; txComment.Caption:=UTF8Encode(t.Strings['comment']); if (AnsiCompareText(Copy(txComment.Caption, 1, 7), 'http://') = 0) or (AnsiCompareText(Copy(txComment.Caption, 1, 8), 'https://') = 0) then begin if not Assigned(txComment.OnClick) then begin txComment.OnClick:=@UrlLabelClick; txComment.Cursor:=crHandPoint; txComment.Font.Color:=clBlue; txComment.Font.Style:=[fsUnderline]; end; end else begin if Assigned(txComment.OnClick) then begin txComment.OnClick:=nil; txComment.Cursor:=crDefault; txComment.ParentFont:=True; end; end; txAddedOn.Caption:=TorrentDateTimeToString(Trunc(t.Floats['addedDate'])); txCompletedOn.Caption:=TorrentDateTimeToString(Trunc(t.Floats['doneDate'])); panGeneralInfo.ChildSizing.Layout:=cclLeftToRightThenTopToBottom; DetailsUpdated; end; procedure TMainForm.FillTrackersList(TrackersData: TJSONObject); var i, tidx, row: integer; id: integer; d: TJSONData; t: TJSONObject; f: double; s: string; Trackers, TrackerStats: TJSONArray; WasEmpty, NoInfo: boolean; begin if TrackersData = nil then begin ClearDetailsInfo; exit; end; Trackers:=TrackersData.Arrays['trackers']; if RpcObj.RPCVersion >= 7 then TrackerStats:=TrackersData.Arrays['trackerStats'] else TrackerStats:=nil; tidx:=gTorrents.Items.IndexOf(idxTorrentId, TrackersData.Integers['id']); if tidx = -1 then begin ClearDetailsInfo; exit; end; i:=gTorrents.Items[idxStatus, tidx]; NoInfo:=(i = TR_STATUS_STOPPED) or (i = TR_STATUS_FINISHED); WasEmpty:=lvTrackers.Items.Count = 0; lvTrackers.Items.BeginUpdate; try lvTrackers.Enabled:=True; lvTrackers.Color:=clWindow; for i:=0 to lvTrackers.Items.Count - 1 do lvTrackers.Items[idxTrackerTag, i]:=0; lvTrackers.Items.Sort(idxTrackerID); for i:=0 to Trackers.Count - 1 do begin d:=Trackers[i]; if not (d is TJSONObject) then continue; t:=d as TJSONObject; if t.IndexOfName('id') >= 0 then id:=t.Integers['id'] else id:=i; if not lvTrackers.Items.Find(idxTrackerID, id, row) then lvTrackers.Items.InsertRow(row); lvTrackers.Items[idxTrackerID, row]:=id; lvTrackers.Items[idxTrackersListName, row]:=t.Strings['announce']; if NoInfo then begin lvTrackers.Items[idxTrackersListStatus, row]:=NULL; lvTrackers.Items[idxTrackersListSeeds, row]:=NULL; f:=0; end else if TrackerStats <> nil then begin f:=0; if i < TrackerStats.Count then with TrackerStats.Objects[i] do begin s:=''; if integer(Integers['announceState']) in [2, 3] then s:=sTrackerUpdating else if Booleans['hasAnnounced'] then if Booleans['lastAnnounceSucceeded'] then s:=sTrackerWorking else s:=TranslateString(UTF8Encode(Strings['lastAnnounceResult']), True); if s = 'Success' then s:=sTrackerWorking; lvTrackers.Items[idxTrackersListStatus, row]:=UTF8Decode(s); lvTrackers.Items[idxTrackersListSeeds, row]:=Integers['seederCount']; if integer(Integers['announceState']) in [2, 3] then f:=1 else f:=Floats['nextAnnounceTime']; end; end else begin if i = 0 then begin lvTrackers.Items[idxTrackersListStatus, row]:=gTorrents.Items[idxTrackerStatus, tidx]; lvTrackers.Items[idxTrackersListSeeds, row]:=gTorrents.Items[idxSeedsTotal, tidx]; end; f:=TrackersData.Floats['nextAnnounceTime']; end; if f > 1 then begin f:=(UnixToDateTime(Trunc(f)) + GetTimeZoneDelta - Now)*SecsPerDay; if f < 0 then f:=0; end; if (TrackerStats <> nil) or (i = 0) then lvTrackers.Items[idxTrackersListUpdateIn, row]:=f; lvTrackers.Items[idxTrackerTag, row]:=1; end; i:=0; while i < lvTrackers.Items.Count do if lvTrackers.Items[idxTrackerTag, i] = 0 then lvTrackers.Items.Delete(i) else Inc(i); lvTrackers.Sort; if WasEmpty and (lvTrackers.Items.Count > 0) then lvTrackers.Row:=0; finally lvTrackers.Items.EndUpdate; end; DetailsUpdated; end; procedure TMainForm.FillSessionInfo(s: TJSONObject); var d, u: integer; begin {$ifdef LCLcarbon} TrayIcon.Tag:=0; {$endif LCLcarbon} if RpcObj.RPCVersion < 14 then begin TR_STATUS_STOPPED:=TR_STATUS_STOPPED_1; TR_STATUS_CHECK_WAIT:=TR_STATUS_CHECK_WAIT_1; TR_STATUS_CHECK:=TR_STATUS_CHECK_1; TR_STATUS_DOWNLOAD_WAIT:=-1; TR_STATUS_DOWNLOAD:=TR_STATUS_DOWNLOAD_1; TR_STATUS_SEED_WAIT:=-1; TR_STATUS_SEED:=TR_STATUS_SEED_1; end else begin TR_STATUS_STOPPED:=TR_STATUS_STOPPED_2; TR_STATUS_CHECK_WAIT:=TR_STATUS_CHECK_WAIT_2; TR_STATUS_CHECK:=TR_STATUS_CHECK_2; TR_STATUS_DOWNLOAD_WAIT:=TR_STATUS_DOWNLOAD_WAIT_2; TR_STATUS_DOWNLOAD:=TR_STATUS_DOWNLOAD_2; TR_STATUS_SEED_WAIT:=TR_STATUS_SEED_WAIT_2; TR_STATUS_SEED:=TR_STATUS_SEED_2; end; UpdateUIRpcVersion(RpcObj.RPCVersion); if RpcObj.RPCVersion >= 5 then begin {$ifdef LCLcarbon} if acAltSpeed.Checked <> (s.Integers['alt-speed-enabled'] <> 0) then TrayIcon.Tag:=1; {$endif LCLcarbon} acAltSpeed.Checked:=s.Integers['alt-speed-enabled'] <> 0; acUpdateBlocklist.Tag:=s.Integers['blocklist-enabled']; acUpdateBlocklist.Enabled:=acUpdateBlocklist.Tag <> 0; end; if s.IndexOfName('download-dir-free-space') >= 0 then StatusBar.Panels[3].Text:=Format(SFreeSpace, [GetHumanSize(s.Floats['download-dir-free-space'])]); if (RpcObj.RPCVersion >= 5) and acAltSpeed.Checked then begin d:=s.Integers['alt-speed-down']; u:=s.Integers['alt-speed-up'] end else begin if s.Integers['speed-limit-down-enabled'] <> 0 then d:=s.Integers['speed-limit-down'] else d:=-1; if s.Integers['speed-limit-up-enabled'] <> 0 then u:=s.Integers['speed-limit-up'] else u:=-1; end; {$ifdef LCLcarbon} UpdateUI; {$endif LCLcarbon} if (FCurDownSpeedLimit <> d) or (FCurUpSpeedLimit <> u) then begin FCurDownSpeedLimit:=d; FCurUpSpeedLimit:=u; FillSpeedsMenu; end; {$ifdef LCLcarbon} if TrayIcon.Tag <> 0 then TrayIcon.InternalUpdate; {$endif LCLcarbon} end; procedure TMainForm.FillStatistics(s: TJSONObject); procedure _Fill(idx: integer; s: TJSONObject); begin with gStats do begin Items[idx, 0]:=UTF8Decode(GetHumanSize(s.Floats['downloadedBytes'])); Items[idx, 1]:=UTF8Decode(GetHumanSize(s.Floats['uploadedBytes'])); Items[idx, 2]:=s.Integers['filesAdded']; Items[idx, 3]:=UTF8Decode(SecondsToString(s.Integers['secondsActive'])); end; end; begin if RpcObj.RPCVersion < 4 then exit; if s = nil then begin ClearDetailsInfo; exit; end; gStats.BeginUpdate; try gStats.Enabled:=True; gStats.Color:=clWindow; _Fill(1, s.Objects['current-stats']); _Fill(2, s.Objects['cumulative-stats']); finally gStats.EndUpdate; end; DetailsUpdated; end; procedure TMainForm.CheckStatus(Fatal: boolean); var s: string; i: integer; begin with MainForm do begin s:=RpcObj.Status; if s <> '' then begin RpcObj.Status:=''; if Fatal then DoDisconnect; ForceAppNormal; if Fatal and not RpcObj.Connected and RpcObj.ReconnectAllowed and (FReconnectTimeOut <> -1) then begin FReconnectWaitStart:=Now; if FReconnectTimeOut < 60 then if FReconnectTimeOut < 10 then Inc(FReconnectTimeOut, 5) else Inc(FReconnectTimeOut, 10); txConnError.Caption:=s; panReconnectFrame.Hide; panReconnect.AutoSize:=True; CenterReconnectWindow; panReconnect.Show; panReconnect.BringToFront; TickTimerTimer(nil); panReconnect.AutoSize:=False; panReconnectFrame.Show; CenterReconnectWindow; end else MessageDlg(s, mtError, [mbOK], 0); end; if StatusBar.Panels[0].Text <> RpcObj.InfoStatus then begin StatusBar.Panels[0].Text:=RpcObj.InfoStatus; TrayIcon.Hint:=RpcObj.InfoStatus; if (RpcObj.Connected) and (RpcObj.Http.UserName <> '') then FPasswords.Values[FCurConn]:=RpcObj.Http.Password; // Save password to cache end; if not RpcObj.Connected then for i:=1 to StatusBar.Panels.Count - 1 do StatusBar.Panels[i].Text:=''; end; end; function TMainForm.TorrentAction(const TorrentIds: variant; const AAction: string; args: TJSONObject): boolean; var req: TJSONObject; ids: TJSONArray; i: integer; begin if VarIsEmpty(TorrentIds) then exit; Application.ProcessMessages; AppBusy; req:=TJSONObject.Create; try req.Add('method', AAction); if args = nil then args:=TJSONObject.Create; if not VarIsNull(TorrentIds) then begin ids:=TJSONArray.Create; if VarIsArray(TorrentIds) then begin for i:=VarArrayLowBound(TorrentIds, 1) to VarArrayHighBound(TorrentIds, 1) do ids.Add(integer(TorrentIds[i])); end else ids.Add(integer(TorrentIds)); args.Add('ids', ids); end; req.Add('arguments', args); args:=RpcObj.SendRequest(req, False, 30000); Result:=args <> nil; args.Free; finally req.Free; end; if not Result then CheckStatus(False) else DoRefresh(True); AppNormal; end; function TMainForm.SetFilePriority(TorrentId: integer; const Files: array of integer; const APriority: string): boolean; function CreateFilesArray: TJSONArray; var i: integer; begin Result:=TJSONArray.Create; for i:=Low(Files) to High(Files) do Result.Add(Files[i]); end; var req, args: TJSONObject; begin AppBusy; req:=TJSONObject.Create; try req.Add('method', 'torrent-set'); args:=TJSONObject.Create; if TorrentId <> 0 then args.Add('ids', TJSONArray.Create([TorrentId])); if APriority = 'skip' then args.Add('files-unwanted', CreateFilesArray) else begin args.Add('files-wanted', CreateFilesArray); args.Add('priority-' + APriority, CreateFilesArray); end; req.Add('arguments', args); args:=RpcObj.SendRequest(req, False); Result:=args<> nil; args.Free; finally req.Free; end; if not Result then CheckStatus(False) else DoRefresh; AppNormal; end; function TMainForm.SetCurrentFilePriority(const APriority: string): boolean; var Files: array of integer; i, j, k, level: integer; pri: string; begin if (gTorrents.Items.Count = 0) or (PageInfo.ActivePage <> tabFiles) then exit; SetLength(Files, lvFiles.Items.Count); pri:=APriority; j:=0; if APriority <> '' then begin // Priority for currently selected rows if lvFiles.SelCount = 0 then lvFiles.RowSelected[lvFiles.Row]:=True; level:=-1; for i:=0 to lvFiles.Items.Count - 1 do begin k:=FFilesTree.RowLevel[i]; if k <= level then level:=-1; if lvFiles.RowSelected[i] or ( (level <> -1) and (k > level) ) then begin if FFilesTree.IsFolder(i) then begin if level = -1 then level:=k; end else begin Files[j]:=FFiles[idxFileId, i]; Inc(j); end; end; end; end else begin // Priority based on checkbox state for i:=0 to FFiles.Count - 1 do if not FFilesTree.IsFolder(i) then begin k:=FFiles[idxFilePriority, i]; if (k <> TR_PRI_SKIP) <> (FFilesTree.Checked[i] = cbChecked) then begin if pri = '' then if FFilesTree.Checked[i] = cbChecked then pri:='normal' else pri:='skip'; Files[j]:=FFiles[idxFileId, i]; Inc(j); end; end; end; if j = 0 then exit; SetLength(Files, j); Result:=SetFilePriority(RpcObj.CurTorrentId, Files, pri); end; procedure TMainForm.SetTorrentPriority(APriority: integer); var args: TJSONObject; begin if gTorrents.Items.Count = 0 then exit; args:=TJSONObject.Create; args.Add('bandwidthPriority', TJSONIntegerNumber.Create(APriority)); TorrentAction(GetSelectedTorrents, 'torrent-set', args); end; procedure TMainForm.ProcessPieces(const Pieces: string; PieceCount: integer; const Done: double); const MaxPieces = 4000; var i, j, k, x, xx: integer; s: string; R: TRect; bmp: TBitmap; c: double; begin FLastPieces:=Pieces; FLastPieceCount:=PieceCount; FLastDone:=Done; bmp:=nil; try if FTorrentProgress = nil then FTorrentProgress:=TBitmap.Create; if RpcObj.RPCVersion >= 5 then begin bmp:=TBitmap.Create; if PieceCount > MaxPieces then begin bmp.Width:=MaxPieces; c:=MaxPieces/PieceCount; end else begin bmp.Width:=PieceCount; c:=1; end; bmp.Height:=12; bmp.Canvas.Brush.Color:=clWindow; bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height); bmp.Canvas.Brush.Color:=clHighlight; x:=0; s:=DecodeBase64(Pieces); for i:=1 to Length(s) do begin j:=byte(s[i]); for k:=1 to 8 do begin if PieceCount = 0 then break; if j and $80 <> 0 then begin xx:=Trunc(x*c); bmp.Canvas.FillRect(xx, 0, xx + 1, bmp.Height); end; Inc(x); j:=j shl 1; Dec(PieceCount); end; end; end; with FTorrentProgress.Canvas do begin FTorrentProgress.Width:=pbDownloaded.ClientWidth; if bmp <> nil then begin i:=bmp.Height div 3; FTorrentProgress.Height:=bmp.Height + 5 + i; Brush.Color:=clWindow; FillRect(0, 0, FTorrentProgress.Width, FTorrentProgress.Height); Brush.Color:=clBtnShadow; R:=Rect(0, i + 3, FTorrentProgress.Width, FTorrentProgress.Height); FillRect(R); InflateRect(R, -1, -1); if bmp.Width > 0 then StretchDraw(R, bmp) else begin Brush.Color:=clWindow; FillRect(R); end; R:=Rect(0, 0, FTorrentProgress.Width, i + 2); end else begin FTorrentProgress.Height:=14; R:=Rect(0, 0, FTorrentProgress.Width, FTorrentProgress.Height); end; Brush.Color:=clBtnShadow; FillRect(R); InflateRect(R, -1, -1); x:=R.Left + Round((R.Right - R.Left)*Done/100.0); Brush.Color:=clHighlight; FillRect(R.Left, R.Top, x, R.Bottom); Brush.Color:=clWindow; FillRect(x, R.Top, R.Right, R.Bottom); end; if pbDownloaded.Height <> FTorrentProgress.Height then begin pbDownloaded.Constraints.MaxHeight:=FTorrentProgress.Height; pbDownloaded.Height:=FTorrentProgress.Height; panProgress.AutoSize:=True; panProgress.AutoSize:=False; end; pbDownloaded.Invalidate; finally bmp.Free; end; end; function TMainForm.ExecRemoteFile(const FileName: string; SelectFile: boolean): boolean; procedure _Exec(s: string); var p: string; begin AppBusy; if SelectFile then if FileExistsUTF8(s) then begin {$ifdef mswindows} p:=Format('/select,"%s"', [s]); s:='explorer.exe'; {$else} p:=''; s:=ExtractFilePath(s); {$endif mswindows} end else begin p:=''; s:=ExtractFilePath(s); end; Result:=OpenURL(s, p); AppNormal; if not Result then begin ForceAppNormal; MessageDlg(Format(sUnableToExecute, [s]), mtError, [mbOK], 0); end; end; var s: string; begin s:=MapRemoteToLocal(FileName); if s <> '' then begin _Exec(s); exit; end; if FileExistsUTF8(FileName) or DirectoryExistsUTF8(FileName) then begin _Exec(FileName); exit; end; ForceAppNormal; MessageDlg(sNoPathMapping, mtInformation, [mbOK], 0); end; function TMainForm.GetSelectedTorrents: variant; var i, j: integer; begin with gTorrents do begin if Items.Count = 0 then begin Result:=Unassigned; exit; end; if SelCount = 0 then Result:=VarArrayOf([Items[idxTorrentId, Row]]) else begin Result:=VarArrayCreate([0, SelCount - 1], varinteger); j:=0; for i:=0 to gTorrents.Items.Count - 1 do if gTorrents.RowSelected[i] then begin Result[j]:=Items[idxTorrentId, i]; Inc(j); end; end; end; end; procedure TMainForm.FillDownloadDirs(CB: TComboBox; const CurFolderParam: string); var i, j: integer; s, IniSec: string; begin CB.Items.Clear; IniSec:='AddTorrent.' + FCurConn; j:=Ini.ReadInteger(IniSec, 'FolderCount', 0); for i:=0 to j - 1 do begin s:=Ini.ReadString(IniSec, Format('Folder%d', [i]), ''); if s <> '' then CB.Items.Add(s); end; s:=Ini.ReadString(IniSec, CurFolderParam, ''); if s <> '' then begin i:=CB.Items.IndexOf(s); if i > 0 then CB.Items.Move(i, 0); end; if CB.Items.Count > 0 then CB.ItemIndex:=0; end; procedure TMainForm.SaveDownloadDirs(CB: TComboBox; const CurFolderParam: string); var i: integer; IniSec: string; begin IniSec:='AddTorrent.' + FCurConn; i:=CB.Items.IndexOf(CB.Text); if i >= 0 then CB.Items.Move(i, 0) else CB.Items.Insert(0, CB.Text); i:=Ini.ReadInteger('Interface', 'MaxFoldersHistory', 10); while CB.Items.Count > i do CB.Items.Delete(CB.Items.Count - 1); Ini.WriteInteger(IniSec, 'FolderCount', CB.Items.Count); for i:=0 to CB.Items.Count - 1 do Ini.WriteString(IniSec, Format('Folder%d', [i]), CB.Items[i]); Ini.WriteString(IniSec, CurFolderParam, CB.Items[0]); Ini.UpdateFile; end; procedure TMainForm.SetRefreshInterval; var i: TDateTime; begin if Visible and (WindowState <> wsMinimized) then i:=Ini.ReadInteger('Interface', 'RefreshInterval', 5) else i:=Ini.ReadInteger('Interface', 'RefreshIntervalMin', 20); if i < 1 then i:=1; RpcObj.RefreshInterval:=i/SecsPerDay; end; procedure TMainForm.AddTracker(EditMode: boolean); var req, args: TJSONObject; id, torid: integer; begin AppBusy; with TAddTrackerForm.Create(Self) do try id:=0; torid:=RpcObj.CurTorrentId; if EditMode then begin Caption:=STrackerProps; edTracker.Text:=UTF8Encode(widestring(lvTrackers.Items[idxTrackersListName, lvTrackers.Row])); id:=lvTrackers.Items[idxTrackerID, lvTrackers.Row]; end; AppNormal; if ShowModal = mrOk then begin AppBusy; Self.Update; req:=TJSONObject.Create; try req.Add('method', 'torrent-set'); args:=TJSONObject.Create; args.Add('ids', TJSONArray.Create([torid])); if EditMode then args.Add('trackerReplace', TJSONArray.Create([id, UTF8Decode(edTracker.Text)])) else args.Add('trackerAdd', TJSONArray.Create([UTF8Decode(edTracker.Text)])); req.Add('arguments', args); args:=nil; args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; DoRefresh; AppNormal; end; finally Free; end; end; procedure TMainForm.UpdateConnections; var i, j, cnt: integer; s, cur: string; mi: TMenuItem; begin while (pmConnections.Items.Count > 0) and (pmConnections.Items[0].Tag = 0) do pmConnections.Items[0].Free; while (miConnect.Count > 0) and (miConnect.Items[0].Tag = 0) do miConnect.Items[0].Free; cur:=Ini.ReadString('Hosts', 'CurHost', ''); cnt:=Ini.ReadInteger('Hosts', 'Count', 0); j:=0; for i:=1 to cnt do begin s:=Ini.ReadString('Hosts', Format('Host%d', [i]), ''); if s <> '' then begin mi:=TMenuItem.Create(pmConnections); mi.Caption:=s; if s = cur then mi.Checked:=True; mi.OnClick:=@DoConnectToHost; pmConnections.Items.Insert(j, mi); mi:=TMenuItem.Create(miConnect); mi.Caption:=s; if s = cur then mi.Checked:=True; mi.OnClick:=@DoConnectToHost; miConnect.Insert(j, mi); Inc(j); end; end; sepCon1.Visible:=j > 0; sepCon2.Visible:=j > 0; end; procedure TMainForm.DoConnectToHost(Sender: TObject); var mi: TMenuItem; begin mi:=TMenuItem(Sender); if RpcObj.Connected and (FCurConn = mi.Caption) then exit; DoDisconnect; FReconnectTimeOut:=-1; FCurConn:=mi.Caption; DoConnect; end; procedure TMainForm.DoSetDownloadSpeed(Sender: TObject); begin SetSpeedLimit('down', TMenuItem(Sender).Tag); end; procedure TMainForm.DoSetUploadSpeed(Sender: TObject); begin SetSpeedLimit('up', TMenuItem(Sender).Tag); end; procedure TMainForm.SetSpeedLimit(const Dir: string; Speed: integer); var req, args: TJSONObject; begin AppBusy; req:=TJSONObject.Create; try req.Add('method', 'session-set'); args:=TJSONObject.Create; args.Add(Format('speed-limit-%s-enabled', [Dir]), integer(Speed >= 0) and 1); if Speed >= 0 then args.Add(Format('speed-limit-%s', [Dir]), Speed); args.Add('alt-speed-enabled', 0); req.Add('arguments', args); args:=RpcObj.SendRequest(req, False); if args = nil then begin CheckStatus(False); exit; end; args.Free; finally req.Free; end; RpcObj.RefreshNow:=RpcObj.RefreshNow + [rtSession]; AppNormal; end; function TMainForm.FixSeparators(const p: string): string; begin Result:=StringReplace(p, '/', DirectorySeparator, [rfReplaceAll]); Result:=StringReplace(Result, '\', DirectorySeparator, [rfReplaceAll]); end; function TMainForm.MapRemoteToLocal(const RemotePath: string): string; var i, j: integer; s, ss, fn: string; begin Result:=''; fn:=FixSeparators(Trim(RemotePath)); for i:=0 to FPathMap.Count - 1 do begin s:=FPathMap[i]; j:=Pos('=', s); if j > 0 then begin ss:=FixSeparators(Copy(s, 1, j - 1)); if (ss = fn) or (Pos(IncludeProperTrailingPathDelimiter(ss), fn) = 1) then begin if ss = fn then ss:=Copy(s, j + 1, MaxInt) else begin ss:=IncludeProperTrailingPathDelimiter(ss); ss:=IncludeTrailingPathDelimiter(Copy(s, j + 1, MaxInt)) + Copy(fn, Length(ss) + 1, MaxInt); end; Result:=FixSeparators(ss); exit; end; end; end; end; procedure TMainForm.UpdateUIRpcVersion(RpcVersion: integer); var vc: boolean; begin acRemoveTorrentAndData.Visible:=RPCVersion >= 4; acReannounceTorrent.Visible:=RPCVersion >= 5; acUpdateBlocklist.Visible:=RPCVersion >= 5; acMoveTorrent.Visible:=RPCVersion >= 6; pmiPriority.Visible:=RPCVersion >= 5; miPriority.Visible:=pmiPriority.Visible; acOpenContainingFolder.Visible:=RPCVersion >= 4; acOpenFile.Visible:=acOpenContainingFolder.Visible; pmSepOpen1.Visible:=acOpenContainingFolder.Visible; pmSepOpen2.Visible:=acOpenContainingFolder.Visible; vc:=not sepAltSpeed.Visible and (RPCVersion >= 5); sepAltSpeed.Visible:=RPCVersion >= 5; acAltSpeed.Visible:=RPCVersion >= 5; if vc then begin sepAltSpeed.Left:=tbStopTorrent.Left + 1; tbtAltSpeed.Left:=sepAltSpeed.Left + 1; end; acAddTracker.Visible:=RPCVersion >= 10; acEditTracker.Visible:=acAddTracker.Visible; acDelTracker.Visible:=acAddTracker.Visible; acAdvEditTrackers.Visible:=acAddTracker.Visible; sepTrackers.Visible:=acAddTracker.Visible; vc:=not sepQueue.Visible and (RPCVersion >= 14); sepQueue.Visible:=RPCVersion >= 14; acQMoveUp.Visible:=RPCVersion >= 14; acQMoveDown.Visible:=RPCVersion >= 14; miQueue.Visible:=RPCVersion >= 14; pmiQueue.Visible:=RPCVersion >= 14; if vc then begin sepQueue.Left:=tbStopTorrent.Left + 1; tbQMoveUp.Left:=sepQueue.Left + 1; tbQMoveDown.Left:=tbQMoveUp.Left + 1; end; acForceStartTorrent.Visible:=RPCVersion >= 14; tabStats.Visible:=RpcVersion >= 4; acRename.Visible:=RpcVersion >= 15; end; procedure TMainForm.CheckAddTorrents; var i: integer; h: System.THandle; s: string; WasHidden: boolean; begin h:=FileOpenUTF8(FIPCFileName, fmOpenRead or fmShareDenyWrite); if h <> System.THandle(-1) then begin i:=FileSeek(h, 0, soFromEnd); SetLength(s, i); if i > 0 then begin FileSeek(h, 0, soFromBeginning); SetLength(s, FileRead(h, s[1], i)); end; FileTruncate(h, 0); FileClose(h); DeleteFileUTF8(FIPCFileName); if s = '' then begin ShowApp; exit; end; FPendingTorrents.Text:=FPendingTorrents.Text + s; end; if FAddingTorrent <> 0 then exit; Inc(FAddingTorrent); try if FPendingTorrents.Count > 0 then begin Application.ProcessMessages; TickTimer.Enabled:=True; WasHidden:=not IsTaskbarButtonVisible; if WasHidden then Application.BringToFront else ShowApp; try while FPendingTorrents.Count > 0 do begin s:=FPendingTorrents[0]; FPendingTorrents.Delete(0); if s <> '' then DoAddTorrent(s); end; finally if WasHidden then HideTaskbarButton; end; end; finally Dec(FAddingTorrent); end; end; procedure TMainForm.CheckClipboardLink; const strTorrentExt = '.torrent'; var s: string; begin try if not FLinksFromClipboard then exit; s:=Clipboard.AsText; if s = FLastClipboardLink then exit; FLastClipboardLink:=s; if not IsProtocolSupported(s) then exit; if (Pos('magnet:', UTF8LowerCase(s)) <> 1) and (UTF8LowerCase(Copy(s, Length(s) - Length(strTorrentExt) + 1, MaxInt)) <> strTorrentExt) then exit; AddTorrentFile(s); Clipboard.AsText:=''; except // Turn off this function if an error occurs FLinksFromClipboard:=False; end; end; procedure TMainForm.CenterDetailsWait; begin panDetailsWait.Left:=PageInfo.Left + (PageInfo.Width - panDetailsWait.Width) div 2; panDetailsWait.Top:=PageInfo.Top + (PageInfo.Height - panDetailsWait.Height) div 2; end; function TMainForm.GetPageInfoType(pg: TTabSheet): TAdvInfoType; begin if pg = tabGeneral then Result:=aiGeneral else if pg = tabPeers then Result:=aiPeers else if pg = tabFiles then Result:=aiFiles else if pg = tabTrackers then Result:=aiTrackers else if pg = tabStats then Result:=aiStats else Result:=aiNone; end; procedure TMainForm.DetailsUpdated; begin FDetailsWaitStart:=0; PageInfo.ActivePage.Tag:=0; end; function TMainForm.RenameTorrent(TorrentId: integer; const OldPath, NewName: string): boolean; var args: TJSONObject; begin Result:=False; if ExtractFileName(OldPath) = NewName then exit; args:=TJSONObject.Create; args.Add('path', UTF8Decode(OldPath)); args.Add('name', UTF8Decode(NewName)); Result:=TorrentAction(TorrentId, 'torrent-rename-path', args); end; procedure TMainForm.FilesTreeStateChanged(Sender: TObject); begin SetCurrentFilePriority(''); end; function TMainForm.SelectTorrent(TorrentId, TimeOut: integer): integer; var tt: TDateTime; br: boolean; begin Result:=-1; if TorrentId = 0 then exit; br:=False; tt:=Now; while True do begin Application.ProcessMessages; Result:=gTorrents.Items.IndexOf(idxTorrentId, TorrentId); if Result >= 0 then begin gTorrents.RemoveSelection; gTorrents.Row:=Result; RpcObj.CurTorrentId:=TorrentId; if Self.Visible and (Self.WindowState <> wsMinimized) and gTorrents.Enabled then Self.ActiveControl:=gTorrents; break; end; if br then break; Sleep(100); if Now - tt >= TimeOut/MSecsPerDay then br:=True; end; end; procedure TMainForm.OpenCurrentTorrent(OpenFolderOnly: boolean); var res: TJSONObject; p, s: string; sel: boolean; files: TJSONArray; begin if gTorrents.Items.Count = 0 then exit; Application.ProcessMessages; AppBusy; try sel:=False; gTorrents.RemoveSelection; res:=RpcObj.RequestInfo(gTorrents.Items[idxTorrentId, gTorrents.Row], ['files', 'downloadDir']); if res = nil then CheckStatus(False) else try with res.Arrays['torrents'].Objects[0] do begin files:=Arrays['files']; if files.Count = 0 then exit; if files.Count = 1 then begin p:=UTF8Encode((files[0] as TJSONObject).Strings['name']); sel:=OpenFolderOnly; end else begin s:=GetFilesCommonPath(files); repeat p:=s; s:=ExtractFilePath(p); until (s = '') or (s = p); end; p:=IncludeTrailingPathDelimiter(UTF8Encode(Strings['downloadDir'])) + p; end; finally res.Free; end; ExecRemoteFile(p, sel); finally AppNormal; end; end; procedure TMainForm.FillSpeedsMenu; procedure _FillMenu(Items: TMenuItem; const Speeds: string; OnClickHandler: TNotifyEvent; CurSpeed: integer); var sl: TStringList; i, j: integer; mi: TMenuItem; begin Items.Clear; if not RpcObj.Connected then exit; sl:=TStringList.Create; try sl.Delimiter:=','; sl.DelimitedText:=Speeds; i:=0; while i < sl.Count do begin j:=StrToIntDef(Trim(sl[i]), -1); if j >= 0 then begin sl[i]:=Format('%.08d', [j]); Inc(i); end else sl.Delete(i); end; sl.Duplicates:=dupIgnore; sl.Sorted:=True; sl.Add(Format('%.08d', [CurSpeed])); for i:=0 to sl.Count - 1 do begin j:=StrToIntDef(Trim(sl[i]), -1); if j >= 0 then begin mi:=TMenuItem.Create(Items); mi.Caption:=Format('%d %s%s', [j, sKByte, sPerSecond]); mi.Tag:=j; mi.OnClick:=OnClickHandler; if j = CurSpeed then mi.Checked:=True; Items.Insert(0, mi); end; end; finally sl.Free; end; if Items.Count > 0 then begin mi:=TMenuItem.Create(Items); mi.Caption:='-'; Items.Insert(0, mi); end; mi:=TMenuItem.Create(Items); mi.Caption:=SUnlimited; mi.Tag:=-1; mi.OnClick:=OnClickHandler; if CurSpeed = -1 then mi.Checked:=True; Items.Insert(0, mi); end; var s: string; begin s:=Ini.ReadString('Connection.' + FCurConn, 'DownSpeeds', DefSpeeds); _FillMenu(pmDownSpeeds.Items, s, @DoSetDownloadSpeed, FCurDownSpeedLimit); _FillMenu(pmiDownSpeedLimit, s, @DoSetDownloadSpeed, FCurDownSpeedLimit); s:=Ini.ReadString('Connection.' + FCurConn, 'UpSpeeds', DefSpeeds); _FillMenu(pmUpSpeeds.Items, s, @DoSetUploadSpeed, FCurUpSpeedLimit); _FillMenu(pmiUpSpeedLimit, s, @DoSetUploadSpeed, FCurUpSpeedLimit); {$ifdef LCLcarbon} TrayIcon.InternalUpdate; {$endif LCLcarbon} end; initialization {$I main.lrs} finalization try FreeAndNil(Ini); except end; end. TransGUI/Makefile.fpc0000644000000000000000000000513612026633637013434 0ustar rootroot[require] packages= [target] programs=transgui [prerules] ifneq ($(findstring $(OS_TARGET),win32,win64),) PROG_VER=$(shell type VERSION.txt) else PROG_VER=$(shell cat VERSION.txt) endif ifeq ($(LAZARUS_DIR),) # Searching Lazarus dir LAZARUS_DIR=$(strip $(dir $(realpath $(firstword $(strip $(wildcard $(addsuffix /lazbuild$(SRCEXEEXT),$(SEARCHPATH)))))))) lazarus_ok=$(strip $(wildcard $(LAZARUS_DIR)/lazbuild)) ifeq ($(lazarus_ok),) LAZARUS_DIR= # Trying home dir lazarus_ok=$(strip $(wildcard $(HOME)/lazarus/lazbuild)) ifneq ($(lazarus_ok),) LAZARUS_DIR=$(HOME)/lazarus endif endif ifeq ($(LAZARUS_DIR),) $(error Lazarus directory was not found. Use LAZARUS_DIR= switch.) endif endif $(info Using Lazarus dir: $(LAZARUS_DIR)) LAZRES=$(LAZARUS_DIR)/tools/lazres$(SRCEXEEXT) # Widgetset LCL_WIDGETSET=gtk2 ifneq ($(findstring $(OS_TARGET),win32,win64),) LCL_WIDGETSET=win32 endif ifneq ($(findstring $(OS_TARGET),darwin),) LCL_WIDGETSET=carbon endif ifeq ($(DEBUG),) COMP_OPT=-O2 -g- -CX -XX -Xs else COMP_OPT=-O- -gs -dCALLSTACK endif [compiler] unitdir=synapse/source/lib unitdir=json options=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) options_darwin=-k-macosx_version_min -k10.5 -XR/Developer/SDKs/MacOSX10.5.sdk/ #Lazarus dirs unitdir=$(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) unitdir=$(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) unitdir=$(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) [rules] #.PHONY: extraclean transgui$(EXEEXT): $(patsubst %.lfm,%.lrs,$(wildcard *.lfm)) $(wildcard *.lfm) $(wildcard *.pas) %.lrs: %.lfm; $(LAZRES) $@ $< have_lazres=$(strip $(wildcard $(LAZRES))) ifeq ($(have_lazres),) check_lazres: $(MAKE) -C $(LAZARUS_DIR)/tools lazres$(SRCEXEEXT) else check_lazres: endif all: check_lazres fpc_all extraclean: -$(DEL) $(addprefix $(UNITTARGETDIRPREFIX), *$(OEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT) *.or *.res) clean: extraclean fpc_clean zipdist: all -$(DEL) -r ./Release/dist -$(MKDIRPROG) ./Release $(MKDIRPROG) ./Release/dist $(MKDIRPROG) ./Release/dist/lang -strip ./transgui$(EXEEXT) $(CPPROG) ./transgui$(EXEEXT) ./Release/dist $(CPPROG) ./readme.txt ./Release/dist $(CPPROG) ./history.txt ./Release/dist $(CPPROG) ./LICENSE.txt ./Release/dist $(CPPROG) ./transgui.png ./Release/dist $(CPPROG) ./lang/transgui.* ./Release/dist/lang -$(DEL) ./Release/transgui-$(PROG_VER)-$(FULL_TARGET).zip $(MAKE) -C ./Release/dist -f ../../Makefile int_zip ZIP_FILE=transgui-$(PROG_VER)-$(FULL_TARGET).zip -$(DEL) -r ./Release/dist int_zip: $(ZIPPROG) -9 -r ../$(ZIP_FILE) . TransGUI/baseform.pas0000644000000000000000000001603212261763702013523 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit BaseForm; {$mode objfpc} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics; type { TBaseForm } TBaseForm = class(TForm) private FNeedAutoSize: boolean; procedure DoScale(C: TControl); procedure InitScale; protected procedure DoCreate; override; public constructor Create(TheOwner: TComponent); override; end; procedure AutoSizeForm(Form: TCustomForm); function ScaleInt(i: integer): integer; var IntfScale: integer = 100; implementation uses LCLType, ButtonPanel, VarGrid, ComCtrls, StdCtrls, ExtCtrls, lclversion; var ScaleM, ScaleD: integer; function ScaleInt(i: integer): integer; begin Result:=i*ScaleM div ScaleD; end; type THackControl = class(TWinControl) end; procedure AutoSizeForm(Form: TCustomForm); var i, ht, w, h: integer; C: TControl; begin ht:=0; for i:=0 to Form.ControlCount - 1 do begin C:=Form.Controls[i]; if not C.Visible then continue; with C do begin if C is TButtonPanel then begin TButtonPanel(C).HandleNeeded; w:=0; h:=0; THackControl(C).CalculatePreferredSize(w, h, True); end else h:=Height; {$ifdef LCLcarbon} if C is TPageControl then Inc(h, ScaleInt(10)); {$endif LCLcarbon} Inc(ht, h + BorderSpacing.Top + BorderSpacing.Bottom + BorderSpacing.Around*2); end; end; ht:=ht + 2*Form.BorderWidth; Form.ClientHeight:=ht; if Form.ClientHeight <> ht then begin Form.Constraints.MinHeight:=0; Form.ClientHeight:=ht; Form.Constraints.MinHeight:=Form.Height; end; if Form.BorderStyle = bsDialog then begin Form.Constraints.MinHeight:=Form.Height; Form.Constraints.MinWidth:=Form.Width; end; end; { TBaseForm } procedure TBaseForm.DoScale(C: TControl); var i: integer; R: TRect; w, h: integer; begin with C do begin {$ifdef darwin} if C is TButtonPanel then exit; {$endif darwin} if C is TWinControl then TWinControl(C).DisableAlign; try if ScaleM <> ScaleD then begin ScaleConstraints(ScaleM, ScaleD); R := BaseBounds; R.Left := ScaleInt(R.Left); R.Top := ScaleInt(R.Top); R.Right := ScaleInt(R.Right); R.Bottom := ScaleInt(R.Bottom); if (Parent <> nil) and (Align = alNone) then begin if akRight in Anchors then Inc(R.Right, C.Parent.ClientWidth - ScaleInt(C.BaseParentClientSize.cx)); if akBottom in Anchors then Inc(R.Bottom, C.Parent.ClientHeight - ScaleInt(C.BaseParentClientSize.cy)); end; BoundsRect := R; with BorderSpacing do begin Top:=ScaleInt(Top); Left:=ScaleInt(Left); Bottom:=ScaleInt(Bottom); Right:=ScaleInt(Right); Around:=ScaleInt(Around); InnerBorder:=ScaleInt(InnerBorder); end; if C is TWinControl then with TWinControl(C).ChildSizing do begin HorizontalSpacing:=ScaleInt(HorizontalSpacing); VerticalSpacing:=ScaleInt(VerticalSpacing); LeftRightSpacing:=ScaleInt(LeftRightSpacing); TopBottomSpacing:=ScaleInt(TopBottomSpacing); end; if C is TButtonPanel then TButtonPanel(C).Spacing:=ScaleInt(TButtonPanel(C).Spacing); if C is TVarGrid then with TVarGrid(C).Columns do for i:=0 to Count - 1 do Items[i].Width:=ScaleInt(Items[i].Width); if C is TStatusBar then with TStatusBar(C) do for i:=0 to Panels.Count - 1 do Panels[i].Width:=ScaleInt(Panels[i].Width); end; // Runtime fixes // Fix right aligned label autosize if C.Visible and (C is TCustomLabel) and C.AutoSize and (TLabel(C).Alignment = taLeftJustify) and (C.Anchors*[akLeft, akRight] = [akRight]) then begin w:=0; h:=0; THackControl(C).CalculatePreferredSize(w, h, True); C.Width:=w; end; {$ifdef darwin} // Always use standard button height on OS X for proper theming if C.Visible and (C is TCustomButton) then begin w:=0; h:=0; THackControl(C).CalculatePreferredSize(w, h, True); C.Height:=h; end; // Add extra top spacing for group box i:=ScaleInt(6); if C.Parent is TCustomGroupBox then Top:=Top + i; if C is TCustomGroupBox then with TCustomGroupBox(C).ChildSizing do TopBottomSpacing:=TopBottomSpacing + i; {$endif darwin} {$ifdef LCLgtk2} // Fix panel color bug on GTK2 if (C is TCustomPanel) and ParentColor and (Color = clDefault) then Color:=clForm; {$endif LCLgtk2} if C is TWinControl then with TWinControl(C) do for i:=0 to ControlCount - 1 do DoScale(Controls[i]); finally if C is TWinControl then TWinControl(C).EnableAlign; end; end; end; constructor TBaseForm.Create(TheOwner: TComponent); begin inherited Create(TheOwner); FNeedAutoSize:=AutoSize; AutoSize:=False; end; procedure TBaseForm.DoCreate; {$ifdef LCLcarbon} var i: integer; {$endif LCLcarbon} begin InitScale; HandleNeeded; Font.Height:=ScaleInt(-11); DoScale(Self); if FNeedAutoSize then AutoSizeForm(Self); {$ifdef LCLcarbon} // Destroy handles of child controls to fix the LCL Carbon bug. // Without this hack, it will not be possible to hide form's controls. for i:=0 to ControlCount - 1 do if Controls[i] is TWinControl then THackControl(Controls[i]).DestroyHandle; {$endif LCLcarbon} inherited DoCreate; end; procedure TBaseForm.InitScale; var i: integer; tm: TLCLTextMetric; begin if ScaleD <> 0 then exit; ScaleD:=11; i:=Screen.SystemFont.Height; if i = 0 then begin if Canvas.GetTextMetrics(tm) then begin ScaleM:=tm.Ascender; if ScaleM < 11 then ScaleM:=11; end else begin ScaleM:=Canvas.TextHeight('Wy'); ScaleD:=13; end; if ScaleM = 0 then ScaleM:=ScaleD; end else ScaleM:=Abs(i); ScaleM:=ScaleM*IntfScale; ScaleD:=ScaleD*100; end; initialization {$I baseform.lrs} end. TransGUI/transgui.ico0000644000000000000000000025503111367026322013550 0ustar rootroot F00 ¨%a  ¨ E h±U‰PNG  IHDR\r¨f IDATxœì½y”$wuçûùEDîKeí{uUoên5Ý­Ý-#°a„ÁàíÌ1>ãçcìgìñŒçÙoŽm`¼cÍû±–„ ³ŒIH-¡–Ôê­–®½*÷¬\â÷{DÜŒ¨R7j©«%ëž'2#"#2#ãnß»)c [ô£GÚu±l›¿¸þzŽ|ã$míºœíß޸ͯe¼õºSË!&t¼l”×@Â@Ê@Há½NØÐcC/ÐiAИ*üµ‚P,˶ݲëòÇ_ÿ:{nÿž-Ú\r^è/°EÏ snÜæ“,Ù¦|>6`ë[ƒm Ë@SÐmÃC‰AorQHF å@*ñ8`ã=tÑÐwÐÀüâ1øL~/'Œw¨{‘nÇù´%þÐY´¾2`©`·6à†µ¸Ný ØaÞ(ìÊÀhúЙdš§›Y ¤:ÞCæÜ:•Äãn¿êµ~îAxÍ1xW>gG•¶µAÃßoAë2èváöðàãÆ}Õ-ÚLÚHZëú+œ•|ŸÙö1 hQ¥z-8ë-¥®Í³# ÃY°c@ÌRx‡ãÒÖØÊ¢ Ö@9`§¼÷Ô€„±׺þ{Û_[@Ì_7ðJÅ{¯*à4@ï÷„Æ?< +J©Ï³å\4ÚH–e½P—nûí>‰ï)hÛÖ€‹Rp\¥.+õ^Õ mcÇ¢@ L\1lP ” –0kÔ?yÄ?F¼ŠÇȱ _ÐÁcnOëË—A÷ÏÑg=Ô½sX-ÐûÀY‚¿Ÿ=uêê}0éäÅ)mˆImEži­±,‹¿þë¿æÑG%™L>Ö€Xß(¥\c Æ”R(¥”el[¥ˆ‹N¦R9XýÂÞÐ,^ßkY³ÆDÆðN¤#`|Ó^ ˉÞVñ$J9xLÝÀcúš¬ï´9Ôõ·Å„DÄß_À Dü$Ç4ð„EÓ?·VÊ5Æ~ ýôÝÿÏm{ÉK,£µV/œÀý‘¤- àéÑGåî»ï&“É\,`J)åuÆ(ãQ—Rê¥Ñhô­fóá–ÖŸ(Õ蘟혟¿)£õ/¬i}åØÝ@Fk“׌gÆ[¢™<†´ @»㺄¤Nh[“Àpüù–¿Ž†ÖUÿx¹–¸„މ†ÎÕêÆØIÛÖ©Fãgoÿó?ÿø{>ò‘/clµå l*m € ¤d2I&“ÙL K)¾ï.šÞ3jYÖu±Xì©Têºt:=ÇM«ÕR‹++¿»ÿþÓ}33×Ç•êL+E—m›N­Ýˆ§õ­¬‡ª·ÿtƒÇ˜ã¶ü n<³¾HÀ°Mî‹àiìž%`ûçkáiuEà£X¡}QAñ·ÉÚ&@û@Tk³ xà–[~ûÔoþæÇ_òm´fË Ø<ÚHZëuËs¤°?¯1ï-`·mÛ¯ŠÇãoL¥RW%“ÉÎD"aÇQ–ei¥”‰Çãª#—»âŒã\±øoÿÆÄꪭm|`Oâî‚8¼0tÓß×Äc^Ïüφ¾XÄß/¾Ì­üµhø°e €²l éå\ ÿxK¡€†1vζM¦Ñ8|ûŸÿù+ã#¹kË Ø\Ú/…™Þ%`úð’H$ò“±Xìuétú`"‘HÆãqlÛÆ²,<ŸÀ²,˶m”RXJé‰K.1SѨµ|çv¶\¦©McÚÌ(Œ.Aüû žÆíñ÷ý/TÄcȨŒ!$ <æÖx ½Œ—” ë‰%àúçYÃK ”ä¢ ±ý¥æŸ?£ûÁž~à·7êõ»¢±X÷Ø¢ §-päpíå‘1æ\LŸUJ]‰DÞÇoH¥R{‰„‹Å°,Ë(¥\¥–eYJ)ÛËÒš{÷rrmùÛogÜVðo@‹ c6ñ´r‹Àç„Þö?׳ÜÐçãxŒêøÇfýÔÂøÄ €vö`Ûç_õ¯>N‚âÖÀšLMk+gÛœ|ä‘×<ú­oõ_~øð¼ÑZ)ÛÞB¯7¶ÀR³Ù¤^¯FŸæ„ÌxeYÑh4Ìô>ˆwS"‘¸!™LŽ' ‰Dž Ó¯[”e1†m—_ÎS““äî¿G)0† Ác®2Éíx WÃcFÑÂ’³L VãÆ Ðÿ\èw§ À>2l|€ÏßNO ˆ{‘ð÷Ǽ„"w†¿ùñ¿ôòÇïð/¹ålm € ¤¡¡!víÚE*•Bk½‘éM4Õ–e©r¹l¦¦¦zÇyy4}S"‘¸>•JÄãqÇÁ÷絘ö–eÙÏÄðgJÑ‹1òêWóäÉ“ì_^ÆUŠŠï÷“{¨ú¯%A'‚'$Ößð×ús 2µ¿]Ž B,Šùç.ú×êö‚H@Ã?‡˜ImÑrÀäw¿ûºf³yG$Ùr6‰¶òž#Éxß}÷±°°€ã8Êc…™¾T*™ÙÙÙÇüðüüüM…Báp*•êÇãÆ¶í6ˆbúgÍðÁ¢ñ8ß»ç"Ÿü$ýxÉ6Ýx>¸hWaž¿F€ˆ/¨}† ? å'f¼¸b=(+AÐ~×ÿìZ蜮ÿ^r$©ס¬‚nضu¯ë~ï7¾öµ+¯¸þz­]WY[nÀÓ–p´¶¶f×ëuÇq-ËÒ!¦ÍüüüO …—¥R©žD"aúúúü|ŒeY›ÆôÂøa+ ¢—^{-<öÙï}˲0ZÓI†ÆOy‚<~Á\‚Ћg!ˆß/I?†¹AQÇCýx‚G"Nèø†ÿb‚AÒ„Kà ëWÙÞwÝéXÖà–¸E@[à91­µeÛ¶I§ÓîÜÜÇï9zôèáùùù·ùš¾'‘H˜mÛ¶ ÎfjúL6W /fÇM71óÔSt—JT•"kLÛÏ–PàÆ°žÄäm<ì3aÛÄ·ð´¿’. '‰E"D:ýãìÐgAõØo䫸 È|í£8øÊW vmхЖxdŒQ¶më'Ÿ|ÒùèG?úÒ™™™·—Ëå7Äãñ¾d2iFGG•¤êÏÙŸ¶Œ¿q»Ñš½ûö±ôS?Ŭï 𬇙/hÿnÿaPq ÊL S]bø$A`Æ›Ð{Ç?§”‹‹Ð m“䢰µa §Ní¾|áù´%žù~¿U­Võ­·Þzc<ÿ€ã8»›Í¦388ˆã8Zkmð2ùÎ ¹ß m®ýJ)bJqÅë^Ç]GŽ0ôÈ#D”¢eL” !@ùWñA Ï—¯0{ÏÌ€/ï¯áióœ¡I€3@ ÄÂ0`Í?¶ƒÀ:Á?ŸŸ2¼cSÿÔÿà´%žc,¥”þêW¿:úØcý¿o{ÛÛz_ûÚ׺GŽq?úѪV«eÅãž|!?¼`´·—K~ög™{òI&êu,?A(\d/1~1÷…‘5ž0ä (;þÚÆCòT4 BR Aª1þv‰óã!pêxÀ&H$’ü Æ.ôÜ¢€^ð(€Ö~H"ZkÛ‰DÜ÷¼ç=oÝ·oß?õ÷÷7\×¾ìe/£Z­róÍ7S*•H$À… ó5õŸq?@<Îú§ ~ñ‹ XI­qñ4¼„î¤ '\wk¤öVñ´¿”ý b/C‘Ðg×€Ápúo#ô™:ž`YÁ³l<02BŽlùÛ J¹McìïG"ßüƒ|ùöýû-­µ¶¬­º€ ¡Üøaú•åuÑêêîîOg³¤R)»\.óõ¯«¯¾š÷¾÷½üÕ_ý•J…çj <“F.ûÒ±—¿õ­¹çŠEZJ¡ü4á(žßŸ%Ëxš]âö.óK”@„´ûÚ˜âèà1wÌÿœ„ý ⟠°6$ÿ_ªE€ˆ5Ñ0†–m“o6“ù¥%1[‘€ ¤LHýÄCQ\ZÂñ“;^¤dá[K±h”Õéé3Ÿ½ï>ö]v?vÍ5d‡oç;\~Ùeüʯü úЇÐZ‰Dð³ùž?mŽÅu]^ràÝp¥Ûn£ÃÇÇfgyó;ÞÁøÈßýîw¹öÚk¹á†¸ë®»Èd2çÅÀ›¥íϵh×å%‡ñèË_NùK_"ª–1ÔÌû/uú‹xRO㹂° Äù%G@êþ!¨6„@óKš¯à ’b,µbHbP‹ Œ¸æ÷%l@³eÌ–öß$zÁ€ÖÚïXé¾,a|ïÅK€÷dág† ã—ÅjœR¥°\—•ï|‡¬×ùÙw½‹ããÜ{ï½¼ò¯`nnŽ©©)‰ÄE±Îw7 +fÇë_ω/}‰¤_¼$¡:1åËx¥½ž0°¼Ü«¶†F—ëÐâÉÍm˜øÒLTIøUÿ¸–R4\—ŽH¤ÒÙÓã]^©üùa§îâ’ØÊë¯ ü˜·¦áþÝðνîô2Ñt/X)°’@Úº^­‰=ʧ>þq¦¦§I&“<|ä‡&Nã8‘HdÝFÛËÆ}ácεï|ÇqÖ];‚ërðe/£±{·WdY¬áiÚ<–^õß·ð˜°H`ÖK1zR8$ñ~¹¡â.ˆàæ–ð ¸"äaŒ†®[óÏѲ,V€Ý×]79±¿×õØÚz|/”þ£ßA©r5\.KÃm#ðÍÝð–ý·è»¬ž6ËøK èTŠžZ ŽåÓŸþ4Õj•b±Èêê*/}éKÑZ·™ù™˜þ|y&¦3~ø½6†ñÑQº^õ*ÎÄÚ þ"¾¸$ÿD ÌpiàY!Ðி?ŒHÌ^ª%1HÊ€%kP„‰¤—ü÷e ·X¤ôØc|öŽ;ˆG£=z”ÁÁAFGG½¬¼Xì™þBÿ\ÛÚ‚!!fY\úªW±æ84´ÆVªí›— rþEû* Nlnq ÿ>H €4© «Br h.‰Eâó×ýïP%`ú2˜ªRv ?ñö·ÿ«Š-0pèÇ^²í1þPL©ßé‚wë‚-±kÑVÒ;ÏÂc~Ù^z½ºÊ±ï}o sø¯à©§žâ²Ë.ãî»ï>kXðB|ü³…7n“÷aÀh;C‡¸kß>jGŽSŠŒ1äýÁË ¿‹0ⓘøþP68(¨~¸ZP´¼¸R_ Ñ„ AP‰@ð´µmw¾ÕrÆöïÿÓ~Ꮆò[£mÑ…Ñ‹B„æÑ]L²”ŸYÚò†Vþj7¼wÀ˜ÞAÐQpc`ÅÁ–𓄤ló·L ÅT–Î:F)šÕ•îú÷gb|WkÆÇÇÙ±cÓÓÓøm¾6Ô;×¶L/¯å½ÖšÑáaz¯½–ü‘#m0O˜º†gºKl>Ü&,ŒØK7 aâhèò`…›{HÂäø7°a| eÝ´¬VAkÇD"ùÏï{ßÚ‘[€›G/ p‘IžA~yÓ¼.éñ?–ÜŒpƒŠ¤¿M4X†ÐЇ.`®1tµÕUòÙ,_üÊWxûÏÿ<'OždïÞ½,//?cnÀ…2ýÙ@x­üa1Ûfï+_ÉW?ô!ÒZcüß*5ÿòÞ!æ!y%‚@¸³oø~È"]„%AHHÃ?¯¸ âˆö¯ƒ©+ÕZÕ:¢#‘[ÞÛmÿéªoTþȳ-ó“èG]„Ãz»bJýϸiIJ>ãûÌ¿®_½$´Ó‡Ó\å„án72 CLæ´1t//3uê÷?ð×]wõzññq¦§§‰F£ç!x.“þ|µýFæo·ÑÒš=W\Á—GGq§¦¨*EÊâ¬/Ù„úªá:©*½º¿?<L09Ÿƒ'`Âf¾øûòy_èšRºnLDE"Ÿxßm·½óªo4n³‰‰l1ÿ&Òªð†kx³îb ~3 ÿe˜l¸iP1°‰ÄZÚd‹ö’UyˆåÄt5 G‘ ¼6¤J%2¥÷|ç;ìÚ¹“Ó§Os饗²´´ô­€s †çªí72¿,­V‹ít\y%õ©)r–EÄuI´é’>ÿâ•ð„]7žÉ/í¿ÂiÀe¼Ä!7´H°/ _8é[ :ïõSt\×}ÿýð‡÷êo´ZN4ºÅü›L?Š Œî_›‚¿€ƒÃ¾¹[ZUm¬b“4¢ÕÖe¨%I+b&‡GbA`ç€êÒ3©ß¾ï>úúû©ÕjŒŒŒ0??¿Î 8¦®Úþ\@kMÊq¿æŽß~;Ý~u Â3ýåw‹5Áà è$‘‚°©/¡ÁpE¡dûI\¿†gþ7ý{,ƒ´ê>´…_iÂ'ú¶m³½eö_úQm­ïBÖ†ßÏÁ¯Ý|–´²3¾ó:‘h*±Â&lxÐ…Y´×êð·w+E¾Z%S.óÈѣؿŸ¡¡!öìÙC>ŸÇqœçMÛËvX?ÇàÒk¯åÛŽÃh«¾vy$ƒOJ‡!h#ðÛ¥f@Rˆen€ ³—Pÿ 혿©['®Ô÷mcÞ<`À1ƈüØ¢‹@?* ]‰Ú‚ÃYøßð{Ð3÷uÔŸa/ÈýÆžœ@‡N®pkáeÆùc´ÛMêç%N2Ø, j =@ei‰Ùtšï>ðãLLLÐÛÛK>ŸÇŸpѵýF¦·ÓÞ¾kÑíÛ©=ù$F)ÆÐ¿á^I.øf— °A÷Eã Ø'L/Qƒ°¿_¤-ÜXœ|c~]CÑòæ¶¶Z_\úaa­±áGà··Ýã¥îZþØkâ¬CÕþ0ëM™hwE Fh%ýsIž{¸×~Ø%pÁ+¹­Õ(U«r„ééiŒ1d2,ËzZúnx±mûiëg³l´(6 c 60rè«@Ó˜¶_^Ú~mv†& °±Ím¿Jþù€Ÿ[SÛ/ÆàŸùßÖ×°e÷?ôÃjØ ´öž‘_í‡lƒTòY)PÒSN¤9êáÄ£°ÙOo6n ½Tljo+/ÒòJü`S• éj•'Žç©cǸä’KèíímGÎGË?œs¦½?öc<Œi Jóƒ •þ€Q[Òt!=ü$5UN$¾­4¸ §øJ\›€±—ðjº 4¿˜Å2^+MÐ;4”"n ‰åe–R)ýþ÷9xèÃÃÃëÚ†m–o®×O»‘J¡ülº¾áaLo/­ÅÅ6˜)Z_: ‰É/}%Í/|’ç/  ¦º VÓ»]Ÿ‰Â{ Lâ1þºLãÐ<€-ºÈô‚ €gAJù¾a~rüÃ.J{¡#;–ô““PœÄ¯¥Û­0ª€[²_°Aö…©“¬/dI¤Ç†;à„Ûb s4üó•! ¤ŠEJkkü¾Ùl’ÏçY˜Ÿgrrwd„Úâ"q¥¨û]‚DÂz—G‚´—¹wí4^o¿[UñN7…ßn5þm5?L[VÀŧ`)¯#ŒVð{ûáÿÙã¥îº8R‰&¾½äêG ²Ø¤ å¥R»^ÇÓòÿ<âÏËÈ+1é3¬ï…·Q@ÄXþŠû¯[¾,XI§ùþ÷¿ÏåW\Á¥—^J½^oÏó»˜Ú¼>ŒÕj•ÅÅEΜ9ÃÂÂ¥b+Ã`‘ ÒΈ”P^¸u÷ÆŠ¿° °ê­5`ª`7¡…¿uàhX T‚-ßñ\̯ý>‘ÚwOž-…ïÍ=žWàÔ<Ú²íóij+Oëw§áïÂMÝà*жoò'|Îpι´¸²BÛ¥—h©1<3V2ßDx&O¨0¸!aIý{Á‘òKâY ^(`wwóıcœ>uŠ]»v‹ÅÐZ?+­Ï^Û fgg™eee…F£áM2ÖÛ2ƒƒ^]¿Ö(‚¿XG‚ôo¼‡5‚¸¾´î*‚n‚Õò„ôÝ ø¿ܯí†ÿõ³½n‡gýß!`æm.=¯@)ÕV¡BÛÎu8~ºWÀ'öÂî>ï½%Í&DÛJÅž|0ËŸT˜]Pî³Jrl—PŸ’&ž/ 0l‚¬¶º¿O-)øC\)²åj•Ù…ž|òI®¼ê*úúú¨V«ë¬€‹¥íËå2n¨íšÖ×uQkkdûûYŠFQMå't?Å…j°.g¿Öë[nTl NÇàÿ¶àý.K6Þz]ëÇs ?!éÉ'ŸÄ Z*=+ ”—î¼wï^Ò™ÌÖ8ñsÐó"äæçóyî¿ÿ~¯3ëÏdÈçó¾=¸Î°( - ÿiܼb)hÙàȰI™=/h¾L®i_ï©›'xp«À0ž9Üß7â/ƒ,…EÄŸ_ã ±`PêÀ"/×H” ‘ÏSìèàèã3==Íàà`;dw>Ì^ mÔöÅb‘ÙÙYΜ9³NÛ ¹®‹Öc ®ëb´&™ÍRÉf).-µ‡…„9Õ€z®ĪZ· ªì1~# 7ÛðÇ–ÏGëŸí½ö­¦¼ï}Ôo¾åºÏʰm›B¡À?Ýz+W^uZë¶òÙ¢€ž 7ÿ¡‡âÕ¯~5¶ßÜW}@Ó7=ñ˜_·¼J½ìßÞ&æåò;bÖKŽpb¶‡“qNá¡÷Ò¾k Ï4ïò¯ká1nŸN©x“Üu‰ ÈpL™^#–Ì´“ »Qì@„ñcì±r™X½Î©©)Nø``,ó4ñy0˜ÂÛDÛÏḬ̀¸¸H¹\¦Õ šçj­Û‹¸bm!Ðj‹F±{{)--µ¢Â”pÂOHëë2˜&Ø®—qyg~xÄOæ‘çÔôçÚgŒÁ ¥¥FÛŸap¾dÛ6ù|žfèlÑÓéywÀûs„AÔúÑ`âï§3ð‰+à¦@²XÂø’¦.Õ £ñRŦð<^ÆÓÔ xlA!0·¤¯Êc#%±5ÿó9‚PŸD*xÀ—¸Žuô´oâù³1­I•ˬ¬¬pìÉ'Y^^fttt¾W?HÛ·Z- …sssmm_¯×Ï©íÏ%´ëz I½½¦tô¨r”³®"œYSSJ×ÁÒÆX|;ÿøªeÛ„=“¿®÷åJ…|½Žó@iƒÜ¢§Óó‚ÿPú¾hèOµñ²ú&ºàŸÁUÝÐR`Ç@‰Ù/š^âÔŠ@K‡«ú4. ~}’`öœY)<ÐãfÏb¨$ÀHhQ€d|v ;NÂfÒÀ"hr‘ÀÐü›D¡ÀjWÇžzŠééiFGG×\ç«íþý3iû$ÜV Û²Htu©<óG†‰hÑvgLM)]Õ2ƶ"‘§"ð?¬fóÑ_Ó7Þ—–ålÌßNÙË;˲°-ëÙ[–å]xËïÿtÑ€0pÊ8ŽRͦmµZÏmQJk£-K×»»‡[}}?aÅb?a:;_bŒ‰'™z}H4F"‘`iq‘Ø@?J)Óhµæ¬Fóq·ÿ3;wgduå¸å%ýXÆs ¶ùE¤‹.fff8vìX{<Ö0ZƒeY¦ÙÔ¹îM»àÓû õªø,ñçà $q}ñMEªÐZBk³îVñÀ!<¦ ×ý £×€`Á€Š A›à2Ç^J_Ó¡í‚Hõ\¿¶à²_¾_¼Z%R«1}æ ÓSS\vÙe¸®Ëââ"³³³ÌÏÏS*•6]ÛŸk›kŒWØÑaVgfpÀŽS¥RŸÔ##ï_8fG©VËVM?‡Ò¶2F»‘ˆjf³ôøø/7Óé·f3™žÎÎN“_ZR^˜ÓÑ++Ëfdd„¥¥e’CCtww355eç"ÑA×è%Ǿ>>2ò?ZKK·µNžü3kuõˆmŒRë ½-Údºè.ÀÑ£G¹÷Þ{Éf³´Z-/Дã¨V>Ïðââ{.U*3¦•ôÓzÅßÓy#Œ#­§Äf“ÒÅVÐy‹ z­“ ,—€v|9>Ëú1W’ÒNùt ²2à²U€U‚p X’ˆ$8€RŠ„1$ªÕ¶°¼¼L±Xähkw¡ÍÔöç\».V$‚ÕÑn&™üŒ~ßÚÀÀ£!ÆW(e”eicŒÑõz¤22òj7‘xOçððá¨ãD,£Íö‰íîÊê*‰á!Õß×§NŸž´‡†°l W»ŒmÛF¡P ×Ùi†‡‡ÍéÓ§ÍÄè(-×MMU«¿¿òªŸk,/ýiã‰'þÐ4›U¥”‡'lÑ&ÓEµ–––˜™™Ù˜…¦ŒÖVòÜ\&sw£TzeÊçsahñóÃhÂÚÞÁcLÇp«þÒKÐ…¦ ­$ñÆüsýk,áùøaÌ@Ðûð´Ü ž+ ãÁ  ©'wDð‚4Á,‰Nˆ{“¨Õ(–ËÌÎÎ299ÉÐЕJ¥|1´ý9q]£”R‰\nµ±{÷MÍÁÁwÆ·PÊ•a¥b±Ûó3Ý]]ïnö÷ìîVƒƒƒfqqÑíïï·ͦ]©VÙ66ÆüüŽíL%™šœ"—ËÑlµÈ¯æQ+++ `xd”¹¹3fblÌm6›öÔZíw#W\ñyägêÅâãÉDbK\ºè@x\•ç¶Z­–ŠÆbͽ;v\Õ«Ô›W?ÿyz”²”1íЛ0äÛ‡çÈ Áܺ4p„ >/Y}2Œr Éûz4¬¹nž t( =âRÈ(+i&tE@¤ñB‘`®½tv Ê…óÆxY‹Õ*V£Á´Ÿ£¿cÇ2™ Åb˲ž¦ù/D<ÓZkm,­U$“©Õ‡†žp0¨f3Še5Qªi´Æm6wc~9ÿÒàÐÐ@45c&¶o×sssV"‘°c±§OŸ¦§§›F£A¹RaÛè(ùÕ<–e‘Íf™ž™!×™CY°ºšgphb±ÀÚZ]mÛ¶Í^XX0ýÝÝ-Wëý;öíûblrò¦¹……‡£Ñ¨­µÞ›H½ºB´ŽoØ­VËtwwë+¯ºê8p¯îîÞw&™4£Œ_ß_ºÏ*ÖÏ_^táJxZ"/Þ!H ,!g5ÀFav1á!/JÒä È(í¨ÿ™,ë5¾X7R¿m6‰­­±šÏ355…ÖšžžÖÖÖhµZO[šÍæY·o\\×]·>Ÿã\×U-×ŶíaÛuÇl¯™ZS·ZªQ«]o\÷–lGÇ÷R™ÌoïØµ«|bµ”2ƒCCV©T²«Õª’&'±XŒt*ÍÂÂ]¸Z“Ïçéëï£X(b´¦3—cqa‘t:E4ayy™ÞÞ^*• •JEíܹÓI§R-ǶÇ8ðù¾žžƒ…BÁU%Ø¢M¡ç-Èck­Ý‰‰‰]—^zéÿÎår×ôõéH2© […cÇÚñùögüµÄ÷Sx€Û”ÿz” R ¾y^x/Šç£§ñÒ ^ ÀI‚zÿŠ|™ åU7€‘x~xº­ ½á$Ót”"¤AOA±D¤b1­¼œ±µ5 …ËKK,,,0<<ÌC=tV `³´}xz­×q»ÙjõÔëubÑèÏ+ø•X,öcÛÆÇm˶ÖÚSÓÓÓv<™ ‰rfö }}}Ôëu_㱺ºŠ¥,r¹ÓÓÓttt`Y6+«+ R®T¨7Œ °¸¸H<'‘H099IwO7õz­]gï¾}î©S§F¶o»½P,¾¦X*=•N§-³&ܺè@)¥´Öv<oíÛ·ï}}}ŸÞ¶mÛà®]»ÜH$b¹J)wb‚3ÇŽÑKuÐÄ*Øü€¿–XÑÆýc%W?,ô¬+Ô¯§ý}e‚Ö×’Û/Œ,oãx ,®‡„ô[Rd)'6î`áÖaŒÊŽV«”êu—–˜åòË/'¶ xž-³Ÿ£Ÿs`Y–Æü–mÛèë8°V_cxxXwvvºÓÓÓÖàà /h6š 2??O<'™J1yú4ݸڥP(022L!_ÀÕš®®.fggI¥SÄ¢1æææèéé¡^¯S­VeeeDZɤ3ÌÌÌNgH&“v,k)•›¸ü²Ë>rϽ÷®V«n*•RZ?-gh‹ž%]lsJc¬l6ÛºüòËellìß<8xÙe—¹±XÌ®T*J7›Ä{z8‘N³f Z©¶¤™&;‡â CÊq‹xVA… ½…gÊ×ñü~8ƒ§ñûý÷=þ—”$©”˜ph¯D ½e¨¥ßñ†(^.€0¿:V"°>œiûÅ.¬­aù`~nÇqèïï§Z­>£ >æøLþu¯Ûn@«E®³óU:ÐÙÕ©ûúúÜþþ~kvvÎΤ3Êq–––èëï£V«Q­VéïïgeyÛ¶ÉærÌÏÏÓёŲ,VVWèëí¥R­P¯×ééîaqi‰h,J*•baaÎÎ.\×¥X,ÒÛÛG¡P@ûnÂÜÜ]ÝÝÎÐР«áº];wþ¾ïÙÒ@å5FÝ¢LS(@uvvºxÿèèèß^yå•öÎ;u­V³WWW)—Ëä²Yf˜%h/%À€g⯗ :ÓÈdÙ’_ö/\ HÌ‘„ Éä“–Uái·<|@Bâ¤<Ù^ÙÎò ç*hŽ\” úìII­Á F›Mœz|±ÈâÒ…B±±1ÆÓÀsñíÏÅðgaþöÒÑÑ¡³Ù¬®V«V__Ÿ½²¼‚ÑšîînææçI§RÄãqæççéééi'.õ÷÷SÈçÑZÓÕÙÅÂÂ"©TŠX,ÆâÂ"½==4êu*å2}½}¬®¬bYYÉd=¡‘_]¥§§‡JµJ£Ñ$×ÑÖÚN$zûöíÿ}ûøøÕjµeŒ±+• ù|þ¬‹üÎ-:7]À£,ËRZkÝÓÓóÁl6ûk/{ÙËZÉdÒÊçóV¥Rñ}ÿùÏ+˲Œ$ž IFå¡C‡¶º ƒ. }óÍ7ÿe½^ÿµ×¾öµ-˲ìÕÕUU«Õp]·mºE"z»»ééïçÉînÎÏ“ò;Ò$ ˜Çà1¿˜ÜÒ 3Žçógýí‚“ê>‰&ñ˜µÃ]"èý'7Áe½Ù/n†„ò¤^¢FŒXqÿ™'®:2z\ÚkEj5Jõ:E_ìß¿ŸžžΜ9C$9g8ð||û³1üÙ@ ¾V'“ÎÐÙÙÉää$Ùl‘H”3g<°om­N¹TfllŒ•ÕUÀÐÕÕÅ™3gÈd2ÄcqNÏŸ¦¯¯—F£IµZellŒ|>²¹Ž3gfHgÓ8N„™åÙ¶KQ«­16&x€C:nƒˆŽ±lÛv³¹ÜŽËú½{¿ýíÿó–[n±>ô¡¹®Á8÷¹UxVºbÑô?øÁ?šššúõ7½éM­h4ê‹EÕl61Æà8±˜—žÍféëïg´¿ŸÒÀO(¯/½tâõ}è*x œ#`* Á•CïcxV‚´¯’±ÖM‚Ü1á¥Õ•\‹Ðv©…—á<Cªkþu¤ä¸‚b”*Ã5<%õá¼×xe·V£U¯³âGZ­£££ëÂÏdÖ?Sã;;;) ´Z-zzzXX˜'“L&YX˜§»»c «ù<ýý”+eêõ:™L†É)/«”Åääi’©µZµí2”+Z­Ý]Ý,.-zçM$XZZ¢««×u)•JôôôP,1ÆË嘟Ÿ£»«Ëîééqc‰Ä¯îؾýþîïþνóÎ;mÛ¶ÛnSx‘„´-:;mª µ¶-Ër?úѾññÇÿ¯ï~÷»µã8öââ"ÆÏ5ÅbX–Õž~F‰ÅbŒŽŒäû““ì]]%«C‰`(EùÆ ÐñÙ%\W#è( *…0Ÿ&È2 ƒŠâ«K¨O´»|>\,f| %‹'¤JQ°ƒAWâšÿùF½ŽÕl²šÏ³¶¶ÆÌÌ »víâk_ûÚ:æÜlm¶íBÕj•µµ5¨”ËÔj5¶mÛÆÒÒ¶mÓÑÑÁôô4¹Ž,Ëbjrб±1¶mÛÆöíÛ%â¶ZD"Ž?AWWÍfƒÇephˆF£AµZctdÔ úxÀôô ™tÛö†ýÔªUýýäóyeÛ6»wíúãééé{~÷w÷ÔOþäOZ‘HDÖ¶6dÿÁüÁ¦œÈgp«T*™O}êS|Ýë^·c÷îÝzuuÕjµZc°m»­ù‰Éd²½v]—…¹9ޝ®Òµ¼ÌV“YBu¢¹…!¥g½0›hùp[kñ½Å,—®¿Äö!@ñåºRe(ø€Ôˆ‹ ×ÍŸÂâ.H™p•À"ŽÄ"\ ÒÓƒI¥&•N³gÏî»ï¾v Öº-ÂÚú\¯Ÿi9ó‡…ÃÚÚ===¤ÓifÎÌÐÛÛ ÀâÒb(•JtvvR©T¸úškxã~ŠÞ¾^²™ ­V‹üêª÷ŸG£ìܹ“ë®»ŽÁAÒÙ ÍF“'NÐÓÓMÄqXX\d`` ˆ, °¼´„c;är9ææféÌubYËË+jhhÈ-—+ÙLºç¡‡¾}zzں馛Œ¸—[t~´i€RÞÌùL&COOÏWNŸ>ýšk®¹†H$B<oûg¢ù%=XR„·mÛÆöñqž8q‚#³³ì)±ýÖTR«/©¶(óH]@Á&ñºûˆ(9úÄì7Ž ýWeýSò[ñx/1êŸOJ‡×Bç«€bó7ñf(cH*EÕ×rsssÄãqyôÑG‰ÇãO7CÛ‡µ~øµ0Qww7³³³ÄãqÒé4““Stuvãº.ÓÓÓì¹d‡.;ÄÐÐÅB›oþkîýÖ·(—Jm Âv2é4###ìß¿ŸÃ¯|%¯¾áÕÌÏÏcŒ¡Z©°¸´D:&q˜¥§§—z½N¥Z Y6™l–3gÎN§Èd2¶«]·³»ûwïÜy×Ç>ö±¿Ë[Þb¿þõ¯F<`‹Ú4 <ô_)e€éý×ý?®¹æšt"‘0€A°Qû˶x@ÈÆŸ$I× ñóË¡÷-Æ—ìÀ&ÞØ°5ßÕI °fÛŒ‹ÅصkÍf“‡~Çqžæ§o–¶?›€hµZd2‰+++ŒŒx¹üµZ•\g'Zk^õªWñên`~až}ìcüÅ_ü%÷Ýwår™F«…RŠH4ŠeYÔëuÎÌÎòÐÃóõ¯'N°û’K¸á†˜ãĉŒŒŽ°¼´ŒRŠÎÎ.æææèÈf‰D£,..20ÐO­¶F¥R¡¿¿Ÿ¥Å%’©„ŠFc ¸r5Ÿ¿ããÿøÊOÿôO[Fk½å œmªð™ßËß}÷Ý»•R—I$ƶm‹ÅÚ ‹Å¼‡%%‰´×år™™™N‹t­®ÒOƒNà uW{Úàž”CÀø’Ú+yú°¾e—€sJÿûEû Aiïž’œ…ª¿¯ÊúI9¡.î?Wµü&§Ù\ŽÌލñqŒã ]—D2ÉØØÆ.»ì2¾ýío{á3ßÅÚ˜îû\üûäÈûF£A:¦««‹D"ÁÞp#W\q?ô7ß|3÷áóÔñãÄãqR©Žãy“ÒÕH"=±X¬mŽK9x<‘`iy™ûï»ý/ÙÏË^ö2î½÷^¢±Éd‚ùùyúúúh4”ŠEXY]ÅCwwssód2¢Ñ(ù|ÞÊår®1æ€cÛ…{ï½÷[;wît:¤·ð€g¦M¾`mÛ¶mö+_ùÊÛ¶:tHc¬L* bÙ6K‹‹œ^Z¢¸²ÂÞF£=Å×"èΞßn)¾¹Œðho #Jg_q#$ÃP¶ à'£±çñ°†ZèçT ü:Ð'ÖDtÝ“EÕ—=qÇ!ÑÝMrçN¥ÆÇÑ©”× Å¯„Œ8Ôj5.¿ürNž<ÉSO=ÕvÎWœ¯¶žöZÖ¯xÅ+8|ø0Û·oç‘#GøÀûßÏ?~ò“LÏÌJ¥ÚŒ/LŸL&bbb‚ññqFGG¢¿¿ŸÞÞ^²Ù,J)굑H„Õ|ž333\÷ò—“ËåX^^fuuljÐÑÑÁÜܹ\˶Y^Z¢ ŸJ¥B­¶F_?‹K‹8ŽÃÀÀ€šž™QÙLæŠb±xǧ?󙥷l¹çE›.`½pçw¾ã†npâñ¸qG…™>l¢ÉCT­V™œšâä‰L–Jt•Jthê°–^Âc:R~ÁYDÈ1†ÀT¡a´—ÏÍâ ˆ9/¡Fy/BCÂ|-Ÿéý¢#« –&¡Ô‘$ü·ËÞö¶‡ ==‡¶­•1J…ï¶mÛ†ëºLLLà8ßúÖ·Î*.TÛŸíµ¬•R\ø0>ø øÀ¸õÖ[Y\^&N“L&×2Ëf³lß¾ýû÷³sçNúûûÉår¤R)’É$©d’T:M6›¥««‹x"AÉõ5sæ •J™·¾õmLOOs⸇äWó´Ü½½½Ì/,H&I%SÌÍy¹®vÉçó P,•RÊu"‘t:‘ØsêôéOœžœT[®À3ÓEbŒOéK_Úï8Î¥»wïÖÆ¿ƒdÀôÆJ¥'Ožä»ßý.÷ß?§Nbey™¥j•r¥ÂX«µ®>?œ$‚a‘€ÉËÚ^²ø¤xGÚ†K&_¸±¨ôä^ú„@1&ME}íojÓ›Ø-o*ŽJÂT>“‚ߊZÖï·Œ¹?×ÕõDtdäçWæç³N$â—“% (õŠevîÜÉW¿úÕgôï/TÛ‡×òßÜyçÜqÇ”Ëe/ÃÏæÈwíììd÷îÝ—ΤI%S¬®®z…©)öìÙÃÞ}û˜™žF_pÔïe–K ô°¼ì„]]H˜Íf‰Æ¢RFm5›M·X*íìÈdÊß¼çž{_l®ÀùDdžËÏ=÷ᢕkíMº¹ñÆ?xÇw¼å5¯y-Ò ì·Â:yò$§Nj7Ál6›¬­­‘ˆÇÉ&“Lvtp¢VcA% 0²€uo_!¨ä“¾‚âIBÔ ˆ5ˆ'É@Ò «#<·è£“`üH€ízš^Å`! _uàŸÜ ä `´VD'ï»ov|bâ¶H"ñ«£•Ríx•RŠF£A¥R!›Í255Å5×\ÃÐÐÇo· ;6 füðµÖhxL &.:ŽCgg'ãã㌌ŒÍfÛ³ c±¹\ŽÞÞ^ßG÷23šÍ&µZ•|¾ÀÌÌ ÓÓÓØ6õzÇœJµÊ‡ÿîïø£÷½Ã׿Ö[om ù¹yºººh6”Ëe†‡G( #Ùó$“I/eZ»V¶#K6þ/3³³Ÿç»ÞõÄ•W^i]zé¥ZžÇ’ÂÃq_,tÑ€eY°~üÇüžÛo¿ýs_úÒ—Þôú׿޵OŸ>ÍSþTœB¡@½^§ÙlÒh4X[[óÞ·ZÄ£QL:Í‘d’\µÚîÑ·ˆÇ˜Ý5Î…‹ªxŒ.>%U¸A€ü‹ ”£{ ŒïNké•VÝíÀRî‰Àg|X0 Ìú€ƒ¶,K¯­­Q>räÃÛ·¿{eaÁq"c¼fm Y*•c~~žV«Å®]»8zôh».xFp¶×g[o|¦L:M­ê9Y‘H„ÞÞ^ÆÇÇ"•Jy%·–M*¤··Á:;;‰D£ Ú5¸®×6•J’ÍvÐÓÛCOwG?ŠÖ^§ ÙÙY¾ô(ÿrç¼ãï`phãï³›L&ÃôÌ4ÙlǶX]ÍÓß×ÇÚÚµZÍï%°ŠB©‰ñ ÷±ÇëÙwÉ%óï÷Þûªßùßá¶Ûn#¾`®€ŸGy„_ûµ_#N·#-B–eQ.—¹æškøÀ>Àsrµ!ˆÖšz½Îk_ûÚ?¿í¶ÛÞ¸k×.ûرcœ8q‚ÅÅEŠÅ"µZ­ÍôõzF£Ñ®‚³l›t"ÁB.Çéµ5úµÆÆK¸‘âiÎÅcØ*An€øöb¶ è'Dø/€O°$4-ÿ°X.X"â0íÀÝÜaÁ7ÙP2Q›éQª])¬´v /•&séô“+‹‹û|w©-”R”J%”RÔj58xð ·ÝvÛ:6OÛŸ‹"‘±XŒ¡¡!ÆÇÇ ‘Hx1þˆÔ 288è•ò* m4 Ú²5¶E«m”ÿpŽm#ðÈ#°}ûövýÿí·ßÎþ/áM7ÝÄ?ÿó?{Õ‚CCK%Ü–KggW»åX"™`zfšŽ\ƒ¡X*Ðß×ÖÚŽF£n"ÿñû÷¿÷ _øÂŸ|êSŸrÞþö··\×mG+žO’û¼¼¼Ì]wÝ…mÛëÆ´?W’ó„î<[º¨]-ËÒ>ø 511ñMcÌßøÆ7~zÇŽ®ÖÚ®×ë^ l©Ô¶¤pE¤c,£;—ËâXµJ3Ÿ'×ù7F€ÔCÀè‚ÀKn¾Äë%„(³¥¿ ! Z¬„†7âÊö“~´ OÆáßløßV (4 ’SÊ¿b)ï_Ñ-¥ŒîîžP¿\ÊfÉ)†âñ¸iµZV±,‹Z­FÓï½âÄ .½ôR2™ F£”{¼YÚ~#i­éèè`Ïž= ‹ÅPJÇéîîn£ûÉd”Âh±ÊR(¹ÛÆÂ¶ *ÅRÞmê ÙlÐl5YYYá‰'ž`eu•Oýã'ù“?ûSvlßÎòò2Ѩ×r¬»«›f³I¹\fddÄ+r ¹ óó$âAK±ÑÑQ+ŸÏÓÝÓó} IDAT;½==_xç;ßùØ‹ÁpÛ¶Éår›&òù<étú¹§ þÏ@ÇŽc||œÃ‡ÿù—¿üå7îÞ½ÛŽÇãPáŒ6Ñ*Q?{LâÈÑH„ÁFCŸÉdLí¾ûH6j,iÖ!æ¼4îÿ~c‚üÿ>Çjô-×pi²a% Ùð¾<T΢éåÈl<å͸s›±˜•Ïd®éßµë?Gz{ßX*³ÙdÒ¤Ó)†Ú¦²m›V«E½^Ƕm¦¦¦xå+_ÉØØ>ú(‰D⬚ÿ­7¾>2ÆH$Á‰8¤R)¦»»»mRã;:Êcvo²ŸÂ(ƒ2m Þϳ°Œ_ ¢µËÀÀ …B‘r©ÌÒÒ‹‹‹<ø½ïqëg>Ëo¸‘b±È±ãODIgÒÌLŸ¡ÃÇVý¦!kkkTk5FF¼È•P¥RÉU–Õ}àÀ›ÿík_{Q¸ÀºJÅÍ<ßs¥çc2. ÖáÇïùò—¿|û£>ú–‰‰ ½¸¸h×j5´Ö´Z-o*—.lR©”I%“&åužQ…|Þr’I¦ÊeŠ=dRžiÞFÏÊÕ‚U¼ü€0h'f»ë#õ!aaY>û†fSðí(ü Äÿ' ® ·ÆWÊ­A7c±˜56v#ƒƒ¿Ö›L^—N§íâÊŠéìèpÕÜü¼ŠD"OëX#e­kkkd2æçç©Õj\rÉ%|ï{ßó²é6¸g[o|}¾ÔÜj¼‘a™l†»v180à7ö´Ð!áãÍ~cBcÞ(cü·Šöl` …c{½Õjµ{ ìØ±ƒb±HµVãŸþéÓ\}ÍÕ¼ôÚkyèᇥ\*ã¶štv ¶-ƒt*Åôô4ÙlÊRä yúûû¤š=66æÎLO_¿ß¾÷~á _øŸ·|êSÎ;^@WàÅHý.,,,pêÔ)/ôÚkÿâßøÆ›:¤öìÙ£s¹•JÅcˆzþ¦ÒZ[õz]Õ fX^Z2ÅRéLuf扨ã|i-~¹U.¿ÞVÊÅ[âúúÕwÆèŒT6ߤatô÷y›í?Ûìö Éh¤íRY¦‹…_M¬òÁ`ÍFµFÑHT‹éõK}Ül65A_XXÀ–-[àr¹´¿Ñ+ýù¾˜µ‘„¥¿Ïó<šÍ& E8G˜àSúúQßÊþ0û®“wY ψª  Ï`2QˆÝ.B¡âñ>T«U øô3Ï`vvûõ_Çü?þ# §ƒu vØí0x ø|>t»"êõ:ÃÊ@¦¬v “&R6—õMŒõ¹ßY÷Òz×=³ÙŒZ­†cÇŽ‚x<þW?üá/?pà€»Ùje›år^Ìå(!‡i½þ*I&çy“iÅÐht¡‹m¨š±£4E€ÿÚ’#§D *˜W‘e˜€’ 8'%#pȼH4ΈŒN åù¡¼î2Xoå{ß àL±²H’ÅÂÉ‘Èu|(ôYO,ö‘j¥bxžö…ÂR¡T$¡h˜s8XZ^†Ëå‚ÅlÁòÊ2ü~?8ŽC±P„Ýnך£ë‘”R ¸îºëÐßߣGÂjµž×ò¿Õu>ßøXÅar¹<ǃ#œ6€•}¯îÜè•¥Œ¡(Yf¿Ñ«QB@;6“Ù A10ЇR©ˆááa9rÍV >ðö\y%öìÙƒçž{FÝnáp…B¥)‘HÀáphØ@P0Be ¯×Ëçr9Éæ°ÿÒ–™™/ìß¿ÿo/uVུ޵£W/(§Ó £ÑˆS§NÉÇ'n·û°Á`ØY*•\œÑ˜E*UÆÒl6v1³‹›ÀãáÑÓÎ2 2YJŸ°ÔÛïu ÙS¯Tò´ÛM9® QL_³m[Ëçõ.‹¢xŽ"©®¼úI”ý½îáù^ûbÆ´ãŒq»Ýòj*%¿òòËvרØGùXìóÄ`¸<ìñpb§C6›4::J …O)e-°³k0›Œðû}H&Ym½ÃáDbuÁ§Ë‰z½®}Ÿ*€jÓTžç‘ÍfÑn·155…C‡±¿HÁ¿µ?߯󼢲oCGY¦*ËAý|å!Õ…T)% JņêÊ2£&“.—ýý¨T*ÈårX^^ÆÒò2þõk_ßÜ~; ùž|êI„Â!H’ŒJµŠh$‚z½†n—Í(( H¶XK$ P*•àr:¹®Ø…Ø÷ú}¾Gn¾ùæ÷AèR®w]ýMLL00Éá@.—£ ƒ! `ME‡†ˆ{ÇþìÙ³àÙ¡^)o$”2ŽRú #Ïcyq•jF£ÕR ÷÷«¯º JSˆ´+µ–ç­,FéeXB·+¿øÒK±¯ýëŸ øý7÷õ÷.Ÿ;Ÿ×+û|>)Is‘P„oµZ(Kèëg=ý †P—K´Z-Äb1òyH²„°/Œb©øºn¶Ç¡ÓéhÙ‘f³‰L&ƒmÛ¶áž{î¹(á+Ö^ýÎ Àh4¢P,0AÖõEfº€ õÆ}!Lºžg ) ¢(ª½—çx˜L&ˆ¢ˆH8‚b¡ˆr¹ŒB¡€J¥‚§Ÿy¸î:ìØ¹¯¾v6« k™5ØlV˜M&d2xt}#‘Ê¥’FIÎdÖ‰DH¹R–$*û·ÌÎþ?~ê©þïP€­w“ øìg?« #ž~úi|ík_£<ÏJ)'Ë2½é#¡;ff¤ûï½v›í­2¤TÎs# OYÚ‰²°Ê’DÉ,ûù–B´!à8ƒAæ8Nš;u §Nžœ}èá‡?{úÌ™÷õõù>põÕ(•Jòð®¥V‡?}æÜ.«€³gÏ" ÀÀóX^ËÀïóC’eäò9„BAH¢ˆZ­†H$¦âÔp:¨*a€¾& Ûí‚çyPJ1??-[¶°éºÝ®‚¾Ÿ_\¬µß¨8ŽÇq0™LhÔh4š¬IAˆ’7¥j|ÏT$Õ3) Z|èÔ†v–×í'Gˆ‚˜ ‚Ö=xhh'Nœ@«ÕÂ_ÿ:þû—ÿ¿ú‘à±Ç¾v§x0Ž¢R&ít8‘N§a³2vb­ZE$E±X€`eÄ¥V«Å÷õõIk™Ìõ›§¦þýû÷ÿ?ÿ;ø U‹Uæ'„Bd0ô¢Ì~ƒÑx1›d0%"Q@Û8ž—ÈÏSŽçñV7Âq<ÏŒFL&éìÙ³ü_}ùË7~îsŸ{âž{ï=ŽûÜe—_îýÍßú-ÉãõÉ—Û²e O8Æ4‡ÂÈd2>‘JÂjµÂî´c-“ÛãÝáD.Ÿ‡ÛíÑ\{·Û «Íªžö£(sÕ!ÏóX]]…ßïG?Úíöy-–^U!>ßv¡I:*YEíߨRfò<§É7QXT½4é¡ý MÆ T®½ `xš6™Œp»ÜèïÀàà B¡ŒF#NŸ9ƒ‡x333ðz=±\.Ãçó¡Õl¡ÕjÁëó¢P(Àîpê¼^/ ¥A€×ë庢ˆ¾þþÛý>ßøÍ·Ü"?~œ3 ï5÷çq½ë @½è€Þ¾®Š ,­ôF.o¶½¥Æ÷²,sÇÉÝnWúÖ=÷¸nýýß¿ù޽{_üñ“O>vùž=ºêškŒƒƒƒÒõ×_£ÑȧÓ)nbr…BÇaûŽíÈåóh6›lLV¡Y’Dµ±Ø€ÙÌGàr9±¶¶³…uº)•ʯ³Æ’$AßF=“É Ýncrrrñc£Õ>Ÿð¿™À«B¯¿U‰Xív•jEóD¦¢=ó´Ø €¬( ýïD×yÊÞk! X‘H„ކ‡‡Yé±Áˆüà ¼ðüóøåoD dE-¬Ál.ŸƒËíFWÑjµ´Þ;kNÚj4Ùs…<‰D"’ÉlöU–$ìÝ»—t: zTïçõž@?~Ö§]qõyBÇqm·ÛòþG‰ÿÞùÝ/ß½oß Âqÿæñûv|ì×~~àÚH’(b||œ,9sú4FFGa2™°¸°ˆÄcq”+„B!ÈTF¹\F,G»ÝÖFfU+UTkU„‚!Ôj5ˆ’ó–Jèt:ðzÏAŸ´X,Èåsè1ú ¸ü”ÈŸ_fɉ‚šB­ Tqûéë½Ê¤ìó8žƒÉh‚Å"Áëõ¢¯¯OË ¬­­áĉxøß¯ÿúo`ó¦M¨5Ø\@¯×‹F£¡t¥¶#•JÁíö ÕjC’D8öœÇf³ ³ÙÂýf‚½ÞDâ{·ÜrËü/jVà’)¥2º¢¨ ÌïÎ’e™ñóÙ…--.,põå/ßxëg>óƒÕDâ'œÁøû³[¶º?þñKÑhT6 Üôô4¿²º‚r¥Œééi¬®®¢Ñh`brKËK% XY^ëßÇ®t~¿ét&³ €ù|N§“aɬ‚>¿}ñ>|ðúëÁó<ùÞ÷PTÒX@¨*BÈ:@E­ÿ°öúÍh4jï·X,ÈçóÐ,¼ŠâSÊ,½¬PÅœ²¶W'À^ÒJ{ÖŸ9*6ŽƒÑhRª#ˆÆ¢Åb8ßýÎ}8}ê>ò«¿ŠJ‰…&V«Åb^¯—åÿAa³ÛP(àñz4N…ÝnG¡P€Ûí&.§Sêˆbpvfæ/¥_àPà’{*šüNË¿’BãŽãäF£!÷¾ûœÏ?ÿü¯—ŠÅÏgóù;vî$#ÃÃ4›ÍJ333œÛíâ<ˆÉ‰Iˆ¢ˆs‹g1µiš­–––0==F½ŽT2ÙÙT+¬e³˜ÅZ6 ƒÇ¦ÍÓH&èv»D±TB·Ûen38.lÛ¶ ƒËKKxàþûqèµ×P(0>>  ¥ÿT Ë2 ‰dYÆøø8µ¸ü°öç 'TP©T¢êîMÀYÞ_ÖÜ¢bëú5®K!ª¿‘’&$„€P”2̈7ð0›Ì° VôÅûP.1šðÂÂr…î»ï>üÅ—¿ŒÍÓ›17?‡jµªy+‰Ä*üþ€ÖrÌ*H$’šrA°bm-ÇC!©Q¯ÿöÔÄÄSû÷ïÿç_ĬÀûî(4—'„PŽãh³Ù”Ÿüñc߸óÎß][[ûôØÄÄÓã¦3[¶ÐmÛ¶É‹g¹X,Æ{½^:t¡P>Ÿ_9ˆH4·Û¢¯¯‡ÀààAÀ±cÇ´¾}‹ aØÀÉ'‰D JÖ2kˆÆ¢pØíøý†BXXXÀ“O>‰S'O¢ÞhÀ¬´½®TWÀh4j½ÿhŒÀz½Ž¬¢pžxâ‰7Tº=Ÿ _è±Á`@³ÙD¹\†Ëå†(vY*P9ߌ÷£öwTûè_µüZրꔂª ¯ã@(Ïs0šŒ0‰æu¡@¡P@.—ÃËàÁÀG?ö1”+œD±«³ð 2ót @™RV8¤† ¤"¢i€Fð¤ê5Àžæˆâ˜ÍEÆò+•J( ¨Õjh4xàpå•WbçÎX:·BjÕ‘0ŠÅ,& #Êå5„Ãa”+e­ÁI.—SA¸\.I–¤ðèÈÈ_üäÀOÞ~Çäø…!]r§¢ÝoãDB¯°Î¤…ùyü¿_ùÊ/Ý|óÍ÷ÏÍÍ”dùóãÞøÃR(–EQ䦦¦ø\.d"‰M›6!—Ë!³–ÁäÄ2é 2Ù5LmšB:F¡XÀ¦Í›L¦P©V±Ii\Y¯×155…d2 Q199‰sç΢^¯c×î݈D"ؾ}¦¦¦ðÊÁøÚ?ÿ3¾ö/ÿ‚C¯½¢µÔRÓ|~¿ãããšõÖ§ÑÔ°`y™ÕÄb1Ȳ £2„ã­Äöã|5Ö?߸6õsÍf6#©T,1¦æ:J0í¹÷òú¿¬”«lÁ^ ‘2"T^ˆò»S€1¾©Â×îV› }}}èïïG,ƒÁ`@:“Á߸±H W]u%R©ì;Sµ¼>/J¥,kdª*°r¹§Š¥""‘o4™$ÁjýO›¦¦>ýè£þBe.¹@€ŸÊÕb¥å„£”ÊNG2›LÆÍSS¹óÎ;ÿH–å«¶ïÜÉ  ÐB¡ ŽŽr·‡?räÀq<ææç122 àÌ™3,Ë8sæ ÆÆÇ Ë2æçç119Q’°¸¸ˆ©©)´Ûmœ;wÓÓ3èv:H¬®bjÓ&Í=½þú_ÂðÐêÕ:öï/¾ø"Ïž¥T›‰  „¾‰¦Úcß¡XT¯H¯ŒF#’É$`ppkkkër¼UÿBéßc0°f …by#P\zª+ùU2*8HP‡RYý¡@ Ï@éÒÀJ<­N@·8ÂÁh`…¼^/k®xÅR Ï<õ4.Û}®¾æüä'?ÝnG±X„ÍΦ8ÖëuD£Q‹%ÍcªV«ˆ„#(—Ë0›Ì00¶8çöx`4þ<‘Hüè–OúÜŽ;¸é_€Pà’^×ãM³ø:ŽÄqœc ¿ÿæ=W\ñ“˯¸âÁX<~ͮݻqåž=¥2|>ïöxÈÉS'át9qæÌ)xÜnøý~?q.—>Ÿ'Nœ€×ë…×ãÅñãÇàóúpüè1CAx<œ8yñx<7Ž?†ÒÓápà“ŸüOðùüø_ÿü¿ð¥/ýöÝ}75š«Š0 ‚€h4Љ‰ ­¶ÏçÃÌÌ ®»î:ø|>­Á£¾êã8”Ëe‹EŒ¯³üzkþF–ýBÖþ|ϫݙìv;ò…¼ßè¹ýʯ¨Q|T²—ú&â(ÀŸ¢¨Tú¢2²2@ЋŌH$‚x<Žááa˜ŒFtDßùÎwËåpÓM7¡Ñl ÝnÃãö0F ’.m4êðz½Œ, ë»èõzQ*—a2›I8–DYŽMMMýwIqÇwüBd.¹p‘…-<¥”¶ÛmÉépÇGGÏíñ|Æét  P™ÊÔç÷ËÛ¶mçÉÊ•*fgf4T~óæÍXYYF»ÝÅÔÔ æççÑé´±yóf,..¢Óé`hxgÏB–e cîÌ(dŒŽŒbnnžÇ€ÒžËétáÚk?Ž#H$Øw×>|ûÛ߯üÂ!šÐ«.½ÍfƒÏçcÃ1,Fø|>F&ŠÇa·Ûa±XÐßßÕÕÕu^€þ\-..bbbBZoÛÚ¿Ñó<Ï3 P@·+‚(A@ +~?Q³€®…ZDÁàÛÏ>C½ ¥ Ôà‚€€r'%`·‹ë¸+++837‡ï|ûÛ¸mï^|å º.#ý´ÚðÇüÈf³ZÞF£©xEX­V-\PÆ’ó6›UrØlŸšûñ£>ºïÞ{ïå÷wWR«2ßë’+}ÁÊ…ßF¸v»-YÌfã@ÿg&&&îpº\ý­V‹ÎÌÌÊN§©TŠC.»†•åeÌÌΠQo •Javf•Jɤ÷çóH¥RزeÅb‰Ä*¶mÛŽR±„T:…­[·"›Í"“É`×®ÈårH§SضmZíÆÇDZmÛ6œ>uûîÞ‡<ñ–––`4™àp8@eŠv‡qöN'ü~?<L&–ãƒ@4…ÕjÕçyÄb1ï®_*(µ²²‚Í›7ã°õÃ:ÞL¸/F!¨UF`±XTpt™ˆêÈÌbSH2Ã% Š%Wù„RP¥"PËÈ€LhÈ"E™(m›)GÀSV™h1³†¤±XŒ…EV9øÌÓOãŠ=WàCúö?ú(VVVár»ÐévÐî´ÍfÙïB)šÍ&¢±Š…‚vîËå2‡È¹sç …þ2³¶öÔ§?ýé•íÛ·s333ïÛPà=qDò8ŽãdY&­VKê‹Å>´eËìK;vîø§¡áá>“É$íܹ‹öÅc\"™àB¡ dYÆÂ"£èr„ÃüÂ<úúûÁñ<ÎÌA8!æçÑׇL)NŸ>A œ9s}}qp‡ùùy £+Š8~ü8fff0=3ƒ_ú³?ÃÇ?þ[ø×ù¤Òi¸Ün­Yo`Ý_‡‡‡1>>Žp8¬=¾òÊ+qÕUWattT‹ýU—[–ex<øýþ×ÕN¨€a*•Ïóèëë¥ôM]ù·¼Qè rÔâÞÐk‰¨OçÊP ÕWÁ>%C +éB*3Æ ¬Pˆ íe´Ž*ÇŨ–F£èëëÃÐÐZ©à®oܳф-[¶¢ÓéÀát¨¤t:´;¸\.”*Ëh6[ðxÜ(‹ƒãy^rºÝ}›6mú3Qñw÷wÀû¶Xè’{*Àu¾EášÍ&@'ÆÆþ<ÿŸn“SSRµR%ý}}| àǩӧaØà‰¹¹9S{}83wv»Ÿ§NŸ‚ÅlAÀïÃÜü<A€ÇãÅÜÜœÂá0Nœ8AGpêÔ)جVxÜKEüʯü †††ðÔSOáûïÇ3Ï<ƒµl‚ °Þô:`/ ¯¯’$¡£\x*¯ÝçóÁh4jÖ^eó)ǫݎŒŒ`eeåu @yžG.—C§ÓA?–——5öà[µöoöœþ>M9•J%­p D-ÿXï/®×Xªý”ª»¯ë é|ÂpU!På}ÐBÂ<%%i1›át:Ö…Éd§ÏœÁ<€ßþÄ'°}ûv,..‚RÀîp JÁ­´SëtºƒÈf×àpØ É2ó¢QäóyxÜn^”e©Q¯ÿþèðð“wíÛ÷íßüÍßä?ò‘HïG/à’+œ¯©¾Z­Jv«µÿòË/ÿ¦Y®¶ÙlÒž+ö|!ÏK¢ˆH,†•刢ˆh4Šd2 I’E‘H±¸|l KË˨Õjزe ’©jµ¦7O#•JivKKK¨ÕêØ±cΞ=‹Z­†ë®»¡Pb·‹……üÍÿüŸxòÉ'Q©Va±X4\–e‚ ô¶‹ÃívCÍŠ«]uÕUš­^àõÇ.Š"†‡‡Q­VQ¯××]t„H’„••ŒŽŽâ¥—^Z§.&Þ3Ž€¾¦Àét¢X,B–%]&µö’5aÆzªŸÖ @߀0ß(Õ (¨³ýš‚aeĽÁj ±®(Âh¡@¹\FµZéø?¾ IDATÅÃ?ŒÙÙY\ýõ8vôÜnš$YÖËÍA»Ý…?@.›ÓÀÂf³h4†R©L8ƒccc½¼²òìm·Ý–¸á†8£Ñ(¿ß¸—\h¼pôb\Q¹F£!9lö¡+¯ÜóˆÝéÜl0Ä={öðµZdײ˜˜G©XD6—ÅèÈjõ:òù<ÆÆÆÐ¨7Ig0>>ŽR¹Œd2©©)Ôj5,//cbbn©d “S“¨T+X^ZÆäÔ$ZͬV+n¼ñFp‡ï?ööïß—^z ÕZ V› N—K£ÂÚl6D"D£Q8mDöôô46mڣшƒ¢Ûíj¨úùý9¡Úä•Òª?G”R,--áŠ+®€ÅbyøÓþ[Qét’(+Ýõß¿OGýW‰þëŽKCþ5V`o.‚š ŠWÀˆCœ*€U x˜Íˆ¢ˆX,†r¥‚|>Ó§N¡X,â»÷݇?ýÒ—p͵×àØ±c¬FÀãA³Ñ€,KJ±P.— b—Í_øýÈær°ÙFÐj5¸ÁiuuupÛÖ­ú“þë­·ÞJî¼óNˆ¢ø¾¢ _ò#!ºÿ9ŽC§Óá†ì°ÛûöìÙ³ßíõN@ܺm›¡Ûíª“_ ƒ A8†ÑdÄâé3ˆD"ŒŠ{ò$¡08Ž`qqQ#;v ‘Hv›'OD0GŽ;Ž¡ÁAlÞ¼™¡óž~ê)Ü}÷ÝxõÕWÑév!Ün7 W(ÕX€‘H6› 6› ˜ÅÈȈÖCßjµâòË/ÇË/¿¼NøßlQʆ_®®®®Ë¨nh"‘ÐÀÄZ­¶núîOkíÏ'üê}«ÕÊ&óÔkŒ©(KêŽ*¿ Ìb|µBP#þôX€z0Uz¬+$¢jv ×Cºb¨0 J/;â±Ê¥òù<Ò™ ¾ò ~ðAüçßùœ={Ùl6« «‰U¸Ý4M¥E™t.§]e‹ßïG.—S2v^–eÉç÷ntdäGwÝu׃ïÇPà’+¶XŒ×ívI£Ñ Fƒ;¶oÿ¿ƒ¡Ð”L©8<8h°X,˜ŸŸ‡Ûí†ÃáÄüÜ<œ'‚ Μ9›Í¿ß¹ù9&¬7Î;«Í·Çƒ¹¹9Ø뼸¸B88íNdÖÖ°ûòËpõ•Wauu|ï{xüñÇqøðaHŠko¶X4ôz½ˆÇã°Z­p¹\ÁÌÌ 5†Ÿ ¢q‡h4Š-[¶àµ×^ƒÑh|ÓÔ'!D)muh]yÔ Nõ2™ dYF4ÅÜÜÜâkí7Z~õ{Y=#ŠÅ"¤–Ĩ¼Z P§Êuq>Uæ0êh‚T@ïêùJ™0QSŒèõ+4›Ì-"ü?ⱸÖC°Z«aÿþý¸úê«qÕUW¡V«¢\­€#l6‰ÜZíDI„ÃéD&“Ã逤(‚X4†|>P0HšÍb±Øß,//¿pÛm·¥ßo¡À%WªuSfòı±±?õõý6oàÅ€ÇkðúýZ^<‰"‘X…Œþ–/ïŠ"FFFL&ÑiuÐ?Öl6‹V«…Ñ‘QdR)´;mŒOÏ ‘H`mm »víÂàà |>(¥øú¿ý¾{ÿýXXXÐÈ:Çiy½^/b±ü~?A€ÏçÃÄĦ§§‹Å`±X´FšjŒ¯Ú¡¡!Ôj5ÌÍÍilÀ -Y–a6›áñxH$Öuäá866—Ë!cqqƒá‚~1Ö~ã0 Ðh2¢X(`phm0]g× tBTÆ Ó3wžR  ”¨ÂTáVRÂêuèê ”–¤„j^o0Àl¶À*tEQª°ŠÁùùy¤ÓiüÛ¿þ+nÿÓ/bû¶xäÑGÐßßZ­Bì6’É$\n¥yˆØEÈBfm v‡’,¡Ñh s™LFâ üÈe»v}ñÙ^øoï·Pà’§ á”RžR*ú¼ÞÝÓÓÓbµZa2šøH8ŒR±ˆJ¹‚ѱQŠ,ÿ;91R±„\.‡ÑÑQÔë dÖ2B§ÓA:fýóºmds9  \*¡Z«â7~ã7”‘Û'pÏÝßÄãO<ŽùùypJÊK¹F£Æ»÷z½зiÓ&LMM! Ád21pÊdº ¸ÓÓÓ¨×ëH¥RoÙ(•JëÒ€@Ïbž={“““š·¼žt±Öþ| @µvN‡¥rT’á%UPmüÁØ@ ”¬!J Si£W¯ ¿r`çmÙÜË&ôÎ Ïq0x œN—Òy™Ñ„³Ù,^úÉOðÜ<‹«¯¹¯~ §öTzP »Ý†t2 ·Ëv»ƒn§ƒP0¨q”b,> É¥bñ†~´o߾⦅BK¦Ô £^¯ã‡?ü¡b&'&þÒëóYeY–Bá/Ê’Éâ}qÈ’„d2‰xœåïWVVX—“ gNŸ†ßç‡Õjũӧáñx`³Ú°°0§Ó ›ÍŽ` €?üaòyüÍÿøx≠•N7`ÓUå™Íf„B!D"¸\.Øl6Äb1LOOcbb^¯W³ôªàë…ç!Û·oÇsÏ=‡J¥ƒÁp^% ¦û}ôQ\vÙeGÐz`eeEë&´‘ ôÓZû7òÔiÁËËˬSÆõ×_‡R…¨*ªÔûkVÒõö_Sšç¯;7³Dì?ƒÁ£I†ÅbF0D¹/Ž|>Ïf 6øÖ·¾…‘±1ÜtÓM¸çž{”4¢«É¤ ²Ù”v‡ét .— ÝNGÃòù<)˜L&ƒ‘ÑÑ¿M$“/ÝvÛmk7Üp1ôç=¸ä àôéÓÈåó<Ñãv_3>1q=àõzx«ÕŠ……yxIìvǶmÝzûOøã[o½•¿óÎ;ÅŸ÷Pà’ïy.Ÿ'Çñ’$‰ããÿÅátr”R) ò+Ë+à8¡PÉDàBáR©$QB|8Žt:…f«‰©É)dÖ2¨×똜˜@>ŸG¡PÄÕW_É©IÜß}xðÁ‘H$]×éÔ.D‡Ãh4Š`0‡Ã¯×‹¡¡!LOO£¿¿_³°*v±‚¯.U\.Öèå—_¾`ðä“O–––088¸®@ˆçyäóyÔëuÍZ©8ÀÛµöç»U•Ïó8wîvlߎn—õ-lµZèt;l|˜"ô½‚aÅ©'½4_#`Kùé=U‹hï±¾u;6ž)‹n§ñX ÅbÅbù|Ï>û,¶ïØo¼>ø ‡Q«Up°Ù¬ p¹”f¢]„B, °;jµâñ8îÜ’l³Ûÿhh`àGwíÛ÷Øû!¸ä 'I’l1›ûÃÑÈ$IB__Éåò¨ÕjE±TD¥ZÅðð°ÖfdtõzkkY £Ùl •Lapp¢(auu“SSÂWþöoñèþý „Àæphî¥ÊÐ ƒ°Z­Ø´i6mÚ„`0¨쨂¯{+øú¥"ü‘H›7oÆ‘#GÖ‚ªõ_^^Æ+¯¼€)€n·«½®~Ž$±ã …B˜ŸŸ_Çx;Ö^¿¯÷ÝåtáO¾ðÄâqìÞ½ÓÓÌ; h6›èŠ]Æìƒ:¨ÇTûR…þÛ‹ë/€*Ï(µº¸wÁ‚ „$àFX(…(vÑ_éG>Ï®Ÿj­†û¾óìܹÛ¶nÃÒÒÌf3Ö²køü¨×)`·³0Àív¡Ûé¢ÝîÀ"ŸËÁf³0xØŒýÝÒòòË·íÝ›ûy.µ Ê>tÍfóV›Õ6‡i»Óáˆ×I”N¥ƉÕU„"xg a6™qfn~Ÿv› ó p»\˜œœÄ¿þË×ð½G…ÙlÖšm‚ Uà©í¸¶lÙ‚-[¶ ho6›×!úoGðõKµâ£££¨ÕjXTJ†U Ïd2áùçŸGµZ!kkkÈçóðûý뀣££ëÈ·cí7Þ×/QáõyA8Ï>û,ž{î9€ËéÂØø(>ó™[±sçN´;ð´öáTT  ÚsL{JB~@ zÆ<JTÚ0©Á³Å[—…%…°´¼Œ……|çÛ߯ï|êSؽ{7^{í5˜Œ&˜ « x<´š ØHgÒp:ï&¢X*Âf³qf³YœŸŸŸØ¹cÇŸ¼|ðàÞ[o½•»óÎ;¥Ÿ×PàRû-lÄ—) ÎÆãqŒFyey>¯6« +«Ëp:]p:X^^†Íá€×ãÅÒÒ+£Û./¯Àh0°P!™‚(ŠÇóÏ=‡ï?ö}X,­¢-`×®]˜šš‚ÓéÄæÍ›ñ‰O|7Üpb±l6ìv»Vš»Ñò¿c®|Öìì,B¡&Ø<Ï£V«iî¿Â@*•ÒjTKOf2p«Ùç¸7î ¼‘ðfžÁù–(Šp8ÔŽR ÀoüŒCY†@–u¬?ÒÙZß"œê Ad}ܯvR½ õjx@{–ãÙ`A°ÀífžÝÈÈœdJñØþý8z䮹úȲ §Ó‰zѬ­6+ ¥"\nZíº]N§…B6ehm£Q‡Ëé‚,˼ÙlF8ùo~¿ÿÊ}ûöIû÷ïçßêt¡ i¹Ðöny—Ze©çp:Ç<^/V–W`2 ‘H$„Ãa¤’)H’„X4ŠL&N§ƒÑÑQä²9Ôë5Œ¡T*£P,`xx…B?úÁ Ê2¬V6z+`ûöíZLÙe—áŠ+®€ ZµÙÆþ»éÖ©M>vìØgŸ}Õj.— /¼ðÎ;§ œ;w333ëBžç±¶¶†f³ ¯×‹\.§Õ­¿]k¯~‡~©Ý‰ûúúSSSøÔ§>…nøìvx"vBT–ŽÓ£óˆ‚æ«ÆŸ¬'éöBÙQ]¢@uÔ 8 Èf ,ÁPýe6 ”*<ðÀý˜˜œÄ?x=8ˆR¹ ¿×‡F]mî@:“‚Ëåd=Úøý ‚Ï¡\)“¡¡!yyyŲijêïÿãÙg¯¾mïÞÆ 7Ü@L&ÓCuÖC©TºèvcV«õ ³Fog]J@À<³Ùdò‹EÔ ŒŽ X,¢Z­bxD‰ûK%  ¡Þ¨#—Ëa``ív™L±x²,#‘LhóäN?Žt&£ ·ÃáÀîÝ»ár¹`6›ñÁnÀìÌ ; #Ìf“f!wWðµ ¸ÅbÁŽ;ðÜsÏA’$Íúë@:Ö(¿ Aårn·[SêE¸1Î×ï…–þ"ÛÈ=$ ñxwÜq>ùÉOÂ`4¢V­¢P,‚ã4s¯aj¡sí±Ž ¨¥ õì?@G š"Ñô‰ò·ìUÅ3 T– ‡Ùb†]d©[5XY]Å«¯Âýß½Ÿ¹õ3˜››G.—ƒ`°²Â¸´Z½lÀÚÚšÆh4¬î ÄÚˆ ‚Àq‘l6ûö[·ÞþÊk¯}IÍ œ¯¥¸zMmÛ¶ O?ýôE ²,˰Ûíøƒ?ø­íÙ;Yš|©=€ €¥ÝéÌ3b±(»"Òé4¢Ñ(@D2‰P(ƒÇÒÒ|>, áö¸át80¿°»Ýǃd2‰R©YÔ8ŽÃìì,Âá0!øÐ/ÿ2¶Ìn$KàÙlÖÜ,Y’5×òg±T!÷z½¸ì²Ëðè£âàÁƒz,IB!(›Í¢¿¿_éÑß –––°iÓ&Mé­ÐOkí7 ¾vH›Ú‰DÀñ­b°T*áG?ú!®½ö\uõU¨V+¨”ËZÍF2™„Û­– w ‘Ë3€ Vg݃ŠÅ"œN'S ¢Øý#¿Ï÷ïûöí{å _øÂy§ ©çÝåráÚk¯}«—h“µ].$IzǯËK‰¬óZ­V{rb>ŸKKçàr¹àp8°²²»Í¯Ç‹Õ•›¶ã ™L‚‚P8Œd*Y–F±–YC½Ñ`­ÄÄ~¿_Ê®ºê*lÛºO8˜Í&-}&Š"dÈŠ%ûžå»C¡Î;‡Z­¶Žú«.µ!èFfàòòòëÈ@o5¶ßÈ0T}ã­z¿ÛíÂá`xŒØíö¬JþQîkµý샙×RzºãRöKôniï!PhÆÊwÉJóQÂõàGÀyX,¬‚Á`ƒƒƒ€ÉdB:³†;¿þux=^ìÚµ™µ5x}>Ô P*Ãnw(î„$©ÃF=(–‹ZåeµZ…Ûí"VÁ*uº]ûömÛ¾*˲iïÞ½r§Ó!çãv¨çW’¤‹ÞÞÍõ^Q¦F½žÁY¦ˆ„#¬üTR{m ÍV±h …RÕJñx ÕJÅb±X F¹|>¯’(iÅ8ýýý¬Ÿ?.Û½[»Ð °ÈTF§Ó$+Í*åSíg±Ø¾ Š"öï߯=·qé„èÀl6 Q5À x½Û¿ñûÎ'øúí|¯*AP¯×ÑiwΫ,)(ÇzhÂOYjO3îl'g™(­ÄõáÔ!ÐIõp”Lƒ#p„c™3ì;¢Ñ¨’ª ‚ç98xO?ý4¦§§188Ï¡\*±y‚íDQ„ËåD¡Xd)@BѨ7àñxX[q%KT«Õùx,.™áò™ééÛöïßoÝ{/œ¿¥¸ ò^ìön®÷‚àÒkk‰ù¹yŒcp`%Åe‹Çãh6Èæ²ˆÆ¢EéT ¡H ‰Õ¤–º[]]…ÏσN§ÃÚ>‡Ã0™Lغu+Œf: âN8‚nWB»Õbå­”B’em£Jó àüùN-5¦{úé§qèÐ!­iãÊçó¬-—â¨õ *ýÕétâB¤”‹µöê}ÕõWoUO#›Í‚ç ŠµïýƒÚúKMãkªœGÒ+:ßi%*â¯@ ]˜ ¼  š „5]aÝ„mºÐÈÈc3Š"î¿ï>œ]<‹~ô£¨Vkk+^(Âåt¡«ô p»YË0u¾@µZ…ÇãA­V@‡¹v§h4òÇ¡`pæ–›o?νլÀ¥^—< HY?hK³ÙL:ôj!‰pÁP.//! Â`0`5‘€WI &«pºœp¹œX]Ya©@Ÿ‰DƒÙlN©Öâa2aµZáóùFÑnµ˜&æ8H¢ŒN§ Qêͳ—%‰)Q„$1»(нaïâɸ뮻ØIÙ`¹Õ˜¾Ñh ›Íjq¾*Ìív9…°²ñ¢ûi­½^øõJ@CyžÇZ6Û U·œ•ùë@:@ÉÙ+€ bõÕJÁuû ' Ê>PÍí§TW(Ä´Š¢Stô" €Rp †c40† E° b¡@?ŒF#––—ñàƒ Ž`tt‚Õ‚V«I’`w:P(öÚŠ7ëMÍú›L&˜M&”+x¼Ôuâ°;$Ájól޴髲,s{÷îÅÏKKñK­€B J‡>]ªT©ÍnÓJaF#‚’©$d™" #“ΠÓé""§Pbc±Êå*Õ KçQPšRÕØ~ =¬@W¨zšBP„^­8ìµÞ¤UÊód=g€Éh‚Õj…ÃÎèÞ£££¬UÇá…^ÀãO|×_=¢Ñ(²Ù5¸Ýnt;tÚxë/Xð\Ïú³.M‚E@¡X„ßïç.—$×lýÂþýû埗éB—Z¨P §Ïœ9ô£ü€ qS““H§Óh6›ˆÅ¢(•*¨”+ˆÅb¨×ëÈòˆF£ètºÈd2‡Â€d2…P0·Û ½fµZ5tŸÊºÝnO@dÕõ—zñ.(Ä®¨Lá$e|™&ô”ÀÛSêç<ôÐC( çÿô+™L®£«.*•ÒÀÌwÒÚoÜT  …:ÝÇëeSqýßH‰Qí}êbªA^Ý‹œ(…Dšg´ÜÜøÉª¾!J 1¦ ëáÐ×ׇá‘aØl6FþöwP¯×±sÇNH’ ‡uv8%–ôx<(•Ê0™Ùd§r¹ ¯ÇƒF“‡6« ²$q‚U@ÿÀÀm~Ÿoòæ[nù¹.¥P…_¢”v !èv»+O<ñÄŠÁ`@4£j*P’e¤Ò)%$à‘H$áóù «+,càt`uuUkÆÙétõW’Ðét™+©ðÍU—”‚J,Þ—eT’•¦—2DI†¤Ô¯K]V~+É2ÚíŽNpÞF ’yDQÄÝwß}ÁÏQŸW ]Tœ@ý µŠÅbÑõíZû7Rj£$IÈes0xP*ƒ’^(EtU< V5‹¯yʳúyÁ^Ø@u)A—Áéþ”(ä Æ 0Â"°Z­‡ÂB<‡Édbƒ÷ßx<Ž+¯¼år’$Áår¡Äè¿ ß¢ ¯Ç‹jµ žã!V”Š%ÖK ÓF»Ý&ÃÃR»ÓñMMNþƒ,IØ»wï{~ºÐ¥V@GÙG=|äð­÷»ÅÂøÚv› n‰ÕL&#þR©  !›Í¢Óí ¢gsì<^8BP®”Q¯×!‰Ê/1!W-?»/+ ,·M–ØëTb¯©Œn§Yy¯(‰E‰Ý{¡ÁÅüЪexæ™gðÚk¯iä™7ZŠ’\‡¨@¹\F>Ϩ«¢(¾cÖþ|¯æšÎdÀñ! IDAT’$¡^g“…«Õ*xž‡ (–ŠÚȱn§ ·Ëb¹¨Íl·Û|(–Ý^ï''ÆÇï‘GÑB÷šð^ Úª„&€ÕÇ¿ÿýÅr¹ŒíÛwÐd" ¯y&Q‰D/äÙ4—H •Jår‘H­V kÙ,úúáñx Ë2VWVËåÏå€F%Ï+V^–dH’*쪠«`ZO °‹RVÀC& Tbœ‚N»ƒnWdÞ‚Ì.ðóåæyž‡$Iøæ7¿©=§_*`JÅd2á×~í׉D Ë2 …‚öwª7‘ÏçÑh4´Ï~'¬ýù„_½ít:ðù|xâñÇQ(XcRЧO”PJ7>¸Mi ,Ô§©&ìzŽë,¬[;}‡Úª\ý&êŒA“û0Ðß¡¡!X,äóyܽo >øÁ"³¶A¡ÿ–Ëex<½Øßn·+a‚2eLA·ÇJ¥ ޵¤#­v ±Xì/<.×À§?ýiéèÑ£œª´ß+ë½àH``@ @@ýÌÜܱC¯B0$‚ÕŠ@0€z½Žb±ˆH$‚v§µL¡P„ét ~Ÿf“™õ~w¹`·ÙÐítaµÙN§‘Íf±šXÕi¨é1-@ï±&¼T³øj—[™Êd I%É èŠ"dY!€,1 °Ûí00Q“ézðïСCëÀ?µYµÒ^¯ŸÿüçñòË/㡇¿øEÐPÕÝ'„ Ùl2֚łn·ûŽY{U!l Úí6œNÖ)ø‘GÉbÖÎÕ)ª¹Õj«¾ÕQ€{¡Âú j½0ëC =œÈ\}bQ z=LF¦¬6" M8‚ð<|?þÑ0=3ƒ€ß«ÍƬ¿‡ÕjE±T‚ËåF·ÛA§ÓfÙu¸(Ç£R©Áãö Ùl»Í&¹=žø¦M›¾ô^4zÉ!D šj”Ò2!¤ÙÅÅGù^ª^¯ãÊ={¨$JH¥S½ô_"§Ó‡ÃɦäX,ðú¼H¦’ „C0Dz5ËèëïG»ÝÆÂÂ2™ jµ*Õ‹t½BPãÊÜTIýÁd(ö‹yg1H{Vù&eÔÅ „c^€Áh€Åb Xðû188ˆÁÁ˜L,//ãÛ÷Þ‹@ ˆ±±1H’›Õ†¢’÷ïv»è´;p»=(—ÊFƒ•²Jn+#&„˜MfŒONþ_FžßvÛmR§Óáô„­K¹.¹0›Ìղ뼀R¥rú?žùŠÁÀ“þþ~ê÷ûX:¯ZA4E³ÙD6—C8‚,ËHg2øý0ð¤’)xÜnX,*Õª6fqqéT N§GÿU]Ò«TÏ!³<·Ì@(™RH’’¤ºÏ‘D_`5²$AY¨À¦ xèá‡ËåX ?Ï㦛nÂO<矟úÔ§`·Û5Á×·ôE[¶lÁþábqq4k ô˜‚*øÓZû·²©8€ÉdB À‘#Gð‹/(€ ’9!2tlD¨‚ÜsçÕø_ Tìt½|ô2êc5åØ áöÚ ØCÇÃh0B6à5ÅÈèèÿÏÝ›ÆHvé™Ï¹7ö}ÍÈ}ϪbuQ)jiÉݺ-ïFn©EÁ­i·Ì»í± ̆óÃŒa`>V©¾óC»“§œóÀ@ý%A&4tÆ»›f ôâw܉ŒÂ‘}–íàó™\ÛºÆ_þË…­k[üëý¯yâ=O0éšÚù/ºËjÀ0™Lò÷þÞߣ×ëy Ѳ,º])ŠÒf¢?ÉBÿi¿ÞîññxœP(ÄÉÉ _ÿú×U šð$Pº~C½Ïþ{¬öñïdj<Öÿ2›÷Ä?ʺmRO0™êEÀ83Иƒ!dFå÷¤L8!ŸË³ººê™‡ììîò•/™\>Ç/\¼(µõ±X piOÔþ†i¨2Aš‰X–E¯×#•NS«ÕÈe³"‹±´´ü¿ûLsê}îsöp8»xh@¿hÛq¸pá‚ 8®ëZÈ,  4…]àॗ^ºw|tÄã?îNMMÉEÞn«2@Öú…éi,˦X,‘ËçeptD*" srr"íà îlßfoŸV³uª^uUïk°Ç·% vzg‚*ôt®\ jÀuÚí6œ„þÏÿ‹ó 4R¦,Ëõ̽¥ xæ™gXYY¡ÙlzÜü`0`ww¿ß üyìöoÇa4 ……BƒA^ÿþ%®_¿F0P×kÌV8`îx±A*ðOƒxžÀg"OðÀþS¼çP˜`t! ù;LÓ$è÷ ‡‰Å£ÌÎHV 05… ¼ð |ëßäãÿ8‘H„vGªëÁ`ÈkJ+3Û¶I&’ÔkuoRt»Õ"›Í‘HÄv\wãé÷½ï_ÛÚâ·û·‡] <ô`jj $# û4Øš›W¯^{ñÅyÏ/¼‡p(ÄáÑ‘rw1½EF9:: ‰N¥8*aÒìää„ÑpÄÊò ‘H”R©ÄÝ;w(•Jê>Yç{Ð vÀ=µØ]%²½îÁqý¯þÚõ]‰H@ž¯…EÕJ•v»ãíL£ÁÛqކ GC\W¦õãñô®è8¡Pˆ_þå_¦ßï{ àh4¢\.{mÌ.ØwºÛÿ¨2À0 ’ɤjrøú׿A¿?@˜r8¸Vü)‰$‰£h‘ޏ«¨RÇBhªP_ƒS—d|mø±Ð8ÁWQˆ aàóûå¤áˆì7Y\\buu•xlÛËKËô‡CÖÖ×ÿ™ã8‰‡] <ü Àª|>O6›uKñ %X{ó7¶^ûÞk<öøã,,,P©TétdŸ@W „ ÓlÛ¦T*’ËåðûLŽI¦ä„_=nªP X,rûö6zCí¨²/àt­íØ/ñÛs©Ñçãû‰úÛŽƒcH¹±¤üõ¼Ê~ld{b#×u¼™{28 q˶<¼B^6ù7G£Qþæßü›r,— '''4 øy}éů¯Q§#mÚƒ333œ={Ö›ŸøgögÉÉÆjǧô.BžÇ‚æè5ÐgLô¡ëþSlâ¸öwÇßd  «pCCxe†@k ¦é#”ÍB±xœ9%ÎårŒ,‹¯~å«l^Ýäýï?†aÈÁ" ¶Æ¢1êµ:ñx×qév:RØnaú|D"aêõ†¼×øýv"™|ü½/êRÀ|X¥ÀC†aÐjµxöÙgùõ_ÿux0PÑ+•Ë7¾þõ¯µæfgE¡0í25•ǧˀTŠh$ÊÑñáp˜t:íñýù\Žr¹Â`0`qq‘p$Â`8äæÍ›ì0R ÜV-ÀÎäÎí¸^w «Ò½p=Ù°‡8§˜½ó;ªuØ Ðhc,l×Á²¤PȶmlËÂq]¬ÑÛ–5¶5´¼š^§‹: øÕ_ýUΜ9ãõ²÷û}J¥ÒO$êùi¾ëßmÛ6FÃ30ÙØØàÂ… „B!Z²Þ% Òjµxá…$Ò^›J裨­b”ú}÷‡˜¯ÃOEþy Þ{®b´Y˜´s…TúCY” ”²kŸnŠDȤR,//³¶¶F4¥\­ð§ò§ôú}­i4š¤RÒ`8’J%©7äîhªû{½>¶m‘H&©V«är9#‘HÉfÿ~>—{æóŸÿ¼õ°J‡ô1øÿà¸>ŸÏ±m{$„Д`S}?¾téÒöáá! óÄb1’É$ÇÅcÏú»\.3 ) 4 ÚméãÞï÷©TËLMMy™d*ÉÑÑ!·oߦݒ‘Ü} …÷ôëŠ*´ÛÕ=ïš KÕùüsOi d€Ç±Æ H Cð0õe9RNlÛ6¶FðmÕ|cIl`82Hßz_„Ãa~ý×ý#pppð3×ýoWÿ;ޤ2Íf“ÅÅEžyæ.^¼H$¡T*1 Èd2d³Yo¨êõë×¹¶uP0ˆcË]Æh¼—ª+Qи`Ÿ'ÒcN=þCˆSOƒ00´(I•]œ¦#eÓ•` H$!‹R(X]]e~~¿ßÏo¼ÁÿárñâEr¹ÜX ]«Å:m¹û·Ôg*RWÌ€ãÈŽÁd2)B¡c;NèÉ'Ÿü¿lj|N•ÞƒïŠ`rÞââ"Ï>û,üpкwîÞ½úµ¯~ÕZZZ.\àää„N§Ãôô4½^j¥ÊÔ”‹E2™ þ@€ããcñ8ñxŒÃÃC¢Ñ(s³sŒ†#®]»F±(Õs^óˆ«wý €ÆêÆ!×~›f"µÈ½r³› ”­³[f®Î lG}Yžùˆ5²Y#,kÄh8ÂŽ<µü'>ñ }ôQoì”^ï$ Щ¾^øív›••~í×~¿úWÿ*¹\Žýý}ƒ…BT*E§Ó¡Õj155E8Æu]^~ù%9ëÀgzèý)°Fê'2ï_](êP§íÞ¡»GŽ­ÇÇX€kI±~¨‡3È?J*åd¡H$J"gaaõõu²Ù,ýÁ€ï|ûÛlooóô3Ïx›Ëp8$¥MCƒ‚¡†ì Œ¬‰¤ÂÔ¤ªN§cÌÍÍÙ¡HøÉ'{ìs[ª€?_ówE˜lùøô§?­Á@­ ì )Ážëº{ßúæ7wG£«««®®õ}>ÇGGÄ“q¢qÙø Éd2”ŠE×%ŸŸ¢Z­Ñïõ™.…B†ÁÞýûܾ}‡^¯‡pµÍ—£Ñ>p¹ƒ»Ž—RŽË ìIN‡`0ȧ?ýiºÝ.ŽãÐh4h4M÷Óìöó°,‹z½N§Óa}}¿õ·þñ/þE|>×®]Ãï÷³¼¼L"‘ð&ïD£Qr¹œ§` ƒ”J%¾÷½×”W3^Œ“þ„œWLÜ7)ë1Lã¢/ ãùäó§î› "KÐ BC•jñ‹q³Ïç# ŽFÉd2,¯¬°²²B$áàèˆÿç÷~H8̇?ô!J¥±X !„gÚi·q]¡üêò~×¥Ón“J¥eOë233cŒ†#ffgÿþT>ñóŸÿüŸ»à»"ÀXô©O}ŠÇ{̵mÛRY@EÍ7on]~óMfg瘙ž%‹sR*L姨Td­?](Ðjµh6›Þô]åØ ߬Œ4 ¹ºy…Jµ‚ ?îÄKã]­ðÓ¬€=.´RУ µ®@[‹96®ó,/(Œ›ˆ$û`{ìÁxqŽäcFRI8ò¾† †2èv»üʯþ ?þ8Íf“VK¶Gë…ü“ìözáF#êõ:½^sçÎñÉO~’_ù•_Áu]nÞ¼É`0`yy™\.'3¯j•d2I¡ AØJ¥ Xþà—Ù?Ø' *~~r'öN}&ÄäÿÄäxq¹€')?aˆ‰…>X4 5aW÷NõhœAŸaÊf¡h4êY‡ÏÍÍ!„àµ×^ãå—^â£25•'‰Ðl6ñûý„CaÉ ¨cƒ~ŸT*¥tA‚ÁõF]g"‰Ø‘h4þÄãÿ+ÇqüŸûÜçÜ?ÏRàáMý¨w,óä“O8B]hJ°×íõ¶¿øÅ/–MÓï{æ}n¥R¦ÕéP˜.0è¨Vªr‘ A©T"N9:>&‰HÆ)‹Þ”[ À;w¸w÷ÖÈBZ†I’­<¤úoBæëqÿ®ðÔn-A¼‰ÁQý“´à„hȶm%²ÆT =¸iÀÑ[¤r'©:4²ŽFŒìN‡P0Ìo~ö7åo0 R©¼-ðàâŸì&¬Õj }ôQ>õ©Oñ±}ŒÑhÄ7°,‹••‰ûûû”J%VVV˜››c8R*•9…9 I 6!¼A-ß}ù»8Ž#SxÍ—^BójñN8‰±>`\ës\<ºp²ð§g Gc ©H\Õ•è=·Rªì%‘HÄY\”¥@:¦ÛïóüóÏsóæM>ñ‰¿Œù€_€m;Äã jui+&î¶w? J™ù|ÞvàYe)îþyú>üð6ÇïüÎï¸@À±mûAJ° T_ýõ­ÝÝ]6Ö7hÊ9mAމÅbÄãr‘›¦I6+{d155E£Ñ¤Óé0•ÏøBŠ:®\¹B³ÕR “=æJ O/\‰b?¨Äãü]ÇUî“Vã– ¤¶@ïôã$w,$²åP’ñ‚µ%C`Ûž.À)`4ÂY†P­VøøÇ>Î{ßû¤gúvéýƒ;þp8¤V«1xâ‰'øßø >ò‘Ðëõ¸qã®ë²ººJ$áþýûœœœP(˜õÊÀ›ÀT­Vév»$ Oë÷ûÙÝÝåêÕ«ƒ!úÝîàQsÂ@†í+UŸ  búÌ1& ÏóJWu+PÏÛùQ lO6Œ±¦!˯ À¦©dÂ!¢Ñ(ٌԬ¬¬‡ÙÞÞæÿû˜ž™fmuÍ£d¥W š+Ø•s&>¿OúÖj$ ì‘E¿? –ˆc†áóùY]]ýŸòùü£¿ùç8]è]†Ã!=öŸúÔ§œ‰2@öÑ+W*׿ý­ou‘ˆ8{ö¬‹Æ8)—q\‡\.G½^§ÛéR(d?@½F>ŸÃvNJ'¤3L¿ŸJ¹B:!‘ˆsãÆ îßßõ½Lë•0ÈuÔN¢ëü‰~µèåãÆ¦¢^Àp5ﯕ†Z04f&é5­p­A°$`i0Ðg–‡#Yô}|>ûoÿm,Ë¢\.{Ón\øúZ×j5lÛæ½ï}/Ÿþô§ùð‡?L»Ýf{{Ã0X[[# ±»»KµZevvV `ipïÞ=çÏŸÇ4MÊå2½^L&C:¦ßï+%œ_M6¹téFŸ?0Ö†ÚÙ‰lÀ•^~ÚjëàðN»ƒæÐÓ©¼a TšoOp€p݉ "d * „!0L —‚1ø|&€œ,¥eÂëëëÌÎÎâ—^—ÿóK\|ò"gÏž¥ÙlÊ>„´‡Ãøý~šÍéTJ²4¶-éÀº8â3}Ôë ±²²d»B¤9{ö_9Ž#&§ ý×<ÞàmJçž{n ô(A¡(Áo}ë[·÷îßçƒü íN‡f½AaJú¸”OÈæ²ø|>JÅ"ñDœh,F©XÄð“I§©œ”±l›…ÅBá0Õr…ÍÍM•¢ oÞ¶—‹yl'î‚î8#˜Ì WK‚ÝñÏìq¯€,Té ÅAjk}mé2@ÉŽ# iB©  GP$ÕJ…|ô/ð|€ÃÃCÚí¶‡èT0P­Vq]—gžy†Ï|æ3<ûì³Ôëu¶·· ¬®®Ê†˜óóóÒ†­Raww—`0ÈÊÊ †aP­V±,‹l6륹ÚJ;“ÉÐh4¼Éº—.] O¬Ê DoÅ~¿ŸH8Âp0äòåËüÇßÿ|ñ‹_¢ßëáó™Œ%ÅB˜” Œ‰²BgŒ?ù:8E)Žñ¿ÏG0”Ó…’q–––¼R Z«ñÿþ§ß§\.óÑ~”Zµ&Å>.t:]ÒJ*l±XŒz]Š…Ç¥«Æµ[-„€L&k†a‡"‘üÂOxÓ…t÷çÛÒ?‡ã¡Õ_çÝÖïýï¿OeHJ°sogçê÷_Ýž_˜†D¢Q"‘ÇÇÇ„CaR©¥“.Ïå©Õjôú= …n—z£N>'›†Ç! pus“ã#0Æ]€®«fܨRÀQ;¹'V ßd× œ1`èÍp´Û°Ëd£ãXêùOK‹µDØYR!hIœ`dT©0RB!i Ò >ûÙÏÒï÷½ôÜu]úý¾lƒ6 >ðð™Ï|†÷½ï}”Ëe¶·· ‡Ã¬®®"„ðÆ”/,,xsöööˆF£¬¬¬œ:gmmùùyêõº¥{`«Î0âñ¸7¬åÆìÜÛ! ÊRÀ/BÀG ¤^«ñâ~‘ßûüïñüóϳ··G6›!•Nã8®êæÚXh¼°rÁ«ÏÓ8Pe‚Úé9…ÿé!¼¬Df²Äðû¥o@,#—ϳ¶¶Æòò2¡p˜·nñ¯B¡À…G/Hÿ‰fÃkŒª×ë$Si¥ÝN¥h4êCå6ÔÔbƒA#“Í27?ÿr™ÌÙßüìg­+W®ð_xèàÁCK"ƒÁ ¿ó;¿ã¶ÂË\h!ºŽãì¾ðïÖª5.^¼è¦RI*Õ ÖÈbJ1­V‹©Â£áˆJ¥B.›Ã0 ŠÅc’É$‘¨ ñx‚üT“r™«W¯b+SO×ëbÓ@ŸH©S{Ýìxz… è¾eâ8Ž'v1;`{ÊAež¡¤ÂÞã½su‹±Ê¬±0GMõÙ¶C¥ZæƒüúЇØÝÝe4y¦ùÈGøÌg>ÃÅ‹)‹Ü¹s‡X,Æêê*ŽãpïÞ=z½‹‹‹d³YŽÙÛÛ#³¼¼ŒeYܽ{—^¯ÇÒÒ™L†££#z½ñxœ©©)F£‘§DÌf³D£Q:ÕªØ"„à{¯½Æp8Âôù”ß) JÅ/¿üøGÄ+¯¼B»Ýöp„D2A4ÃÁ‘Á4 IDATÕ–c:s`,æ1„1!0:¥x÷q<À0NÓ²²0U) ‹E½ƒÓ…Žëòíï|‡^x}ìcÄ1jõ:éL†N» @"§¦†" ZjÊîÇãÔë5"‘ˆ˜™ž¶{ý~îÂ… ÿ§cÛ|îÿccˆŸ÷ñ®Ò: øä'?©³Û0 =; ê|óòå·^}åÞûÔS²=©ËçÁ•Þù©dŠP0ÄqéXš>$“œ”N ›ÍR«Õè÷ûÌÍÏŠ„±,‹·~ðJ'e¤ÖÞ\°]O˜Æƒ;¹– 3¾í °è ŠÐUŒ‚ãèÝ]ý\•R¤z =¦Lõ XÊaȶ•¿à„¹‡¢-[5)‘í8üÝ¿ûw©×ë†Á/ýÒ/ñÜsÏñØcqttÄÎÎÉd’••F£÷îÝc8²´´D:æððÃCiž´´Äp8äîÝ»J¥888ðÎ9{ö,®ëz–d…BAZh«QfºÐ¥G©XäúõëDÂrÉññ1o^~“W_}•LÓ$ÓétxóÍ7¹té’ì54Н»^¬Æ„¶<,@b„ÆiÁ”y½º\’„Ð)…ôj4 Ù' ŒDcÑ()uÝÖÖÖ¤õø˜?þ£?¶mìq|¦I8R¢rÔx¿/Áf³O9Õë5’ɤììõÕ¹#3ÚùBáãçÏû;_ûêWí?þã?6'¿þ<í<ñž|òI677'³Ýît»Ûß}ùåê'þÒ_Êd2÷èèP$â1•HøÈbvf–V«¥|ædVÏå0 “ÑpH<çèèˆk×¶˜šÊãuêºÒu¼‘—Ú¾ZÝRµŒãe–¶«úÑÕÃ])4 ©MóîW,¸=@Uèê>øS,øßn4œ?žúOÿ)³³³rÌúþ>Žãx*½J¥B©Tòv÷ápÈÁÁ®ëzT^µZ¥X,’H$X]]¥ßï{“‰òù<@@*2ÛmÂá°gÄR*•èõz²vŽÇ½òÀ¶¥×~*™b÷þ.ù©<õzF£áMî÷ûìîîrãÆ Ži4<òÈ#œ={˲PgxBC¡û¸Ê?ÀSŠ CÙŒ¹(QºF*HÈ·F=."ßuªõ¦ÏÄïú …#Dc#¦¦òl¬oP.—év:ܸqƒç¿ô%þÚ_ÿë<ýôÓܸqÇuH&”OÊ€`“\6'[¸-‹d2I¥R!’ÞGÇG,..åJ™B¡ðOöö÷¿ù›Ÿýì½d"aD"ççÍ ¼+2€·;t$JP[†µÊ¥×_ßúÁåË\|òIfff©Õët»]¦¦¦èõzÔåøf/+H¦R„BAŠÅ¢‘LR*–ðûÌÌJÏý7ß|Óã°=ŒÂ›…œ6ûx÷M4é´]gxãó]m9æzME“íÅÞC&{ ´pÈV„òÐv ú¼ÿ™÷ÓíõØÝÝ%ŸÏ³°°@§ÓáÞ½{¬¬¬FÙÛÛ£T*155Åüü<ív› Ãð¨¯ÝÝ]J¥ù|ÞÏvç΄¬®­yå@µZ%‘H0==¶)F$“I¢±GGG4šR¤¤Šb±Ýn—7ÞxƒçŸž^xýý}¯/x²^Ó@CáeRb $ƒ€PØà„pÈPâ$C;ãöc…QøM¡@X4F,c~až3gÎP(èüÉŸü ›››|øÃ–Á,Åu\Ú]9\¤©½b’.ŒÇä®Þé´ÉdÒ´;mp!‘ˆ ¿ÏgÃáÙ÷¼ç=ÿk½VãððPü¸ÉÑ?ËñðÀxAzú¬¢]N÷´´eØÁááo|ãý¥ÅE155å+šR±D,—º€“’Ì 2ióÎçó´ÛmZíSù<±X Ã0Ù¹wÏ£ÁôÐJÇC•“b“íÀzá W;ßLЍîBσp ÔòcdÇáiÂ=ž(I ™˜p¬AB[‰ˆê:…|žÅÅEšÍ&»»»˜¦yjQW*¦§§=zowwŸÏÇÊÊ ~¿Ÿ{÷îQ.—) LOOS©T¸sû6>ÓôØ‚›7oR¯×Éd2LOO{Ŷm“ËåˆF£´Ûmš iŠ2 ²··Ç—¿üe>ÿùÏóío›R©DPMðѯÿÂ… „CAÓT´¡ib Ó”‹Ô4¥°G˜ºžWɾ)0„)ù}åd`zµþd™`hë1PìÀTN†O*C¡±hœt*Åêê*kkkÄãqŽ‹E¾úå/cÛ6Ï>û,ÑXŒF£)€¡ zd*Åp4¢ßïK;±fŸ?@(¦^Ó%ÃÇuÌ™éi;“ÍþÆÆÚÚs£Ñȶ,Ëüy³?üÇsÏ=§)A=E¨ ¼ö½ïÝ®T*Þ4ád2ÉÉÉXÐh4èv:LMMÑïtK&BJ'%‰`€v«M4Åq]¾ÿúëÞü=G¹uQZvWÓò~½K«¿×Qµ¿ÐšMhª2Á–öžHîîZ_ ËŒ±3Ñ$8AIj¥¡×…¨”…¸r~;N~e\aªë÷ûÙÙÙ¡V«1;;ËÌÌ Õj•ÝÝ]+++˜¦ÉÝ»w½A,…BR©ÄÎΡPˆÕÕU ÓäÆQ(8sæ ¶m³··Çh4bjjŠH$B¥R¡^¯ …ä.Û¦R­rõêUþàþ€÷ïþß}ùe:ñxܛΫŽòùO»Óá+_ý*;;;<þØcD£QB¡¤ Û¡Ûé’Édi·Ú†A4ó4ív‹t:E·Û5ñ¸Íç.œ?ÿ¿ ñß]øQUÍ¥ÇS„v¾ño !8³qÆ=::"™L G(•ŠøRé4•rÛ–Y§ò’&¬VkÒ44™:ßã l[•Æ€k;ÊxíŸÄ\yÿxœ˜6‘çâÕ÷à‫‡ŒK׋$òù\ÒáÆU£´¼çp¤¥™Î<Á’RYØöG-¦œZ|{{{Äb1–——FlooÓétXXX “ɰ¿¿Ïîî.‰D‚••,ËâÚµk”ËeX\\¤V«qóæM9hS±FƒL&ã5 •Ë’YÉd2r¨h­†eY¤R)Âá0Ãán·‹‚`0ˆÂ39}â‰'H¥’¸®«´úáW‹Ÿ±@ ‹To€ÚñÇB-*#âZD$]ˆ—Mxå*5´6@©M±¾¾ÎÊŠìÜßßç _øÁ`§žzŠF³eË×Z«Õ†B^SPZ %ßQ^(ÌxŸ_Öø¦o<(E˜˜†©©n«ÒÀ4¼rÀ0LÐvà¸xS‚„jŠ9u …6ŽuB oðI£Ïh$J4£P(pöìYòù>æÖ­[6660 ƒk×®qrrÂüü<óóó4 ¼rÀ‚b±H·Û%™Lzˆ«ÕÂP2YY—eY|>ŸôÕO¥xäÂyÇÆ0LLUŸ¦ú2¦aà3M ŸZȦáuõ™¦ý( P¢ÿ*`&†¾rB…ã>­œd´þÀTìD($‹zYÒêÚ*áp˜F«Å[?xKJ𣒠m¨–áH$Bµ^#Ox‚étZy "}Ôäa! :ÝŽ±¸¸h‡"á s³sE©:.YÀ»>ÀiJ0 º?¢K°üÊ«¯^Ûß¿Ïc=FU¥ZÉd’r¹Œëºäò9šÍ&N—|>Ï`0 V¯‘Íf1 ƒ““ÏjÌq|¦É[—/S©T$à:ª3ÐÛz½Ýy¼Ókqô½·[OÔú®ÞÉÕóàjºq"ƒxÀ•Øêy½ç’òdÍYËE?Qãêrc¢&N&aÙ6«««.p||L¡P`qq‘z½Îææ&½^õõu …ûûûloo‰DX__Çq¶¶¶¨T*,-.R(ØÝÝåÞ½{Äb1oŒùþþ>n—L&C<§ÙlR«ÕðûýJ7ïÒjµ°-KNÙQó tÓÒ£>ÊâüöÈ’€æ¸wÀñèŽÀ …ŸâÔî®n«Ý^x” Îô5C‰†ÎÆô¡ÎÆ Di˜Ò=(“Éf8{ö,‰D!ׯ_CƒÓMÕ2<è÷©éB:ã …BÔk ³ @0•—º”H4òÓ4‡îÏCü®?ŽÚœ¤ßÿþ÷OR‚]&(Á£ããëß}ù»ƒT*-fgfI&“t»]šÍ&ù|Çv(—ˤÓiÏ©FŽ…JP.—Èesž~>—ÏS:9áÊ•+ø C¥üúoÖ4Ÿ^ðúu¨Å.Æ@ž x=*‘O ÖÏ3Î\ÇUÏ1Þï÷ÊT©¡Õ‡Z«³Dt†ëkë„B!nß¾M©Tbvf†ù¹9Êå2››› ‡C666ÈårÜ¿Ÿ;wîH$X__g8²µµE£Ñ`ee…\.ÇÎî.ÛÛÛ$ 666p‡{÷ève Õm²U%xI§ÓضíÙ—E£Qü~?ý~Ÿ~¿iJé­eY<þøã$’ jTšÚÝMÃÀTA@f \áݧA¥Â)³ÃÀ0'n{ÀßXÿ/0¼á!²e‰ž8Lí$›…b‘K‹KÌÌÌ`š&•J…íímfggi¶¤®$Q«×ˆÇc˜†A«Ù$“N«NA‹dblh4›¤Ò)F#Ë0 ƒ©Báé@ p°‡Ãá;ÎÞà¿„@€P0è9‡Ãažyæp]™® )L ßxXªÆ SjöµSv7 ©…®ûHëÇ@!Þ˜ží˜PÔ¡pôï5$- ‡äx±|žååeüÍf“J¹,k{Õô3Yôz}Òé ÍfÃ4e€Ô,€€N»íY‹9ŽK"ž Z¯Šd2igÒéh<û0€ã8â ƒzøIÿü·± Óý=Ô(1 sóæÍ«¯_ºÄÙ³g…ãÈ6X­èõzLå§è úÔj2õB¦þñDœp$ÊÉI¿ßO*•×% r°¿Ïõë×ñûýÞ.,44ä­õÉH<û„´(Hÿë}èä¶-Ñ}äN} ¼Óõ¿Ð±â+&ƧêçÅÕ¾úb<‹o 7à÷óÈÙ³œœœpõêU„œ;wŽD"ÁíÛ·¹ÿ>Ù\ŽÕÕUšÍ&›››ô{=ÖVWI$ܺu‹{;;äóyVVVh4\ÝÜd8xúøƒƒ/øæòyºÝ.GGGضM:Ƨd±Ý^Oé–½ýÁm‡µ°°À#çÎaÙ¶ÚýMoç7OÕúrQÊûMÙ`dj¡*e )ƒ‡iL¤ô»¿NñÇbJ 4¡&Ô½†WÃøL“` H8!‘ˆ3?7GH1Ñh”“r×qH¦’^·d  ®J‚áhÄ`8 ISo4½VèZ­N2™ÀqÚ­6ÙlVw½þ€mÛ›|YÀCÀO&Á@E :oöF–u÷å—^: ¬¯¯»:ݬTª¤Óiå“i–HP)W† “ÍÒTA"ŸÏÓëõèv»d³YLÓÇkßûÃá!Œqª®À;¹@õâÖš¹áj%Û˜!p=RÙÕÌ‚\özO\ ¨»&j¡QþSV×ò÷ Ø5€!Ÿ_¦°Žëâóû½”=rëÖ-ööö( ,//S«Õ¸zõ*–e±±±A$åæÍ›Ü¿Ÿééi–——)W*lªsÖ76d{윔Jd³Y¦¦¦è´Ûà8’~õ™&•r™N»M4%ÒWƦšéÙxîÜ9fgg%#à÷cj °!¦9®Ëq0^àS f É  ‚Î !Sys¢!h¼ Äx¡{ A Èàê õù ƒ„Ã2Ù,þ@Ó4†I¥Ò¸Ž¤’Édh·;à"ÍCªU"‘~Ÿ2I§å¬Ñt*E³Ñ݈ሠ„#á >Ó̸R¦úŽj€wAp◠ߘ'Ÿ|’aÖêßãÍW_y…§Ÿ~šL&£\‚å81m“•S¢V«E.›Ã¶ªÒ£Íkp ‡ÃÊæÊÏ­[·¸sç>¿Oe®·€qçômWÝïx¥ÁÄ+!”ÊÐõŠY¹ø]E Wª u&€u&cPת¨ ¼=9g_d8sæ ;;;ìíï333ÃÒÒår™­­-„lll ¹~ý:‡‡‡ÌÎͱ¸¸H©Tò2‡ ~?×®]ãèèˆùùy¨ÕjRËoš ÉÓîtˆ'D£QºŽä½}>B¡¶m{®Æ@€ÿâ/âp ý÷ù|ªKo¼k?¸£Q|MÅ囆¼T_òhJqVá­uáz‹_bòbz¿Ã4ð«× Gp]—d2I«Õ¡V«+±™\Ⱥ0¡v÷N·K6“¥Õn!Äb’ˆF£¦I«¥¨ÁA_¸®ËtazÞ4Íe×umÞá~ø`\>ÿØc üä'?  ü¡, V¯ß|饗ÙlV¤Ói·Ýn+°´¨Êd3e•úG"ÊåLÓôÆ9[–E&“‘AÇX¶Ík¯½æÕí™§»§öãô"—Ü2x»ñÄëöþ;‰ô+–AzàŸÑÑq¼|À{¼ª"$Ø¥³ P½­xÙ‰P5¬) Û&rîÜ9‰‹lmmaš&gΜÁ0 ¶¶¶(‹,,,0??ÏÑÑ[[[ø|>I ÁÕ«W)–J,--1;;Ëþþ>·¶· …BžuÖÁÁn—t&C<£­‹ýÑh”ÑhD«Õ’(w$@0ä=ïy 9w¡¼Ï0=Þ^×á>sŒ ¦ÖŒw~¹˜ÇÒa¹ë›ž¡±„’Ë/Ÿáó¨GÐÎBb ² 8ô¤a~!£Á|>OÐÇï÷áóùh6e¯DOÍH+aP@õ Ôª²Àqd Ìd2´Úm†¦E<'žHÄ×ͪ Q¼6àá€É(û“œ~š|pŠPK}?¹téÒ­»wïJqF.‡išœèÔ?.Sd3Yš&Ýžì ‡²©%›Á0 *• ©TšL&ÃÖÖûøü~¹HÕß/wðq=.ÈÕÚ›\û»¿÷£‰×?vÆ(½×í§Ò]‹zÀÖ¶0ñ8¡µìJßî(j+ “Ëå¸sç@€3gÎpus“J¹ÌÊò2333Ü¿Ÿk[[„B!Îll`Û6W¯^¥R­²²²ÂôÌ »;;ª¿?ÌÚš4ÉÜÝÝ¥ÛéÏçÇ-Áõ:ÁPˆx<Î`0 ®¬Ëc1‰Š÷z=šÍ&=öÎ_`8(OA¿'2u½n˜^Y`¨ñ^¦ ¦^}¯wøñûäõèóÌqý/ÄxÁ#&² ý¦ †º_¦O=:>ÂÍå å´¤f£ašÄâ1jÕ*ñxÔcñ$ ÐŲF5(ˆB²(‘ð²#5? ¡ÞäÿÆ3øÉS~ˆ„Ñ%¸³³sõ«_þòpyyYœ9{–b±ˆãØäsy:ííV[¢þ®tÓM§Ò^ê IU t°±™Ÿ›# Ñl4¸téu Ã8ÕÍrkjÎñ¼Ýû”ó½W&Œ‡}jXQëŒ&uTíÉzWW˜LÔÉž^Ð5>àJû­P0Èȶ¹¶uË—/377‡cÛl^¹B­Vcuu•©B{÷îqãÆ â±ë ®lnÒh4X[]%—Ëq÷î]n^¿N<g}}®b ºÓ…±XŒr¹LµZ%•‚™Ži !HL ÕèÞÀ–ÇŸx‚L&-Û SvôƘT”à©”_àa†W"˜*øéŸ 5 Ð8&ë/¨wE›•ž )f@v iHª˜–k×®‘ˆÅH%SAÁµFt:-Í[{}2™ FS ¡âqjj¸¨a2ôÕ¨7/(D"w8…b€ ¼#çà‡dÌ~Šc‚´˜4¢ëÂþK/¿|¯ßë±²²âÖëu²¹<8©”‰éÔÿ¤ŒéóK¶^c8’Ëåèö¤~ “ɪ c‰Dxë? \.ãó™xÍ80Ät‘> T?Žsô;5(dA–.¸®J!Ô)ŽëŒ?¤ˆSÁC^J¡RÆI$CH~{dsíÚ/¾ð[×¶FÜ¿ŸF³ÉúÆÙl–Û·osëÖ-’)©qït»\¹r…N§ÃÆÆ™L†[ÛÛlooK p}v»Í•+Wèõzr f,ÆáÑ•J…xÆï÷“ÍfqÇóˆF£ƒAz½½^[)/^¼Èà3M|¦Ó”àŸOazÜSü¼Oe 2#Ж߆÷ÿ1p§~Æ`2.Ôµõ¾”Iˆ1ZZ¬Ã¯Ïgò§ò'ø|>ò…¦i‰D¨Õåœ@Çue/@Vúº¶C2™¤V×Ô Ÿz½F:•Æ éõzR/Ðjb˜ñhŒz½é3 62øï ø)s€A 9Ý%Øïõû·¿ô¥/ñÌÓϸõzvGò©z„U2™$”©0$©F8Û–M6—•"¡¶lp‰ÅcCðúë¯Ë7†µ9 IDATÅ0Ôί&Ðh…žÞÕ½ÓðêsCåì§; ÔéêµMVDrŸÑÆ—’ &—ZÅ&Ü‘äḲk.c;6—ß|“/üÁð‹/P©T<žHDvœ]ÝÜdgg‡©B•ÕU*• o½õ–eqîÜ9¢Ñ([[[ÜÛÙ‘àÊ '''¼uå ŽãpæÌÁ 7nÜ T,2==M2™¤X,rtx(]‚§¦°,‹âñ1ý¾ô¿ †B´ÛmÚí6>ŸO2Ý.ëëë,--a[ò=6MS-N¹Ûëºß§¸áÇôêwC˜Ê9h žÊ I#šš%0Íq‡¡ÊLÓ7Ñ,4¡` õYL&S|ík_ãÒ¥K<þøc8¶M:­fŒ,Ò \öûýD#éš”LââÒnµ=jÐv©$Õjp8"»Õ¨±‘eÓë÷X4†ëº#¤¥ß;¢ß%ž€§QíŸäx‡–bàºî$%XûÁåË[wïÜ)œ;wޝ|å+÷|tt„Ïç'NS¯Õ‡ÌÍÏyLA.ŸÃÕr™x"A8¦Z­ŽD(‹\¹r…<ûzý¾Ûu ¨ “2“wq”÷œt± ¡9:¡¸|aÈÇ{?ãëäx©Æøº©»MÓG  ÛíðÆ÷_çû߃b±H ð&õ¤ÓizÝ.;;;ø}>ææçñû|ìP«ÕÈçr<òÈ#²'àÊ ÓdnnNú îí©ë“çì™3Ôëun\¿N0baaÑpȽ{÷(œJ&¥~¥Â±úÒ™Œÿêu,Ë"¨tðÝn׫yñÑ{Ÿ !Àç“×U ‚F£¦aÊëéÈ‹ç:.®®c L]²ŒÂ=¥¼®ÀÅ‘€¨ºpB56PgU2sÓ×W{Çuåy®À±m.™l–W_y…ÿûßþ[Î?O,– Ýn‹Å=GeÃ4h6›žUÝp4R¶j ¯AhïþÉDœV53;#³"Ë&™LS©”]¿Ï'B¡P×qÝàG–¿?óñ. úÃý“R‚ÿþßÿ{Û0Œ¡mÛ“”`ªZ¯_ÿÎw¾óÔc??{æŒ[®VD«Ý¦Ûí27;+-³k52™4~ŸŸýã}"á‰x’““®‹d šM†CIíyíÕïñäÅ'½n?)´jw0’±óÀûñâÇÞâ×  0\•çËbÍÛ{ÊCT– ~æŠqôqz] x^»ôG‡Gø|>’É$™LÆÞ©'ú,--a{{{4ÔÕGy„j­Æ[o½E `qi !{{{´šM¦ Ξ;G¥RaëÚ5"‘‹KKô{=nݺ®K:Æôù(—Ë¢]˜š¢«|Ç!‰ ‚Ÿ~¿ïiF–ÅGUýNÍ×q0}>é¸lø 9ŽÍ@Ò¦†©º y( \Y"àJÃùžøp\éõ Tšw\Bºê*ܪ ÍuB‘¸ð•çŸçwÿíï’Ÿšbcm½ý}OöÜï÷™žž¦©¿D"ÎÁÁ!ñh ŸÏôZÔƒƒá™Ù5DÅ$¦¼58ØnµÝ\>'ööJ£ÑèÄ0Œ€ú¼ÿÌÇ»¢pÝñpПæø1”`S}/¾ð ·nß¾Í?ô!p¡|R&™J )Ÿœx"¡ZMM¸Q `«Ù$›Ë®fåóy|Ê'O¶Å¥˜Gx¿ÇËËÿ ½Çà9OAèNlî^cªr¶:!àô3+4#0¾ðúíoݺI¯Û#–4ßââ"©TŠr¹L§Óaee…™ÙYööö¸ºµE8áüùó¸ŽÃ~ðJÅ¢×þ»»³Ãµ­-"‘gϞŶ,¶®^¥Ýj±²¼L4åÆõëììîzåAµVãî;Ø–Åt¡ Ün¥f¦©MN¦ï^× ¼ÐZ"Œ'Ö导Qáš:]Ç%àL¥Ø»¿Ç¿ø?þ¿û»¿K4ã‰'ž ×ïãºð«V*Ráç(|Iöt»]2Ù Íf €DBF#øý~u ê"ûÞ˜´ƒƒƒ{Žã4…¾ñ‡êg;Þ%ÀÏv­çÏf2Ôëuúƒósó*-mHé¯ÏäèèP– ±'¥"¤R)%Y•@Ô«¯¼Â…óç=ºÈ[ã*Ç×;‹‹ÞÉÕm÷Z2ø©ñÕº3P`ù<†®Œ‰Ð žÛ±6Ö78þý~jµB³ÑäöÝ»’Ïç1 ƒ»wîÐ ˜™aaq‘R±Èå7ß$²¶¶†eYܹs‡~¿ÏÌÌ sóóRý·¿O"™deu•V«Åµk×0L“Ù¹9ü~?ûûû4›Òsn~žV«ÅþÁyí´ p¯Û% ’H$èõûÒ-×çSï{ Å{OÃÐÁÞPõ–!dúî3}¸¸Ø¶áÊÉÎÂüá†)Çq‘X½£vvÅ–àà8†*Á\W¡4.8b¬À´ŸÏG,æèèˆÿð¾È׿þ ¡0 Ê„&–÷¥3R~ÞVîJNÇ‘8¤™Ã„B!Ž%8è8tÚÏÙQ*BÙ­" ptt@2•¢Ûép\,^ÒŸ–wº†Þà¡êxî¹çÜ_|QS‚ºA¨!„Ⱥ®»÷òK/íü¿ñ7Ö}ôQ·Óî˲¨Õj¤Óiü?ž¡h¹"ÓÔl.G·Ó¡Õj3==ã8^Q0(pïîì°ººÊh8TJ¹pÇH¼ZÐcÙàøu /<€ød™*¼û©èsAU¹>-éRÁ®Ò³ñùLff昙™ecãŒZåË—I¥RD¢1JÅcnܼI"‘àÌ™3 †Cnß¾Íp8dvv–h4Êññ1÷wwI¥Ó¬®®ÊÆŸ«W øý^y°¿·'[®§¦8sæ {{{ìîì`ú|är9¸êÆŸt:M§Û¥V¯#„ðÔÕj•_þ¥_ú Þm…£˜0<÷ Dz1}ÒöÛ¶ÕüÇã¸Ãñ®•«tÓÐT®t>Àt‘F¤Â$‘ŒQ¯ÕxþK_âëßø{{÷Y^^a}}££#âµ±m›L:C¹\–²àHDaI%YòÌÎÌÒív,Ò™4ÕJ•@0@$awg—dRžÛn·™™™¡×í1²,7NW77+ÅRið))ðûÀ;y“”à¿ü—ÿÒÝÜÜ´ Ã:ŽÓcL Æ6¯^Ýzñ…×?öñ³³³ÃíÛ· žÈb¤@™~¯O³Ñ$—ËaÂkeF£B03;Ãî΀^¯Çk¯¾ÊÚÚšäÛ5à½*áqú wmÉåË¿Yk÷]ÇEÕ®£AÁ¢¡21Y”êÃ+ää@ —Ñhè!Ú‘H˜XlÕµUƒÅâ1ÛÛÛ¬ª6ÞË—/Óï÷Y˜Ÿ'•JQ,¹¿»KZñü•Šž Y]‘Æ ÷ïß§ÝnS(˜™™‘ÍA››ŒF# ÓÓrP«’Ífi·Ûœ¨Ùx<ŽeÉ”x8’Ídxâ‰'ä+ù1%¡@ŠoÇAúå;Ÿd¬‘VëI´˜§DZŽe#\Cq°¨² þöÞ3Ê®ë¼ÜçÞ—s•PU@€B  EÒ –e "-Éb7-‹MIݲÝcÍÉ–Ûiµå™5#-qYìQ´%(‘Å`J Q"‰@"€È¨œ^Î9Ü{Ïü8çÞw ƒfò¬UÀ«ª—ê½wÎ÷}ûÛßÞKÝ ËìÀp9¨ÕxjçN<úè£Hg2ðz}ÆÈÈ2™´ÖÁPÝ–@Š¥"¢¶y[­zº{P,a4`wØ1;7—Ë ‘(‹Ì[ Ñ@³ÕDWwŠÅ"D~¿óóóp:9qâÄAJi’°ѻ㸒¥o r!µ%¨½µz}lÏÞ=©Þ~[(‰ÒãÇ“¥K—òÈÈê3£Ñˆùä<3 q¹I§¡Pª}p«Õ‘0(U ÉœN'Μ>ØÂ"‘0Úm‰Õœ*(GTlCø¨ÖÒÓÞ55zk uè|WW Îö\ÔÊ 0ê0:gƒÆ ìð©E¢©ƒh@?†–-ƒB)R©FFê?59…ã'Nh.?åJ§OŸ†ÍfÃвehK&¹`$FWWÒ©Nž<ÉF—,A:ÆB,†V«Î]G¡P@.›e­Æÿ—$ V«Ífë7lÀªU« Š¾üÏþc£Ãìõ4ˆ‚ÆŠcä &,Êî‹\ œ­IE‘2¬…™©|ãSv› ”Ä#?ŒS§NÃétbÓÕLäÓf³‚"Ÿ/ +ÚÅýf“y+ 0ˆ8¼ àpÂ`4 P,ÀÏÁÁf½h$Œb‰ƒN§±XLGQ=$IBµV£ÃCCÂÅ Š/^Ü”ÒÞ-À•Ì3ëoÿ¥/}‰Þÿý—k Vä?~æôéÓ¡¡á!tˆÂ`0 Ãd62B G¨Õë(–J…˜Èc6›…Ãå€ÍnC2‘„Ål%lÁìÌ :„Oüáò¬Õ"9û—.ôÔŽŸÚ ﵬÃt÷E@Ô"ô÷H8®@Tékajä"tTp@V“›D‘p}½}€ÀÄ(n¿ýʤ±g×.Äâq„‚A(”âìÙ³L¦»§}½½H&“˜žžf4à¡!´šMLLLpG\Œ&ò¹R©L&|Ú­Š…$®öl6›5E`5ú«QýÕ¿ÿ`Ó~üÕEu d ƒA;\Y×€mr‘ˆÚc"jÆ*V«Dqê…ðàÏĉÇáñzÑÛÛƒáåËa2™J§ÐÝÝ|.£Á—ˉٹY8œNFäóyü~´[mÔku,ég¦,àµ}"‘€Õf…ÉdÆüü<f™ÖÛÛË-ÝI(‹ÁépP©Ý&Ï>óÌo(°@‘(¥M°rWyÅÃòeÖÛâeRûµ·3/qÂöíÛ•K[‚ ÄJ§Ï?±cǦÿã‹_t¬]·Ž9z„´ÛmD£ÑÎÏ£h@2‘ä¾v.¤ÒiP°v`µ\EµZEww7êõ:QÄñ㣸ñƙѣ,kí½Ž¸GÑWÔÎ3Juc¦è焟”ý™À–ÖÔ-uóS¢Âêo¥fêa$ªZzZRmY‚@XÌV¸úÜX2°×nÝ‚B |îݳn·[OŸ:ŸÏ‡áåËÑl401>Î 1nX,ds9”âqXm6„ÃaÔëuäx EO&ªºY,|øÃ~ÍŸí³@¢u–DQÕoP`$"$Y†ª¤sg€Ùl‚ÙbÆÅ ñÈÃà¹çö¢V¯ãꫯ†( šyçÌô4\.‹è…Ó–lµQ«ÕÑßßÏ6:Ç‹x"«Í‹ÅŠX,—Y›U*ôt÷ V«i)Ù\‹v»333°;œh·Ûðz½Ôáp>ðÀÑ…xü !D¦”V´À*ÂkÎÞò€õ„­°Z­¯ËýÝu×]tûöíz!6À¬ÅûöíûãOzÃðð0žyúø~˜LfÄbóܹ†©Ë’ŒH$ÂÔk9ƒ‚l޵- £pÚl(–Ê8vô~ïC¿‡¶$AT7Y¼QÚIï Øè/ ùSÒÎ%¤Ó-Ôj@i§à¤!C º€jw öQ»?Î"àÏ±Ó SM®Ê#‰m8N¬]·×\s ¦g¦ñÂÉ033X,Ž}Ï?j•!Øn óóH§Ó°Ûíˆp£ÐD"J)ìºÁŸz½®‘“*• ¢‘V­ZÅ_¶+Èu·U8V2‰ò@(hµŒLÈ#ãá‡ÆÎ;™XŠÛU«W£··çÏŸG(B³ÙD­^ÇÀÀ Š…"ïnÄb1ØøgXuKV¨¢°¨jÇ>“¢3™L°;ØFg=~¥b‘HõzÅRkV¯ÁòËéüÜ%¨(ÊìÎ'ŸœÉ øÀu Ífù9zØqåŸ2Ú‡†èÏÂ'~¬p®=ÔkuŠETªUøA\{íµ¸ûî»qï½÷âÉ;ñoÿöo¸þ†`P‰:‰„†¸6C¾P@½VƒÅl†Á`è°ÿ^u™} gjFà©§žÂŸÿùŸã¾ûî‡B)֯߀ë7¢^¯ÃápÀh4 —eîÒ²$¡\*# ¡\.CQdø}~äò9˜Íf8d3l¶Dˆ&7ßl4QoÔðP,2e`—‹Í›Øì6X¬V¤3Xm tÝöÑmô†n ?öùÚ?ÿóÙÓgÎì&„ä)¥y0L«Î?Û2óôj{à·]oj >AQd©¨Ä½Þ‘\ù©Å.B÷ÜsÚljY;=gÎ;}ê…o¾åìܹ‚ ÀLJ5Zíº»ºÑlª™›L$š×}:­E)…ÅjÅÁ°qãF h#Là—GzÂe¾ ° ^º*#”Í6«›XÏÔº ÚÒðŽƒ¾ôà›˜¢s€èÞöÔV¤Ê>T¹È.%*¿‘ãü0 Úm(’ÄyEƒf ò¡}ù|gNŸÆ“;wâÐÁƒ˜_X@<C™O!z<ˆ¢ˆz½àºë®»â÷ÿ¥–šú “v?|è~úÓû0::ŠH4‚k®ÙŒ\.‹®®.Ôu”+  ¡P(‚‚ùÆâqXlVØ­VLÄãðx= „ X("j’æ]]¬…'Añl6Ì3æççñGV4)“aS§ýÈGi0ÄÉ“'É¿ï{å'ž|r´V¯ñÈŸ?ø0Ý´iÓbrÉo¹ÞÔ@3ËàŽ¶P«×Q­00èJ׫l úêõúØî]»27ÝtS`xx˜Ž‘6'y¸&`l!‹Ù —‹S„ÛÂá°¦ Z@e³Yx¼^X­VÌÎÎâܹóذaºq(çúS}+€ô¨Ù¾ºÙÕ"a‘Š˜î÷ü•Ôxþ‡ëþS¦FzŽ p@QeÒ©©C§„ ‹  £# n~@Íhøõ©…j •¡—6òz½øÀõ×ã×_v»ññqìÞ½{÷îÅùsçpþÂ4›MPJ±aì\¹’Ýÿ Ú—.UA½ÏsçÎáG?ü!N:›ÃŽeÃCذ~½6èr¹033 ·Ë³Ù‚©éif%G …CèkU´Z-ô-Y‚|!Q9-xv»&“ ¹\>ŸðëéEµR…,1«tÕƒ‚û2Òµk×¢Ùl’ï|û[ÒŽÇwLÌ-,œ!YBH‰RšPôZ ¤ÕjíV[û{Û,àM9Ô7`ýúõøÍo~Ãgé™Z®Çã‹OÛé¯ûZ×Ë´õ^‚¹Cž½qëÖ­H¥ÒˆÅØ´šÇ‹B¡ˆf»‰î®n´ZM­'k4¹ ç d2% ½}}ˆÅãhKÜ5kVC‹¹Úí€.å¦|:M}þüÅ"ñ>ô³–_'¾_ò¯šæsÌPRÔD(nö 3|ÄûˆÚ¦VMMª°VÙA&°ëë»zCƒÁ€•+WbÅŠø³?û3¤Óiœ:u O?ý4ž~úilݺ6›íõ©ÿuÍÊ3ÓÓøùÏŽgž} ›7oF­ZeZ ¦¦¦‡!µÛ¨VjX:ˆB1E–‡‘L&µ4_%û˜Œd³9AHí6*• –,YÂÙ€ |>Æ4™ÌŒø33Ç Y’Éd±qã\wÝu´\)“Ý»vãî_8~üøyYQ’„:€¥4  vÔAh+Š¢|ãß ÿøÇa4¼¶½ó¦ê¦ôx<¸õÖ[ßÐÇz‰–`[?@ñ$R©³{vï¾úÚk¯³{½záÂy2Ð?€¶ÔF!Ÿƒ[Íb1˜L&¸Ýn1¨²¶J¥³H­6œ&ÇÇ111‰åËýV'Ñ©mx=à§’„Ôè­·÷b¯ŽQ> ðúŸ^²ÙÕƒBîûU³ ö+ÂTl4E"ö˜*[YÃ(8‹N¡Ââ£FèÜ×åÌ):Ø´Ã^Ý”Á`7ß|3n¾ùf|å+_Ѳ¿+Ýü—nüd2‰|{vï†h0  âê«®†Ñl®ÚS, ü~?fggawØa³Ú0?7Ç;?r¹‘MÆþTÇx ð#‹ÁbaváðxÜ ÃmQ)ÍðJÜÞtðõHñ^i½DKðR0Ð5>>~ú©§~µú“Ÿ¼C\±b&''Q¯7D!Iò¹<.,f âñLÜ1¨X,¡ÙRKÆl ø R©Μ9ƒ¹ÙYtuuC’$MŸ_õT£n§e§RUH±ÖéP‹yþ@¸‘ÁIç~tÉ { ÚA Exí¤Ñ][¥%|0‰•ªÇ*|JøI£b@eT\¼Ñ_éýQ×koüZ­†'vìÀ/ùK´¥6\.®¿þz,Y²'Nœ@wW7JÅ"†– ¡Ï ðûˆÅ`±Zà°;095©µ3™  k÷UÊš[’"IHg2šÑéÌÌ Ì3fçfát: ( FFFè¿s#ª•*yà¾û”'žxböüùó(ãUE‡ògùW†¯Öý pâÕj¥ÕjU{¯„7ñ–·߈u™– j)®— «I²<³gמ׳£éLn· V›U“Îòú¼(—Kh4êðûP§Ý.7Ìf¦1h2™àt9!µ%ˆZ­68ƒÁÀ7 4:j:›úÍD8´]ý»øÐ hg³Bí €,ŽêZ¢¥>¨.uWOõ1ùa Þ ;~ˆú¸Ú¢–-ŠÂ6YÑÒï×ú~ý¶KáÔ]A`\…O>‰/}ñ‹øÞ÷þ Ý==¸öÚë°bù sõ'\n’©4‚¡lvÒ©ÁK׋„¸Ž?óŒ "ŸÏƒRÖPÑzÕcÒËÅbó¹Z­ê5&-ŸËåÐl6±ví:ÜxãtóæÍäÈ¡Ã䯾üåܽ÷Þ»ÿÜùó‡AH’R¦”¦(¥ æÌò¯ytêþ²  Bˆ@±ÛíÔh4^‘°~½åD 7j]¦%¨fU°tª  túì™/œ<Ù»vÝ:ø}~8.>øSå#À¹\N'*‰;¼^”J%FæèŠ¢Ý–XÛÐçƒÑ`ÀÉ'pÓM7ÁïóA’e.2 €R(DíÇóh®{Þ¼¥Ç÷„F×Ñjy¾Õ á*Bj­žêSnE´/ªÏ Ôƒƒ@3Ñ¢¸úÕ‚A{ì¬kÉ} ‰Ê-Tñ€N½¥³/·ô-=Y–ñÜsÏáç>ˆb±“ÙŒßýàïbÓ¦kpúô †‚ši4A³ÙD«ÕÄÒ¥ƒL’LVDÙ!àñy±0¿§Ó¥¹G,¥¾X IDATƒAP ”J%>Û_E«ÕF¹\€×ãa’gf ÜÙø“Ÿü$žݻÉ/{¬²k÷î‹…bq†°~ƒ§ûjÔÏñ/5â—ÔA¨BÚŠ¢È„j³Ù¨  ¿^¯ñ»ö¸LKPŸh-Ájµ:¶cǎ̺õë[·n¥'N'¹\N—Ss·%DÐæ½kµ*"a¦  ^O=Q@4E«ÙÂ\*…Çã£Û>Š–$ñ2›vPwÖ<ƒ¦ùTü¹ ÍÙ)à;Q\«¿©:Òª¢ŠT»aç0`  P)ÇZÝ¡hå‚w`O[- (dÈ`*Ä(OÉ_Ï¥ßøŠ¢àÈ‘#xì?þÇFG±fÍlºæ¤Ói\}õÕ(‹¨Õê^Ž…ùˆ‚ˆ`0„ééix½^8œNLNN" A™9lww7D"¢\.c`pùBŠ¢ #•Jk]€™6¯ÏT~òð©T+–/ÇÆ«®¢•J…Ü󵯷yäá‰X"1 LiƒÒ².Ý×oü¸± !¤ÁU®%Y–å;ï¼Ùl–>û쳯 ^¢_ïÊ@]ú–à%^‚*X;räÈÙ©ÉI¬_¿µZµq¼>T«.ãì@Ëf™š®ÝÎNBàó1‰çj­¿×Q!+2¬V+Ž=‚|!£ÁfÿÍž¥ZNÍžgç ëÿÓþUÌÚxqRÔúýê=ª¬C@ãtæät7ãLù7„°”—¤ñT¥ Pv¼È2Ù$ E–™æÞQS/8VuÿÏ;‡ÿóÿßúÖ·P(qË-·àÓŸþ4÷q Ãét`nn]]] Äã1ôöõ2mýr}}½(‹h·ÛèêŠ"LÁh4 ·¯ÉT6«6«™t†³µ1Þ6—‘ Z­@’$8T*elÙ²™~ü§¢(’øý³?ýÓ™ÿõÍÿow,‘8AÉóž~œ²ôþÒt?øåA¨‚P—e¹µ|ùrùßÿýßéý÷ßOÍÜ6ýõή޵𢖠ݾ}»*Zƒ:%¸“©ÔùçžnÓú ¬¡p˜ Bšˆ¢ˆL6 »Í'w·Qø@­VC¥RYDrØí°9ìȤÓ0™L…È-,`tt·Þz+êµ:CÓi'!×Zmº¥Îh¬~¯P3þ8iHíÅ©·ÐÀ@^ßëÛ êOµ^?:€  fÔ[3ƒÎÕyªÜDÕžëõˆþ—¶ô¦§§ñè#`ïÞçÐ×ׇÛo¿¹|k׬d³9lX¿Ùl­v}}}˜…ÑdBo_3:q»àr¹15u’É£‰"Ò™4—BjKÈq«³jµÊÚ½¡0J¥2((|>âqÆês88sö Ü1) Q¯×KvíÚ…ï}ç;Éã'N\lµÛIlüsV@'Ú«ÿ«A¨.BCI’$Éf³)wÜq¾ùÍoR•óFl~à]~è×]w݅˵ !5Jil÷³».nûȶu¸îZ<ûì.f &+ðGý¨7(–J éçâ•V+ó·ËraK¿ßz­Žb©ŒH8Œz½ÁÔƒ–-[8Y£ƒ²C``%`qw@Þ¬,пùªAÛÀ:tQ-T¬@ûRÀÎ=h×ײvŸ‹2 ÝýkóB÷äczúD!ˆWþqº\/ÿᇑ#G`0™°~ãzÜyç!ŸÏC–eD»¢8>zÁ n§ÏœAW4 AJ§¹O¡ŒB>áåèT*h¶ZD&“ÍfG(Âøø8¬V+Ün7.\¼× ‡Óñ‰qö¾ J¥2¢Q¦Úëóxñá|„:œ²ÿù}dçÎ¥_=õÔÅr¹<ËSø¦¢(%°š^Ÿî «ó !užîË’$)Û¶m£÷ÜsâŒív&“é ÃUÞõÀË´5Ý@®±ññ3{÷î]ýéÿr—xäÈQf0ûÏbÛ­12P«ÙD©T„ÏÇZFÉ»žÃá@­Vcj¸±N:…­[·2z° j™»Þ?¨ó«áZeàk[ ‚VÓCE ;·â­M0”v>Bçžy­¯òÔl\“?Ž…T¯¯â>À¦ê(X].·%.GöZÚ½ú–^.—Ã;và¿ø%ˆ@ð»¿û»P¨‚Õ«VÃa·ãÈáÃX³z ZͲÙ,6lØ€bµøV¬B2•„ÅlFOO.\¸‡Ó€?€'NÀëaÔíR©„h$Ê8ùú—ô£Ùl¢Z©`íZžMp}Äx<J)º»»áõzéÀÀr¹ùçú;vì˜H¥Ó“*„¥Tmëék}à+¡Sç7Áëü‘‘úå/™ÞqǰÙl$éÍi™¿áð¯—h ª^‚*X“dyzÿ¾}±f£‰¾¾>j4¹^`‘éÕq)'æÒÂŒD3Ù,L&³æ{×j·ðûÑh6QãvØV«û÷ïG«Õ„Zs¿ìa®õéy Ïþ ¨^‚Ú.Vx«O׫¿TkyÒ9¨>“à<µ¾ô)çà: $U:Ʀ”£*¼(Ë ¨"CVd­Èîj4„—[*1H4 <ôÐCøË¿ø <¾c|>û¹Ïaùð0L&‚Á .\¸‹Å‚hW”ûú™Qéô$º»»ár»J¥ÐßߪÈÈd2èïïG¹\F©XDÿÔêu(TA(âê»VAÄc1„B!˜ÍŒÚÕÕJ)J¥n¹åºeËj6™ÉÏx@ùÜg?;õƒíÛ÷¦ÒéS„¯óèÔù3ük¬ÎOÈ ‚P¡&ËrËl6Kwß}7=rä½ûî»a6›¡pâ7²›¢®w}¼ä”`ƒƒ*à¡ÇOž<¹oß¾Þõë×cll zÅB·&.a0àñxX OIRù|.— &³ñxF£‘»À4091sç΃}hZ à„ªâ¡:¨Ÿt"¿Öª×Áô/Žôj_ŸÚ««Ó%ൻŠúkÊC”‘~TÐP¥ª‚‚ 6 BÍ*˜œ®&CÆ ö"ªS¯ª9pÉÒ#ûívÏ<ó üÙƒˆ'âØ¼e3,f úzûDpàÀôôôfgç0²jår©T ›6mb^ƒõ®ÚpÉA@_o/.Ž]„Óá@0Äè±Q„Â!ü:|á`¢ÀJºžž4šuFðêéÑ¢´+ §ÓI?þ‰OÀïó‘ÇùKúãÿ8~âäÉ‹’$¥Ôa3]Ÿ¿ä«Ä?cUAZ‚ ´$I’m6›|ÇwàË_þ2,ËLÚ\Þ”¯®÷Äð2-Aý|€¯R©Œí|òÉìµ×]çY¹’>ñä“Ä`4Àãõ¢\*¡^¯!B–eæíæêp.|Q©TP¯×Fa6™4T}ÿ¾}X½fõe6¦ú¿;›‘@݀ܔ‚¨q™èL);->µöç?ÔÀ9,º^‡tDTÞ´ËÂÎáðV ÖJDG² ¡ €(2@HøýXÔÒÓõògff`2[ð±} Ñh/^D0ÄÔÔdYF__ÆÆÆP„‚Aœ={‡^¯@__œN'Ž=‚þþ~‚ˆT*Í€=þ¾lذ™lÍf=##ˆ'âÁ@sós°X˜9G*•ÄêÕ«±uëVZ¯ÕÉ©S§ðÿüüç…§žzê"çíëë|ýÆ¿\ßàâ4’$IÊÐнçž{è¶mÛÞ²¯®÷Ät"âKL ªhlöÄñãgg§§¯ÀÆZ)•‘Ëçátº`³1n€ t¸Õjápá–ÎNØlvd²Í&‚Aî¼;¡¡!6$¤ï³³'¨õñ5`ІÉ1c }ftµ/¯„I]ëŽ6Øét¸ÎrLÅø)aÖ\„ýÐ4x»OM÷ ]gBôس#„,žË§£££øÉO~‚ÇcåÈV¯Yƒ@ €eË–i*Ãv»ÇƉDÐh4033•+V"ŸÏca~[¶nA6›EµRÅÆ‘H$ PŠ¥K—âÂÅ 0™MˆF£8{ö,œN'Î;‡H$ ³ÅŒD<Þž^˜,&$’ ôv÷Àn³ã¸ž®]»gΜ&ßûî÷jOýú×cÙlv†°:¿M)-é¸ûú¹~~û¥ê|µ½ùV­÷Ìp™)A½—`Ym Æ“És¿ùÍo®þ¯ÿí¿Y×®]K3™4Éç ŒP«¢R©h¹\Ž·†œÈd2 ŠŸß‡z½†R±ˆh8ŒR¹‚L:ýû÷axhXÛdÚâ\°ò@©Âêq][°s[5bCóÅ#:öPGcgD'6¢†˜Àˆ¨úkžåTaû_A÷|ÙÏ©¢@Ø­™Þ¡¢• Ìž¯3—áÂÜ÷ÓŸbôøqH’„›n¾˜Ÿ@0D"™@­Vê‘UˆÅc¨Õj‡Â˜™™@àö¸16v.· ^ŸÇŽE(‚ÕjÅäÔ$!Š"bó X>4 µ÷WréóV«Õ«û‘H$`4°¤ Μ9 §Ã‰-[·R›Í†d2I¾ý­oµøÃNMNMBÊ:Þþ¥é~:€ŒÅ×$„H²,Ëú¶ž:ö¬Öùoõz׃€—[wÝuƒÐTf` Œ©U{úéß\Œ-,`Ë–-¨×(•Êðú¼DÙLV›N'sŠ‘d™K?±ñ`× oZÌfx¼^ÑdÂéS§1;7 “ÑÔ!à¨G…y‘qÔSA£ív~µ˜,R V <*z¯ ¨i?ˆÎ¸²j5®Þ˜çºLE»w-Ö~¦¨ßð¬@—ÉPEa8€ `vfÿôÕ¯â _ø&&'188ˆ7`ùò嘛ŸƒÉl„×ëC"Î,³Š©É)DÂa´¥6¦g¦˜SN£‰tš{éT ù|ƒƒƒˆ-, Ýnc`p3³³°ÚlÄì,ß CˆÅ `6™‘N¥哞ápˆÞyçÔår‘ûï»þÉç??û_ùÊÞÉ©©ã„<€"¥4©ãí«ß,€8w_„¢Á`¨)ŠÒ2›Íí»ï¾[9räݾ}»Fæy«£¾~½õGЛ¸^Q8”O NNMŸ9zôèšÿtç‚Ñh„Ñ Âåv!—eáHÍf …b^&£ :ÝÕI¸««‹óÏ[Ìw —ÃýûñŸï¼S3Ñâ¨ÉuÑ[ßÐè»Úõôõ¢ÝW§ç–š DÝäD„z~©Øî7‹³•Kô Ôôž‰hOGCõA@2‘ÀC=„gŸ}Ù|«FF0¼|9æççv¡V­¢R*cåȲ٠r¹Ö¯_D2‰f« ŸÏ‡©©)Dººº019 ›Í—Ë…“'OÂïÀj±`tt”Ù˜‹"b F«ÕF:Æš5kP¯×Q.W±aÃzäò9H²ŒáeCÔÏ4ÿÈè±c¸÷ßHîÚ³ç¢$IIBHSÇÛWûù—«ó뼟ß~;Öù/·ÞÇЛ´^aJPk ʲ<½k×®…b¡€uëÖQ·Ç©%¡X,ÀãuÃd4!›ÍÂh4Âíq£T*£Ùh"à0€0ŸƒËÍÂl6 ƒÑˆp$»ÝÑÑQ$“I¦®tÒxµ- ›ôS¯À¾½ Ç–—zŸ¾ °8‚S­f×ýn&ñ¥¶øÔk+Tá% {|ªf*  =%ö8²$3‹Ù\ßûî÷ðÙÏ|Oýú×p¹ÝX½j7nDµRÙdF @,‡ÙbÁhÔøúƒÓÓÓðûü „ “É ··õz‰D=½ ©ÏçóèííÅìì,ªÕ*1;; AÑ×ׇ™™ix<tuwarr Á`€elÍn¹õVºrÕ*rüø(ùëÿñ? ŸúÔ§Žüæ™göɲ:zrÿ¾}ظq#’é$Óp{P戲ßïgCAy6d±ZËå ‘ÏTQ­Uáõxár: >tˆ;Ö,2 ×ôø6e5»FÈéP|Y¾è[¾ù±¨"`7S·§Žv$tU=sØ·᥋êoÏS|¥óKHí6@§Ë‰jµŠÿøGøßÿü øá·Ãë÷ãºë®ƒÏëÅ’%ýP™l@ù\™l‘H¥bÕj~¿±x ’$¡§§ îÉ 055›ÕŸ×‡ñ‰q8¹ÏØøèELNN¢¿ dYF"™ÀÐÐJÅJ¥"FFFàt:éUWm¤FƒüÏ¿ÿûÚŸ|þON>üè£Ï•+•qA*”Ò¼¢(qt8ûÓ`›,ÝWyû%uã›ÍfyûöíÊåúùo—tÿrëíûÌÞ uiK,@ßl”«Õ±={öd‰F£´T,Á€*ùlN§6› ¹|l(¨^¯¡\.ÃçóB°[m°Ûmšð¤ÛãÁ¡Ã‡‘Ë1AÉ1÷ wíA' PÁÎfW#2¿?9ˆ:,D;D‹†ü{_Öß¿ý+¥Ë6è¢l#† àƒ@TËå†$Éxø¡‡ð¿ý÷ÿŽûï{ «V¯ÆÖ­[ KJ5¿ƒá0Ú­67„£Z©`vvÇŽÃ×jˆAÕNµúºV]'WkùŽN€ÚׇìÓaª`§6Þ×É.¨‘hþ‚Ú¡DthÑ¡”@†\N'šÍ&ž|òIüâ—áÌ鳈D"ؼe³¦ŽÛ–Ú˜_X@WW—ƦìïïGµZA¡TÀ²¥ËÏçÑl¶0<¼¹|R[F ÄüÂ<B¡fgg˜«ŽÝ† .ÀëõÁívctt>¯!˜žžDÿšÆ'&°f͘LFºtÙ2ø}>òðÃ+ÿôÕ¯ÎŽŽŽQ { ‘G_ç«#ºª"O•#û—åí+Šò¶­ó_n½ý¨7`]Ú ó~­¾%X‹Åãçž|â‰z0$k×­¥ÅR áEƒÙl+›(‹hµÛðûüh·ÛÌ ÓÍUƒrL](Á`d)áÁP-—! ¢–Œw:rTr&àbøŸÿ,Ú뚊֜ƒ–´kŽþ{i7A=8:væœÃ«)8ýv‡ &£ÏíÝ‹/}ñ‹øÆ½ÿŠL&‹Á¥ƒØ²u a¾^¯™tF£ ~¿ÉTF£6› ±v\.Ò©4|^ FæçæøA¤’,¨×kˆÅbˆv1»±r¹ŒÞÞ^$q‹E ô ‘H Ùl! b|b ¡Pˆ®^³šJ’DŽ>Lþø>•ºóÎ;÷= B<ÝÏpdÿrQ?VççEQ¬PJë²,·—/_.oß¾]yðÁéÐÐ$‰ùZ¾]ëü—[ïÉ @¿.ã%¨µ)¥±=»w}òŽ;Ö®^µö€ÙbÛía Á­6BÝÁEÞb1#žHÀ 83°ÌúØ&"Ò–Úp¸\ˆÅã=~×_=jõ: qQZ®öò)¥‹ä¿´Ô\ æTÙUŒCß·ç÷I%H¼\ZªM$ªÀ¡zh@Š,`·Ù@D‚£‡à±ÇÃá#Gàr:±~ý^þpùôT`” §D£Ì÷®/`É’~Ô Ôë5 £P,¢V«aIÿdÒȲ„H$ÂH=Š׃ùùyØív„‚!\¸p6› 6› gNŸF8†ÑdÄùcŒàc±X‡éàÀ ¹pî<¾úÕ¯{ì±±r¥2§cð]n`Gì¿"‘çíÔÏ­ëû̯p½ê–àôôé#‡¯ÚvÛGE¿ß@€$É÷ßl¶hÌ@¯×ƒj¥ŠZ¥Šp$€Í©ÛíLS Nà  ÑnµðüóÏcÓ¦M PT@•ž«¦úõ`íygt`]¯CþÕÛ=:(PÝø1OùµÒ€tÀDœ˜À:2sô5 8wö,}ôQœ|ሢ€®®n¬_·ív¥R ^¯éL €ÏëE2™‚(Šðx¼XXX€Õb…ÛíÂÔÔì6Ì æÆÆàñx`6›09É9eYÆÂÂúúú¹\Ë–-E¡P@6“Åú ëQ.—Q®T°lh“““0ˆFlÚ´‰Z­VÄbäÿþéÿUûþ÷0±MBª‚ ´©¢T¶ñ‹èLé-šÔ›ÏY"Ï;1Ý¿ÜzÏ—¶?ó™Ï\V2L–å™_íÜ»þ†z¯½ö:zôèQ’Ëçø†÷¡V«¡Z­jòÙ\V» »9>³îóù™@±ˆ@0ƒÁˆt:…©©)œ=sW]}š&¯ËU~­Ú›×ký«œÕâšÿ5@Whì┿Ãêl~”Td «Å³ÕЉñq<±cvíÞ BFFFÐl4àñz™ îì ¼>/@˜ob(‚"+( èîîaêÉÅ"úûûQ©TP.—1<<Œr©„V«‰Á¤Ó4[MƒA¤Ò) „CaÌÏÏC¸ÝLMOÁíqÁívãä‰`³Ù`w8`4é–­[A…ü¯{ïmÿèG?šž˜œ#„”øûZUE_çë©»êÀN]„¦ m½0Ç[=°óF®÷ì¼â” æ%xüĉ“ÇŽí½éæ›qüøq‹D#Qé·Z­p:œŒ\¿/ ˜Õó0Mp:]¨V«!0ŒØ»w/Ö­[ÕùGM¹É%›SÛ¬‹ÄÙbŸjè ~§£´x¿«K-÷ ¯ ÔÁ“Ù ‡Å‚…ù<þøã8p`?ê\.Ö®[PŠD"ŸÏ‡l6 "“Í@Dø|~$l*Òåvan~޽NN'¦¦¦àv¹aµZ166§Óƒh@2‘D$Á "L# £Þ¨#Naxh˜•Z¹<–¯Xx<ŽJµŒ[n¹….Y²©tš<µs§òÝï|g~ÿÁƒÁXy-J髸!MžÊï$"Ï•®÷ôðj§«µÚØsÏ=—ýЇ?ì„Ãt~~ž8].ä²YÍ)¨Õfè¶*Ç! ð2Hæ7 ƒRE;¬ .^ù ç±fõj4› ¨Œ¿Ž@¨º_©~ÄÿEŒ<¢Ûèør©™8» MЗ2•a4šÀ¸ð <üë§±óW;Q­Õ°|ùrȲ ¯× §Ã±±1xÜÞý`ÚùŠB‘Ëæ˜Ñ?{zzÐl4P.•100 Ù/ZÆ<š ²Ô^Vd|~Äã (TéØrY¬ðz½˜˜œ„Áh‚ÓéD½Þ Û¶mCoo/yü¿À~ðƒÔ¾.´Z­$!¤Eiè&õ.­óÕNÏ¥œÊÛq`ç\ïéxõ-ÁÇyáĉÖoØ€…X ÍFÅbnºáU¹p*Õ*þÂaP0Àf·Ánw “Í¢-IèîîÆÂÂdYÂÞ½{02²’àëFnuÏOÏóï¤óúhÄaQ„âsûT;%ø`аîUˆ¢‡ …|OìØgžy™lV‹×^w" •J2Ï»l”Røý,úƒ«&§Òz?Ál6ÁåravvŽƒvVLM±^¿ÝfÃØØ¼/ QË&ÉdÐÕÕEQL&100€R©Œd2‰Í›7chhˆ*ŠBb ø—þçâ£ÿñc•JE•âj)ŠRþm>›ÍF¿ùÍo¾«¾W³ÞÇÚo±^mKp!?ÿ«¿ªG£Q²lÙ2šL&µ; fêÁ„Èe³ÜJÜÁ"›,Áïó¡Ùl¢X(Âífé/(`±XpîìYLMNÁb±@àPëpu]šyvÀP0p. @’™aJ(B»ÕB±PD(æüû2Bá*•*êõ:¡ò… Vïçr9È|Æ"“eι>¿eQ¢‡ÃŽj­Š›nº‰Þpà tnnŽ|ýkÿRÿ£;ï<õãŸüdOµZ#„T@iAQ”—¢îÆðbEžæåyÞIDž+]ïîãí·\¯ÔjÄg>ŸG$A«Í˜Ž=Ý=h5Z(—Êè[Ò‡z½ŽJ¥‚ÁÁA”Ëe45ôô ¡X*¡Ùlb``…\’"3K­l €Ýá@"‘ÀêÕ«é?øAäóyòø/Iy䑹c££c”ÒŒÊ໤Î×›l¨_¯Èó’Ÿ:°ó^[ïø-Z‚Š2óäOÌÞpã[·n¥O?ý4)Šp¹Ý°X,Hh83©VªE ‚€l6 ›Í ‡Ã¡Ñ„=&ˆ)Ë ÃüË¿„h¡È"Ù%\¼ƒ k j <ìXÜÔ¬£ÔTŸ}o±Z §NįvîÄ©S§¡P`íºu0™ŒÈç ðûüÈd2¦s nJŸ—ùÝ«8›hd©ª…ºÓåÄüܬü™ÝngB““p:ç~~^¯—†Rðû| ”bnnápÝ]ÝtýúõˆD"äÀøÎ·¾Ý·ß¹V»P>µÖ+|m°:ÿ ð “YS©×‹uøöÿËߤõj§O9súô©S*NAáóú4@_ÍeÙ\€ËéD±P@»Ý†ÏïG»-!_(ÀåvÁl2£\.Ãjµ" azj ÷ßÌ&3 ¢È\vTB/Ÿç  ñ¼_+Ô±];P6âk±XaµÙ091‰{ÿõ¸çë÷`zf^ŸëÖ­E4ÑJB˜$·ßç(Ïç°1gõr›Gü`0ÈpR‰  ÖQ*— YÆÃÍ2Yôo"Ž X,2ºn0ˆl6 Y–áñxOÄíŠâöÛo§+GV’™éiò·ó7Åÿú¹Ïݵg÷Þ¶$Íqênö%FtÀ•wUê.ŸÔk¾S'õô«R©hŒÓÿ<©ä+Yïg|½Ú–`­VÛõ쳙߹é¦À²eËèùóç‰À#¼ÕfƒÓÉøí¶„P8ÌÞ´¼J¶0ÅZBàóøP«w8íÅ…Ý»g/‚OýñÃf³¡^o Pf0Jˆ SÜÑž=€* d „Âh4rQR`|r»ŸÝ…C‡¡V¯cÕªU0™Ì¨ÕªH§™ë‘ñ àõu"¾ÇÓ‰ønñXœE|§“³û˜IÊì,‹øv‡ÓÓÓp8Zôw¹\0™ŒŒ0äaÞzÉDNõ\ºt)]µj‰ùÆ¿þkýñ;ÆÓéô4!¤B˜§^EY,Å¥Gö_VŠëLäQŸçæÍ›‡;|>ߢ뼖õþ [¯²%˜ÛàÀ™cÇŽÞxÍ5× ›É ›ë€Úþ\n¬ É$ x½^f)V®  A4ˆÈ%r0›™¯€,Ë%V ï{þyÄb øÃ?ü$†‡‡a0 ÈÚm¦ÃmN€E{Á FˆÜ©‡RŠL&éé<ÿÜóôü…óDE˜­,_±¡`SÓS\ðTÑ"»¢°Ë¡p²$£/ h¿»« ­FKsÊm4(—ËXÒ×ÇíÒª@¹ÌæQ.•Ðh4Ù!W,B’$‚!$“ ˜-fÜrË­´·· dû÷¿ßþكΎ(¨ ¾—ÜVÉ=ïj€OÍP¾úÕ¯¾âu^ËzÿЭWð,s,ÀH&ÏízöÙ«ÿú¯ÿÆéê¢ç/\ Á`‹I.^¡nør¹Œ`0ƒ("‘ËÁl¶Àår²T¸ÑDWwÚRår=½=h¶šeS“SøÚ¿| #«F°yófô ÀïõÁî°³R€€—µFéLù\/ŽáÂùsXXX@.Ÿ‡@^“Ù©ÕF(B:ÍZmgQ”O¥R "Ün7‹ø&#\.b±Ìf3œ.æççaáîGssŒÝg³Û1;3 ¯÷§§§át:aµX1‹ÁíqÃj±bnv6›"ìïï§›6m‚Ñh$?{à塇š;{îܸ¢(*À§÷ÔÓG}ýÆÏ|*V¤_¯Çöþð를Á0ÄÁ/ĉƒØ÷üóp»Ý¨V«(sÓP›‹š-f¸\nU#‘hdiNl62™4dYBOO?jµ²™,pê…pâøq8].ƒ!8v¸\.ˆ¢ˆF½F³Z­†\6‹r¹ŒV«‹Å× ŸÏ‡¾¾>ƒAÌÌÌÀï÷CjK(ŠG‚e…b‘‰nò(ßÕE«ÕF©XBWwcñ•ËèíéÑPý¾>†ðW+U,èC­VE­^ÃÀ3ÖlÔèîêB©TD³ÕDooÉͶn½–®\µЬÑcÇðÃþ0µoß¾ m¦Á×âTlUƒïRdÿEZûx|¯f½QÇûÀ%ëe¼µ,€sl|üÌ3O?½öö?ø˜aõêÕ( Èçó°X,p¹\Z«‹1Ú˜N ÃÉtîÓ™´Æh6›(Ù(ЍT*ˆF£°X¬ˆÇbppÖ\­ZÅüÜ,Kg9‡Ú|ûÜnØì68].MIGED£Qf\£|"‘€Éd€Ûåf—,ÊÇãqå.Äb 0óº~~~V«v‡ósó°Ûì°Ûíþ¾ÕŽ™™­ÞŸšœ„Ãå„ÙbÁ|lv»…Ýnǧ>õÇ´¿¿ŸìÚµ ýüç…½Ï=7V©TæÀÄ7›”Ò2}±“î¥ ¾ºð>ƒïu[ïŽãñu\/!ªz V !5…ÒÙO>9]­VpÕUWÑb±ˆV«©²æóy¸œ.ØíväóyP…Âïó£Ñ¬3 ±Û “ÉÈDCEf7V­VQ­VáóûøápØÑÓÕ‡Ã_À®în¸ÝnD»ºÐÓӯχh4Š@ ›ÍŽ¥K—"ü~?õ:J¥‚šÍ&Êå2½WË“f³©Ù7uT*U‚~ÔêlÒ1²ˆ_«" h0D¥RÑ.—Êe4šM„C!dsYTJ -ÂæÍ›éG·}”Ê’Lþñþ¡ö_úÒÉ'wî|®Z©ŒB*„Ò<¥ô夸R`Ã=ï3ø^çõþ+u™õ-Aµ («_çΟ?súÔ) BX,VØl6¶á)…×çE³ÁÚd&£¹\£ºá+š‰(!ÌdÄjµÂaw •LÁj³aIÿìv'¢Ñ.,^‹Ù¿ßþþ%Ì 3Åò+`³Ù‡‰DÏå`4àt8‘Éæ`æ5{&“aõ»ÝÎ/[YF’N³ZÞfC*a#¶6;2ifŸm³ÛN§µž~:†Ãá€ÅbA:Öúû™tN§Š¢ ÕlaÛ¶ÐøÃh4›ä»ßù¶ôÙÏ~æâöýhw¾P8O)€™iÆéâ?&ƹ€kí7/ÕÚ_±b…¶ñ߉Šõ«_%¯»îºðºuëèùóçI£ÙD©TÒ”qâñ8Œ<—µÞx!ÈeÙ†w¹\(òhµZèîîF½^G­^Cww7ªÕ *VƒA€`ÐÛÕËÄ=IÝ=ÝPÕj•_Ÿe===¨×k¨Õª¼~¯¡Vc™µz_îA­Æ~ÞÇ‘üZ½†%}KP­VQ¯Õ±d ¿\¯¡¿•jFCCûõzzzP*Q.—±jÕ–-¢Kú—³ÙL~ú“ŸH?ýÉOæ/Žs > @í’]5åO|oåz?x‰¥o šL&E–e‰£Óú–`áÀÁƒ§Ž9‚Í›·Àãñ JÁd4±_©¢^«Áç÷„ —ËÂbµÀåt¢P*¡ÙnÁçóA’$òy†œ[­Èf3¬¦¶X‘Íå`w0åœd*‹Å³ÙŒd2 ‹•]N¥R°ÛY¤Îf3°Ûì°Ú¬Èd3°Ûí°ØlHgØe›ÕŠL:Í"»Õ†L&£Õï™L» ¿ìtÀbµ •JÁátò(ŸÓé‚ÙbF&“Ûí¥éL›7o¦·Ýv;í[ÒGöìÙCÿôóŸ}å+_Ùwalì(IB*”ÒÔKhð©DUk_%ò´_NkÿýtÿÊÖû¯ÞK¬W˜T³€z6—;¿{÷î’Ïç%¡Pˆ–K%m°%—ç)½ÃÁØ€-.*µÙ†w8u%«ÙK¥Úí6üþJåZÍ~?Ê• Ú­‚Vw·ZMT*4›ËF Õr•]Pá‘Z»~£‰`P½~ççfÁ@•JÍfÁ@år™1÷A”Ë%4M„‚A”KeT«U¸=.øý~|⟠¿ÿû¿OŽ9Bþú¯þ*ÿwû·÷ð2땦UGáé™™³OìØ±þ3Ÿý¬eýúõ˜œœB¡P„ÓÅè¿©T àñyù†/iÃ0±Xÿ{×#GufÏ­®éçô£ªz^ÝÓã1ƒ!vã³aœ`{$,Æ€±FBXÚEË]dm$~@þ@6‚hl$«‰á˜uœX–ñbkcœà€'2F Fü˜w÷L?ªú1]]{÷Ç­ênc°Å0®#]uO«5šGßï~÷ûÎwŽ(ŠH$â(•Jü}’µT¥ ’"¡T*:®Ãªª‚ÚŠ"AUUX¶ Yæ¯[–E‘¡j*lÓ‚Ò¯ \Ö`Z&ÒršV†a™H'3(«\Æ<Ýß­¬Á0L~—wÞ¯( TMƒaÈdúQ©”¡7èîîÁí·ßΆ‡‡qáüyòÂóÏë<—››;O¸‹®  Ê–Öàs©»ËšÁ÷M>KL Òöb €2!¤Æ›>räÈ'Ûï½÷¦µkײ“'OBE‚^çDEQàïðcf~†ox‡8´°PCOO¯3Z¬9”\Æ'ó’Js GVZÏ%Y€R‘?>ÀÃÉC> E$$G®«Plzí E$ ǽ¸ˆD<ÑÇçþRÜÉ. âüý…|’,Áç195…žîÜzë­,•J¡¡7ÈoG~k½òÊËgÏû-êîâßR³ù.gß+ð]exàsp áPƒRZG›pè'g?=qbͶ{(LÄ(v ›u†hâñ–G@vÛ~ÑhgÓ0#‹annŽ· ãqÌÏÏsªn<Îi»‚€DBB±P!€œP(ùsIr²I–¡–T§)£¤–@…"+Mý=EQ ilÛv^wTŒår6µíìD¹\ƺï¬c[·n…idÿþýìûöM¾÷þûSJ]wýs6þE >J©E)]ö ¾¯;¼ð9X¢%H]1 ´iX–}áÐë¯OoÞ²%ó½ø;uê©T*¨×ëMbN±XB(È•qUMC£Ñ@:†®ë¨Ukèëëktúúú`|𦧧–Åu÷»»»a[TMCOW,‡ÎÛåó¨%$•$ÀŠ¥"EãÙ‚äˆzŠùJ’AàmI—‘8;;‹D"¡¡ëÙàà ºº»ÉÉÑQ<ÿ sï¼óÎóbûì .­º»uwYšl|áýµ/—˜ÔÛ¦Ë:ÿÆ…3?ºývœ;w}t‘p‘TUmöùmÛ†Z⣡pÓSÓMòÍÔô4§ÞF"˜E0@4E6;‹@€‹nä²Ü©8êf >â±æóÜx¤™-8ôßB¡à (%ø¸¯H’ܬ9ȲŒRI#@,C.—E:ÆÝwßÍdY&£££øÏ_ýJ;|äÈ™j­6@w©»hqöÛ7¾ žêWè×ŠÉÆ7^žuho îØ±à-ÁÅÄ z­V;sìÍ7çƒÁ Éd˜®ëPºXÌt7|©Tµù,@­ZC£Á)ĵ…t½Žd2ÉI9µ(JÒ©AQ’MÚn2™lfÉdLËDY«@Q^VnÒ’UUmZ™—T²¤€1Öþ „ ŸçüD"ÛnÛÄ~øaÖh4È/žzjá±ý·÷öýñÇjµÚ'N‘¯ºëZh» ¾‹¨»—bðµWö=ßÕƒ—\&Ú³€W_}•6 sQPPzçĉ±÷Þ{ï×|{ >øàˆ>Åb?i:wÈIH ˆ>ÌÎäÖÙlÑh'&''éäÔ[×/aÆÉÂáfggœÑÜf³³ð¸ñøépNóDQäœóó<[HÄQÈÐ!ŠˆÅ㘚æßÿŽ;î`CCC$;;KþûÅÍ={öœŸ˜p7½Vo©ÝöŸA–™‹îr…—\&Ú³€ááa E r[‚*!¤ž››;½oï^- “Í›6³j…Û`%Ü¢ã,%dhªÊÕveš¦Á4-È 'ߘoÅU²Ž"+X¨.`aA‡’L6Çq“É$êN !éü,Ê”6/%©€RUSÑÙÙ †5k¾Íyä6¸bì~u·ý裎?óÌ3oŽOL¼K)(; ¾öÿZ ¾,8ÍWõù|µå袻\áe_=ô;zôh»|¸;$Tÿ¿?ýéÔ¶íÛ7oذ'NœÀ\~’,¡V­9>‚=Ž I’šp’”€Ï‘‹ÅcO`†zÈæx†\"CpÝr]‰®H$‚YGÈ#6Ç}ã±8&&' Ö}÷»lõêÕðûýäo<5Ÿ=1:ú±mÛs„ƒðÊþRâ›íœýÅ&Ÿªì{¾¯/¼ÿÈÀmM=øàƒxî¹çÚµj৤J!1ŸÏ½¶gÏš›nº©kÓæM̰ bÛ6Jj Á`±X´ÕÞ»¨HÇÉ=”Ò&¹Ç¶y»®R®À4M¤Ò)‡žÛ@oooS€#3ÁBódú3ÐëuT¡F£ÁÇz»»P©UL&±uëV–N§É›Gâµ×^+ýù/9S¯×§ !n¯Š‹7þçÍæÓp8LÏæ{éþ×Þà ÐÞ\B+  @ƒsbƒäú¨Œ±’ щ©©Ñ_<ýtø?~ùËUëÖ­c½½½äСCÐ4 C×]‡º®£Z­"•JÁh¨Tªèéméóuuw檦r~ð ÃD"( ˆ'â;Dfø P0À…l¢ˆH8‚¡ë†ØÍ7ßLDQ$###Öž={&Î9¶ZmÔ]÷ž¿¸—ß´Ïö¨»Ëd±Ò¨‡Ï¥” v¾ÿ>6lØ@ Ãè „„c2€^AHQJ»~ðýïo|êé§Óé4#Çÿ¦i`.—C‡ß¾¾ff¦A)E?²Ù,LÓÄÀÀæçæ¹HÇŠ(8Ÿ+¹AD©„•+Q®TQ,08¸ªª¡T*aË–-lýúõ0-‹¼sü¯ìåW^ž~÷Ô©—PÝmgð¹A`Iê.cÌ£î.3xà ¶mø|>ìܹ“ŒŒŒ>Ÿ/`Ûv@@<ô ‚ÐG)•7ßüäϾ2•J1Ã0pòäIrìØ±– Æü<úûÓ¦¦¦J¥ ŠbÓ.+ b||ÉdÑ(&ÆÇ!Ë2â‰8Æ/Œ# AQdøý¶yÓfd2äÈáÃØ½{wñí·ßvmµΉ߮ÆÓ®µÙÔ]Bˆ·ñ¿áðÀÄ%²€c, ­ ¡‹R½ñ†¾óï?ûÙõ›6m*• + Èæräøñã¨V*èîéÁÔô4übú3d³³°m™L¹lzCÇÀÀr¹9T*åfVP­T±á– X»v-ëêî&ãçÏ㥗^*ÿïo|\«Õ&œÊ¾áÈm—qé_ƒ3¢ûYÔ]à«Ñ¤÷põá€/%²¿mÛa2€ð РO„$¥4 SÿôÀ7=°cG|ÕªUÐu333laa“““äôéÓˆÇâ¨.T1>>Žþt?AÀää$úúú¸t×|™ ºº’»ñƱråJœ>}šìûýï^?tè¬c«U1©.Áàsƒ@ûÆ×A°,˲–²Õ¼¿Üà€/ÅY€iš>ÆX­ rV/!DbŒE2™Lÿöm÷¬øá~¿ñ[ß"ápÕJ Ó`FÃ@.—ƒeYŽF 7ÑL¥R°, þ@éT „Â(ƒ¦©øŸ½{Í=¿ûÝùÉ©©³àUýŶZ‹7þb?=€mÛö§¨»Œ1oã/SxàKÂÍFFF°sçNAEѲ¬€xèÐ^ì&„È„(¥´@0+ÃÃé7J«nXȬâ‰8!ˆD"°mº®#‰À4M‹%0F1—ËáƒN³ Îëo½õÖÜûccŸ€Óq õE÷ü¥fóë‘Çô |×.¼ð%Á!†aàþûï' >Ÿ¯Ã¶í 7èϺÁ‹„’ QaJ©€@0DW D{zz‘H$܉‚¡|>)k[¨×­jµfV*åz6›­åóù å#¹eÆeÌæ/¦îZ^ïÚ†¾¸W±±1¬_¿žX–%BDJi@'€<$ÁƒB@”!H¥89‹´=º‹`m‹bBtJ)g"òu9¶ZÔ+ðyðÀW÷*pàÀÜwß}IJ,AѶ퀀8ø¦—%9¯uBü„ðŒ@ „ÆÀ!`üŸe°cxKÏ% µ“yÚÅ7Û+ûf{eß+ðyðÀWˆÅAÀ4M"Šb‡mÛ~Ƙ› DÁ7¾»¢à" ÀÎÐô¡•­Óß/ð¹²dî$¢[ì»"Õ]¯ÀwmcÙÆØ§üÔÿpí¹÷ïßÇ{Œœ={–‚àcŒu0Æà§}ÄYÎ ;_t ÜIe¡5†ì€*Zž…5pZ²îVöcthhˆ=ûì³lÛ¶m°,ë"Îþr!Ä nŸe®&ÜÂ`£ÑÀ®]»ÈÈȪժ ‚è?øf‚÷1€V°8¸é¿å¬xÐÁ7|ÝY:!Ä$„˜”Rº~ýzöøã³»îº @ ù³yð,Ãà~À«• >üðCˆ¢xU2÷„M÷÷ã…çŸÇO>IA ”Râßàn0p—{ò·_.ú¶mËDë*àöñMðà`3ÆØ¿üô§ìŸò$$.#î^Q®B`YV¯^ÎhÔ |—À² î‡|tt?¾ÿ~Äãñf‘ëï ÆlÛF,ƒ ÈÍÍ!_(º®˲ÜJ¿Û\¼ñÛ»ÀÅ€ö@àf6&U$ }}},:Rcæ57žëóù iöìÝ‹[n¹åš ~W‚e7ì~ÈEŸ±X  ”^ÕŸÉ-¶õõö"Õ×ǪÕ*›ž™! àu]·,ËZÜúsOþÅÀÖµEQ¤¡`¿éTй¿³aˆD"×ÔÆw!cM-þ .ÿ-€Î-¹í’ŽIEND®B`‚(0` 77$;;4'7728885A>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M;<9H99/6770%... +++771*66.BJLI‹TXVóW[YøY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øX\ZøTXVûSWUÛBDAd770%333@@@77/2*333---Y\Z˯²°ÿ¸»¹ÿ¸»¹ÿ®²°ÿw{yÿ[_]ÿdgfÿbgeÿcfdÿbedÿafcÿadbÿadbÿ`cbÿ_daÿ_caÿ^baÿ^caÿ]a_ÿ]a_ÿ]a_ÿ]a_ÿ[_]ÿ[`^ÿ[_]ÿY][ÿZ^\ÿY][ÿY][ÿY][ÿY][ÿX\ZÿVZXÿlomÿ¡£¡ÿ¸»¹ÿ»½»ÿ»½»ÿX\Yí77$VZX™¡¤£ÿ·º¸ÿµ¸¶ÿˆ‹‰ÿ…‰†ÿèëéÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿçêèÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿîñïÿª®¬ÿrusÿµ¸¶ÿ¹¼ºÿª¬«ÿZ^\ÉSWUn”’øµ¸¶ÿ±´²ÿospÿÁÄÁÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿéíêÿª®¬ÿ…Šˆÿ¨¬ªÿåèæÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿáåâÿ\`^ÿ±´²ÿ¹¼ºÿ˜œšÿW[YšSWUM~‚€ò¶¹·ÿ®±¯ÿuwvÿ¾ÁÀÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÏÒÑÿŠŽŒÿjmiÿ@>9ÿgieÿ†‹ˆÿ¼¿½ÿìïíÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿ×ÛÙÿafcÿ®±¯ÿ¹»ºÿ„ˆ†øSWUmSWU+ospó´·µÿª®«ÿ|€}ÿ«¯­ÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿåèæÿ¢¦¤ÿ…‚ÿLLGÿ:93ÿ:93ÿ:93ÿEE@ÿwzwÿ‹ÿÎÑÐÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÁÄÁÿosqÿª®«ÿ¸¼¹ÿpsrñSWUGSWU `dbû³¶´ÿ§ª¨ÿ…ƒÿ˜›šÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÆÉÈÿ‡ŒŠÿfieÿAA<ÿ??:ÿ??:ÿ??:ÿ??:ÿ??:ÿ??:ÿRTPÿ…ƒÿ™›ÿÜßÞÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿ¨¬ªÿy}{ÿ§ª¨ÿ¸º¹ÿ^b`ôSWU"]a_ò¬°®ÿ£§¥ÿˆ‹ÿ‚‡ƒÿïòðÿïòðÿïòðÿïòðÿïòðÿáäâÿ™›ÿƒÿOQMÿEFBÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿFGDÿad`ÿ…Šˆÿª®¬ÿãçåÿëïíÿëïíÿëïìÿëîìÿëîìÿŽ‘ÿ…ƒÿ¤¨¦ÿ³¶µÿTXVûSWUZ^\Õ¤¨¥ÿ ¤¡ÿ“‘ÿorpÿíðîÿìðîÿìïíÿêíëÿ½À¾ÿˆ‹ÿ…Šˆÿ†‹‰ÿ‚‡…ÿSUSÿbedÿbedÿbedÿbedÿbedÿbedÿbedÿbedÿRUSÿ…‰‡ÿ†Šˆÿ…Šˆÿ‹Žÿ¼Á¿ÿåéçÿçëéÿæëèÿæëèÿrxuÿ‹Œÿ£§¤ÿ¥¨¦ÿX\ZäX\Z°˜›™ÿž¢ ÿ•™—ÿ^a`ÿèìêÿèìêÿèìêÿèìéÿýýýÿÿÿÿÿÿÿÿÿûüûÿ~‚€ÿRUSÿVXXÿWYXÿWYXÿWYXÿWYXÿWYXÿWYXÿVXXÿW[Yÿˆ‹ÿûûûÿýýýÿýýýÿûûûÿâçäÿâçåÿâçäÿâçäÿ_baÿ“˜•ÿ¤§¦ÿ”˜–ÿX\Z¿TXV…‹Ž‹þž¡Ÿÿ˜œ™ÿVZXÿàåâÿäèæÿäèæÿãèæÿãèåÿãèåÿãçåÿâçåÿƒˆ…ÿ]`^ÿnsqÿrwtÿrwuÿrwuÿrwuÿrwuÿrwtÿmrpÿ_caÿ‘•’ÿàæãÿáçäÿáæäÿàåâÿÞäáÿÞäáÿÞäáÿ×ÛÙÿUYWÿ™šÿ£§¤ÿ†‰‡þUYWSWUa|€~õ Ÿÿ•™—ÿ`dbÿËÐÍÿàåâÿßåâÿßäâÿßäáÿßäáÿÞäáÿÞäáÿ“–”ÿ]`^ÿGGCÿ<;5ÿ>=7ÿ?>8ÿ?=8ÿ><7ÿ<:5ÿED@ÿ`daÿ ¥¡ÿÛáÞÿÛáÞÿÛáÞÿÚáÝÿÚàÝÿÚàÝÿÚàÝÿ¿ÄÂÿcfdÿ•™—ÿ£¦¥ÿvywõSWUeSWU@nqoñ žÿ’–”ÿhmiÿ·¼ºÿÛáÞÿÛáÞÿÛáÞÿÛáÞÿÚàÝÿÚàÝÿÚàÝÿ¥©¦ÿ`a_ÿGGAÿgfbÿxwsÿwwrÿwwrÿxwsÿggaÿGF@ÿdfbÿ­²­ÿ×ÞÚÿ×ÝÚÿÖÝÚÿÖÝÚÿÖÝÙÿÖÝÙÿÕÜÙÿ§­ªÿjomÿ’–”ÿ£¦¤ÿeigñSWU@SWUcfdöœŸÿ“ÿmrnÿ¤ª§ÿ×ÞÚÿ×ÝÚÿ×ÝÚÿÖÝÚÿÖÝÚÿÖÝÙÿÖÜÙÿ´¹¶ÿ^`\ÿRSNÿ……‚ÿde`ÿbc^ÿbc^ÿde`ÿ„…ÿTSOÿ`b^ÿ¹À¼ÿÓÚÖÿÒÚÖÿÒÙÖÿÒÙÖÿÒÙÖÿÑÙÕÿÑÙÕÿ’˜•ÿqtrÿ“ÿ£¦¤ÿX\ZöSWUSWU[_]ûš›ÿ‹ÿosqÿ’˜•ÿÓÚ×ÿÓÚÖÿÒÚÖÿÒÙÖÿÒÙÖÿÒÙÖÿÑÙÕÿÅÌÉÿRSOÿ_a]ÿ’”ÿtvrÿtvrÿsuqÿsuqÿ’ÿ_a]ÿVVQÿÈÏÌÿÎÖÓÿÎÖÒÿÎÖÒÿÎÖÒÿÍÕÒÿÍÕÒÿÍÕÑÿy}ÿvzxÿŽ’ÿ›žœÿTXVøX\Zè”—•ÿˆŒ‰ÿtwtÿ~„€ÿÎÖÓÿÎÖÓÿÎÖÒÿÎÖÒÿÎÖÒÿÍÕÒÿÍÕÒÿÍÕÑÿMLHÿilgÿ ¢Ÿÿ…ˆ…ÿ…ˆ…ÿ…ˆ…ÿ…ˆ…ÿž¡žÿjliÿTTPÿÊÓÏÿÊÓÏÿÊÒÏÿÊÒÎÿÉÒÎÿÉÒÎÿÉÒÎÿÉÑÎÿfliÿ|€}ÿŽ‘ŽÿŽ‘ÿW[XÝW[YÈ‹ŽŒÿ†‹ˆÿw{xÿnspÿÊÓÏÿÊÓÏÿÊÒÏÿÊÒÎÿÉÒÎÿÉÒÎÿÉÒÎÿÉÑÍÿWXRÿswsÿ­±­ÿ–›—ÿ–›—ÿ–›—ÿ–›—ÿ¬°­ÿuxvÿ^^ZÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿW\Zÿ…‚ÿŒ’ÿ€„‚ÿV[X¶UYW €ƒÿ†Šˆÿz~{ÿ`daÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿqtpÿ~‚€ÿº¿¼ÿ§®ªÿ§®ªÿ§®ªÿ§­©ÿ¹¾»ÿ~ƒ€ÿrtoÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ¼ÅÁÿX\Zÿ…ƒÿ‘ÿtxvýTXV…SWUvuxwû„‰†ÿ{€}ÿUZWÿÇÐÌÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿŽ‘ÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡ŒŠÿŠŽ‰ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿª²¯ÿadcÿ}‚ÿŒ‘ŽÿhljôSWU]SWUTkolòƒ‰†ÿz|ÿZ][ÿ¬´°ÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿœ¢ÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡ŒŠÿ—š—ÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ’™–ÿeigÿz|ÿŒ‘Žÿ]a_ñSWU8SWU2afcòƒˆ…ÿv{xÿgliÿkpmÿœ£ ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿ–šÿ‡‹‰ÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡‹‰ÿ‘—”ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿ¤¡ÿ•‘ÿZ^\ÿnspÿw|yÿŠŒÿUYWúSWUSWUX\Zú‡‹‰ÿv{xÿuzwÿfjhÿW\ZÿSWUÿSWUÿSWUÿSWUÿSWUÿSWUÿTXVÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿˆ‹ÿX\ZÿSWUÿSWUÿSWUÿSWUÿSWUÿSWUÿ_cbÿkomÿv{xÿ€„‚ÿ~ƒ€ÿTXVðUYWã|~ÿ…ƒÿy~{ÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿ‡ŒŠÿÈÎËÿ¹À½ÿ¹À½ÿ¹À½ÿ¹À½ÿÈÍËÿ‰ŽŒÿy}{ÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿx}zÿ€…ƒÿ‡Œ‰ÿafcõTXV|SWUYZ^\ôvzxÿ„ˆ…ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿˆ‹ÿËÑÎÿ½ÄÁÿ½ÄÁÿ½ÄÁÿ½ÄÁÿËÐÎÿŠÿ…‰†ÿ„‰†ÿ„‰†ÿ„‰†ÿ„‰†ÿ„Їÿ„Їÿ„Їÿ„‰†ÿ{€~ÿ_daôTXVçSWU SWUSWU_UYWÍTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñUYWñ‡ŒŠÿÏÓÒÿÂÇÅÿÂÇÅÿÂÇÅÿÂÇÅÿÏÓÑÿˆŽŒÿY][òTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñUYWéTXVsSWU€€€ˆ‹üÒÖÕÿÆËÉÿÆËÉÿÆËÉÿÆËÉÿÒÖÔÿ‹ŽöŠŠŠ€€€ˆ‹üÕÙØÿÊÏÍÿÊÏÍÿÊÏÍÿÊÏÍÿÕÙ×ÿ‹ŽöŠŠŠ€€€ˆ‹üÙÜÛÿÎÓÑÿÎÓÑÿÎÓÑÿÎÓÑÿØÜÛÿ‹ŽöŠŠŠ€€€ˆ‹üÜßÞÿÒÖÕÿÒÖÕÿÒÖÕÿÒÖÕÿÛßÞÿ‹‘ŽöŠŠŠ• ++Ї‹üßâáÿÖÚÙÿÖÚÙÿÖÚÙÿÖÚÙÿßâáÿŒ‘÷VVŒ&‰ “N”¢•Ñ“ó”ú”ø”ø”ø”ø”ø“ø†‹‹ÿâåäÿÚÞÝÿÚÞÝÿÚÞÝÿÚÞÝÿâåäÿ‡Œÿ “ù”ø”ø”ø”ø”ø“ú”õ•Ô–§“W•—˜ô ¥ÿ©ÿ­ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ®ÿ†‹‹ÿåççÿÞááÿÞááÿÞááÿÞááÿåççÿˆÿ¬ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ®ÿ©ÿ ¥ÿšô•°“ý((Âÿ¾ÿºÿºÿºÿºÿºÿºÿºÿºÿ¸ÿ†‹‹ÿèëêÿâåäÿâåäÿâåäÿâåäÿèêêÿ‡Œ‘ÿ µÿºÿºÿºÿºÿºÿºÿºÿºÿ ½ÿ))Äÿ”ùŽ“ÿKKåÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿ×ÿ†‹Œÿëîíÿæéèÿæéèÿæéèÿæéèÿëîíÿ‡’ÿ ÒÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿDDäÿšö•“û[[èÿ--éÿ åÿåÿåÿåÿåÿåÿåÿåÿ âÿ†‹Œÿïððÿêììÿêììÿêììÿêììÿîððÿˆ“ÿÜÿåÿåÿåÿåÿåÿåÿåÿ åÿ((èÿ^^ëÿ—ø™–…¢÷``Íþ~~Üÿáÿåÿåÿåÿåÿåÿåÿâÿ†‹‹ÿáäãÿÛßÞÿÚÞÝÿÚÞÝÿÛßÞÿáäãÿŠ‘ÿ€€Üÿåÿåÿåÿåÿåÿåÿáÿ~~ÜÿccÎþ##§õ–œ”9”ƒ—¸—â”ü“ÿ“ÿ“ÿ“ÿ“ÿ“ÿ‚†Šÿ¸¼»ÿÆËÊÿÇÌÊÿÇÌÊÿÆËÊÿº¿½ÿƒ‡Šÿ’ÿ“ÿ“ÿ“ÿ“ÿ“ÿ”ü˜ã™»”ˆ‘A‰‹‹a‡‹æ‡‹ñ‡‹ñ‡‹ñ‡‹ñˆŠè‰‹nÀóÃÀcLÀKÐÀ­$ÀýÀÌ@Àž+À ©À4Áà“¨ðÎ2ð•Jðý9ð‰\øÍÁøPÊøÛønöøƒƒøïïøVÙø?Žíü?"—ü?æü? `ü?fbü?ÔZü?üãÉþºèþç¶þÿͽÿÿàÿÿ›Wÿÿàÿÿ”1ÿÿàÿÿÛ‡ÿÿàÿÿܺÿøÿY}ÿ€ÿûÿÿ¹nÿâRÿtÿ‘`ÿÿÊ…ÿ€ÿ*VÿÿðÿÿcÇÿÿÿÿÿÿËÿÿÿÿÿÿ<ÿÿÿÿÿÿU$( @ +++111;<76<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86<=86880 @@@333AB>9ÿ>>9ÿXZVÿ„Іÿ®²¯ÿêíëÿïòðÿïòðÿïòðÿïòðÿ¾ÁÀÿptsÿ²µ³ÿ‰ŒŠöSWUkSWU?rus𯳰ÿvzwÿ²µ³ÿïòðÿïòðÿïòðÿ¾ÂÀÿ…‹‰ÿ^a^ÿSURÿ_a_ÿ_a_ÿ_a_ÿ_a_ÿUWSÿswtÿ‹ÿÍÑÎÿìðíÿìïíÿëïíÿ¥©§ÿx|zÿ°³±ÿtywðSWUFSWUcgeô­±®ÿy}{ÿ žÿëïíÿâäãÿ ¤¡ÿ‚‡…ÿ‚†„ÿrvtÿQSRÿWXXÿWXXÿWXXÿWXXÿQSRÿz~|ÿ…Šˆÿ…‹ˆÿ§¬ªÿÝâàÿåêçÿŠŒÿ€„ÿ®±®ÿcgeóSWU!SWUY][úª­«ÿ‚ÿ†‹ˆÿåêçÿåéçÿûüûÿÿÿÿÿÜÞÝÿuywÿbgdÿqvtÿqvtÿqvtÿqvtÿ^baÿx{yÿÜáÞÿÿÿÿÿÿÿÿÿàåâÿßåâÿrutÿ‡‹‰ÿ«®¬ÿX\ZúSWUW[Yç ¤£ÿƒ‡†ÿswtÿßäâÿßäáÿÞäáÿÞãáÿØÝÚÿgjfÿJKGÿ>=7ÿ>=7ÿ>=7ÿ>=7ÿMMIÿkmjÿÚáÞÿÚàÝÿÚàÝÿÙàÝÿÙßÜÿ^c`ÿŽ’ÿ £¡ÿW[YäX\ZÉ“˜•ÿŠŽŒÿbfcÿÙßÜÿØßÜÿØßÛÿØÞÛÿ×ÞÛÿdfbÿNNJÿijeÿ|ÿ|ÿjkgÿNNIÿmqlÿÔÛØÿÔÛØÿÔÛ×ÿÓÚ×ÿÎÔÒÿV[Yÿ”™–ÿ‘•“ÿX\ZÀVZX¡†ŠˆÿŽ’ÿVZXÿÒÙÖÿÒÚÖÿÒÙÖÿÒÙÖÿÑÙÕÿqsnÿ^_\ÿ”—”ÿ}|ÿ|zÿ•˜”ÿ_a]ÿz|xÿÎÖÓÿÎÖÒÿÍÖÒÿÍÕÒÿ¸¾»ÿ_daÿ’—”ÿ‚†„ÿUYW’SWUwz~|û“ÿY^[ÿÂÉÅÿÌÔÑÿÌÔÐÿÌÔÐÿËÔÐÿ…ˆƒÿmqmÿ¨¬¨ÿ‘•‘ÿ‘•‘ÿ©¬©ÿoqoÿ‰‰ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ£ª¦ÿfjiÿ’•“ÿuxvöSWUhSWUVmqnðŽÿ`dbÿ¯¸´ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿœ¡œÿ~‚€ÿ½À¾ÿ«±®ÿ«±®ÿ½Â¿ÿ„‚ÿ›¡žÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ’™–ÿjmkÿ“ÿfjhðSWUCSWU5bfdïŠÿdhgÿ¢©¦ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ·¾ºÿ„‰‡ÿÄÊÇÿ¶½ºÿ¶½ºÿÆËÉÿ†‹‰ÿ±¹µÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ}ƒ€ÿlpnÿŽ‘ÿ\`^õSWUSWUY][õ‰Šÿinkÿinkÿ”š˜ÿ•œ™ÿ•™ÿ•™ÿ•™ÿ„‰†ÿÄÊÇÿ¶½ºÿ¶½ºÿÆËÉÿ†Šˆÿ”š˜ÿ–šÿ–šÿ–šÿ‹’ŽÿZ]\ÿswtÿŠŽŒÿUZXûSWUTXV÷„ˆ†ÿ|~ÿfjgÿ\a_ÿ\`^ÿ\`^ÿ\`^ÿ\`^ÿ…ŠˆÿÄÊÇÿ¶½ºÿ¶½ºÿÆËÉÿ‡ŒŠÿ_caÿ\`^ÿ\`^ÿ\`^ÿ`edÿmspÿƒ‡„ÿz}ÿUYWÞUYWºjnký„‰†ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ‡Œ‰ÿ†Œ‰ÿÈÍËÿ»Â¿ÿ»Â¿ÿÊÏÍÿˆ‹ÿ‡‹‰ÿ‡‹‰ÿ‡‹‰ÿ‡‹‰ÿ‡ŒŠÿ‡Œ‰ÿ|~ÿ\`^ôSWUfSWU'UYWÎSWUüTXVüTXVüTXVüTXVüTXVüTXVü…ŠˆÿÌÑÏÿÁÇÅÿÁÇÅÿÏÓÒÿ‡ŒŠÿW[YüTXVüTXVüTXVüTXVüTXVüTXVõTXVSWUSWUSWUSWUSWUSWUSWUSWU‡ŒŠ÷ÑÕÔÿÇÍËÿÇÍËÿÓØÖÿˆ‹ùuzxSWUSWUSWUSWUSWUSWU‡ŒŠ÷ÕØ×ÿÍÒÐÿÍÒÐÿØÜÚÿˆ‹ø…Šˆ““"“(“(“(†‹ŠøÙÜÛÿÓ×ÖÿÓ×ÖÿÝàßÿˆŒù()5“(“(“"““$”Á”ì“ü•ó•ò•ò•òƒˆ‹ÿÞáàÿÙÝÜÿÙÝÜÿáääÿ†ŠŒÿ ”ó•ò•ò•ó“ü”í”Ó(“”ó £ü­ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿƒˆŒÿâäãÿßâáÿßâáÿæèèÿ†‹ÿ­ÿ¯ÿ¯ÿ¯ÿ¯ÿ­ÿ ¤ý”ô“"“8žð77ÔÿÉÿÈÿÈÿÈÿÈÿÈÿƒˆÿæèèÿåççÿåççÿëììÿ†‹Žÿ ÄÿÈÿÈÿÈÿÈÿÉÿ55Óÿ ï“@“8¤ð^^ôÿðÿïÿïÿïÿïÿïÿ„‰Žÿëììÿìîîÿìîîÿïññÿ…ŠŽÿéÿïÿïÿïÿïÿðÿ]]ôÿ ¨ñ“@“–ç66½úllÚÿwwáÿwwäÿwwåÿwwåÿwwåÿ‡‹’ÿ¾ÂÀÿÒÖÔÿÒÖÔÿÄÈÆÿ‡‹ÿwxâÿwwåÿwwåÿwwåÿwwáÿmmÚÿ99¿û–ê““–™—Ζê”ü“ÿ“ÿ“ÿ23ÿ}‚‹ÿ‚‡Šÿ‚‡Šÿ„‹ÿ>Aÿ“ÿ“ÿ“ÿ”ü–ë—Ï–“À€€ÀÀÀÀÀÀÀàààààààðððü?ÿøÿÿÿøððððø(  UYW¥SWUýTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüTXVüSWUüUYWÅUYWïz|ÿ„ÿ~‚ÿ~ƒ€ÿ~ƒ€ÿ~ƒ€ÿ~‚€ÿ~‚€ÿ~‚€ÿ~‚€ÿ}‚ÿ}‚ÿ~ƒ€ÿ~ƒ€ÿTXVüX\Zׇ‹‰ÿmqoÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿVZXÿfigÿŽ’ÿVZXðZ^\¹’ÿ[`]ÿÉÏËÿ×ÞÙÿ×ÞÙÿ½Â½ÿGF@ÿvxsÿÕÜ×ÿ×ÞÙÿ×ÞÙÿÓÚÕÿVZXÿ—š˜ÿY][ÚX\Z”‹ŽŒÿmroÿ³ºµÿ×ÞÙÿµ»¶ÿ@>9ÿHJEÿDD@ÿZZUÿÌÒÍÿ×ÞÙÿ¾ÅÁÿehfÿ—›™ÿ[_]¼SWUq{}ú|~ÿœ¢žÿ­²­ÿ=<6ÿIKHÿSWUÿSWUÿHIDÿGGAÿ½Â½ÿ§­©ÿtxvÿ‡‹‰ÿX\Z™SWUTlpnñ†‰‡ÿ‚‡„ÿprmÿ<:4ÿ=;6ÿSWUÿSWUÿ@>9ÿ64.ÿmnhÿ“ÿƒÿy}{úSWUtSWU8`dbÿjokÿ×ÞÙÿ×ÞÙÿ64.ÿTXVÿSWUÿ971ÿÒÙÓÿ×ÞÙÿsxtÿ‰‹ÿkomñSWUXSWUX\Zô”˜–ÿVZXÿÓÚÕÿ×ÞÙÿ[]ZÿeigÿeigÿZ\XÿÒÙÓÿÕÜ×ÿ[_]ÿ‘”’ÿ`dbïSWU;SWUTXVû‘ŽÿmqoÿW[YÿY][ÿehfÿµ¼¹ÿ´»¸ÿdhfÿX\YÿX\Zÿknlÿ“‘ÿW[YõSWUVZXŸTXVýTXVüTXVüUXWüeigÿ¶½ºÿ¶½ºÿdhfÿSVTüTXVüTXVüTXVüVZX¿SWUSWUSWUSWU\`^eigÿ¶½ºÿ¶½ºÿdhfÿIK:SWUSWUSWU”d•à”ö”÷“÷dhhÿ¶½ºÿ¶½ºÿdhfÿ“÷”÷”ö•Ü“R“ü­ÿ³ÿ³ÿ±ÿdhiÿÏÔÒÿÎÓÑÿdhfÿ³ÿ³ÿ³ÿ«ÿ”ñ”ýEEåÿEEíÿEEíÿFFéÿeiiÿäææÿâääÿdhfÿEEíÿEEíÿEEíÿAAàÿ–ó–v–ï•ü•ü•üILxþdhfÿdhfÿEHzþ•ü•ü•ü—ì•bV‚–ýµÿfÿ×ÿðÿðÿðÿ€ðÿÀðÿ€íÿ€²ÿ€†ÿ€OÿTransGUI/setup/0000755000000000000000000000000012261774331012356 5ustar rootrootTransGUI/setup/macosx/0000755000000000000000000000000012261774331013650 5ustar rootrootTransGUI/setup/macosx/PkgInfo0000644000000000000000000000001111367227317015123 0ustar rootrootAPPL???? TransGUI/setup/macosx/transgui.icns0000644000000000000000000027706711367227317016410 0ustar rootrooticns~7is32›––€•xffz€• —•”åííéiæäf€í à–“­³³±iÒÑf€³«””•””“hººf“””•“€€U^gººf:€U€X€VWgººfT€VtXUUVŽoY[f¹¸fYZl‘YUUZ–XÕÙZggXÓ×]’bUUbkÙÙ.VU1ÓÙt‹mUUn‡„m46UU9.h{UU}~ž­6HUUDA½©v‰ZZŒoµÙ¶9E@UÍÙÁf™]\]ËÙÙ½@s×ÙÙÕX˜[Z‰o‡XgXW|„€€€VWU‰VUWLhhH€EFiæäh€EA„hÔÓhˆh½½h…€W`i½½hK€W€ZXi½½hV€XtZWWX‘q[]h¼»h\\n“[WW\˜ZÚÞ]ii\ÙÜ_”dWWd’oÞÞ4XW7ÙÞxoWWp‰‡r:;WW>4n“ƒ}WW¢²JDZÒÞÅh›_^’`ÏÞÞÂFxÜÞÞÚZš]\‹q‡Zi’ZY„‚€ƒƒ‚ƒƒXYW‰XWYIddE€EFeäâd€EA„dÏÎdˆd¶¶d…€S\e¶¶dI€S€V€TUe¶¶dS€TtVSSTmWYeµ´dXXkWSSX”VÓ×[eeZÒÕ[‘`SS`Žj××6TS9Ò×s‰kSSl†‚p<=SS@6mySS{|œ­=ISSHG½§t‡XX‹m³×µ@HDZÌ×¾e—[Z[É××½GvÕ××ÓV—YX‡m‡VfŽVUz…~}}~~TUS‰TSUs8mkvïüüüþÿÿþüüüìbýÿÿÿÿÿÿÿÿÿÿÿÿóüÿÿÿÿÿÿÿÿÿÿÿÿñdàö÷÷ÿÿÿÿ÷÷öÜRÿÿÿÿŸýüüüÿÿÿÿüüüü¿ûÿÿÿÿÿÿÿÿÿÿÿÿõôÿÿÿÿÿÿÿÿÿÿÿÿï;8ïÿÿÿÿÿÿÿÿÿÿÿÿñXTñÿÿÿÿÿÿÿÿÿÿÿÿútqúÿÿÿÿÿÿÿÿÿÿÿÿÿ™”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿðïÿÿÿÿÿÿÿÿÿÿÿÿÿÿü¥ýüüüüüüüüüüüüüÅil32k‚“–—–”€“‹ŠŠ‹€“”–—–“†“–½Úáä€å’ÀÔÔÆâ€åáÚ¿–“…“¤ôð‚ïŽìîîñŽéïðô¨“…“žÔÉ‚ÈèççìŽÄÈÉÓ “…“”£­‚¯Œãááè­¯­¤”“†“””“•‹àÜÜ䌔€•“””“Š‚“ŠÛÖÖߌ“’Š×ÐÐÚ‹ˆ„UŠÔËËÖ‹xƒU‡UWUƒVˆÏÅÅÒŠY„VU…Wk†„‰Ë¿¿Í‹‰Љ~^U…V†~g_^ˆÇººÉŠa€^dp„}W„U[Škk˜™†ÇººÉˆ˜€šŽ\tŒXUƒUdg¦Íº‡ÇººÉ‰µÍ€n^UƒUnŽb´Íœ€¾®®¿‚žÍ–khUƒU|[ÅрЃm¨‘‘©o‰Í¦i“vUƒXˆXÖ Õn\”|z”]xÓ€Ò»a”„WƒZ•ŒcÜÜ€ÛbJe||gIlØØ××ÒY–“ZƒY£†tâ€áÚfG7IjÞ€ÝÜ`¡Y‚ U[«ˆççûÿÝwdt ayÞÿÿâât‰¬ZU Ue®{žíã¡…„tRX R|ˆˆªà献eUUs°w³€ðÀ‰^R_St΀í§z±wUU…²qÅð Þ’u?99V†¯ëðÀs³ŠUV–µfׂðí²†Ow“ÛƒðÖeµX[¦¶lÆ„ðÕ¾ï„ðÎj¶®\\´º•]ihhggffedcb€a€_^\¹¾[€+Xª žš““€”€•––——˜—˜˜™™›žž³V€1V•}‘}’޲ ’}›U+/V‡ˆ€y{Ž} {~“U0+Qh“—•|V+3>ZвíòÁtµŒWX˜·iÚ‚òﳈTz•݃òØg·žZ]¨¸nÉ„òדÀñ„òÐl¸°^^¶¼˜_lkkjihggefddcb€a`^‘»À]€+Z¬¢ œ•–•–——˜—˜˜™™š››  µX€1X—ƒ€”€•Ž´ •€W56XŠ‹€|~Ž€ ~–W83Sj’“™—X53BW–XM+1<”=8@Š2}‚‚>6l‚w‡¾ÒÒć‚wm9ˆ^‚„ëììï…] ‡7‚ƒæååë†5ˆ ƒƒâßßæ†‚ ƒÞÙÙᆠ–†ÙÓÓ݈(–‡ÕÍÍØˆ…„S‡ÑÇÇÓˆuƒS‡SUSƒT…ÌÁÁχW„TS…Uj„ƒ‡†È»»Êˆƒ‡|\S…T„|f‚\…Ķ¶Æ‡_€\`mƒzU„SY‰ii”•„Ķ¶Æ†”€–‹ZsŠUSƒSbŠd¢È·„Ķ¶Æ†±È}lŽ\SƒSm`¯Èœ~½««½›È’jfSƒSzY€ÌË…m¨‘‘©o‰È£f’uSƒV†ŽVÒÑq^”}|•_zÎÎÍ͸_’‚UƒX“ŠbÙ€Ø×dNijNm€ÔÓÎV”‘Xƒ W ƒsßßÞÞØgJ>Mk€ÚÙÙ^Ž W‚ SYª†ååûÿÜubq ^xÜÿÿàßr‡«XS Sc­yëâ ‚‚rQW Qz……§Ý劀®cSSr¯v²€ï¾…^S_ Us‹Íììë¥x°tSSƒ±oÄï ÝuC>>X„®êï¾p²‰ST•´dׂï쯄Sw‘ÙƒïÕd´›VY¦µkÄ„ïÓŽ¼î„ïÍfµ®ZZ´¹”Zggffeedbbaa__^€]\Z¸½Y€+V©žœ˜‘‘’“”••€–—˜™œœ²T€1T“{{‘ޱ ‘{™S56T…†€wyŽ{ y}’S83PfŽ“•“zT53AS–TK+1;”<8@l8mk™ÎêüÿÿÿÿÿÿÿÿÿÿÿÿüëÏçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûê8ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ@8ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï@óüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýô"$ÁìüóòòòÿÿÿÿÿÿóòòóüíÃ("(((øÿÿÿÿù5(("÷ÿÿÿÿø÷ÿÿÿÿù'Îüüüüüüüÿÿÿÿÿÿüüüüüüõºýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôf÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû5ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõVðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðCwûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöh¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó!?ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðF`ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿök‚ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü&íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü ¶ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíWÖûúúúúúúúúúúúúúúúúúúúúúúúô666666666666666666666666 it32v‰·ÿýÿ¡^i€gfdb`^\[]_bcfjnqcXOÁÿüÎÿýŽüúÿ ^iebachnw‚š™š¬·»¸´°ªIÀÿüàÿýÿŸYhn{‰—¥°·½ÆËÉÆÃÆÉËÆ¼·°l¼ÿýÌÿýÿ³~ˆ…†‡‡†…€„ˆ–‚€™¥µÁÉɾ¾Ã·´ÀÂÁÀÂÀÆÌÄ´ŽüÍÿüÿ~#4…1$/.18CPCNq—´Â¿ÃÂÍÊÆÀ·²º»Ãƾ¾ÄÍÍ»¦xÄÿýËÿ1üÿˆ4D@?@AA??BJUakquyg€Ÿ²ÀÐÁ¾ÉÀÈÇÀ¸­¯¹½À»ºÆËÌ¿§ŽÿþËÿ2üÿ†/@AEA<@HS_gloruy~j ¶ÆÅĹ¼Á½½ºº¾Å½¶½ÅǼ®ª€éÿþÊÿ2ýÿÇ¡¥“vWQ^fjmpsw|€„‡…jŽ«·½À¹³·ÅÅÃÄÆÁÈÐÌÄÆÀÁ¸µ°ÃÿýÊÿ2þÿ¼†p\Yahkmqvz~‚†Š“–¡u˜¯º½¿º±ÀÄÉÊÄÆÅÑØÓÌ¿·¶´®„¨ÿý´ÿý’ü2ûÂŽNXeilotx|€„ˆ‘•™ž¢§ªxŒ¥µ¹º½¿ºÃÉÈÇÄÄÊÌÓË»²«°®‡ÿýÈÿ2þÿàÓ¼llsvz~‚†ŠŽ“—› ¤¨¬°º¢~¡ª³¾ºÁÁÄÈÃÅÇ¿ÁËÍɸ¬©¬©ˆŠ´ÿþÿÔ¶¼»2¾¦c“ʾu|€„ˆŒ‘•™ž¢¦ª¯³·»¾Ç‹•¯²¼¿ÆÅÊÆ¿ÁùºÊËŽ²§¦›€uü³ÿüÿ…/?Š<€=&;9CQPC€È±‹Ž“—› ¤¨¬±µ¹½ÁÅÈÓ´…¤·µ¸ÆÄÁº¸µ€º ÁÈù±Ÿšrtý³ÿüÿ@OŠL8MIKUm…vb>Ë¦šž¢¦ª®³·»¿ÃÇËÏÒÖÙŠ–¤­©´±¼¹µ¯²½¸¾Ã¾®¬{`‹ÿý²ÿüÿIEKm¤­²¶·¸¶¶³¸»³§’x?]ßÓÓ×ÛÞáåçêíïòôöø÷ÿÆvš¬¼¿Áµ¨¦¡¡|hODK=¹ÿü´ÿüÿ;K€H@IGDW£­±¶¶·¶¹½³±¸¹µ°ž‚]4°êÙàãæéìîñóõ÷ùúûûÿè{•ª¸¾»±©—Œ‡r]LFIK;ºÿü´ÿHüÿŒ:JGGIDKl£®¶·¸º»»¾½»°³À°¯¨‹n9híåçêíïòôöøùûüüþÿÿò|©µºµ¬œ’…ufUHHJHJ;¸ÿù‚üþ®ÿHüÿŒ:JGHBSŠ¢£§³½¹µ°µ»¿Ãžµ¶´°¬‘xO9Èõìñóõ÷ùúûüýÿÿþêɯr€¡­ª¦›ˆ{hRIFIIHGJ9ºÿü´ÿHüÿŒ:KGBa’¡°´°´½°¡™¨ÁÀÌÄÁ»¬«¨­˜zc3‰üòöøùûüüþÿÿöÚ¹¢™ wo“—•}lZIEHJHHGGH>Œ¿µ€·¸²äÿþ«ÿIþI=JEFqŸ©­µ·¸¼ª——ž«É¿ÄÈÀºµ±­®›„lDPêüùûüýÿÿþëÊ­šš—™{_||{o[NFGJIHHGFFEI# €¥ÿû¬ÿ+(/II| §²¶¹ÁÂÀ±š™¯ÁѽÃÇ»µ·©¥—‡lT4¼ÿúþÿÿ÷Ûº£€š–’ŽR_`[QFFKKJIH€GFDH" € ¤ÿûªÿJôì8 3OŠ©¬µ¼ÁÀÁÀ¿·š™œ¥¿ÈÄ嶸ª››•}nb4ƒÿþþëÊ­šš˜”‘Ї„MGIGDAGKFCB@A?@ACH" €¥ÿû¨ÿLýÿ¡_ljXˆ«´¼¼ÀÃÀ¿¼¾µ«¡ˆ™·ÁÀ¾±¯©¤›””„{kESì๣›šš–’Œ‰…‚{|M9CB@>ACGRY\\XSH?D € ¤ÿû¨ÿLüÿw)S¡­¾½¾ÁÃǹ¹º®¹³˜‘³¾µª«§ª¦ž‘oU:¾±•›˜”‘Ї„}{wtuT>A>:üÿ‡w½±ŠT ³½¸±»³¦¡¥™”•ŠŒ—ŸžœŸ•—˜˜“‡r`ANÍ“€|yvspmjheb`^Xdpljji€jiihggffefd?135$²ÿü§ÿ;þÿÙ˜Á­¢¤U}¬·º°­¯¡ šž’ŽŒ‰˜™˜š•‰Ž“ƒoZB=¸¨rytqolifda_\ZXQcmke`aa^`gg€fegatÿûªÿSþÿè”Ê·­µÄP¨­²¤°¥˜˜›”“Œ™˜ˆˆ{oqy}r`N=6˜¾krmjheb`][YVTSN\nkkeby𢧥‹ccgffeefS¼ÿü©ÿþÿÚ¢àÀÈÐßÀCƒ¨¤©¢˜š™•ŽŠ•“€-Œxpggf]L;/3yÍphgda_\ZXUSQONHZnee~ž…R=;V’´qbgeg9,íÿþ©ÿJþÿŸÖãâëóóxMŸ–›“‘Ž“Œ†‹Œ‰€xzzmp\TNE6.27`Ì„[a][YWTRPOMKJGOkƒŸ€=  S³jdf€edd]ÿü©ÿTýÿÑ­ÿõüþÿÍ@~–•—‘‹‰‡‡„…Šˆ€|fX^\SUG?76>;@9Qº¡Q\XUSQONLJIHGAV¡y4 #>LLC/Z§]geeddeX¬ÿüÿü‡ûþÿJþÿžëÿÿòÏ£GF“Š€‚||rx{qa\XNBC;79=@[F>B€DCA@@NDCDwÆzAKHGFEDC‚BAQt’¯ÈÓÒÏÍÌȲo`¡[f€dcdX¬ÿü‹ÿ'…Øÿý‹ÿ1ýÿ¨”šghqx{PI]YTQKA>517:=BEGEDDCAA?ISVIFDn³œ;IEDCCƒBD?„½ÍÕÓÐÏÎÍÍÊĤJ–^fddccd> éÿþŠÿ5 …Ûÿý‹ÿ üÿz|†–„pbTG€CEE2())-14€745@D??DNX\\[OHDi¶?EC„BCEH>›á€Ò ÑÐÎÍÌÌȽŠ%2«c€dc^ÿüŠÿ2… Úÿý‹ÿüÿˆ>SAA>=?BCCA1"cŽˆ€‹Љ†‰•“Y=HT`ca_[[VJFe‹ÃR>CB$CDEGIMD‡ÞÑÓÒÑÏÎÍÌÌËŲh mšZfcdbcdW¬ÿü‰ÿ2… Úÿýÿïà+K2€¤ÿûƒÿ?ýÓnuggd`_]\YOH`€¾s8DBBCEFHJLOSLyÛÒÒÑÐÏÎÍÌËËÉÁžBw_dcbccd= éÿþˆÿ2… Úÿýÿ)N. J£ÿøúúþÿð¦ogƒ¡¥•kfeca^ZXQK[~«—4EDEGIKMPSUYXmÔÔÒÑÐÏÎÌÌËËÊÆ¹ƒ?«_dbccbcb]ÿüˆÿ2… ÚÿýŽÿþ)J- A¤ÿúþÿä—kq•­¬£Ÿ¡Šghe`\cwnLX”²=HHJLNQTWZ]achÉÖÒÑÐÏÍÍÌËËÊÈía y“Ye€cbdV­ÿü‡ÿ2… Úÿýÿ)H, L¥ÿþχesœ®­¨¦¦¤ ¡~ddm|zIHY~¿THNPSUX\_bfini¼ØÑÑÐÏÍÌËËÊÉÈÆ½™:¤o_dccbcd=éÿþ†ÿ2… Úÿýÿ)H,€ ¡Ëu_tœ¨€¦6¨©¨¦¥ž™zE >Z}x¸uIUWZ]adgkorxp¬ÚÐÐÏÎÍÌËËÊÉÉÈõ}K§\d€cbcb]ÿü†ÿ2… ÚÿýÿW)G,  M`u˜žœŸ¡¢¥§¨©¥¢¶É”;'B]X{w¤–M^_bfimptx{zžÚÐÐÏÍÌËËÊÉÉÈÇÆÀ¨Z †‹ZdbccbdU ­ÿü…ÿ2… ÚÿýÿN)F+ =i~‘”’•™š›Ÿ£¥£¥¼Í¦T+Fk“¥bwyŽ®Xfgkorvz}…‰ˆ”×ÑÐÏÎÌÌÊÊÉÈÇÇÆÄº”2#¨j`cbd;êÿþ„ÿ2… Úÿýÿ)F+ ;f‚‰Š‰Œ€‘<•š›š¢¹ÌžJ ,Lr–¬±¶~t||¸llptx{ƒ†Š‘”ÏÒÏÎÍÌËÊÉÈÇÇÆÆÅÁ²wY£Zdbcajÿû„ÿ2… ÚÿýÿO)E*L|ƒ„ƒ„ˆŠŠ‹‘Žš´Ç”? 'Gw‹—¯²±°µ£w{r²†qz}„ˆŒ’–˜žÄÔÏÎÍÌËÊÉÈÇÇÆÅÅĽ¤R ‘‚ZcbcS»ÿüƒÿ2… Úÿýÿ!)C+U~~…„„‡ˆ„¯ÀŠ4 *M~¶å÷¿­³²vo¢¡w„†Š‘”—š ¦—¸ÖÎÍÌËÊÊȀǀÅÄ·+-«da‚bd7/öÿþ‚ÿ2… ÚÿýÿB)I%Qz}|}€‚}‰¬µ€,.T‡Àéûýÿô·°³²±¶rr¶’–™œŸ¡¤¦« «ÕÍÍÌËÊÉÈÇÆÅ ÄÄ¿¯pfXdbc`rÿû‚ÿ2… Úÿýÿ:(, 3v|yz|}}~~y£­u$3[’Èîûýþÿþÿë°²²±µ_otz¿”—š £¥§©«®©¥ÚÍ€ËÉÈÈÇÅ€Äû Kš{\c€bacS»ÿüÿ2… Úÿýÿ' c~vyzz{|{yž~ 8eœÐñûýþÿ%þÿß­²¸šhvl¸£™Ÿ¡¤¦¨ª¬­®¯³‘®ÓÓËÊÉÈÇÆÆÅÅ‚ÄÀµ‰$8«_ƒb d6/öÿþþÿÿ3€ Úÿýÿ'!}txxwzzyy‰i=c£Ùóûüþ„ÿ#þÿÓ©À_Syg¥µž¦§©«­®¯¯°®¸‰rËÊÈÇÆÆÅŃÄý«js–Xdbc`rÿû€ÿ*€  Úÿýÿ'8zuvvxwxyzv U‡Ñõûüþ‡ÿþÿÈ«!2xfÁ£«¬­®¯° ®²¡,lºËÉÈÇÆÆÅÅ‚Ä ÃĹœC¡s]cbcQ ¼ÿüÿÿQ+42215 Úÿýÿ';ƒqvvuwwu€MS—èüüýŠÿ ýÿnjyꮯ¯€°¯¯®­®¢©àÏÉÈÇÆÅŃÄÿ²„F©\cbcd15ÿþ„ÿûÿh Úÿýÿ'<œnttutvuyI+‚áûúúûþ‰ÿ üÿU[oi¼²¯€°¯¯®­«ª©¤šËÊÉÇÇÆ€ÅĂü¨c €Ydbc_‚ÿûƒÿûÿh Úÿýÿ'­wrtsutt{G9®õóôõ÷ùüü†ÿýÿÝ Asaª»®°¯®­¬«©¨¦¤¤‘ÃÌÈÇÆ€Å€ÄÃÂÃÃÂÁ·˜;§m_dbbcbdMÎÿý‚ÿûÿh Úÿýÿ'~œitsrtswV<¾öïðòóöõùüþ„ÿûÿ‡%p_”À«®­«ª©§¥£¡žŸµÎÇÆ€Å€Ä…à ¿°R¦[d€cbcd/=ÿþ‚ÿûÿh Úÿýÿ'1²nqqsqrwc<°óëîîðñóööùüýþ€ÿþÿú,db}¾©«©¨¦¤¢ ž›™™Š¢ÎÅÆÅ€ÄƒÃÂÀû¦] Œ‡Zd€cbd_‚ÿûÿûÿh Úÿýÿ='—Šjrprqqs?ˆëêêìíîðñòôöùþþÿÿüÿµRgjµ¨§¥£¡Ÿš˜•’ЉËÅÅ‚ÄÃÃÂÂ…ÃÁ·•4'«ha‚ceKÏÿý€ÿûÿh ÚÿýÿC'K®gpqoppsVZÒëçééëìíîñò÷÷úýþüÿW@-1l^¦¨¡ ž›™–”‘ŽŒˆ‹h ÎÃÄÄÃÂÂÀƒÃÄÃÿ¯z`¢Zeccdcce,=ÿþ€ÿûÿh Úÿýÿ>' ¦|knpnnriE¢êäæçèéêìíïðòõöøÿß J¹ÿÊ$eY‘¨›š˜•’Ї„oTÅÄĀÃÃÄû¤U–\eccdcd^˜ÿûÿÿûÿh ÚÿýÿD'i¢dplonmtLjÛäâäææçèêëìîïñðÿxiÒÿÿûÿ‰EUv¤””‘ŽŒ‰†ƒ€}zvy>‚ÏÁÃÂÂÃÃĵ‘-3­dc‚dfIÞÿýÿûÿh ÚÿýÿC(+ ¯oknlmnngC³æßáâãäåæçèéëíððšØÿþüüþÿô`E]šŠ‡„~{xurnpU<µÆÁÃÃÂÂÃÄÃƒÄ ÅÅ¿­unZf€dcef(Iÿýÿûÿh Úÿýÿ?(?‚“dlmklmoJußÞÞßààáâãäåçéêêúùïôùüÿüÿ¦5HˆŠ†ƒ€}zwtqnkig^5kÊÀ€ÂƒÃ‚ÄÅÄ»¢N {^fdeedf]˜ÿûûÿh ÚÿýÿA(;66¯eljlkip]J¾ßÚÜÝÞÞßàááãäåçåéíïðôúýÿóN/q†}{xurolihcZYa[<žÊÀÂÃĂůõŽ&?­bƒe hEßÿúÿh ÚÿýÿB(,B˜ƒclijkhoB…ÞØÙÙÚÛÜÜÝÞÞßáâäåçéëíñõ÷ÿ£V€vtqnkhgaW]yzulMYÀÃÂÃÄÅÄÄ‚ÅÆÆ€Ç À®o|—\gee€f g%Jÿùÿh ÚÿýÿC((›P¨aijhijkZNÈÙÖ×רØÙÚÚÛÜÜÞßàáãåæéìï÷ò82wolige_V]~¢°ŒumbF‡ÌÂÄÄ€ÅÄÅ€ÆÇÈǼ G¨ubgf g] §ÿÿh Úÿýÿ(*…ñ §sfgjhhkgB’ÜÒÔÔÕÕÖÖ€×)ØÙÚÚÜÝÞßáãæåü•jied^T]€¤¯©ªœyodWS²ÉÄÅÅÄÅËɀǀÈÉÈÉÆ·‹ L¬`hfg hC"åÿg Úÿýÿ(+|ÿllœ]jfhhfnPZÍÓÑ€Ò€Ó0ÔÓÔÕÕÖ×רÙÛÜÝßãâ-Mh[S^‚¥¯©§¨©§€pg[OwÊÅÆÈḬ́¹ËÉÉÊɀʂËîiˆ‘^ig hg"Zÿd Úÿýÿ(+}ÿÅ!­hehfggfh>¡×ÍÐÏÐÐÑÐÐÑ€Ò%ÓÓÔÕÖÖÕé†+Z]„§¯©§¨©©¨¬Žqj]RT¢ÐËǬ}Z±ÏÊËÌÌËÌʾŸ@%¬qeh gi[ ½v Úÿýÿ(+ÿÿJ†‹_fhfeghNbÐÌÌ‚ÍÎÎÍÎ̓ÎÏÑË[ƒ¨¯©§ƒ¨ª›tj]PKpÁ¸”d4CËÍÌ̈Íȸ‡[©`i‚h j=1S Úÿýÿ(+ÿÿ«:¬_hdgedi_BªÑʉˀÊ"ËÊÊÉÊÉÊÆ®©§¨©¨¨©¨¦¦¥¡yi^RBV¤N%]ÛÌˆÏ ÐÏÎÆ®c•Šaj‚ie, Úÿýÿ(*ÿýû*š{_gcfebkDqÐÈÊÉÊ„ÉÈÇÇ€ÆÅÅÃų¦©€¨§¦¤£¡žŸf^PB?U,•ÞÏÒÑÒÒ„ÑÏÁŸ8.±nhjijiijZ Úÿýÿ(*ÿùÿU£[efc€d^CµÌ€ÇÈÇÈÇǀƂŀÄÃÃÂÂÀª§§¦¤¤¢ œ›„d]ND8'$ÂÚÔÕ€ÔÓÔÓÒÓ€Ò Ì¹ƒgœbljjk€jF Úÿýÿ(*ÿûÿæ§mabebcdeD{ÐÄÆ‚ŃÄ&ÃÃÂÂÁÂÁĵ£¤¢¡ž›š™˜••†cZNB7.$ BàØÙÙØØ××‚Ö€Õ ÔÔÓȰP šmjkjld!Úÿýÿ(*ÿüüÿar•ZeadbbgUL¼ÇÄÅŃÄÃÄÃÂÁÂÁÃÃ¥žš˜˜—”‘Ž…`TK@5+#xíÚ€ÜÛÚÚÙØÙØ×€ÖÒÆŒyyhmllklko9 Ûÿýÿ(*ÿüýÿÈ$­acdadbag=ŠÍ‚Ä‚Á&ÂÃÃÉ·”˜–”’Šˆ†€]PG<2*" ²îàáàßßÞÝÜÛÛÚÚ‚ÙØ×Òµ.dxj€m llkoTÜÿýÿ(*ÿüÿþÿ=‰‡ZcdadbdSRÃÄÆÁ*ÃÄÅÆË¡‹ˆ†ƒ€~{\LB80(!(ßëææäãááààßÞÝÝÜÜÛÛ€ÚØÈAXtmnn€mlp_Üÿýÿ()~ÿüÿüÿ«@§]bac`cbb?–ËÀ‚ÂÁÂ…Á5ÂÂÃÄÃÅÍÓ¯‡‰†„~{yvsXH@6-$ ZûëìêéçæåãâááààßßÞÝÝÜÜÝÆ@eqƒnmnjÛÿýÿ()~ÿüÿþÿò!žt_bad`beM]ÆÁÂÂÁÂÁÀÁÁ€Ã0Êε•…~{yvspmkSC;2)!™ÿîðïíëéèçæåäãâááààßßÚêCvƒonnomÚÿýÿ()~ÿüÿÿûÿ‡Y¢Vea`c_c`?¡ÈÀƒÁÀÁ;ÂÂÃÃÈͯ}m~ƒ|yvtqnkhgdK<6.%ÕÿööóñïîìëéèçæååäãâáÞêÓVftppqpp€oqjÙÿýÿ()~ÿüÿÿýÿÞ ¦l]ca`c^fGhÈ¿À‡Á8ÂÂÃÃdzgAZw{wvrolhfda]ZC71)" 9þÿýüùõóñðîîìëéèççåâçðÉcdvqr‚qporiÙÿýÿ()~ÿü€ÿüÿawZaba`c`_?¬ÅÀƒÁ9ÂÁÁÂÂÃÃÄÆ¼¡˜Šxkhijhfc_[WUQ;1,%tÿüÿÿýú÷õôòðïîíìêçéôÜUgxsrqpt^Ùÿýÿ()ÿü€ÿüÿÂ)®]c_ba_bbFqʾÁÀ€Á$ÀÁÁÂÂÃÃÄÄÅÉÐÑÏ®‘s^VUVXVQNG4-' ´ÿü€ÿþüû÷öôôñïìíøèŸ^Zvyt€s rrsoz5Ùÿýÿ(({ÿü€ÿþÿÿ7Œ„Xd_ba_eWG³ÄÀÁÀÁ"ÂÂÃÃÄÄÅÆÇÈÊÌÓÙÝÖãxYE>AGA0)"èÿþ‚ÿýüúù÷óòüó°gVt|w€vuƒtl}^Øÿýÿ*-ÿüÿûÿžC§Zac_aa_h>ɾ‚Á€ÂÃÄÄÅÅÇÈÊÌÎÐÒÕÛäìíÜ»^=$ !Kÿý†ÿ ü÷þýÃqVs€{wx€w€v uvoo˜{ãÿýÿ(,¬ÿýÿ þÿò"žt\`c^€aXI»Â€Á€Â!ÃÃÄÄÅÅÇÈÊËÌÏÒÔ×ÚÝàåïùÿúÜ¢]+ ‰ÿû„ÿ þýþÿ×€Vq‚~|€zyxxwwxtmˆ³x <öÿþÿIN¸ÿý‚ÿûÿx`œXb_c_bab@ˆÊ¿ÁÁ€Â!ÃÃÄÄÅÆÆÉÊËÍÏÒÔ×ÛÞâæéíñúÿþÿê´q5Æÿü‚ÿ"þüþÿåZn…ƒ}~~{|{zyyzxo}­ž9q÷ÿþšÿýÿÞ «e`a_c^bdRQÁÂÁ€ÃÄÅÅÆÇÈÉËÌÎÐÒÕØÛÞâæêîòöûüý€ÿúÔõƒÿ ýþÿìœbp†Š„€‚}€{ sv£­Y+ŸÿýÿTw“Td`_c_ca@“ÉÀÂÂÀÄÅÅÆÇÈÊËÍÎÑÓÖØÛÞáæéíñöûÿÿþüûÿÿþÿýþÿó¨jw’‘‹‰‡ƒ†€€€~wtš·z€3ŸÿüÿÂ.©a^c``c^gK\Å€ÂÃÄÄÅÅÆÇÈÉÊÌÎÏÑÔÖØÛßáåéíðõúþÿþý€ÿ#ýþÿ÷³u}ž£“‘’ˆŠ‡ƒƒ‚|t·”-4¢ÿ-7’~\_c``c_e; ÊÁÃÃÄÄÅÅÆÇÈÉËÌÎÐÒÓÖÙÛßâäèìïóöû„ÿ%ýýÿû¾€ˆ­¶§œœ›”’‡ˆ†ƒy‰³¦E £i7 ÿûÿžE©Wd^c`acbMbÊ€ÄÅÅÆÇÈÊËÌÍÏÑÒÔ×ÚÜßâåèëïóøý‚ÿ'ýýÿÿȇ»Í¾°©§¤ž—˜•ŽŽŠˆ¬µ` `ãÿj7 ÿ\þÿò" s]c_d`bd]A¨ÉÃÄÅÅÆÆÈÉÊËÌÍÏÑÓÕ×ÙÜßâäèëîñõùýþÿýýÿÿÓƒt¤ÇÇÅÁ¸³°¨¡¡––Œ‰ª¾{1Äÿùÿj7¡ÿ[ûÿx_ŸUcc_d_bgFmÍÂÅÅÆÆÇÈÉÊËÍÎÐÒÓÕØÙÜßâäçêíðôöúüýþÿ݉gޝ²µ¾ÃÆÅ½³¬«¥ š¦Ç“*Ÿÿÿüùÿk7¡ÿ[ýÿÞ §j]cb_e_f[E±ÈÄÅÅÇÈÈÊËÌÍÎÐÒÓÕØÚÜßáäæéíïòõöýÿæ‘_|¢£¨«´ÁÇÈüµ°¨Ÿ¬É«A xöÿýüÿûÿk7¢ÿGýÿT|Œ\`ca`e_j@yÎÃÆÆÇÈÉËÌÌÍÏÑÒÔÖØÚÜßáäçéìîð÷ÿì›\lŽ•”˜› ©±¹ÂÇÈÆ½¯µÎ¾\€Qâÿýü€ÿûÿk7¢ÿEüÿÂ,®[e_daaea]DºÈÆÇÈÉÊËÌÌÎÏÑÒÔÖØÚÜßáãæèéïþë \cƒ‹‰‹Œ‘™ §¬±ºÆËÄÅØÒu €;Îÿþüþÿûÿk7¥ÿL7ƒYe_eabecCÐÅÇÈÉÊËÌÍÏÐÑÓÔ×ÙÚÜßáääè÷ë¦_\z„ƒ‡’–¡¦¯¸ºÃÝæ“­ÿÿüýƒÿûÿk7£ÿLûÿžF©Ycd_f`cfUKÀÈÇÉÊÊËÌÍÏÑÒÓÔ×ÙÚÝßßãñé¬bWs€}}~„‡‹“˜Ÿ¦¨«Æå«0ôÿýü…ÿûÿk7£ÿJþÿò"žw[dc`f`ed@‹ÒÆÉÊËÌÍÎÏÑÓÔÕØÙÛÛÞêè²gTn}{y{}€ƒ†‰Œ“™›œ®×¶Dläÿýü‡ÿûÿk7¤ÿûÿxc›]aebbf_kOTÆÉÉËËÌÍÎÐÑÓÔÕ€Øäæ¶kRizywxz{|~€…‰‘œÉ¿Y€OÍÿýûþˆÿûÿk7¤ÿFýÿÞ ­ed_fbce`j9—ÒÈËÌÌÎÏÑÒÒÔÕÕßä»qPdwwtuxxy{|}…‰†Œ¸Âp1¶ÿþûýŠÿûÿk7¥ÿCýÿTy“Yf`f`eefRSÌËÊÌÍÎÏÐÑÑÒÛâ¿wO_tvrruvvxyz|}ƒ€§ÁƒŸÿÿüüŒÿûÿk7¥ÿAüÿÂ,¯^ddagagdf>uÕÑËÌÌÍÐÕÜßÃO\ptppsttvwwyz{|w˜½–0 ‡öÿýüŽÿûÿk7¨ÿ<7ˆXgbbgahda9i¶Ð×ÙÚ×ɪ{PXmsooqrrtuuwxx|{s‹¶¤Dläÿýüÿþÿé×á¦ÿ1ûÿžG¦`agaefbica<>?J\jpml€nqqpstswxow¢´j 1¶ÿþûý•ÿþýý§ÿûÿx]¢[fchaidgefjkjk€mkjlmmnooqrqtvoq˜¶}ŸÿÿüüÄÿ6ýÿÞ ­ifcegbjbggfhihijiklknnmoppruol޵Ž& ‡öÿýüÇÿ3ýÿTpVjaheegfghghjijkjkmlnonpsoiƒ°œ7läÿýüÉÿ&üÿÂ%­gbiaicfhfhihijijlklnmoqnhz¨§K€OÍÿþüþÍÿ-7†[ggcgffhghjhikjkmkmpngrž¯_ 1ÄÿþûýÍÿ-üÿ«8±fbhdfhggihhkijlkknngl’³s­ÿÿüüÏÿ+þÿò"–”^dgffhggjiikjjmmhg‡³…!ôÿýüÒÿ(ûÿ#Ä|\hhgfihhjiilmhd}¯”1 oíÿýüÔÿ'þÿæKÏ[diihjjillict¨ CQâÿýûþÖÿüÿoNͤm\`cdfdaanŸ¨V€IÎÿþûþØÿ"ýÿÞ *¡Ë³’{yƒ’©®h2°þÿüýÛÿüÿ‡3|§µ¸´¬™l“ýÿüüÞÿþÿ@€ € wöÿýûàÿ þÿï5€måÿýûãÿþÿ÷uƒOÍÿþüþÔÿüÿ¿IX‰USWe0‡ +caQS—UXI¿ÿüžÿ·ÿûÿ`ŸÿûÎÿýŽüøÿ^žÿûàÿûÿ_ŸÿûÌÿýÿ³~ˆ…†‡‡†…€„ƒz&@Îÿüÿ~#4…1/.18CP?•ªÿüËÿüÿˆ4D@?@AA??BJUakqt}N“JÿýËÿüÿ†/@AEA<@HS_gloruy|‹;’æÿþÊÿýÿÇ¡¥“vWQ^fjmpsw|€„‡ŽŠ’¨ÿûÊÿþÿ¼†p\Yahkmqvz~‚†Š“•¥n’wÿû´ÿü’ûúÁNXeilotx|€„ˆ‘•™ž¢¤²?‘`ÿûÈÿþÿá“Ò¼llsvz~‚†ŠŽ“—› ¤¨¬°º¢ =ÿþ²ÿýÿØ ŽŸž£T•˾u|€„ˆŒ‘•™ž¢¦ª¯³·»¼Ð['´ÿûÿ`€É±‹Ž“—› ¤¨¬±µ¹½ÁÅÇÓ³)´ÿûÿbƒÊ¦šž¢¦ª®³·»¿ÃÇËÏÒÔãEXÿü²ÿûÿ`’šÌ¡£¨¬±µ¹½ÁÅÉÍÐÔØÛÛï”ÿû²ÿûÿ`“>¼Æ©´·»¿ÃÇËÏÒÖÙÝàãåîÔ7÷ÿþ²ÿûÿ`“ p×½»ÁÅÉÍÐÔ×ÛÞáåçêíîúCŒ5îÿþ³ÿûÿ`”(®ÛÃÌÎÒÖÙÝàãæéìîñóñÿ„Œ ÿú´ÿûÿ`”bÞÓÓ×ÛÞáåçêíïòôöø÷ÿ¿Œžÿû´ÿûÿ`—'²éÙàãæéìîñóõ÷ùúûûÿÿû´ÿûÿ`• mìåçêíïòôöøùûüüþÿÿû:‹žÿø‚üþ®ÿûÿ`—3Éõìñóõ÷ùúûüýÿÿþêǶM‹Ÿÿû´ÿûÿ`–ŽûòöøùûüüþÿÿöÚ¹¢˜£f‹r³€¶·±äÿþ«ÿþH#˜SéüùûüýÿÿþëÊ­šš—™x€‚‚€ ¥ÿû¬ÿ'› (¾ÿúþÿÿ÷Ûº£€š –’Ž‚† ¤ÿûªÿôì7 ™(ˆÿþþëÊ­šš˜”‘Їƒ„!8 "' ¥ÿû¨ÿýÿ¡_ko ™&Uë๣›šš–’Œ‰…‚z€13CE?T]aa]WG*  ¤ÿû¨ÿüÿw'%š&/Á±•›˜”‘Ї„}{wswI,EA8;Kbjkjihihg_G € ¤ÿû¨ÿüÿx)]W˜Ä”Œ‰…‚|yvspmmX<9AOckj€h€gffgeQ¥ÿû¨ÿ üÿt#u²Š —0 tЊ‹‡„~{wtqoliffUBUhnliihihhggffeffL ¡ÿû¨ÿ üÿ‡w½°‘)™MÍ“€|yvspmjheb`^Xepljjijj€ihggffefd=235$²ÿü§ÿþÿÙ˜Á­¡¤Sš2º§rytqolifda_\ZXQblj€ke`aa^`gg€fefatÿûªÿ þÿè”Ê·­µÂˆ—,"œ½krmjheb`][YVTSN\nkkecy™¢§¥‹cchffeegS ¼ÿü©ÿ þÿÚ¢àÀÈÐÞÁ=–%|Ìphgda_\ZXUSQONHZnde~ž„R<:U’´pbgf€eg8+íÿþ©ÿ þÿŸÖãâëóñ€ —, +bË„[a][YWTRPOMKJGOkƒŸ<  R³jdfeeded]ÿü©ÿ ýÿÑ­ÿõüþÿÏ9•/(>;Qº¡Q\XUSQONLJIHGAV¡x3 #>LLC/Y¦]geeddeX ¬ÿüÿü‡ûþÿ þÿžëÿÿòÏ¡O ’)7N=›áÑÒÒÑÏÎÍÍÌȽŠ%2«c€dc^ÿüŠÿ2… Úÿý‹ÿüÿˆ>SC1>f_`_\`lj-FYbba^\[VJFe‹ÃR>CB$CDEGIMDˆÞÑÓÒÐÏÎÍÌËÊŲh lšZfddbcdW ¬ÿü‰ÿ2… Úÿýÿïà+L5¥ÿûƒÿ?þדr†vfgda^]\YOH`€¾s8DBBCEFHJLOSLyÜÒÒÑÐÏÎÍÌËËÉÁžBw_dcbcce=éÿþˆÿ2… Úÿýÿ)L- J¢ÿ÷ùùýÿï¤lf‚ ¤–lfeca_[XQK[~«—4EDEGIKMPSUYXmÔÓÒÑÐÏÍÌÌËËÊÆ¹ƒ>ª_dbccbcb^ÿüˆÿ2… ÚÿýŽÿþ)J- K¤ÿúþÿã–jp•­¬£Ÿ¡Šghe`\cwnLX”²=HHJLNQTWZ]achÉÖÒÑÐÏÎÌÌËËÉÈ­a y“YdbccbdV ­ÿü‡ÿ2… Úÿýÿ)H, B¥ÿþΆdsœ¯­¨¦¦¤ ¢}cdm|zIHY~¿THNPSUX\_bfini»ØÑÐÏÎÍÌËËÊÉÈÆ½™:¤o_dcd<éÿþ†ÿ2… Úÿýÿ)H,€ ¡Êt_tœ¨€¦&§©¨§¥ž˜yE >Z}x¸uIUWZ]adgkorxp¬ÚÐÐÏÎ€Ì ËÊÉÈÈĵ}K§\dbca]ÿü†ÿ2… ÚÿýÿN)F,  L`u—žœž ¡¥§¨ª¥¢¶É“;'B]X{w¤–M^_bfimptx{zžÚÐÐÏÎÍËËÊÊÉÈÇÆ¿¨Y †‹Zdc€bdU ­ÿü…ÿ2… ÚÿýÿN)E+  'Gw‹—¯²±°µ£w{r²†qz}„ˆŒ’–˜žÄÔÏÎÌÌËÊÉÈÇ€Æ ÅĽ¤R ‘ƒZdbcR»ÿüƒÿ2… Úÿýÿ)C+T~€€„‡ˆ„°¿‰4 *M~¶å÷¿­³²)vo¢¡w„†Š‘”—š ¦—·ÖÎÍÌÌËÊÉÈÇÇÆÅÅÄ·+,«da‚bd6.öÿþ‚ÿ2… ÚÿýÿE)H%Qy}|}~‚}‰«µ€+.T‡Àéûýÿô·°³²±¶rr¶’–™œŸ¡¤¦« «ÕÍÍÌËÊÉÈÇÆÆÅŀĿ¯qfXd€babarÿû‚ÿ2… Úÿýÿ:(+ 2w|zz|}}~y£­t$3[’Èîûýþÿþÿë°²²±µ_otz¿”—š £¥§©«®©¤ÚÍ€ËÉÈÇÇÆ€Å€Äû Kš{\cbdR »ÿüÿ2… Úÿýÿ'c~vyzz{}{yž} 8eœÐñûýþÿ"þÿß­²¸šhvl¸£™Ÿ¡¤¦¨ª¬­®¯³‘®ÓÓËÊÉÈÇÆ€Å‚ÄÀ´‰$7«`ƒb d5/öÿþþÿÿ3€ Úÿýÿ' }uxywzzyxŠi=c£Ùóûüþ„ÿ#þÿÓ©À_Syg¥µž¦§©«­®¯¯°®¸‰rËÉÈÇÆÆÅÅ‚Ä Ãþ«j s—Xdbc`rÿû€ÿ*€  Úÿýÿ'8zuvvxwxyzv U‡Ñôûüþ‡ÿþÿÈ«!2xfÁ£«¬­®¯° ®²¡,l»ËÉÈÇÆÆÅÅ‚Ä ÃùœC¡t]cbdP ¼ÿüÿÿQ+42215 Úÿýÿ';ƒqvvuwwu€LS—èüüýŠÿ ýÿnjyꮯ¯€°¯¯®­®¢©àÏÉÈÇÇÅłĂÿ²„Eª\cbcd15ÿþ„ÿûÿh Úÿýÿ'<œotuvtvuxI*‚áûúûûþ‰ÿ üÿU[oi¼²¯€°¯¯®­«ª©¤šËÊÉÈÇÆÅÅăü¨c €Ydbc_‚ÿûƒÿûÿh Úÿýÿ'­wrtsuts{G9®öôôõ÷ùüü†ÿýÿÝ Asaª»®°¯®­¬«©¨¦¤¤‘ÃÌÈÈÆÅ‚ÄÃÂÂÃÂÁ·˜<§m_cbbcbdMÎÿý‚ÿûÿh Úÿýÿ'}œitsrtswU<¾öîðóóõöùýþ„ÿûÿ‡%p_”À«®­«ª©§¥£¡žŸµÎÇÆÅÅăÀÿ°R¦[dccbbcd.<ÿþ‚ÿûÿh Úÿýÿ'1²nrqsqrwc<°óëîïðñóööùûýþ€ÿþÿú,db}¾©«©¨¦¤¢ ž›™™Š¢ÎÅÆÅ€ÄÃÂÂû¦] Œ‡Zd€cbd^‚ÿûÿûÿh Úÿýÿ='–Škqprqqr?ˆëêêìíîðñòõöùþþÿÿüÿµRgjµ¨§¥£¡Ÿš˜•’ЉËÅÅ€ÄÃÂÂÃÂ÷•4'«ha‚ceKÏÿý€ÿûÿh Úÿýÿ>'J®gpqoqpsUZÒëçééììíîðò÷÷ûýþüÿW@-1l^¦¨¡ ž›™–”‘ŽŒˆ‹h ÎÃĀÀÃÂ…þ¯z_¢Ze‚ce+=ÿþ€ÿûÿh Úÿýÿ=' ¥}knponsiE¢êäæçèéêìíïðòôöøÿß J¹ÿÊ$eY‘¨›š˜•’Ї„oTÅÄÃÂÃÃÂÂƒÃ€Ä Ã»¤U–\edc€d^˜ÿûÿÿûÿh Úÿýÿ?'h¢dpmonmtLjÛäâäææçèêëìîïñðÿxiÒÿÿûÿ‰EUv¤””‘ŽŒ‰†ƒ€}zvy>‚ÎÀÄÂÃĵ‘-2¬dc‚dfHÞÿýÿûÿh Úÿýÿ@(* ®oknlmnngC³æßáâãäåæçééëíðð™Øÿþüüþÿô`E]šŠ‡„~{xurnpU;µÇÁÃÂ€Ã„Ä ÅÄÅ¿­umZfdef'Iÿýÿûÿh Úÿýÿ?(>‚“dlmklmnIußÞÞßààáâãäåçèêêúøïôùüÿüÿ¦5HˆŠ†ƒ€}zwtqnkig^5kÊÁ€ÂƒÃÄÅÄ€ÅÄ»¡NŸ{_fdeedf]˜ÿûûÿh Úÿýÿ@(:65¯emjlkip]I½àÚÜÝÝÞßàáâãäæçæéìïðôúþÿóN/q†}{xurolihcZYa[;žÊÀ‚Ã‚Ä€Å ÆÆÅÆÆÃµŽ'>­bƒe hEßÿúÿh Úÿýÿ?(,B˜„cmijkioA„ÞØÙÚÚÛÜÜÝÞßàáâäåçèêíñõ÷ÿ£V€vtqnkhgaW]yzulMYÁÃĄůÇÀ®o{—\g€fefg$Jÿùÿh ÚÿýÿA((›P¨aijhijkZNÈÙÖ×רØÙÚÚÛÜÜÞßàáâåæéëî÷ò82wolige_V]~¢°ŒumbF‡ËÂÄÅÄÅ€ÆÇ€È Éǽ¡G§ubgf g] §ÿÿh Úÿýÿ()…ñ ¦tfgjhhjhA’ÜÒ€Ô1ÕÕÖÖ××ÙÙÚÚÛÝÞßáãæåü”jied^T]€¤¯©ªœyodWS²ÉÃÅÅÄÅËÉÆÇÈÉÈÉÆ·Š L¬`gfg iB"åÿg Úÿýÿ(+|ÿlkœ]jfhhenPYÍÓÑ€ÒÓ-ÔÕÕÖÖ×רÙÛÜÞßãâ-Mh[S^‚¥¯©§¨©§€pg[OwÊÆÆÈḬ́¹ËÈɃʀËÊîi‡‘^i€g fhg!Zÿd Úÿýÿ(*}ÿÅ ­hehfghfh= ×΃ЂрÒ%ÓÓÔÔÖÖÕé†+Z]„§¯©§¨©©¨¬Žqj]RT¢ÐËǬ}Z±ÐÉËÌÌËË€Ìʾ @$¬qehhghgi[ ½v Úÿýÿ(*ÿÿJ…‹_fgeeghNbÑÌÍÎÍÎ̓ΠÏÐÏÐÒË[ƒ¨¯©§ƒ¨ª›tj]PKpÁ¸”d3CÌÍÌÍÌ…Í ÎÍɸ‡Z©`i‚h j<1S Úÿýÿ(*ÿÿ«:¬_hdgfdh_BªÑʇË'ÊËËÊÊËÊÊÉÊÈËÆ®©§¨©¨¨©¨§¦¥¡yi^RBV¤N%]ÛÌŠÏÎÅ®c”Šaji je* Úÿýÿ(*ÿýû*š|_gcfebkDqЀÉÊ‚ÉÈÉÈǀƀÅÄų¦©€¨§¦¤£¡žŸf^PB?U,•ÝЃÒÑÒƒÑÎÁž8-±nhjijjijY Úÿýÿ()ÿùÿT£[efcded^BµÌ€ÇȀǂÆÅÄĀÿª§§¦¤¤¢ œ›„d\ND8'$ÂÚÔÕ‚ÔÓ ÒÓÒÓÒ͹ƒg›blƒjE Úÿýÿ()ÿûÿæ¦macebcdeC{ÐÅÆ‚ŃÄÃÀÁĵ£¤¢¡ž›š™˜••†cZMB7.$ BàÙÙ€Ø××ÖÖƒÕ ÔÔÓȰP šmjkjld Úÿýÿ()ÿüüÿaq•ZfaebbgUK¼ÇÄ€ÅÄÃÃÄÃÃÂÃÁÃÃ¥žš˜˜–”‘Ž…`TK@5+#xíÛÝÜÛÚÚ€ÙØ€×ÖÖÓÇŒyyillkklko8 Ûÿýÿ()ÿüýÿÈ$­acdadbbg=ŠÍƒÄÃÂÁ€Á$ÂÃÃÉ·”—–”’Šˆ…€]PG<2)# ±îàáààÞÞÝÜÛÛ€Ú‚ÙØÒµ-cwjmlmllkoTÛÿýÿ()ÿüÿþÿ=ˆ‡ZcdadadSQÃĂÀÂÁ„Á&ÂÃÄÅÆË¢‹ˆ†ƒ€}{\LB80'!(ßëææäãâáààÞÞÝ€ÜÛÚØÈAWtlnmlp_Üÿýÿ()~ÿüÿüÿ«@¦]bbd`cbb>–ËÀ„Â…Á5ÂÂÃÄÃÅÍÓ¯‡‰†ƒ€~zxusWG@6,$ ZûëëêéçæåããâáààßßÞÞÝÝÜÞÆ@eqƒnmnjÛÿýÿ((~ÿüÿþÿò!t_b`d`beL\ÆÁ„ÁÀÁ€Â4ÃÃÊ͵•…~{yvsplkRB;1)!™ÿîðïíëêèçæåäãââáààßßÛêœBvoponnomÚÿýÿ ((~ÿüÿÿûÿ‡Y¡Vea`c_c`?¡ÈÀÁÁÂÁÀ…Á6ÂÃÃÈͯ}n~‚{yvtqmjhgdJ<6-%ÕÿööôñïíìëéèçææåäãâáÞêÔVgtq‚poopjÙÿýÿ((~ÿüÿÿýÿÞ ¥k]ca`c_gGhÈ¿ÁÁÀ‚ÁÀÁÁ€Â5ÃdzgAZw{wurokhfda]ZC70)" 9þÿýýùõóñðîíìëéèççåâçðÈbcvqr€q ppqpriÙÿýÿ((~ÿü€ÿüÿawZ`ba_c`_?¬ÅÀ€ÁÀ‚Á6ÂÂÃÄÄÆ¼¡˜Šxkhiihfb_[WUQ:1,$sÿüÿÿýú÷öõòðïîíìêçéôÜUgxs€r qqpt\Ùÿýÿ((ÿü€ÿüÿÂ(­]c_ba_bbFqʾ€ÁÀÁÀÁÁÂÂÃÃÄÄÅÈÑÑÏ®‘r^€UWVQMG4-& ´ÿü€ÿþüû÷öõóñïìíøéŸ^Zvyts rsoz4Ùÿýÿ(({ÿü€ÿþÿÿ7Œ„Xd_ba_eWG³ÄÀƒÁ€ÂÃÄÄÅÆÇÈÊÌÓÙÝÖãwYD>AFA0)"èÿþ‚ÿýüûù÷óñüô°gVu|w€vuu‚tl}^Øÿýÿ*,ÿüÿûÿžC§Z`c^ab_h>~ʾ‚Á€ÃÄÅÅÇÈÊÌÍÐÒÕÜäìíÜ»^=$ !Jÿý„ÿþÿüøþýÃqVs€{xxwxw€v uvpo˜zãÿýÿ(+¬ÿýÿþÿò"žt\`c_baaXI»ÂÁÂÂÃÀÄÅÆÈÉËÌÏÑÓ×ÛÝàåïùÿúÜ¢]+  ‰ÿû„ÿ þýþÿ×€Vq‚}{zzyyxxwwxtm‰³w <öÿþÿIN¸ÿý‚ÿûÿx_Xb_c_bab@ˆÊ¿ÁÁ€Â!ÃÃÄÄÅÆÆÈÊËÍÏÒÔØÛÞâæêíðùÿþÿê´q4Æÿü‚ÿ þüþÿåYn…ƒ€~{|{{€yxo}­ž8q÷ÿþšÿýÿÞ «e`a_c^bdRQ€Á€ÂÃÄÄÅÅÆÇÇÉËÌÎÐÓÖØÛÞãæêîòöûüý€ÿúÔõƒÿýþÿìœbp‡Šƒ€‚}~}|€{ sv¤­Y+ŸÿýÿTv“Td`_d_ca?“ÉÀÁÂÀÄÅÅÆÇÈÊËÍÎÑÓÕØÛÞáåêíñöüÿÿþüûÿÿþÿýþÿò§iw’‘Œ‰†ƒ†€€€~wtš·y€3ŸÿüÿÂ-©a^c``c^gK\ÅÁÂÀÄÅÅÆÇÈÉËÌÍÏÑÓÖÙÜßáæéíðõúþÿþý€ÿ#ýþÿ÷³u~ž£“‘’ˆŠ‡ƒƒ‚|t¶“-4¢ÿ-7‘\_c``c_e;ŸÉÁÃÃÄÄÅÅÆÇÈÊËÌÎÏÒÓÖÙÛÞâåèëïòöû„ÿ%ýýÿû½€ˆ­·§œœ›•’‡‰†ƒyˆ³¦D £i7 ÿûÿžD©Wd^d`acbMaÊÁ€ÄÅÅÆÇÈÉÊÌÍÏÑÒÔ×ÙÜßáåèëïóøý‚ÿýýÿÿLJºÌ¾°©§¤ž—˜•ŽŽŠˆ¬µ_ €`ãÿj7 ÿþÿò" s]c_d`bd]A¨ÉÃÄ€ÅCÆÈÉÉËÌÍÏÑÓÔØÙÜßáåèêîñõùýÿÿýýÿÿÒƒs¥ÇÈÆÂ¸´¯¨¡¡––‹‰«¾z1Äÿùÿj7¡ÿ[ûÿx_ Ucb_d`bgFlÍÂÅÅÆÆÇÈÉÊËÍÎÐÑÓÕØÙÜßáåçêíðôöúüýþÿ݉gޝ²µ½ÃÆÄ½³¬ª¥ š¦Ç“)Ÿÿÿüùÿk7¡ÿ[ýÿÞ §j]cb_e_f[D±ÈÄÅÆÇÇÉÉËÌÍÎÐÒÓÕØÚÜßáäçéíïòôöýÿæ‘_{ž¢£¨«´ÁÇÈû¶°¨ž¬ÈªA xöÿýüÿûÿk7¢ÿGýÿT|Œ[`cb`e_j?yÎÃÆÆÇÈÉÊÌÌÎÏÑÒÔÖØÚÜßáäæéìîï÷ÿëš\m•”˜› ©±¹ÃÇÈÆ½¯µÎ¾[€Qâÿýü€ÿûÿk7¢ÿEüÿÂ,¯\e_daada]DºÈÅÇÈÈÉÊÌÌÎÏÒÒÔÖÙÚÜßáãæèéïþê \cƒ‹ˆŠŒ‘™ §­²ºÆËÄÅØÒt €;Îÿþüþÿûÿk7¥ÿL7‚Yf_eabdcBÐÅÇÉÉÊËÌÍÏÐÒÓÕÖÙÚÝßáãäè÷ë¦^\z…ƒ‡Œ‘–¡¦¯¸»ÃÝæ’­ÿÿüýƒÿûÿk7£ÿLûÿžE¨Xcd_f`cgUKÀÈÇÉÉËËÌÍÏÐÒÓÕ×ÙÚÝßßâñé¬bWs€}|~„‡‹“˜Ÿ¦¨¬Æåª/ôÿýü…ÿûÿk7£ÿJþÿò"žw[dc`f`ee@‹ÑÇÊÊËÌÍÎÐÑÒÓÕ×ÙÛÛÞêè±fTn}{y{}€ƒ†‰’™›œ¯×¶Dläÿýü‡ÿûÿk7¤ÿ<ûÿxbš\aebbf_kNSÆÉÉÊÌÌÍÏÐÑÒÔÕ×ר俶kRizxvxz{|~€…‰‘œÉ¾Y€OÍÿýûþˆÿûÿk7¤ÿFýÿÞ ­ed_fbce`j9—ÒÈËÌÌÍÏÑÑÓÔÕÖßä»qPdwvtuxxy{||‚…‰†Œ¸Âo1¶ÿþûýŠÿûÿk7¥ÿCýÿTx“Xf`f`edfRRÌËÉÌÌÎÏÐÑÑÒÛâ¿vO_tvrsuvvxyz|~ƒ€§ÀƒŸÿÿüüŒÿûÿk7¥ÿAüÿÂ+¯^dd`gagdf=uÖÑËÌÌÍÐÕÜßÃ~O[ptpqsttvwwy{{{w˜¼•/ ‡öÿýüŽÿûÿk7¨ÿ<7ŒˆXgbbgahdb8h¶Ð×ÙÛ×ȪzPXmsonqrrtuuwxx|zs‹µ£Cläÿýüÿþÿé×á¦ÿ1ûÿžF¦``gaefbidb<>?J[jpml€npqpstswxox£´i 1¶ÿþûý•ÿþýý§ÿûÿx\¢[fbhaicgefjkjkmmnkj€mnooqrruvoq˜¶|ŸÿÿüüÄÿýÿÞ ­iecegbibggfhihijiklkmnm€pruol޵% ‡öÿýüÇÿ3ýÿToVjaheegfghghjijkjkmlnonpsni„°œ7läÿýüÉÿ&üÿÂ$­gbiaicghfgihijijlklnmnqnhz¨¦J€OÍÿþüþÍÿ-7†[ggcgffhghjhikjkmlmpngrž®^ 1ÄÿþûýÍÿ-üÿ«7±fbidfhggihhkijlkknmgl’³r­ÿÿüüÏÿ+þÿò"•”^dgffhggjihkjjmmhg‡³„ ôÿýüÒÿ(ûÿ"Ä|]hhgfihhjiillhd}¯”0 oíÿýüÔÿ'þÿæKÏ\djihjjillics¨ BQâÿýûþÖÿüÿoMͤl\`ccfdaanŸ¨U€IÎÿþûþØÿ"ýÿÞ *¡Ë³’{yƒ’©­g2°þÿüýÛÿüÿ‡2|§µ¸³¬˜l“ýÿüüÞÿþÿ@€ € wöÿýûàÿ þÿï5 måÿýûãÿþÿ÷uƒOÍÿþüþÔÿüÿ¿GV‰SQUb.‡ )`_OQ—SVG¿ÿüžÿ·ÿûÿ`ŸÿûÎÿýŽüøÿ^žÿûàÿûÿ_ŸÿûÌÿýÿ³~ˆ…†‡‡†…€„ƒz&@Îÿüÿ~#4…1/.18CP?•ªÿüËÿüÿˆ4D@?@AA??BJUakqt}N“JÿýËÿüÿ†/@AEA<@HS_gloruy|‹;’æÿþÊÿýÿÇ¡¥“vWQ^fjmpsw|€„‡ŽŠ’¨ÿûÊÿþÿ¼†p\Yahkmqvz~‚†Š“•¥n’wÿû´ÿü’ûúÁNXeilotx|€„ˆ‘•™ž¢¤²?‘`ÿûÈÿþÿá“Ò¼llsvz~‚†ŠŽ“—› ¤¨¬°º¢ =ÿþ²ÿýÿØ ŽŸž£T•˾u|€„ˆŒ‘•™ž¢¦ª¯³·»¼Ð['´ÿûÿ`€É±‹Ž“—› ¤¨¬±µ¹½ÁÅÇÓ³)´ÿûÿbƒÊ¦šž¢¦ª®³·»¿ÃÇËÏÒÔãEXÿü²ÿûÿ`’šÌ¡£¨¬±µ¹½ÁÅÉÍÐÔØÛÛï”ÿû²ÿûÿ`“>¼Æ©´·»¿ÃÇËÏÒÖÙÝàãåîÔ7÷ÿþ²ÿûÿ`“ p×½»ÁÅÉÍÐÔ×ÛÞáåçêíîúCŒ5îÿþ³ÿûÿ`”(®ÛÃÌÎÒÖÙÝàãæéìîñóñÿ„Œ ÿú´ÿûÿ`”bÞÓÓ×ÛÞáåçêíïòôöø÷ÿ¿Œžÿû´ÿûÿ`—'²éÙàãæéìîñóõ÷ùúûûÿÿû´ÿûÿ`• mìåçêíïòôöøùûüüþÿÿû:‹žÿø‚üþ®ÿûÿ`—3Éõìñóõ÷ùúûüýÿÿþêǶM‹Ÿÿû´ÿûÿ`–ŽûòöøùûüüþÿÿöÚ¹¢˜£f‹r³€¶·±äÿþ«ÿþH#˜SéüùûüýÿÿþëÊ­šš—™x€‚‚€ ¥ÿû¬ÿ'› (¾ÿúþÿÿ÷Ûº£€š –’Ž‚† ¤ÿûªÿôì7 ™(ˆÿþþëÊ­šš˜”‘Їƒ„!8 "' ¥ÿû¨ÿýÿ¡_ko ™&Uë๣›šš–’Œ‰…‚z€13CE>T]aa]WG*  ¤ÿû¨ÿüÿw'%š&/Á±•›˜”‘Ї„}{wswI,EA8;Kcjkjihihg_G € ¤ÿû¨ÿüÿx)]W˜Ä”Œ‰…‚|yvspmmX<9AOckjhhgffgeP¥ÿû¨ÿ üÿt#u²Š — tЊ‹‡„~{wtqoliffUBUhmlihh€g feffL ¡ÿû¨ÿ üÿ‡w½°‘)™MÍ“€|yvspmjheb`^Xdpljjijj€ihg€f efd=235$²ÿü§ÿþÿÙ˜Á­¡¤Sš2º§rytqolifda_\ZXQbmke`aa^`gg€fefatÿûªÿ þÿè”Ê·­µÂˆ—,"œ½krmjheb`][YVTSN]nkkecy𢧥‹cchffedfR ¼ÿü©ÿ þÿÚ¢àÀÈÐÞÁ=–$|Ìphgda_\ZXUSQONHZnde~ž„R<:U’´qbgeg8,íÿþ©ÿ þÿŸÖãâëóñ€ —% +bË„[a][YWTRPOMKJGOkƒŸ€<  R³jdfee€d]ÿü©ÿ ýÿÑ­ÿõüþÿÏ9•/(>;Qº¡Q\XUSQONLJIHGAV¡x3 #>LLC/Y¦]geeddeX ¬ÿüÿü‡ûþÿ þÿžëÿÿòÏ¡O ’)7N=›áÑÒÒÐÏÎÍÌÌȽ‰%1«c€dc^ÿüŠÿ2… Úÿý‹ÿüÿˆ>SC1>f_`_\`lj-FYbb`^\[VJFe‹ÃR>CB$CDEGIMDˆÞÑÒÒÐÏÎÍÌÌËŲh lšZfcdbcdV ¬ÿü‰ÿ2… Úÿýÿïà+K5¥ÿûƒÿ?þדr…vfgd`_]\YOH`€¾s8DBBCEFHJLOSLyÛÒÓÑÑÏÎÍÌËËÉÁžBw_dcbcce=éÿþˆÿ2… Úÿýÿ)L- J¢ÿ÷ùùýÿï¤lf‚ ¤–lfeca_ZWQK[~«—4EDEGIKMPSUYXnÔÓÒÑÐÏÎÌÌËËÊÆ¹ƒ>ª_dbccbcb^ÿüˆÿ2… ÚÿýŽÿþ)J- A¤ÿúþÿã—jp”­¬£Ÿ¡Šghe`\cwnLX”²=HHJLNQTWZ]achÉÖÒÑÐÎÍÌÌËËÉÈía y“Yd€cbdV ­ÿü‡ÿ2… Úÿýÿ)H, L¥ÿþΆds¯­§¦¦¥ ¡~ddm|zIHY~¿THNPSUX\_bfini»ÙÑÑÏÎÍÌËËÊÉÈÆ½™:¤o_dcbbcd<éÿþ†ÿ2… Úÿýÿ)H,€ ¡Êt_tœ¨€¦@¨©¨¦¥ž˜yE >Z}x¸uIUWZ]adgkorxp¬ÚÑÐÏÎÍÌËËÊÉÉÈĵ}K§\cbbcbcb]ÿü†ÿ2… ÚÿýÿW)F,  L`t˜žŸ ¡¥§¨ª¥¢¶É“;'B]X{w¤–M^_bfimptx{zžÚÐÐÏÍÍËËÊÊÈÈÇÆ¿¨Z †‹ZdbbcbdU ­ÿü…ÿ2… ÚÿýÿN)E+  'Gw‹—¯²±°µ£w{r²†qz}„ˆŒ’–˜žÄÔÏÎÌÌËÊÈÈÇÇÆÆÅĽ¤R ‘ƒ[dbcS»ÿüƒÿ2… Úÿýÿ )C,T~~€„‡‡„°À‰4 *M~¶å÷¿­³²)vo¢¡w„†Š‘”—š ¦—¸ÖÎÍÌËËÊÈÈÇÆÆÅÅÄ·+,«da‚bd6.öÿþ‚ÿ2… ÚÿýÿC)H%Pz}|}€‚|‰¬µ€+.T‡Àéûýÿô·°³²±¶rr¶’–™œŸ¡¤¦« «ÖÍÍÌËÊÉÈÈÇÆÅÄ¿®qfXdbc`rÿû‚ÿ2… Úÿýÿ:(+ 2v|zz|}}~y£­t$3[’Èîûýþÿþÿë°²²±µ_otz¿”—š £¥§©«®©¥ÚÍ€ËÉÈÇÇÆ€Å€Äú Kš{\c€bacR »ÿüÿ2… Úÿýÿ'c~vyzz{|{yž} 8eœÐñûýþÿ"þÿß­²¸šhvl¸£™Ÿ¡¤¦¨ª¬­®¯³‘®ÔÓËÊÈÈÇÆÅÄÀ´‰$7«`ƒb d5/öÿþþÿÿ3€ Úÿýÿ' }uxxwzzyy‰i=c¢Ùóûüþ„ÿþÿÓ©À_Syg¥µž¦§©«­®¯¯°®¸‰rËÉÈÇ€ÆÅÅ‚Äý«j s–Xdbc`rÿû€ÿ*€  Úÿýÿ'8zuvvxwxyzu U‡Ñõûüþ‡ÿþÿÈ«!2xfÁ£«¬­®¯° ®²¡,lºËÉÈÇÆ€Å‚Ä ÃùœC¡t]cbdP ¼ÿüÿÿQ+42215 Úÿýÿ';ƒqvuuwwu€LS—èüüýŠÿ ýÿnjyꮯ¯€°¯¯®­®¢©àÏÉÈÇÆÅŃÄÿ²„Eª\cbcd15ÿþ„ÿûÿh Úÿýÿ'<œottutvuyI*‚áûùúûþ‰ÿ üÿU[oi¼²¯€°¯¯®­«ª©¤šÊÉÉÈÇÆÅłĂü¨c €Ydbc_‚ÿûƒÿûÿh Úÿýÿ'­wrtsust{G9®õóôõøúûü†ÿýÿÝ Asaª»®°¯®­¬«©¨¦¤¤‘ÃÌÈÇÆ€ÅÄÄ…ÃÂÁ·˜<§m_cbbcbdMÎÿý‚ÿûÿh Úÿýÿ'}œhtsrtsvU<¾öïðòóöõùüþ„ÿûÿ‡%p_”À«®­«ª©§¥£¡žŸµÎÇÆÅŀĆÃÂÿ±R¦[cbce.<ÿþ‚ÿûÿh Úÿýÿ'1²nqqsqrwc<°óëíîðñóööùüýþ€ÿþÿú,db}¾©«©¨¦¤¢ ž›™™Š¢ÏÅÆÅ€ÄƒÃ‚Ã»¦] ŒˆZd€cbd^‚ÿûÿûÿh Úÿýÿ='–Šjrprqqs?ˆëêêìíîðñòõöùþþÿÿüÿµRgjµ¨§¥£¡Ÿš˜•’ЉËÅÅÄÃÂ…ÃÁ¶•4'«ha‚ceKÏÿý€ÿûÿh Úÿýÿ@'J®gpqopptVZÒëçééëìíïñòö÷ûýþüÿW@-1l^¦¨¡ ž›™–”‘ŽŒˆ‹h ÍÃÄÄÃÂÀÃÂÃÃÄÃþ¯z_¢Zeccdcce*=ÿþ€ÿûÿh ÚÿýÿF' ¥}knpnnsiE¢êäæçèéêìíïðòõöøÿß J¹ÿÊ$eY‘¨›š˜•’Ї„oTÅÄÃÃÂÃÂÂÃÃĈ ÄÃÄû¤V–\edcdcd^˜ÿûÿÿûÿh Úÿýÿ?'h¢dplonmuLjÛäâäææçèéëìîïñðÿxiÒÿÿûÿ‰EUv¤””‘ŽŒ‰†ƒ€}zvy>‚ÏÁÂÂÀÃĵ‘-2¬dc‚dfHßÿýÿûÿh ÚÿýÿA(* ¯oknlmnngC³æßáââäåæçééëíïïšØÿþüüþÿô`E]šŠ‡„~{xurnpU<µÆÁÂÀÂÃÂÃÃƒÄ ÅÄÅ¿­umZfdef'Iÿýÿûÿh Úÿýÿ?(>‚“dlmklmoIußÞÞßààáâãåæçèêêúøïôùüÿüÿ¦5HˆŠ†ƒ€}zwtqnkig^5jÊÀ€ÂÃÄÂÄÅÄ»¢OŸ{_fdeedf]˜ÿûûÿh ÚÿýÿD(:65¯emjlkjp]I¾ßÚÜÝÞÞßßáâãäæçæéíîñôúýÿóN/q†}{xurolihcZYa[;žÊÀÃÂÃÃÄÅÆõŽ&?­befehEßÿúÿh Úÿýÿ?(,B—„clikkhoA…ߨÙÚÚÛÛÜÝÞßàáâäåçèêíñõ÷ÿ£V€vtqnkhgaW]yzulMYÀ€Ã„ĀůůÇÀ®o{—\gef g$Jÿùÿh ÚÿýÿA((›O¨aijhijkZNÈÙÖ×רØÙÚÚÛÛÝÞßàáãåæéìïöò82wolige_V]~¢°ŒumbF‡ÌÂÄÅÄÅÆ€ÇÈǽ¡G§ub‚f g] §ÿÿh Úÿýÿ()…ñ §sfgihhjhA’ÜÒÔ€Õ€Ö,×רÙÚÚÜÝÞßáäæåü•jied^T]€¤¯©ªœyodWS²ÈÃÅÅÄÅËÉÆ€ÇÈȃÉÆ·‹ L¬_hfg hC"åÿg Úÿýÿ(+|ÿlkœ]jfhhfnOYÍÓÑÒÒÓÔ)ÕÖÖרÙÚÜÝßãâ-Mh[S^‚¥¯©§¨©§€pg[OwÊÆÆÈÎͰ¹ËÈ€É€Ê€Ë ÊËîi‡‘^ig hg!Zÿd Úÿýÿ(*}ÿÅ!¬heifghfh= ×ÍÐÐπЀÑ*ÒÑÑÒÒÓÓÔÕÖÖÕé†+Z]„§¯©§¨©©¨¬Žqj]RT¢ÐËǬ}Z±ÏÉËÌÌËË€ÌʾŸ@$¬qeh gi[ ½v Úÿýÿ(*ÿÿJ…‹_fgeeghNbÑ̂̀ÎÍ̈́΀ÏÐÑË[ƒ¨¯©§ƒ¨ª›tj]PKoÁ¹”d3BÌÍÌÍÌ…Í ÎÍÈ·‡Z©`iih j<1S Úÿýÿ(*ÿÿ«:¬_hdgedi_BªÑÊŠË$ÊÊËÊÊÉÉÈÊÆ®©§¨©¨¨©¨§¦¥¡yi^RBV¤N%]ÜÌŠÏÎÅ®c”‹aji je* Úÿýÿ(*ÿýû*š|_gceebkDqÐÈÉÉʂɃÈÇÇ€ÆÅÅÃų¦©€¨§¦¤£¡žŸf^PB?U,•ÝЀÒÑÑÒÒÑÑÒÑÏÁŸ8-±nhjijiijY Úÿýÿ(*ÿùÿT£[efb€d^BµÌƒÇƒÆÅÄĀÿª§§¦¤¤¢ œ›„d\ND8'$ÂÚÔÕ€ÔÓÔÓ ÒÓÓÒÒ͹ƒgœblƒjE Úÿýÿ()ÿûÿæ¦mbbebcdeD{ÐÄÆÆÅÆÆ‚Å€ÄÀÁĵ£¤¢¡ž›š™˜••†cZMB7.$ BàØÙÙØØ×ƒÖÕÕ€ÔÓȰP šmj‚kld Úÿýÿ()ÿüüÿaq•ZfaebbgUK¼ÇÄÅÅ‚ÄÃÃÄÃÀÂÁÁ€Á'ÃÃ¥žš˜˜–”‘Ž…`TK@5+#xíÚÝÜÛÛÚÚÙÙØØ×ר׀ÖÒÆŒyyhlklklko8 Ûÿýÿ()ÿüýÿÈ$­acdadbag=ŠÍƒÄÂÁ&ÂÃÃÉ·”—–”’Šˆ…€]PG<2)# ±îàáàßÞÞÝÜÛÛÚÚƒÙØÒµ-cwj€m llkoTÛÿýÿ()~ÿüÿþÿ=ˆ‡ZcdadadSQÃĀŅÁ'ÃÄÅÆÌ¢‹ˆ†ƒ€}{\LB80'!(ßëæåäãâáààßÞÝÝÜ€Û€ÚØÈAWtm€n mmlp_Üÿýÿ!()~ÿüÿüÿ«@§]cac`cbb>–ËÀÃÂÁÂÂÁ†ÁÂÃÄÃÅÍÓ¯‡‰†ƒ€~zxusWG@6,$ Zû€ëéçæåäãâáààßßÞÞÝÜÛÝÆ@eqnnonojÛÿýÿ()~ÿüÿþÿò!t_bad`beL\ÇÁ„ÁÀÁ€Ã(Ê͵•…~{yvsplkRB;1)!™ÿîðïíëéèçæåäãââáà€ßÛêœBvƒonnomÚÿýÿ((~ÿüÿÿûÿ‡Y¢Uea`d_c`?¡ÈÀ‰Á6ÂÂÃÃÈͯ}n~‚{yvtqmjhgdJ<6-%ÕÿööôñïîìëéèçæååäãâáÞêÓVft‚p€opjÙÿýÿ!((~ÿüÿÿýÿÞ ¥l]ca_c_gFhÈ¿ÀÁÀÀÁÁ€ÀÁÁÂ4ƲgAZw{wurokhfda]ZC70)" 9þÿýýùõóñðîíìëéèççåâçðÉcdvqrƒqoqiÙÿýÿ((~ÿü€ÿüÿawZaba_c`_?¬ÅÀ€ÁÀÁ:ÂÂÃÃÄÄÆ¼¡˜Šxkhiiheb_[WUQ:1,$sÿüÿÿýú÷õôòðïîììêçéôÜUgxrss‚rqpu]Ùÿýÿ((ÿü€ÿüÿÂ(­]c_ba_bcFqʾÁÀÀ‚Á€Â ÃÄÄÅÉÐÑÏ®‘r^€UWVQMG4-& ´ÿü€ÿþûú÷öôóñïìíøèŸ^Zvx‚t ssrrsoz4Ùÿýÿ(({ÿü€ÿþÿÿ7Œ„Xd_ba_eWG³ÄÀÀÁ€Â ÃÃÄÄÅÆÇÈÊÍÓÙÝ×ãxYD>AFA0)"èÿþ‚ÿýüúù÷óñüó°fVu|wvvuu‚t sl}^Øÿýÿ*,ÿüÿûÿžC§Zac_aa_h>ʾ€ÁÂÁ€ÂÃÃÄÅÅÇÈÊÌÎÐÒÕÛäìíݺ^=$ !Jÿý†ÿ ü÷þýÃqUs{xx€w‚v po˜z ãÿýÿ(+¬ÿýÿ þÿò"žt\`c^€aXI»Â€ÁÂÀÄÆÆÈÉËÌÎÒÔ×ÚÝàæðúÿùÜ¢]+  ‰ÿû„ÿ þýþÿ×Vq‚~{€zyxxwwxtmˆ³w <öÿþÿIN¸ÿý‚ÿûÿx_œXb_c_bac@‰Ê¿ÁÁ€Â!ÃÃÄÅÅÆÇÈÊËÍÏÒÔ×ÛÞâæéíñúÿþÿê´q4Æÿü‚ÿ"þüþÿäZn…ƒ}~~{|{zyyzxo}­ž8q÷ÿþšÿýÿÞ «e`a_c^bdRQÁÂÂÃÃÄÄÅÆÇÈÉËÌÎÐÓÕØÛÞâåêîòöûüý€ÿúÔõƒÿýþÿìœbp‡Š„‚}~}}€{ sv¤®Y+Ÿÿ7ýÿTv“Ud`_c_cb?“ÉÀÂÂÃÄÃÄÅÅÆÈÈÊËÍÎÑÓÕØÛßáæéíñöüÿÿþüûÿÿþÿýþÿò§iw’‘Œ‰‡„†€€€~wtš·y€3ŸÿüÿÂ-©a^b``c^gK\Å€ÀÄÅÆÇÈÉÊËÍÐÒÔÖÙÛßáåéíðõúþÿþý€ÿ#ýþÿ÷³t}Ÿ£“‘’ˆŠ‡ƒ„‚|t¶“-4¢ÿ7‘\_c`ac_e;ŸÉÁÀÄÅÅÆÇÈÊËÌÎÐÒÓÖÙÛÞâåèìïóöû„ÿ%ýýÿû½ˆ­¶§œœ›”’Œ‡ˆ†ƒyˆ³¦D £i7 ÿûÿžD©Wd_d`acbMaÊÁ€ÄÅÅÆÇÇÊËÌÍÏÑÓÔ×ÚÜßáäèëïóøü‚ÿýýÿÿȇºÍ¾°©§¤ž—˜•ŽŠˆ¬´_ €`ãÿj7 ÿ\þÿò" s]d_d`bd]A¨ÉÃÄÅÅÆÆÇÉÊËÌÍÏÑÒÕ×ÙÜÞáåèëîñõùýÿÿýýÿÿÒƒt¥ÇÇŸ´¯§¡¡–•‹‰«¾z1Äÿùÿj7¡ÿ[ûÿx_ŸUcb_d_bgFlÌÂÄÅÅÆÇÈÉËËÍÎÐÒÓÕØÙÜßáåçêíðôöúüýþÿ݈gޝ²µ¾ÃÆÄ½³¬ª¥ š¦Ç’)Ÿÿÿüùÿk7¡ÿ[ýÿÞ §j]bb_e_f[D°ÈÄÅÅÇÈÈÊËËÌÎÐÑÓÕØÚÜßáäçéìïòô÷ýÿæ‘_|ž¢£¨«´ÁÈÈü¶°¨ž¬ÈªA xöÿýüÿûÿk7¢ÿGýÿT{Œ[`cb`e_j?yÎÃÆÆÈÈÉÊÌÌÎÎÑÒÔÖØÚÜßáäçéìîï÷ÿëš\l•”˜› ©±¹ÃÇÈÆ½¯µÎ¾Z€Qâÿýü€ÿûÿk7¢ÿEüÿÂ,®\e_daaea]CºÈÅÇÈÈÊËÌÍÎÏÒÒÔÖØÚÜßáãçèéðþê \cƒ‹‰‹Œ‘™ §¬²ºÆËÄÅØÒt €;Îÿþüþÿûÿk7¥ÿL7‚Ye_eabecBÐÅÈÉÉÊËÌÍÏÐÒÓÕ×ÙÚÝßáãäè÷ê¦_\z„ƒ‡’–¡¦¯·»ÄÝæ’­ÿÿüýƒÿûÿk7£ÿLûÿžE¨Xcd`f`cgUKÀÈÇÉÊÊËÌÍÏÑÒÓÕ×ÙÚÝßßâñé«bWt€}|~‚„‡‹“˜ ¦¨«Æå«/ôÿýü…ÿûÿk7£ÿJþÿò"žw[dc`f`ed@‹ÒÆÊÊËÌÍÎÏÑÓÓÕ×ÙÛÛÝêè²fTn}{y{}€ƒ…‰Œ“™›œ®×¶Cläÿýü‡ÿûÿk7¤ÿ<ûÿxbš]aecaf_kNSÆÉÉÊÌÌÍÎÐÑÓÓÕרØäæ¶kRizxwxz||~€…‰‘œÉ¾Y€OÍÿýûþˆÿûÿk7¤ÿFýÿÞ ­ed_fbceaj9—ÒÇËÌÌÎÏÐÒÓÔÕÕßä»qOdwwtuxxy{}|‚…‰†Œ¸Âo1¶ÿþûýŠÿûÿk7¥ÿCýÿTx“Xf_g`edfRRÌËÉÌÍÎÏÐÑÑÒÛâ¿wO_tvrsuvvxzy|~ƒ§ÁƒŸÿÿüüŒÿûÿk7¥ÿAüÿÂ+¯^dd`gagcf=uÖÑËÌÍÍÐÔÜßÃ~O[ptqqsttvwwyz{|w˜½–/ ‡öÿýüŽÿûÿk7¨ÿ<7ŒˆXgbbgahdb8h¶ÐØÙÚ×ɪzPXmsonqrrtuuwxx|{sŒ¶£Cläÿýüÿþÿé×á¦ÿ1ûÿžF¦`agaefbicb<>?J[jpmlnon€qstswxow£´i 1¶ÿþûý•ÿþýý§ÿûÿx\¢[fchaidgef€j#kmlmkjlmmnooqrrtvoq˜¶|ŸÿÿüüÄÿýÿÞ ­ifcegbjcggfhihijiklkmnm€pruol޵% ‡öÿýüÇÿ3ýÿToVjageegfghghjhjkjkmlnonpsni„°œ7läÿýüÉÿ&üÿÂ$­hbiaicghfgihijiklkmnmnqnhz¨¦J€OÍÿþüþÍÿ-7…[ggcgffhghjhikjkmlmpngrž¯^ 1ÄÿþûýÍÿ-üÿ«7±fbhdfhggihhkijlkknngl’²r­ÿÿüüÏÿ+þÿò"•”^egffhggihikjjmmhg‡³„!ôÿýüÒÿ(ûÿ"Ä|]hhgfihhjiilmid}¯”0 oíÿýüÔÿ'þÿæKÏ[diihjjillict¨ BQâÿýûþÖÿüÿoMͤm\_ccfcaanŸ¨U€IÎÿþûþØÿ"ýÿÞ * Ê³’{yƒ’©®g2°þÿüýÛÿüÿ‡2|§µ¸´¬˜l“ýÿüüÞÿþÿ@€ € wöÿýûàÿ þÿï5 måÿýûãÿþÿ÷uƒOÍÿþüþÔÿüÿ¿GV‰SQUb.‡ )`_OQ—SVG¿ÿüžÿt8mk@#6HVm´b=`…ªÇÛëûÿÿÿÿÿþÿ½%aŸÎòÿÿÿÿÿÿÿÿþýüûüýûÿË -€Íûÿÿÿÿþûûûüýþþÿÿÿÿÿÿÿùÿ“%lºî­»þÿÿÿûûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþú-3~ÉúÿÿÿÿÿþûýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿŽ=×þÿÿÿûüþûüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÚ D”Þÿÿÿþûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3@–âÿþÿýûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿcVÛÿÿÿýûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ‰Úÿûûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ¢åÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ­àÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ§"–óÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ“öÿþûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûÿG zïÿýûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúÿLÛÿýûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüûÿ«ÿþûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûÿüm]íÿüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûÿÝ:§ÿýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûþÿœDâÿûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüÿÜF}ÿþüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüÿù‰ ²ÿûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüþÿ¹*,ÛÿûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûþÿØQIñÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüýÿén `ýþüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýÿð‡LltaB]ÿüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿô‘ U¿ùÿÿÿþÿê‘–ÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÕuÛÿþÿýûûûýþÿìK÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿË/–îÿþüüþÿÿÿÿÿþûÿûR»ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿí¶úÿþûýÿÿÿÿÿÿÿÿÿÿýÿ÷3'ÛÿûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþûþÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÑ 'âÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ ÒÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿHéÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÚÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ—%ôÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûýÿÿ÷ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿP—ÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûýÿÿÿÝ%Hÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿß(÷ÿþÿÿÿÿÿþþÿÿÿÿÿÿÿÿÿÿýûûÿÿÿÿà˜CÊÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿž˜ÿûÿþüûûÿþþÿÿÿÿþýûûþÿÿÿûÒ‹= ‚ëÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿS(öÿþÿÿÿÿøõþúûûþÿÿÿÿÿä¬g(4¡ôÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿà•ÿûêÄ“^KÿÿþÿÿñÒ£i2 N¿ýÿþûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿŸI.^Šm]6l×ÿÿýûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿR$‹ëÿþüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿß;©ùÿþüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿUÄÿÿþûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿNoÚÿþýûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÛ!Šëÿþüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ–0£÷ÿþûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿI=¶üÿþûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿتþÿýûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ‘CçÿüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿEBøÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÔ áÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿœÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþA#òÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÑ gÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ‡—ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿû8 ©ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÈžÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ|ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷3 ]èÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÃK¸ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿv=†÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿò)+aÏÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ¸Hšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿk 3màÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿî#P­ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ±:{îÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿc&YÀÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿçAŒùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ§ -cÒÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿXIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿâ 4pãÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ  Q°ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿR<~ðÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÜ'ZÄÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿ•CûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿG /eÕÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÕ K¡ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿn 5ræÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÄ"S´ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿö%=òÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿR(\ÈÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿmD’üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿv 0hÙÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿrL¥ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ[ 7uéÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿè:#U·ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿ¯% >„ôÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿîf!*^Ëÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿù™DF•ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿò§c/ 1iÛÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿüØ r@M§ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿç·’qD8wêÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿòÅžƒb:$VºÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿùÒ§‹oJ)?†öÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿýݱ’xV3 *`Ìÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ纘€_< F—þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿðÅŸ†hE& 2kÜÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ÷ЦŒpM-N©ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿûÙ®‘wU4 9wêÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿþâ¶—~^; $W»ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ꾜ƒeB%@†öÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿñÈ¡ˆlI*+`Ìÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿ÷ѧsP0 G—þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿûÚ¯’yW6  2kÜÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿþã¶—~_€ðÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ滚‚dA$(\ÁÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿjG)D‰óÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿôˤ‹pM.  -c»ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿøÓªuT3 Jƒ×ÿýþÿÿÿÿÿÿÿÿÿÿþþÿûܰ“{Z8  +d–Ûÿþþþÿÿÿÿÿÿþþÿþä·˜€a>"|]Ì®Agñ¹0…¥6µ‚~œCo>]ŸbùÛÁÀÉÑÄXxQ{F'Rx*wT¬ÃçjÓòæ ˜ƒÙr 495½ §(¸C ’Ëùˆê䀱ÐÁŠÞã7­†|'FÂ.î+­I­ü&1yÀ*éP¥A">íæîÜ|KD<×ëäþgµDºRY¨mü¦Ãí'‡ÚCÚ>|Ai±§âz{{‘Õœ3&«æýJÛ"àô“Rx «'²Ä8Úm¶ÐuM%‹}ç%.™Z ?Mž=K3Ë G&ôn$˜_§z!Œ=긢#a¬ý9n­4c ÂÁBN=°|±txÞFÏÌò~gñù €]ÚöM:ÿt<1á†*Ãsr‹Â)6פÍp‘ˆŽ€K`;bŸL4²‡3+ÄRЪmF⦶ÍA)¸* *ƒ2e;ýÌÿg6Zÿ[n¼ÿ`ðÓ”Íä1_Í»Møÿ$H7Bë©ÝÄgè4‡uü‹ó,0gï!Òöœ«;Ü¡dɯÎ&uw¿"TÐa—Öbò’¨þ‡¼f€éhÊr¤ Ȱ‰&§©4²¯×ßPfÖÂL¯¬RŽ…“¾?0ÏÎퟷÒë:±"•rëCþPÒ«#:>]¦sST<Ç¥·“Ï—Ôˆæo6np‹TŒ´lÄpÿ'¬Â;sžÕ!—US ôÒhž—mèƒf>þî€TãœÒùö×V!z=¦T~›‹óã(]i²ÌhÏÑf»lºL écw³æ“"¡ýìã\¡åšÏéV ŸJhðçfÝô¡Œe¥rõ-:‘¨I­=Ä ƒašÒ/>³Ï “6–ëÿ…Œš_eóÏuÿ& bÉÍçl‚ýIù0:‡gBo1»ÈBòýÕg•TZø&ú¥ƒ¨¶Âõ™hwdeQ›çïˆ2_†ж@ž‘cLË_%;µøZm­U´ò‚Xz–È î»0éiæfúÒ£<D6ˆà—\ cåÈ@û—ç“IâÓ/ìwJ—ë²H÷¾ð„Pˆ¹&§VÓÌï9¦‡vá6":ÑÑÝÃz¥æ!¸O{¢´bJO_ÐÒ@Cy“-ˆ¢zGð†;’[TËåòX:ô¸ŠN ·;';4,—Ug2äbuˆìu(=Ò{ÐI½ÅûN=ÁJÎûïs‹{¢w¤ÚìÂD7 ›zÃæùq Ƨð¯Ä•=1az±+x¦šý‚ž^W›R Œ Ý®ìd³Á^]í÷üp8\ ›+Ðöʧ¯ (ÂLMñrnU6™éçh¢Èûé&Ñ]¹K;Ág7ä6‹Âýë?Ëœ7cÒT¦éÖ/#Ô_—jüÓÂ}¸dòèã~î°=„8…öÅ´˜±#3 t¬fu#¤”½mj`ü{ˆÃ^Ô÷ê¼Fz“p"â€lÕ êª)Ûíù<„Lb½òÅùQcTòº†™ôƒ5ÃàVõÆvò"÷yÌÃóF1kÛÌÇj¬GE•{Û¶ Æ·dñL0ëÖ0×ij7‹`~ g1Г“PŽ£ñŒÑ‘Ë#Wx9ý~ÛÏϪøü:‰‡áÔ°ÍÚÐU<&Näplj¸W½ ƒc'‡ö³ DÖ acûò0œ'Ô€Ëê¶ý/ð9"=0ü—¬CcvùÎR‹Çÿd}´¿h …h5þíÏS…e`\&Ë»w£ 2/Hï’ ße:~ºUVÇq¡Bû#”Ê®Á—:jÖ·ŽGDÜü‹…½ÑÝñ¹—Š Ð¤Ð" ã Ö"ŸIÙ¥·CO™s²QËQЬÿLè¾ùyYƉ7#èqoòãȃBÜ6¦|-LL·>û3^ïNuñO¾Ö¾)íj>ÝH†1ë¼¢ïù’%øùAîˆ1¥»›C«*ÎZm.&óJ{úù“ZŽÝB†Øª6©ØÅžKË W9á}›À²Z 9Ë®Ka2^¤¼¼K×°˜Æ³? y;5ŽÁŒËÍä5.D݉™P &ã!ï`а~#<¨ñ±“˜Â¬ü¸P¦—lͰ'•¶iE\+ä¡Cs¬‰%±á%çdm•úU¹å'6_7ï·ƒŽfyÇ*9Â#jw\#¡ËÛž¾‘Rܪ¦^–ªùšÇ”ßì#.+\Zãi ¥ryêùŸz1_»|kÙWÃgÐõ¤¬.ÂÙTqöÔÎ̼’ƒa»?'W )J¸M“S´×§ø¿æ×m¯i÷*O4Z:¾þšq/‡²Ñ˜ËÓûÌbór¶^ð×d}f¥î`úÆZÊÅLªã¶¯sG\Œý±bpH"+qËuÑ+˜þÝ]Šuü¡Îä>zX8¼«ÍbU.aÕ‰¾lÒÈ^šBÂa:*$= @…Ç["Úvr C¥n¢“ø.jC ºEôá‹Qºd1\p[sZ-x@Æq}°/%Ǧ8ÕñV•Ƴ’ö o§¯–-ÏÕÚ5‘Á¦ˆü.Ã|)›AÒ…ßMM¾˸߮ÿQSg2=‡>ñ<-ÂÅL4XÕùónxµ<Ë-2¢R‰ê>¬Ðº¬/µu•0Ó9§díNÎà5Œ‘1Ç1ºÀdsj ïâ€$äðŠä`xÓ,Û1CGÐ$P!·Ì:‰B¬úø;öÝŽ¥ÇƒÕJv;:Õ˜rºvü{ ã7Ãû'¼¸åìU8Ø5Pq UúÛ?dlAÁà3FåÛÙæÆ=‘›3£j"JQ•v¥ìlÀõ6Ú^ŒfeÕQYG5Žßˆ§Æi÷Ã9ÒšÆ\†‰R àÁ-㪖xv@ÀfNØÊÁ›¶ÎôPæÐ™žhÂà€…%Q‰#oÃÓˆ4…¢HÉ{Ú,,i¿©Ë­A“ —ôo̪]¤W\ 7-Cåêè1ïhw¬´çfðE™‚X­¥‹9\@Ñœ$Õ¤>ãtj#ÜùÈ4QCœ½¼Ì(7-a|0ÄÄQw½7Ïw}·‡Ǻs‚-ͧÉ& ñ!C€÷å‚ÈPÇ—Ò\Bð©CÓdÁÍ(¨}²ð¸·ëÁ4>&{¶ ­ŠJ´P~ØgH…Ï™{zȶ)¯qNÔÅBYÐú¢ªy06ß{8 U±þä^¢ ½ à„AH+®'úÄÔP²Þ\DY«aK N#€+QoS(Ж#Oé×sYÈ9Ág«Oð¸̰Qh†ÂßK+´B7"d¡iË`íZ6‰oØK[HÂxc÷Õ¦;wÂRЃ!ŽÏa5ßgl6çmÃgðÍ”ºÌÉ TEø ý4Ëžå Ôm«“ÿ-˜‚(Ÿ:v©‡-XM½åY Ýw`—¦–Ûèt-<ŠÚ È{ʵ„ÒíÏæÂî^woóHxs‘ÚñQoN£9 é躖ÜTY Ðv8¢éh"âRG°à»{´æ7 )¨ÀBÍqevh˜êfJ|~ýϵ r}~„-æü÷ˆ­>õNà ¤èÊ¥yÖ9íÀî) š¹W!9â0µ\ fÓäÈçJ÷@H‘ ªß’(ÉXXú¡®}œ@‡zÿ7÷[¿Ûÿ@àm¹Í•ª%#%¶o¹“ß[%nâ|Š%Ö¿>á_­c·(`]¾·8ªZà2 x0t&ŒaèM÷²Ç+¼óÃE‚±œ•'®Ÿ~cI8kœ«¼U ûCÔ¨}@nÝ׊D Fæ•ç‹#à]£ÓÛq×ÌCÊ¥MÒïm/­Q lï[,ÀӠ麀 âõáÄa5ç†È{ÊO'õÉ$Ûhv»§ÒÃÙ“Sü“qa!K–§.ïx:ï½æ•UâzrûV)[Zß&)V¤¡ =ébh[F\žÁ:^€ÓçZlžŒ7y¯÷€—/“Úg´) d¾Ž5šŽX|o›a^7tèFé‡0ÇÍn«„ƒ°¢";"(!¤]hv µAiìžÁ,®ô[ v[êœÚ©ðGÈÆU±ðcÒ7ÐÙŽà7ÿ\Ö½úíhçûÎX#àäÍŸT¾=вœ$³ò±ÞT3<òþV'ˆ¸&L·ÍÃí¼áöÝ0}·È`½urá½Õ©-ø¤7Žà¨ãh?ú/ª+:Ë4H,EË¥Ž\åw§™òcšñ4-jN?#¿k§NEŸ«Ç˜Þ†Ò·tÆúµWµ Ø!PÕçI“þ‘Þ,‘YÞ_A (Þ±ñçÂû÷þKû¹»î:ùçÙµ@ª ¥¸µEqûà )ûðüªˆ ÌÙ¨&aÁÞ)ÜÒp t„„{I…Zuã1¤¬G«–$aŒ`9×M~e:úw}Rð…}Ôñfô]æñVx„-±SÐ+‰ùÈz³§Ý0uv¶´~?(_²ÔO‡aõ)¬@ú?±yròÜQù´EÊ_çŽu_î¢ޏ;lÐNe»ðã¿ô/IÚH¶`±²½t6Ô(¥¼%üïïÖ€Ü3¯yÀrw-TÏ×z"“w›ªjo@P{8ÕèKU[Åçùk¥¡|.¨q¶‰5W£Öö¯Ì¼¦Eúì8K1»ŽN ­·†ƒX¸Šíâ?¦é—Ÿ€fã/ BBR`N¡Ž¥¯‡QÐñ¯sÔ¬ô_6¯‰âä:Futî–»>H^ˆã±µ°#çí•í«ôûŸR8Ù\Ï[æ-Uï•Ïj茪ü²LÜÙ´2ñë]Ú¾d_§Òý¦޾X²Œl‰Æ×$&dÉŠÏH'¨ÐªKð;ép¾0çXǬ0Am Vìo*ÿeoÜlÞZ‡i\ ÜL– uQ¨‡^ÒRÍ¥C1º \px;þvСMD£OÙ‚E‚W=†ŸW]®ÄZ™Aù¤ú%}Do šZ'*Ý®¶ÉJáá»Æf#¥@ÎÖ„Ï& …/¬£ÇÚróÔ§6¨ó8žÉÎE z&¢º7 ê½9V»gË3Då8vòÃAeÎà˜›}æáÓÓ´…_MîzŽ"t¹àtIªÊZB”f#üÚE¶;O1#ðæåÃ;$~õ†EG`ÝÙ¡¶Çá¦qøi¼? -|˜–^/_éq¬Ôš@©ðÀæ9X[µä—S<°ÄsØ>@hOûÌ ÉiâBK |5o¥(~âî‹[ßJƒÒ=–°a)‡î ­øãq¢åMf†iÈš"ÚÄ¿AçÍGÍeú¢ðÐöÆ©O^2E}Fuw®T q¬[“¨ÈHÐcÑJ˜Ô¶d¶/—w€ËC(SˆŒ¸ŸÒ•hwã,®x’äµÎ3°à«[#FëF–ª'm„Í \*»Mvn×Â^av îÚ¿ž2öÒV«SÄEAº‚‚„Aä20ŠéïôaJ<üUá«z"˜µ-]t£-{8´yѸšÔU†=Ûôp3#rÉãã½ +ciÿ¥MòØA"áZâ™=Ô¦#…%!ì{HГ7Ç )„Y„C*ºÄ¨ÆäÝ™S‡¶kuJÉŠ-¶½ ‡-;Ó¥¦®§±:5’a§æ,xÙ±ßkóÖˆ}Uà¬Ð½}õ= Ýò-Ä æÓ‰6\©&#± åŸÈÀÈ~Öñ˜ií³ãã¯Õ<ìÓ!€ÊП9Lí¦ TøÞª3dËIA$ÿ[.­V»‹-KëÍH'þ&~p˜ñTÀ©¬¶é@åý€ÒY à÷Jí&Mù\·úšåÍNi«.!Î8²‹©xp'fºÑÕUJ¾N1Ö#ŒÊÔº"GMZ"[¾ŽDBëq|ÍÊÊ*ubÏ`æƒ Îi¥>()ðØ¨°®@ëW÷œÌ•GkT©šä±dÔ_ê{ZyîMŒQã“1slmÅýü'ÑêÍ\p ·a4—yEŽ ÉKyÑA²ƒÙi3†fr\=ˆG«ðDÇð‹5Äœ9݇3'ôé˜aòßàªä©Ü‘½Ó†sàHÇ:ýº°\-¢n]áM)羈‘x|]¦¥¸Ca|¹êü¤)¯û’Æ| 82äOn;þ2¾’QRð°[µ#6Ìk¥Ò‰EXaÌÇ©?¾_ôâYãŒÐê™hŠª²\àÓ¤x‡[Þi],GŸl“rª/ –ó«ÕÏZÈΦã¨Êñ›Äøuî¿—íèÍ÷å!¿Z4(hÄñ :ù(,EA¡È>Ö=&ÊÆß‹ÃbÏ’ÙïâÓŒ^‰/äR 1ûò«È—àöl¤Ø.»\ÍËé>ª ÑS§UO[» ÆÓ hˆ³.ï8R¦O£0‹Ú(ûuѯôÇát~î—8¦SÃV»_ls„a_9nQ‚Ùªo³ÒE¼^PE‡øÂiŸEǨkñûÏýWUë·Àà¯yéO.éJ‹‹Ð"†«yTÖpšFÍÒñ#V_æL†×æ–½kÁ£E÷©½H&£JÈ|ê`µ BÜÏêã|Bؘ‰jüQ¥f-qÁ%-HJ¹sa[±óËá|Ã)“rnÁÅýñ½Y´èf‹‘’—Š{‡ 7)Ù|UÎ|]øoÅ=îÚ$†]’ÒcâÀ>¨°NIÚÁ¬m)Kø¿âàë^Ùj…M‘Œ)ø!ýº+|c“ýš€¬b¾ˆcU¼V<8™m²g™ÖpEŒm/’âFv>ˆ‘Ýç>Íscôd[s¡Åá ›Búè X²àV\x”åWL=WÈ AéÉü6–¾ÜFs¬^.¿êTd‹ùô3Øy¼7æB†ê¸m_­Ó3o“L‚6›v©/5¬‰'B‡mÉg'V$»•sœéÁ½z q7ŒµÛѾ›£t8¶zšÎ±ÊMüXJ)¶)—ª°/Ö†yHºG¾€‚ôË^¾ÓHÙ¶š#2òta<17œãÉaxYA“!—§n‚׬öàHÒØA‡ô…¶¼ÁrÄ(î¹Yè…òŽNõOÿ]³ÔEEë]êò­~îÇÔú/‘e—dGP—©ÁØÇ£h(]Ð&? !p—`o`ëvžÄêÐ3âp×?Eý5jW'•º´M0ÝòTµ*¿>I¯šNuxR†üN›ß¶Ãnõ/¤ît³UEWy$ßê( pq Î¥.2#žÕ¨cÖ3Zõ§Mž’S¹&h '-;”²®Å\—L¢> š?ËypŸ6Ñ…g©RÐ2âúÃXxNñ¿3é3:êþôíu†~€¼±æƒõ%9Ú.û„\çXl1Õ_aœúàI–èÝÑo™T ç7GV‘Ô¦V¥a'´&˜FM%ÁJ(#ÓIÎ:VÕ!o´”GF½åuý‡ÊlpAY%Ûë?òÝÿk-þžŒ¦vNšV±æÂ‰Ú©JN`šµ‚bÒm;Üò÷ì%¡Ï‡mŸ ªÿæ7›eð°Åž¢ŒC³”‘Ÿö„/À¾²´ÿƒJËŲ ö\ŸgBW€GôÛ£òègd N‡Þ% ŽÞÄ"0íÈ· ïï‰N(HÖ¢íì®®ºÙEu'§,³T´È„ó3}"G½IPǰ™™8,8s},"ƒœ|&ø ©„:¬› <Žj´\û(ÇÑñ¼7O±•wbú:Ñœž¢‘€¿_'­Ço‚{·óCó§ê©ñK‚êÌVÁxؘMĨo‡ŽŒ’µáz¸Š«»½h!ïSd,.ˆÿb_Î0g¸¯? 'K ®o”Ì +w)³ŒV ¡ÓéHa%eäXЄFQÕŸ8gÑëÈŸûþ«wÝ_ÿlÇ„K„i_ÇQIÔ³ÜJíQï ~§!%˜œÛýûUoÐHÀTó`UNi[ÃAãÆžWÝäü’|L/¢ÿ®2@“©õŽÙ×ÄDÓf‡‰)÷”:{\8ìâš †ï't'"Q|Ye¤¦†%™e—çVLÌß9#‚~9Ÿ8­U$G¥ h¦pñ|V @5.!ËhË>Ñ ½ï¾ÝžÃœù‚»KðèYKÁíâeãRX¬ ˜”QîüÓÁuªK°è:‚Q¯ ôÏÈ‘;uJ•¤ñÞëmCGj’×Í–?Àzo· Å…Šå‚Íšà7œ· …£À£Waé‰AxöG÷š†×Sªæcø^‰O{*N¡¾[ÖôÌH#¶câeO§ú]h2sÿP€ÚräpÃøèJ0…,0"U?.z$P7óÌ#½qWT=c¬ãtú˜ÐÂÈjäp™o¦ŒƒƒäÍ+qþÏ îÉEà!vVûµÓ¶ ’ï÷‚«7EÑB xÅʎ埞rY^[Ò3æèA-§†$2üÿ„Œˆ.¿J褂FÎügc/Z®ó`ö»&×|‡×¿æ°¦P…6n ì®dÐÅæq€—uýUr;åÔz¿¾"¾›k¥­ê„,ø4bFÏF3ÍmƒÕ#W=„hâDî£!Yszõ YÖÿz«ÛêßõÛØ·CËtI|ç×Cr¬®}–°uóÈf)p°ÌÊÞb¼–.®¥CÚ±S,Ø!ÙíUýf¨î\É7"§¸°Žóo!¥Ñ5~t£~[wÑÇÓf[ê8 &÷ûbSöŠ5ÖìÀµB6רôœú&º ùR«óY¤«È¦B9ÎÚÑ­e“1-±}Xuö>aŠc©g‚÷Âú=¼f®‰Q½‹.¯÷—†'zKd Óä°æ)«BE6’ÐMõéðóªÇ*Ž9·Q¨N×2ïIáæfLj»²ƒå ÀX÷¿"[–>!ÈÔ,Gå£m ¬&,¡Z"œÑ̘Í­¨ÛÅՌڛ£íµöóÒÚfûE»…3XÀ‘ ÈÝ7’büܰñã9u Ô-ý£†ÿyHH7|iÜ¡5×HáÜ=|Í!'ÃŒêv„rúñ€„I6†Ü äî‡#éEgv¡íøí3MAó åU⪵>¹3:.e01õ »‰“°»%vSž©¨?.–_ÿ`ƒcÆl”UȆP8³/ŒÐDQí¡ Ò  Ýšewð p.Hê‹=U6~¿ëÙºC=Wªîݰ´TÊC^!7¸RÖW•½û¦>#<%#ÏÓHK “]ëc? þ:¸ÕV·ï‡/ùô .ÿ<ù—Äé…Mˆ0ˆžãóÀ^&îÇ ?=Ú}ln1íð_›…ÒNN~XÇt™FQ#’\ô„CR;…×ýpJ¬¹èЇż³)O~/,b A­:*JyÚhè ÞŸ…UBö (éŽ]z5ùŒ×€ÆaoØs& 4Ökc$gõ€Ã+FËhÁÉJÝŒ´'[öÅ&¾²li£çÉÄâÍáùöVïšÄâ€^æïÝyp2 jÔ•ô»R¡M—wp÷¦nx€wÌ3«×î#„ !"mV³¼ŠSžþÑS (øÖ ä¯ñ4?”°é`³æ?-¥oý´Î•†œLs ™‘zMúGúÖrÃÜï¶ç³†t6ÝÞ(]4®ðP€{ÁäNƒº{é-¿Ck&œÂ:tÐéŠØPù/kÆ=lëc'+ï§òRÇIZðýµ -Šdã›rî­y+,ùä푪H#—A…"”/‘ A$©êÆ7ãmÈù›hd]D3® »l=ï»Rö§§Ä{ó|Y ÷íuå`ú§lJº6µT”v/죕v/×§þÍ©.ò²ü‡£Ùù‘ ŸZá×ÂÎ,Å™´»Ež¾í“xå‹Ht$W‹9ÔÏÚdô<~ÀHA\õÛnÎxïš%çô·E1ÌËöʤ°8ž&7x1Ö”;Te¿ßÿ€*ªÎé˜Éäù¹~ƒ{Ë KzÕeÚšz3¬DRÃ6›B¨a{>ãʾ0P=Rú£›¸ô‰¤ª}uçFæ¢òÒi=:* z¹°gÍ!˜õ˜Ó­„qKˆdeL¸ÜðØ¹®àŒ ^¯Q úVš ¼kL¥¼ïŸ“ò3ZÉîN‡\)Ý#]fuÑ«.X‚¿ÈCL¼%¼"šB³«O¦R®?3Mž¿hU”ôÕ®×Z‘& ´JE¦BÊò÷Ùê 76Œ¦ÓeöÐÌF×cJ“6D¤ CžjK¥˜‰DlÛýýq”øI"'\*›oD²Žñ ×åS4½ÙÍ_áu¦ŠÅ!KµÚ§ƒõ^%°èÚ°Ž¥ýÒdñJDtësù<’šêÏAÝaßéfÕòùÛÃdÐeû8ÚlïΠ9{…)¨/Á(v#@9ôü)¥Jƒ%ÀyíäŠ•Ýøå (&•ðKϹ7ÓR®’Z˜Ò2*ˆD«þªUh½N×Z>°×KQOĪ1Ø9^øÞ åkàJÖ=ÙÎñ"&qƒa/°ÙvÀÖ àY7ÚM€Ñ» ÅÒ¢DL÷#“BîªHÄx³­q¡¡ƒ› £½vë˜.*¿Õœäýâ]Y‘ÖׄO>šïdD[…£Ež§"¯€¿²Oy.…æ~èÀož±8Ä Ÿø.`# êŠPEïÙõ@8ù¿uªñ–ýšÑç/¡šÞè.û®&YRÁ¨R0=uuVLœnœ|‚ßæûl¾„¢ÆÜ¬¾ ŒèbÚÌò^7Î ò‘"*Ç“üÑ ;[Ž­ÔìíÍ—ÜØ>(äX_U5 ž… ±kÁÐ×yâ>p7šzCyTäøñy«aNµm†µMÍóFpæ5+k£¹¡u½u§Öcmྯþùy@¦èa×?º"ݶÚϤäàö"¨ôWV";Ÿé_ÜBV\¹ò´l3ÙþöÀ»Ä¶«F#ÿ*Êkºgï³3ÞÎ=Ä€¶ÌÁ¾!… Ñß~k(5ÅA­ËeDßkV9œëç @Èã ƒArÝ•Á}«‡ɰ²$ (ª] †ùkö¶‡Ž<Â4Y-É;ÆjèZgêD„4]›û'ÏMî%yÕÅ€dÝ·4ñ(1Õ™qFŒïNþ³LGð͸>Ô4Hgh˜³gÎÂñ²pKÎl¤@…"ž½iAç î¥\ð„òN>Xœ#ÊË gë,KÉÿQ&ZÃûQ ftˆÝÒ¦Y‹0ç»8Å„*wù …Ñö!2Æðª—6'ÿ{hãÖj;|àâ²qï6òzßúsqÊnó(rô²àÚŸ'»@#£ smÓ`˜&ûˆâfÂÏ—fLJ§¢³,âÎEŠÜWŠgªðhYþî•u½äîË3m×(]¬*ˆóèÆ,ħŠfõh$NmˆS`%³\Ä ÿ2fvTKŠ^>Œ³"»`tüY.JNIw}Ãe{§}$ñãAumSdHŠñHÂv’(;Á¯X“õŽvQMÆk‡érd<6BÅ þ)-–Åv€‰[y>—}?¨!ªêFiƒP嘞Ìa|1^#ŽÅŠl÷ˆW×n]ÐàDÊ_x6±qYhpìÄuRíPdX ÝÚ|Šýª² Bݼú®éîÿHY¸ºQ\5Õ{«›q³Àç÷I-3Øîæ€U¥SO˜t‰Á'"©Rä¡_ ¬h‰Ünv¶M² pþ[8!òq† ¾U žÓR9~~yâ´A?[Ãé Ù+Ë(ííÆ–ÆéA©4 ÄO¸?WXʺþ®ë¢P\E”…•Ǫ.¼88t“+c}l8À:\ÁåÏCMphÄYþ/ Õ^eiûæÞBÀEÈr"i¶C>•c5|½áê­G0÷sCžSàYgÈ®À4Ö\ú‚ÍXÆh+Vœÿ;6ÁUJúÞK3®›ñÁ܈h1ÿ>дmy9>j$ü¬áD¥hÑ ŽºfNxÅà¼hÀZ„ÏâáÈüð¥˜.7”±A„ø{Ùmî¦7‚­A »–—߉cºsB÷® ©9 ö©ð2H ‡LU`3㼤1BR•ïG “_ŒŒãÞ§z8œˆ›Ú<©Øžð# ¦ÐF¶wÞ[v·ˆ"s*&‡@/5 –ù0ãâ| ^¯ž0x߆Ü£¶¦FдŒ¿ˆ&ä×_ªw4Û Ü@#1oaª"l\·¤ãù\½–í}•¯½F’u͉9ÂŒ xŒíÌñò1T\9Hp&ÕŸšc|4AöÄºí°ƒJ(žšª×œƒÉÏŽ¶'bÿÖÀPS؇àõ?Ï€²³Ý‹êzýÆî—œy3¼ !ÜW ÍŒ#² “•š/æÃeã¿Ó3;1°™#"Á|œòu¤Oì› \´oGS^DæM1Z0ÌØd9¯¶«ª‡1w;‰ ›Š—*P6Oß÷¥Ø˜ÓTq›ZüE‰ï•–6¹mDZQ«:éaxE :þ;Ó“ÒcUš0ÌS}ŒeÕÓh¾Áå¿3$q:1ÏΦŸ/ÁvÏlaN¨±ÏÕª’‡ êýgS!óÈ⤑ !ü¢|—nÕÄuý˜hOÐ0gü…¤œ`z Ø=™ÐnJA N©f`-õœ«ÈŸƒ¬¸‘§Eù7ä£ÍD1çÅ["¯ÆjÜ\…2Â’]-8´NI Ľé“ýS­\þO&mûÆÍ ¨&Úö>¼}“q©åÏqÔ™!á´Úó({[œ*Âï·Âµšc2xø£0Ñ+õÙ)¦þsË¡<;ÈlߨÆ~tÁ¡‚z~Ê/²Ö—Wö¶G5Jn`š00´CŠßÕoY0Ÿ÷ñ}é¡“ÉÙDbÝE%=N3ŸW+¯– “íҸä‚i­Üò{êBòöŸ¥)É%™¹eŽr†¶™OÙŽm±yÕ‹ ç~ ­îV¹H&¯p4¬ ÉÏ”ïp]䨅/BÇ7ƒªqÁ/»m”ì–Œ.Ø{‰:Êü³ˆèà'yOY»Kªñ⬩٠79JïÞŸÞì•MecTz“™M+Ž¢2Ú|~šùÄ×Ã0.¨ûj ßCö9\çši¼H$6Uæ9GÀЬÍ;YvÑÇ|¸CZúvhZ» ‰ê"Nú:Ûmº{ÈóÞÚ|z¸àqEÞGëÙÜ]‰T½¬¸`ÒaJüìŽÉà£Ä™-´ˆžšBÞSÒ¼=ZTܰèí:Õ·“Àîè½¹ø}ᮥ*œŸ …@±½¦Ñ©³û©ñJWß)î}Å¢z™NLì«\-TçÕt2`Ç‚±^šu2A|FL®CÄÑ>°§$hTÇ u½¡Ð=?£NßÞ— LûÐ~ù…ڄΗ7#ªÛ“ì€'áð |€ï;+™Ý3Á:¹HØ7γBî±JÁ ¼Lxa¸Ë¼¹åÜæ̉ãÉ™â„ü´_¡\@xÊÄÉ^IYm”•aŒ¦ÿ8ÙlÚEë9t+üh:uSûL¢z#I—2ko²Åæ¶z©§Ëhç5–¿hþØ‘èßû’LÐÏò8ÁMå.=d¬â#<“«GSKá ˆâ²:W«~ÆÎ²žúYÈïîeòaœ¬¿Ã%èjw’ˆj:”ûr¢¿Zv·¥¹ '…Ws®”Ý» -Ìþ%Yí`!«kn‡û ×P½ÉU&ô’áÉ8ÿÚ¿¡ÑªG1D#ÃZû[Ð@ ²j lÒ¾s]\F÷®5ãe¦æÇ!ZЧ:F"ôïCÙÔöY®2¨–jÎÀ=(Ñ8•MÇÑÅÁŒ¹PÓ•ô½¨eWÔkŒÿCRU†…Ì…c´EGð mÍÖÐ®ÃøÏü{ÀÙ3ò…ZT@ÜLJÐ+-NŒ»Fpøü؉õÃoÓåî«Æ9㨕;€µ¬9ã¡mèµKëf<Ùõ½Eëp{}P`”'ÀpA(AC½”™‰.†9–«d$ô}½c“Ç#–…S^Ó¯BC³9–`1³ÊnÃÙH¥N ¦+ pùBÑY±~àBLy/ "¯ó}âµ]ÄløÊt¸éA gŒÛ… ‰wG4ÐG%ÜNô,½(rßĈǹšÈ2új¿›VÁ‚6² G¶jŒ¿ŒÃÅžÊZ±¦…´Ë-ë‘—BлÁ†Òg›âº?wËÚú |itR[‚¦ÁjwIi r¤/‚}Oчˆî¬vRs’€:G¦¨ÄLà r€aº ýÃíꎫ©¨}½k€Ã‚¢K¿$«š­q†væ7Úµ1ú<éLë‹Ø$:Ë1Ã@:ìÒæŸÑym¹Á"÷*âOz $ë9 3µ¶é;Àéœ*K½ëB` ¿‹ý:e, ÿ4ƒ‚"¦ú(ä7mÂËŒû‚»$ë y# m`ÁN_ü+zhÞï5(d޼0ð¸Ä;v§ñ¤b¥Ìᙩ*Lú¦¹À‰FýÉåFz_0A¹^ù(xi™¤ ¿¶‡#@ )x÷ÊֻɊÿ)i™ža…ÏÔŽGÓú'£ƒÒž˰¼•{Qï‘ÜۖмËÏX&.nÎLr‘®³y;¢¶1ùþxx:FÀ(¯âîlÛ¯í ½Ÿ%:s—BÞ>ìøSÒ|u3CoílAcâã¸Ù]e¬èÈ yy~|Ôš*F0ý¼tpD\)ÿm…xšRÁîëÖvNÙŽòÄ·ŒÊÚ]'ïñlD¶Òà iù±Š¶3?… ׿kfb|e‹8]GÇVТÛ”ì) ‰EãÛ4ñ¥2½T`Ù‘*Óe¿[èû̺gEƒå4ùÖ8áCìàm‡sô£õV#ÐRœòú05^i¶3CﮕîÊ/ïn¸GÇÒ×€žãÈ9CߟºÑhŽ"Ö )÷dA¸¹B„Gnev¼D^7‰’®‡V¦gù  xn×35„ž17wR¨DX¬Ø¬7+å ÕTañ\ɽU‚èäÜjËO¢uªÏI{F !BrAPGíþI¥ù™ÏLœºPš.IžtïzD€zX9áH„åÄp·|ü;ã×±¤f<cJfÄßÏíô-aNÔM8·{²>ŽÿY:<†ž³Õ·ÁãhúQÙéú’"ËhtˆÇê«|=¸:FkŽû%ñG(zÒ"<&’l8ýå£CŽÑ‹Ð*~ g`šFÊ£ÿ%”ÜÞPìgÞ zc×-`O‹â0§ Æ <›SÁÃÊɱûWáܯ`}bVø~ä¶®Ñ š'Š©‡ªÓÓ2e²êÚ¢q¾·éªt‰¨6 tRÇëÈ—:ûÂÆr{‚øJ5Ûÿˆ–t¨&„ îæÓ½ç>>ï±?ÑÊLJ9.["L?KEÚø1‰]Çׄ *\5:^‡íËü!›h–l9ɇÔÙý£ì[>–‘ ÕèFè" ^SÈØÛäÌêX wBLIk¤o£–qÅ~ºº´¹Ç6$Ê1W…L¿AN×yhÚ)/ ìhB 2”¹Êà9Û³‘‹gùaŽ5ïE3Ÿú ~|ËQÙŽàÐ>êõ¹ägÈb]óa÷hÃG:ÎÙ:ï¾@á‹É'ÉØü/*õI—A1/¿!~‚ß×mpËá4t®üütNã3õ-úY"Ö(õç—}¬5 v òç B2H,G‹‘!ø¬e¹Z’7î^óŸ¼R@¨ôÃã ] ú4ìÒAëöÓWÞ÷žq ñ­¸€ õF¹åž½aŸ2¸i­ZÐ-ÚæV;þ` ¿qŹ6Mýô5ÀÊÏèÉææ³šQ)'Ò)/ÚÍžö²(øâVI ¡s6ýòm…¯gy/ª –ÐÓWˆÓä77~p"Œb-à§îñÜ&óÿH „$ŸèrðËMnÚù ï¶I\ïp:uzhNFÖÃÍIÁ;gÜ!yyš_}nOšz2‰Lkç–UTH5Èï”ÎàÉÇøv{‡Ÿ&•vTŠP•ˆ½øcú­*¨"øŠš+`/-‰¢`VEé˜{†vÍKñ!¤È ö+øÇ-é©ÒˆëÞ×J³HÐÉÞ{sUÃd^÷TD¾á¢D1 ûþë³uÛ…³É£ä‡I4Çù ®ø[Å-þé©`—Àz¤QyVZöfÂ)-ßdz} … ìM]5u…Î@è~òUr¢kÚ†íÏ®Ìn§‹q­Ã7—&tQ<̯ÀÕOíiào7õ»(ý ¦RÈEw€¡¾ê^yò0P³ŽÜkí ÑB"×I=, €áEð¡®ôÆÇ€ÈG;ÞU€Nþ_O—úÕœùI\û4=goöÔâÒ›¾ÖÛ¤vn}Óø§>_VGÖ+Í5ò»…èš7 Ì«‡$ì¼õãåj¤4n2é[…Ÿ³XM¯¦yÿYÙÿ1èäœJçßÀW‘»’qÞ´=jg˜gL÷¨h~ãÇj¡I±móM)¡ëgÎvD /ÞÄ–ò1¹|F¥èâ™»ùsNt> ô~sd¢zbÇt$ÈÀ$Ô(‡zq.®'¸”¹v8ädgÕ:¨ "l~OaÚ$¬íÃi FLØs)ꙆfÏxœÇ£ªˆç&`嫉èe]FOúù{ ²î"Ë?zãÌÇ©„)ë£4§Ô!\Óz'ëÿ_ð\ÒÁP‘÷é“’½«1a饛£âŸM¹~Ðþ¬sYÑæS­ŒxÁQƒ½o·i"ì Q;Hºá} ýkV2R¹,£q Ó'rq:ȬŸ¼Âç )6²æºi@».&ùþÀ¼ƒûØAc‘~(ÝãŠüÓx„©œaÔ].uH,0†Q,P``ˆ–›m²i}+èâh.‘WDbUe–{ U‰ÐçyQ—’ŽúŸØîœVœMN+ŒpË.aP¤…¥ÃI¿§;°_ÕÖß5‹‹\9oSÕÄàY ?ⵋôÊ€'ä,O£zb‰R ’³z2‚‡D€`ÇáÓl~;GáÓ(—Z³²bˆáœ ç4ãXµT­gnºVÅ2£ž! Ïa"Ϧ 'n†}œ,_ã¡E˜¾t4ØáyÚ?V˜cÑã²ïLÚ%E×'üÍÙÌo ÿdï_Á’Qƒø7­Æ«kø/Êü=.tô¡Ü—Ip»’)â’ ˜–¨O{F³tœT _x €—‹8ÂdÉO*¾Ù*¶ìõñqZÌWû¿¬Àš?znóQØ)>ŠÚ]² c͘—¨H|È3'K~÷P íÓÙä~ø_•!=³ëpÇ‘çüðô?MÆfh—^sO}\ƒ<&,{3è¥åY“ÉñmVΰkç(15á,ðÇ|.ŽmF/ùCbâ™&àoü] 9he8wwnoýLøºŽ)Žx‡½P ò— ²ó@ZÓ%BüLw½Âì7Ž:Ôq¿¿úMÓ’ŽKYÌH}¸·lne|-9#“¦8¾¥¨…=âqbaDE6-WN¶¼ ¢þGÐäi.Ž@DcëInzúžtüÞË<ÜyÔ n%eh[0>ËTþý^é”Ï8´QfÉà$-°¨Œd¥‰è³«WѰç) bö‹ï›ÙŠ3³üa‚ÍZ²Žò€¶'%¹IBWOàRÙË̾¿ÔÊÈ_É*(Ÿúòð¬„=²›t )°æÃ”Óq 7˜j]˜ÈÿQ9VZ²ñ CµQ²”“ sIFn*TàüÂà 9XnŒÃA¶X`¥¯Š^”<W¼Uqìp;®%ÙëªLaNIçr`2§s.﵃'¿‡¥%lpÀmA©^rД#bqJñ Z¤£:±²{ é|°þª+Ÿ} Gñë:‚:iACWDA!%h†`ŽN°Aòvä%‚UpE‘‘“Väùhv–Û Ëÿ8ÎrV™Ð½:rZyñ5.Ð!âï´nï±T”câ/H‹¶Š>BÔÊÐESì¼íŸK3,IȪmb4áS'yëfv–£Á'T!ܬDgÿa~³\="¦ ­f½ADZäjÐ(ñ˜¸_¢Ðô3°=Oº%C7Uö'jwN8ýÚCb½K¾œÍÒz £ãÓpü0ÚÜv0ŒÊ‡vÏeàÎM½Ù`ôjrsӢĩŽ5øæ÷¹\ÉZ €jKµíØÀô„¶¬âOrÖÛ_R6Lµ vòëî³½>›‚#÷WÜ÷E;dß“*Kt~zuµR3÷f¯Ý±)ë”"æq5wEvÃ$—¦5Ù™Npr´;¥f7®b8wó"x™c;(Õ•ðdUsÛÏÖ8b'Ò‡£§ðÓåEs0°˜ º'Ø)Jn¾Ýbª¢¶1â²®ò؉æïi0‹è*œbØZ/¸3̤—gÁvÏ9ʀ열ùÔ%J ªâì§ë‘ÀÈöÓK/Œwìí¾ûvÝ âæ6ëÜ€v—D#gÐ"RL+ê֫޼…ÅR^ ¿&=ŒI°áú–À¥Yi¨ÚTSRºD>f­·ü÷êCU)›hìܺ¿y<~ã*‰«ŠŽ²$y\žŠå nÆáxw qzŠ}†?Lcúi¡m£•Þ´µH…ÃNü0$Éò&Ù,G…ŸË“KÕ¨ÒCAÐ@øWþHz¬ÞZ±±ó¶åo{GXâ|3¬iR!ÑïY´A§êƧ£›æ‹«)Éå¸y9ÅŲ“Æ•¡½Ÿ Ș‡d¨j^MlìÀ®Ãþt·!õt‡û@Æy¹î¾™‘oóò}’;­ÚË%×mèÇ’SšÂXË “€°'¡é8 xù-î.¤ß†¡H€Ã‹•×JîTŠ#:å >Lëá"-ÿmyG:²|d ô+ m âAŠÒŠr?Òè¿:šQ„å¡¿®ùgŸñüBi¥L:1CE½ƒ°Æ‘\ „²‘ŠR3Ī6kö¸(KSëó¥6è¯nc˜3GÔãŠUíãÆÅ®ÛÜÝÿ[8ÄD÷ë÷ó » Ô°!ÌõC5à8ˆÔ þ"²ª=‡ÚÓÙ+£± ºÖ[µF…óP¸¥o «ú–/$%!/äJȰÏw"ØY^š›`ãY/+BbEË +XóñQì”åöúÛßo¦Wðõ»þÑyü=Yï·Ð3øzZßo±v·Ô“öúþßPOÛì(©*Óû$è-Óû†yIãTÐ;Š÷NÄ œ,É´l¶+6E÷½ RƒÎb*ü팛HŒæ ÷qæh¡¶mS×4Åa•M£.ÂA»bú8/tÌQ ù˜œ ›6 C1™H7ëlÉGF–^œ3þvÿT)}}´|ÊÌõ/^f±$¿æ0u-W)s¥û ¶Û‘xá»'_Ôñ4U¢_ZÕÆ¾í©Ó0ÞôåÌyW„3J¿…4ôªé“rÖªä<‘@=^5ÛÕn…G9©Û:dÁh9UÇ¥­RŽtÚ#x8yK»ƒÃ*žI$öP*&¦ï½¾s˜¯QeÃT”…L¸gýw7÷ëÏcsËâ¨d*Îz[Îúoà‡® à éûôFÕÄ5Ø-L* $‚~±*åÔðß”£ÍD/81 oOš`ð{~ô¡#Fâ)J•i2…½)%²Ñ4e)u;]hF ½%¹Vk§IÚ’0Ôà™*SH¿—*ézvay`h ƒ|˜ G{?ûs,?èìg½õŽ—ŠÖ‚4v¸«þÏ´ÇŠÒÙ^6íûM[èÌnpËÇYˆ4‹œ´Ip…-Äù:M‰­LÙ&*?ƒËòM¼°'Ú‰*Pe¬ÿ%¶:àWö5~efssbÝ´˜Y™«Ãàlì=‚°žØhôŒpG¬ù=D<ÛË­ë;Ï:@­%æˆ-¼ŽËRåá£û+àéöð¬wJgbVtßq5)ŸÉEî;,jÇ׈øµ T;DTOÖ´<9BÜj”t±ç k²gñÜI4þÆ8R'á?îI;U‘¼ !\u9²¹qÞÃFÄçÿY:a‚ÌàµÏ¨ƒ¯nÙØŽ¯ÈÚÇü5UP´õðqôP`©Òiã÷ÈŒ?رú.d†ð™Õ»ÍÚL›kÃ[Ða•¢;`&ç„ÅM¾Ð͘{ÐʃÂaÎD3HÎnÑ× iäRïÎñ¯g½ …œ¼¶Ô€Ï^ ˆi0ÖR éˆ<âRMgÙÓoß±ÆkTáL~L‚•æÃÿ¹Ì`,_Ć-^uQ½%µÄ_w¹R'ªµ–dÔ½ºó ³ék²a$~r®œ%â²×hƒéªÂ ì¢ÑðöK¿ªëmµ~†øgN»àMA•Ž(`WøñF‡½éÜé~T¶Ë­PÊkјd]aïï2©B”®HW{.çˆÙî£Äeºí"r2IéfNh×ëm%ïîù!ãý88iý?šê,™¦°'–¦Ÿ ÑR­bRÛçAøÛ6a(<ýþ÷fuöË÷øÍ2uT¨Òñä À¾ÛÂÏâX¸!Ï <’`û~˜epŸìëèßS¢åFŒÉ`ï™Ê£ÇØâ¸û,qKxèpÑ.ÏÑ­ˆÜÛlùHlY˜­©g®„3ùtOðúkàa‰ì¿ä!âZš>Ü€cß]¬ÓïéÁú °¤e EŠñ/Ì@¢*Ñ[Ô£µwý8®MØ…Tkw͸„î|ŒÂúÕ±¼!ŠS:Âe¶x‚)&pm—Ä œÜûBöŒ¯–ëº Ï•¦S¹h[Ž8þ*†ŒãÕ­€`‹í¶zÈËŸ!›Ám5ýViGQžÑ!ÿëŃÐ'€Y»3°‡¦Ái’èÜÙŽÒìÞ¯M:2èy‚¡ä€ ˜Lc0„¯´ ÔÅ€ò›CF.Ð1+jÍD.b<È£5¾LÂtg ut²ª„U TžÖj%ò@AåMݼ‰0×ÏÆ§‘´j£ã¦øÿ+a(è¢ÏûâÁïéû“æì@ ¡¡ØmL484ÉBä!ˆÛÀÀ±n]’ؽC÷˜ÖWýÞãŽ%§¢ú…C®‘¢+í°'¤›zIKÍßbËÎijðY.Á´]ƒ¥šPÿoÔnÝÄËö‹þœJ˜x“jÐHfÁcÙÁÆsl´œtÌ߃‹¤ Ja‚‰öY\ÀäÏV&^⇠ëÊ3ùžÅÇŠº£ß‘…Ëóúƒö ¸î¥ñ‹1Eóãé:`“ÿZ\Û›h¯U åø{¦MΆCÎB:=Ç’OA“ˆ©ô6õ^–F-ßD7òGÕ“¯UJJ¬œ@Áô°ÁVO Üöù”C®ÜÔ½7´f·íR¬å’©I^ó€å¼6¢¢y2³ÑÈ»‹®„²ÅNS^†-ððàkhä¶ÅulêdïÿF7à\òò3§˜ëð.Jeϼ8û Ÿï6:±ZUhÙvÈ2°uÃÈžŸð¼½‹0¹_(mh1¸ ‹7ýC8$×оš'g6LCí«–yRÍÃKï–â«BlW™§ÜAe‘Ö/íï\^àüräK)irxmÎé¼™æ3—½›ü$Îýˆ]¦úv{%mÅslŇP‹ì‘¼ þ‰tfé[!:ëò¹nôÜ<&*ýÛÓbó Ï ò™y­¢8%j˜ð=ð‡ MÿÌÂ`µ)?#dQþñ<ΓÓGbem¼l耻vc¹àYøVd|âÕÍñÑ=E»ŠÄ[¢S9¢ë[muuR7gÁÏrÛ'»EæÙ·*W‡âgð!Ü®+u#WGj7k_8Ä1—Lp—\Åz•S¯×½L~LSÙ,’F¡¤àK]ÖèÞf©È—uY2` ]©ÈéÞºV ¹T¹nòìVs‘4|§SÿvöDÑÇŽîO›GÅ‚yž]c% å¾SÄÓ`Ñ6FYò —'‡-î2ÆÆB@AIþReVµÚM"#ªÓúüEúœxù8Ô¼6ˆ;Ú½”€nî¹Y¡‰sJÌêáaOI—Ķ ·¾ššR™„ CJy›z¯ÀqÚù•SfÁ,_pšˆF M~çÒ2;¥ïšÛ=µv,!%On2…®¹šÔÞƒT˜v€u2ŸÁþœ ~¢±ŸYÍ<°"iHZÖ¸ŒÛ6Cî6d Ò@¼´¶´ÈÅÞfGÛö0Ü9uÄž'ˆÍ#?Ÿ0•â‘^¥!µ—ï'0Y?ˆ|Û’yi/I#`ðUÔÔÆë§\¢@,¬‚_0|VfiÕ6Â¥nî[o|ÒØW¯ÒnpÜ@#'·A·ñ KÐþmZƒ8ÒØí䬿àÐÑñ ·ÑÒÀaT¦& 6̸B$v™÷ÕrÐJø£=¤Ò¨dhYOhÂÕ*_,föóÀw¤gï­ôÚ¾ikæ¦bEöq€•?[;l¼H8öØOWÝöÜ¿G¾¿O~…4%Í™ÜYÐŒy–Bz c¢‡a‹¾óÿzøŸ5C¿'wx²Z‹&±%°ãŒáAÒ<á #•ˆêXd}¶{gIãÞôs×Ê8$E°Ä~d·ž¡|¦Þ¦A£ å¡6úCX‹­^Á €É’Æä4“_L*&¤ÙS¸T1Sœn¸yñ7E0á%ªÁ‡Í%µS4¢Qæ ›« ލ àít¯¨š³â.|ò_— âö °¾ÃÕ›5:âhð0Àõ]VÛþ žûU`î¿het’ Sðj`b@ÍåѵŒÎ¯$¤„tÀ›ÂùŒŠú1 ©çð±Ó©Éz‰ZÏ¢ÿZKìw´E×2ˆ'e²î\iþì;‘/à®ä¢&ÙÄ"«âF·16bÚPÑgë@Pµ’ÿjvmäö¯5Á÷¨í~Œg‡ŒüiÍO Çk,z}¤¶´”O@Rç’yÎŽ"½-m3®-@°¿rfqŠJF{Øœ pÄ·¶»÷¡E#(;b7"þálŒ¹Ï;³èµ •\ϯ±Ë• zï3U°Ã¶ÁpÂÌ8Eœye8ŠöÈ™a¾t/¦‹ÉV‡k<èmñ8¦&@”‡õ²ùîx[V91(ìÙÌΆ49¶4ßÅä? Áû • pK€Êv"̬ÐBœ¹‚SÜúh?YŠø}+®ÞóOþGa*²Í‰oM¡=2péH$ä´Ÿýs¹@{SóŠWTRܰXèOÇÜ,RþQ=žhPßM’ÓÌ—¸,vòÿ"xJÕ’ŠÏËm$u“»†è:5n»{• (!3*ÿ^=Eþ0|H')Øx›Ê=œ ÛÃŒz™]V[f[ÖRžLŠ¡> qu[£æå}GšŸ© iðru½“/ƌ҅ïÀùX”¥ï¬i õs<ã+)šÌCÂ_ÔºCiÑ/´ƒÝ E-FZžßÿn}Öm;…MÙ1"´?-žÚ ¼*XŸ]5iDX¸®©Oã®Ú ¶Ï^Æ÷ClAjZ&Æ),k/RCÏóºtX» 1¼êvºÈ×þkL`ÙÎľ„°¼hç%›GÜ$?⺫Šf õÛönñ­«q«üjáûtÁ½“[„Ì¥:UHè«¡hÞ]²ÀÜk[ð4ÚZÞ\ï?ÚöþTã¥Iµ=¾ cÙb(1‘,¿ fGŽ”ÎbÁweb’Plÿ5´/o–V|TWo&ˆJÆpyŠE’Ž)­œú»¿P‡g¿ò+vÔ«Õn # ÉrQV–ŽàÐQºš=š^ÿd4ǺÆpÆR§Æ Zeõ(Óêlô…oçO§±÷l«®B3qÞÌ BܵeÎMŽÅ, ©ŸužS}ç–ýž¦X!ÿy™ï”ÒàíVz¢LŽ‚;8ÏZ‰Ú²Ðeì?Ú•_ª»¬‘2·aŸ®Nº{kü]ÝÍ`DÉ^h ‹h[Ðûèw1lŽŽ×¾8÷¾m{†1ö~)½Ï>RI QH¶ÒöEVWÿHt(¶DgÉŒE åÍÂzæ)Û²Xü®$À4S>,YyôVýg ;€Ñåå™Bs*C£€;‰ß: “ mç`Ž´…ÙM¢øYmyrfb³¼N…C õÙÿ2"FGàçÒ§&¢PAý¡:ðoæ2ñð:“*kô]Y‰ 4J+Åtr“±E=*“·°æ3>ÅN$<Ám™Oå‡üîÌ¡#X|¨eïêÔN5œ®ü2E®NB ‘ER%p¤Ñäá‹ãر•Z'ÍÈØFprºNg¢ÄrùÁîY¯¼³ÌœýÊÑô/±ˆUµŽÑ?î~Föbž‡¹j`ª­‰íçöÊËÂ\Fé0 ã%Ž2MF¸Ä"E`˜KˆPçŸ ûEæWù°_ág­^»Ÿ£b‘<ñ\ÑKó—ŠÀ?¯UéCñXÌ_»T^IÏ£ )DB±-¢®Mg´½‰–E Ï'±ßÎ7*‹àƹ ç@Œ‡÷Jßñ.?dX/ƒ6ª|¦ÿ45(BÍPl‹ldÚßO‰EPþµQ}êö®iÊ®öA"LŒ=¨1?4ø ¸æV_÷/˜2ò¾’úèÞ{ûFp2GÛ­ DÅ¥e^NsIœ¾ËþZç5&7+«…7üq_­Ó¶.][18­• ¤¥$}QÉ|r!Rݲ'F%qgxdD.bë£]ŽY­1jSqüä6wÛå†7 ÕoS®Íƒ×ëª×e±âüƒ m!W¾I“Û3øýz´þç{ Á'¨êÔd'(ɼȷ‚Ìü š¯¬A‰ÙYˆ¥µÿß.Æ[<Þ|€Á¹“|Ú*ŠŽ/ÌŸì¾>Áïˆx å¿Ç¤:ÕçQŽ¢#UŸ™9ÛXnÅxVûzLÚCåÛ†åO.aÜïªÆÇx‘¨ßíµ¹½±s¢ôKÎMÏ9pv@q Þõò6ºèl®ï\‚ZæM6sj,ÀlCȺ’b'E•`36Tãó_÷Ê• ZW¹-; ±ÀS#¿ÈÈp»Ó&Ùúdª&è fÜËÏ['ËÉ-°m«_ô>¢>xð-bÈjÉNDW¤4ø¶gõ.Ÿ®e.Š\>>EUB£Qü{-ýRƒP—ÕË8SS„ yRx#¾|ØDc+ž$*dõ¦i#ûÅðb›¢õâAJ†@W  ²r¬Lc³k°b¼q)VE‡©ËÒÍj>6 Ý4”tÕxÏ­â+â’¥ç‚3“žSþî´î~lªÕÔÏjg6åˆI`M{…œ¥ò*f9vÙAN-ð{ˆæsOàÏ£X™^º| PÍ@+m\«±þˆVOW¬Â´Ù¸˜ ø)ñ ½Ë¡v'@:ÓþiS"Hu‚ºá†‘ÓûXd$K fš=tîâæBföù)lXrÍ’ ÈJGÞ†«~¨ãQšlÍPMË C½Ó¨wÔ<>c††þÞ\)ÖïÙ¯zÝÔmº]I•4µ8•t&a÷gw¥Úõ7ÿ/QÊÙþ¯*ÄT®¿ R\Ïõ¾¥#‡¯ûiKŠ¯Û½%N‡¿s¤¬‰<ÿ?1¡ZAøáx‡mE‡$êiðK1Á`ÞÇh>oÐL¨ÇݬS1KvÉü»Jö8žÂX¿ÎíO‘šžÜ†–ß.ÝF-äN8\Ú닸O“)é]dâóˆ&¼~²Ó›AñÀÖMì¬Ðêpd®¢4§ >3—3ÊòìA²|$Á?>›T=ô¾wD;Žç?’Ö­l“8ðIÍT3á±ÛKwÈ•‹aϸô„7nê°sœk'yA¦‘å’e‚õÙÀÛÏ‘#A˜ÁuO´ãA |D»ï`ïî“Ì‘Á=,Á¦š¾1<¥ÑF4ˆ˜¤A@Œ P#›²uK2sSŠÍ†úH!19Ã-úöma¬ØÁºÃv¤·á“5…Šò)µÙ‰ /Œå³#E*â´ñCãE½ºb üW&Ü|Ò‘g>AD€nšá<2Ö}wà-3¬¾Hë®?PÛs#݇%–ØŽ€ bÉ(Å”ýÃh7Îîç7†96ŸXR?8ò‹Ü] T¶“ tD‚ÊîÚô€"~Óú¼wH¤ûƒx\%š¤ž˜š»²sŠÚO§óOX;ÎI^ÝâÑQž{ín¦vZ¦rqz‹±´´øÔCU_Ðþx䯖Š/zhÂÊ^•p1åªÐ‘íino‚1ošÜ~Ž×šÔSN;ÊÅfÙØ? ܆)Þ¡tq›Œ¨hzoÜzÇ{™5-é‰ó5ÔµP¼­nÍ6)Š+ƒ?˜P,%¦L2ˆÂ–^å#©.ZJC·@ô5‹ÅßÃY0¢ÅÀÍDbæÅÎþ—jµÛüd› @ 5Âöj÷×× ØÊª”áRûL¹ í5ƒÏS¸·ý%?mk5Ã@¾À@ K!iﯳi[}(Cí–6,’o)Ž ²%><é*0³1ýfª‡šXaGUÈB“Ÿµê17iÀö)ØŠôSXÁv·ºó7Ï•¼£2úPë í% Ùò8Ü“àÛÊ/¹ Rµ†VöÌ$§ëX€.­ 4#áÚº7ÄHPþ?0˜ºM­£é¬½A3ƒfÄÄìv8× .¥YŠ‘Sxô"ÜW;­OñÆÿ+Ú—éwÃçÍ´ª…kÄ&É-3Š(QÎ_õNp.ª¤ˆ}Ÿè8^€þø- 1M(nþn¥:9߉5ðü\¼™§:ÀJ|N§ÖÎ/KÚßp=Ó­šÞà,þBLO÷â»A—`æ–©¶è›T¹¥H| ›T÷h¢Cÿ”…‰5iíÚ09œQMÃI›Ïu³|%›‚°’.m>ïÄMbÓ:‚6^ûÞ Y,]Ø|1Xî Ü\‚[Π͇þâ8e…eÕõ¾- Qv°Z$:5 ðÁºýѼ¿B¹±ú&úvamûv)<»jcér‹ðø!§áÄEóMU_…’&ðp7¢Êì¿À*¦uuüÚ×Gäû}f ñe­9 ¸¦°îI©ä<˜Ô?M%>¦ôãâ;_úF€3Ô‡Å_­gÃ[ËIÛÑ™cÚëõx´í¥Ë¾ÓÓ…îCTmIEåiä ?0éÇ9\Î+vvgc¨ýå,ïsO‰ú£»" Ü,—jmaöñ™ £¥8D¢¡¤j‹[þw¹åöòècæA´Cq^7—L±à_ÈÌů\õ„X(RgÓçË¥ÛPá„Ês2·àmu2 ®P²-ã ØC€bªC¸S—V…h¼g¨rðNÝÙu&²L†qö^ÕÅBèX!U&H€ø¾ûÈm‘꜄¶È£uÀ©ƒjI†£+Î'¼Ê´eWäçøïÇ@ã¤CríI`È@¯©^Kžúh¤È#¦¾«œ’HFŠ ›Ê:Çð(¹°M@ô…1?ËÕ\eÒ.9L;rµAiÚ¯‹É¿4€¤¥´y>:¿Ãt`ÔB¶mœ÷ "n'ˆÄHGTöÎéË9RN@¯1q›Þ"»Š±gTáÇ Å#È'TÓRo™ä/ö Œ•W{º(ªˆ0TýAV YKÌæT*JŠð)CmjHÙÆðfaß_ZKBjfîªäã«›T dOQÚ&ôaÏ ªn=!VÊ }7в¦+^£›hgX2'Êò:#‚°Ë]7$òQ¢—5]ûê÷Ùõ‹;M‰Ü"Þa±gš*%'.%·¸J<ý@n¾ñ~¤ˆÜtyø@º]Á;¡8 ey3Q„~tÔ8W´‰š¡xÍ]iöã|ù¶–¾€SîЮr¤ÉÚ±ŠD×B|BµâÊx@æ@#§w9.l)ñøÒb(;þ¡·iJ°A†Õÿ5ÿ9HÊŽÁPR&”{z§O/”_ãs‡ Àú£¦J€>uw´K£XçOů&È–Æeà3¬ì"iEv :ý6„bIJìm[¼”UØ÷lSñJ¢Þ½4¸[ÓsÔã¨Pr  £¬ü¸k´,0Ü6_òoÎù¸r©Wt¼‡rùQû@68²cTµS hY›USAKYô§«NÒW 4Àcbºzµþ!-KpG)î%¾ ò®z NKz±>…YǘgÌdf°dp]DÙåFÙ…rØÆ{pÒlWâLãÊæbôîÖŠn¬LÆänd4c[ünªŽªuª“Àõmª ‹ ÖÿÿÿRz©ï’a| Z)[Ê-rÒ{"œÜJ?¼Ùa mY²›ùÃV1Y¹AE‹wß ©<¯[Ù%m¿ Û£ÄO.Fed‰s~¥;‡ÙNšF«ù0÷¿Ü½ì>Óê#6Àü½ÝÚšámŸÕË?Á›ÎÈ7n½€¥1WÐVÈöV:Ø€Öx ÈÏC lû¾˜ œwhQNd îáµN³°³ =”ÌQ.ããŒuürx–2ܳ *_ Ù]¸õQ˜Ãbé(LÜÆ ͤ…9ô™‡¿†±Î+ŸÚ}M ö§}|§vøŽãxô“ª·ñ/œZj—Óî¥FÇëW¦·Æ~̧¨24#·FEIP ¤«ø¡+ñëOÓü0VáûuäĨ7loFO'rj…9Í“É9ÞzúºÝ–¯‡~<;¶ ©¾ê˜ÆNÊ=ƒD!—°kG‹„Å(š-\#á韣߅cÔh‚”’h5\°éã“WÑ(äoÉ(ʱßë^’é¨óß,ä´v¶fƒ³H{æõ”qÔñ6é?<õËI-¥T‡åXL¾ø¥qšÒ\+©sÙþ$ê´Þ{G ÕžKâœqRka‰]Ý1#ÌSÍáÜŽÞ7Ûnw×*¯\×Ç£ê©N|üÛeLn&óÏë“ìC(Ü\^”І!®SÄRª7>\4ÊâƽO§Db¯zá,t§Ó?a‚a£{\H†r²á&¥ÓµWìŽèRŸº#ˆ¬î÷ K„ böȆH…ƒ2Å3;Ù(Pѧ6‹ «|×yãäöÑaÔñ¨Å-dpFÅíî‘°àG„rÖ(•O¼LY-$ÙGá°½r(gZ F®rÛ‰VtýD¡æêDûJ.:aí ý…€`©rmèo\—‡Fct¥ä‘ÅeóYDÄ€azŒ-ïè†nM˜´J à#s ünÜ'6%–#zé¬.™,«7À„±ðû°çr´¤>âå‡]¿P†gelBÝL˜}h7å ñ÷¡g8ÚW¦ål•κkñŸ§Lå°ƒp)ð #›(I2}ÚW‚¨M˜ÖóÌpPô’h&P˜¤8y¡Ôøã6ÔÝQ' PÞΰ"æZ._ç3±ar»?ÔŽ(p¡ø£wŠÃ€?P €d­¨Í;ŸZÁÏ`;¡pD mhÞThÇc±À–èjõ#‹°ÅÂMˆH¯ƒ¸x¶¥0vSà ¶.V”Ò žW,1Ȧ¶6záo;ÇüBhœ, Ÿ2öAiSlýÆd¡MÏŽžªú„›ZÇÃuècòõ\ÜA»ò=Ñêæ×+™PÈ€ªp^öUiÒˆ«ÏÑ, ra6îlûÔ(¨×sbT^Ńúô!£dï€DGrVl­ë£QRYB’Z¯+}!ŠêŠOâ·ÙÁ¤# Ä3tx›˜=æôt#¢Ú¡8KÚZ™e%wŠöo€{ÌôM,ª—³ -„Ó=ÚÖS¬»ãþ 9r07¦Æ…ºí ²0Ÿ%üíúë*Ê(nP½ ìŒÐÈп¯ŠgË÷Z¶FIb@Azfý™8&½ü"B:ßA[é‰5D¯EiH÷z}è™øƒLFÚòÊ>ÝW y"d·f]æá,u¹º^Ý”rYrF×åú£žÓÿmHs©_-y‚Ô¾Zˆç-Ó‘î>”Ë~ê€åã½NŠ•¢w€ð‹Øªð!³ñl"ðÛÇ%=˜^‹®·c{ÑìfÏù¼û¥mFlVHÀi5ÙíĦãØõļ¥L8ßý¹LsÄiߘ·«vůc¬ ˆ ë?I§¶ÿ7it>Â.-Êã­X©(¯ë0%ó• šI½õ¨íÏåYÝ$¶‘VÜßx¦UŸ LB@üèqýWêÏéýŒ~ñ‘æ÷Z¯J^q.q."Z¿DȨEnÕ„éfO›š¾ø'´óGyöѾí]dQ\´j‘ëV÷âeÕ¶G#hvQ <‘¯1ÃËò?Û58ÛÃ×[ ùÒÖY“þ¸6ª(n0¦—B<à“ÑæŠÍ`+ð¼‘ʆۃÙuö¼fD/Ýâ &å*rªµÂ_.Á¼-;à‡º}u]¸4ö.D¦ùÍáí¸·¾Ã‹yþ9NGn>²Óó®ò¼E"¥SÊ釳V?Iü.’”UdZ ¢#.K#wâ¶—ù˜¨ É}*F­0HdM‰Ç†äÝKùã××ë¸çëŸua–‡‰‘*¼'„ã6¹Ãpº²8¤A³døí-ªGÜ0nßþÀuÖM³Nrs3pqîYÆ ¨³?³«,Ÿ;ìkŒyqdF¸f³ïÝ{(ŸÜ¥m§’fCò¤,°¹ÄßZ/š÷¡Œ*1e@ûæé©“,ë3<Ø]D Y"'«oç7G(RJ€p8lËuÚiG)OeRP›Ï«–ßÁðoçâuc4‹JaC¢Ü‰n‘j¾ð®¿8 Ü8ÉK†vs–]1›d"³Ù¿¯{VšNÃOüŸ¥€Á‰Zê(Œ ‰LM„j`µ;šhªíCl§D”ïI [Á,¡]ªt“­k«•„uœ.å<лWWmuzê½¥wã1m|“‚Š?x[¼˜×XN}e>¨Xo¹+Ñ IºÄFñ»3ŽÛê\j…e?Îl ¬EÆY%Æhq ˜Úú–£qýËû~ü‹{ЬŒ#L¼„s¿^ã#‰*Ý«æ7é”tY›‹ô«‡Ñ(¹µ‹ÿEi/Pr¡òJȽeŒrfÎc#‰yw¤ ¢±{{–-âs÷ÓòUÇ ÞÌÔIyZ~ ã[z¼(¥á°Û¿hÐèƒ –âÛI%qyšwÕGZ7ûÄ¥¤r:ÎÚˆ¶‘ôß´ C›É=£Åò”Éûfš…ë…çêsÉ}‹Üvw¿m­kÔÜãÐ’½Çn¯îî³<6M„󷳆¿{‹£/A(ˆ±¢i)½[ªLu€ê¹–Õ Oq÷òjOh¸¯6¾šUºDÛD‰z7Ac-4ÇÓ¹YÛ+µ«HŒm%äȲùÏ3¾0&eéã:RͿݽ7s(Ÿ¿ì®«r÷<:}g×ÌX%ô:QÄ•êT@anv€I¥ÒFMaÝœ ˆ¡Û ÷æSd~¢åG­~_A + àõÔLêˆO+ÐY^µÕ·Jó¹Mò©Êâ%“™%M€&‘W‹Vª×úåy¬Rs$­‡@À'm’lñôžŽ†ëó((ÛðnæIU¦yc!REéIÙMk>RGpC€ ™¶ ž'G:k\=yúÛ¹ýÔWçwž¢\!NÂ…Ê@¶Ñ»‚ àaÅ)¨)ŸhØ)Äɦ{0¥êÝ´ª“ûÌ´$ÐÐýûüɡޜ…RèKÇèBÝžvkf‡EÀ@]hÔykÝøS%åí \^ºèHè^ 6ögõ„5Ò<úgaÌp黾Jª]¶N‡¡IŸ£ ðl@8…m¡Ñ/ûæ¼®ÆJMKSû“÷ÚÓÒB;½S_hy?ªˆ EïÓ+OCäáI ‹7º.íÔBAÐë¥nßòœK¹ÛÕ ÑÉgè]ÊËÅe^³vnš Ô¥¼E°f'þ'ËÆ uÄõ|'"1œxT`?XÌA$Géäâ™'§ÉucigÊxªWðYõZrK¢ "š·œCó/йÖÞÍmËç¶ÔøCŸ­eõòfGºÉaõ¨©Ó[IY¦Sq_ìFHTló÷¹¹Ž3€¾S:È+¸:×U¸jZ…c ãòò~EÀpzàûœ![)§Žªcq$Ð’®w—ŸñCðˆÜO¢ûQîúé»W6ÕU 3~™kZ´—½< äP ¨,r’ñ"xa°ý^ÿzˆLñ޽b =2J‰”¬¹µ*&/UP`GPÆ=ù~ð×Òº"–¸@Íi™x©l9Ü ]Í3Î9îŸH™¨GÛÒN[‹Ó§Éì Â-Ú!“h„J3º&·ï—;óúÎrÜAöŒ‡Ëq;ºb¤7Ÿ{|–õš¿‰:a/ö©Dûq×Ã(ÛŽ2ì^Ý‹G¨–@þb³±a‡‚¬sá0 lEš“›EïÚ„&ëý†ŠÃ„\Ù¯ð;ù,ÒýÑþdÚPdúÓ±^›ë|õNB…2Æ&×wxê8…%zÚ|˜‘\Ö˜">¨Û¥\Ñ"­¡PÆ@&ß¾Ø÷ÚŸ}¸N-—³nKG»N“2-— §ëì§Î2ËŽŒ¾qGa›ŸêPœá¬~žÝ§/F:\&á¤ñP‚¸«2šÖ„T, ÍtZKE™Z÷ã[xÊC•Âðô¶AvÁ¬@¾Çùneiíò^ЇUÊ{kªß þzÇ«Ÿö–õea$2×±}l8!6lúål`½bƒ€nsþ%¨ÃfiÿT:ÍÜÓ]pLu(¿ÏÊÓãqqÚÚ n«G¾d {Ç~Ò~͆¨a!øßŠºªÔhïR9Uj£•¸HÙ^JÈc+©¶J n”X·#G×˺‘EèVÞúNî¿LO5D•Pràc2qs?žw5ï:¬J~NØr•÷T<¿ƒ§ ÕJ1"®øXÔM„R-m©!Ýû„:ZªV.ÛĽAnIüÃM(Y %Ž8©´§ýpK/ Åð…¬uð »¿E®µF¦NÉTÚFgžá 36„·¿ýè_c¬&¯Ø×V£â^gÒ'äíÌœ/‹Z{8²RœÑš/æéhÞ´ {ÿP>&kÅ—)ÎÂ~yeçÒ—ãØ5¡¢!•cɹ!…QR<òR{ºè âŠÂ­„ ‚ľòSóOxÎ|­g]¯Çû…ó.?V¼ÃFT¸Úcâ Ý’z±7 óWµ=_!Õ}è(cM×ï˜ÿè™R±!ö·RÓÖ®™5A¡°~·© ¹:Š ö¿Âš"H6Z¥W¨½I¤’I$’I$ßÜàÉU9¶@µzh‹aÑå)SFª»Z3¦öDW2£×·¨WÖ쉥‡tÈsñ MÐÓ:”WB>ýªæì8b‘~3ˆþœf—Ž*úcðEÌJ½5*ñ¿˜è?Hßíh+Y!8é}âõeíZiç/ã÷6ÜJœE©Œ%çB‡4(D’Ì»VÂQs8yõ¼ßr›aUô¼;jËwÎU hl©?hÀH.àæß-D¸ÀåOqT -á3”lb· aॺl°¦,Í~]zµ*¾³‚µý¾†ÐqÔz”ñpËâ;£ƒ™ýÖÖ ÊICWɪZN³[eý#æüCöþï©+…ÿWÙsµi'÷kÌªŠ¥¸•zH(¹§KhÂ@"‹œ ˜ÞŒQnàmß Lb”ì1LTz{,ê®÷Êáó†ýUh ̼ 6ÂÆ£|6‡+<{‚T”õÉ™õ‰™½ÜLÆŠìdÎw%ûÕünœ°2 EaÑQ$ŒÍVOÇФÇ(@¾ú•pø!F²Ù …ΞIɛȸ)»ø¨Û¨"ø¦=ò£ŒDѳ*òVˆ§¦D7èjä´„Û,gkO¼;ö)Ðt—EÿP¥þã6#Ä‹k }V”²Œ•þ„\ÝP+ñNÚnÜŽúœ$¼ü3’­Çç°ç穞 :^¹é8< åÌÃv„Ü!Ë›v 5FPjü¾Mw®›¸Ù7„å¿A{Kz#*—ïùA÷ÔzuÖ‰¥êW€!Õ–·u0G/ç^åºË¸‹¡»;Ôç—ÿ3ɾbŸæêê’GÅ«MÄ3ê¸ëeXÕ ÓäËŽÁ;qFïŸ “1t´,ªY@>ìþ?éÓîªÊAâ[„ 4‰y™QÛ T¢~É”:¥>ƒ]øÚ ÐæÚ€z¤×u&/CœÅË“)¢ lÛâùJ¾Œs5O {êÇ õåxEõ VH艮9(ã?ve­Õ¸ ½Lm˜êÍ+ÕàèOA Nl-¥åtB.pŸ8¤-*¢}²‰rQöcË^þQÅ›õ»Ž}˜ª]匭Zýc=¦¯î›ØÐÈ¡^k¨¦jéP=ªarjt(ÓÎ:"·ÞKq5Ï Ê-¦^ ®™â\äÒ'yDß²q ]ÒªÝnw'§:u§Q^[ð|Õ°WrÍ Š–Œ² ö"¯ÆVH¿t¤ˆÜÈøÔ0Yƒ¹joê(ÄEz ܬb;[tˆí9x)(xÀ*ÌÌO–v=í …Ç¢(?+­¡îìÖ‘å‹{TÝ€¶’@õ6¥—4wª¶Øàºpð0=:,ù5ºÖƒån­5•FLéÓß±ðlôB‰TZ.>Zü 2Òx&.à ${À"÷¦8tmBY) ´üškž ËÉ“@šÐÎÖïp¼CèÆE«&°ÕßXаÈIcè}. ‹ÐæO›Ç\a’>Ùƒ'ls„^šQFÞÜû¸9ôˆ½‡Cúè Õ:ž)~8~“þGm \è­5©²º%Ú})‡nD†u™w¢»3ƒ¼‡ïùC»zì…¬OÅ?9ÐkRnÐbu¯›ýÈr«²û»Ú$\[äÊ„?€;ù ‰‘69QœP;£ŸX¶ôÿwv5„¥›ßEo-è­Ã(ï`Â…[ÓŠ!—3~‰˜…ðr4œ”Æ,°¼lã–'ìÅ( F9Õ+`pª;cÉï_GD$bÚ<àÄØÁžKáàAw*~©““,µ”ûÜpëíäú°!h|u €èÆ|ˆxô*çÖÁÚeꢔË1÷ ÷ºBüÎTXác1rãEçÖE“,-K^XŒ­W%ÅsÊh¬ ÕÕ K%û I‚Ð<~aü²qb×t ÌÜèÏ.‚Ð~]‘i%xzÿØ €Ù=B’LZrP±ÃI¨áçßfE‘&Ø>8ã6u¸<8>sÔéL¨â“]_*›ƒ{ KJûƒÎµÏ½‡'‹rôæ-ôÉŒ †½$-—‰*îùL™ åΚ’àø®¸qÛ ÃV;5r57\¶ýý°Ù¨…B½ÇS†í·ùì7ó=]öiN\Ë%` <5Ï/´Å•Îtï¼4 ë2e™xÑß5KÌmØÁ®KúŒøã…nø|Â&é‚ði C¹÷¸ørsh/f3-¤Ì·jéO¨·bzÞO׈ áÏ”xíU|ÙÛOª·[¯ ¯:’˜(bÔc>,còe-"Aå¯òÐ9Š3úã~ä­½CÃxOs‡ÿt*Ñ÷4T’ I”[¦ör´ç(uÁã²²5ãÄ-+úî–‚Lobhâ£òöûVgS9&"_vÛW:ÀvX …ÿ@drDaÅ…ªq±rô1\õjÂÁD[»ÌO ñîõ)‘«NèäH_Yt(´ZqÏâÖ àã:¯7p‹Ò²òZl܈+ú¡TÙµ8ìÔL¢ °DĹ„—çæ&Ž½Ô›Uƒ€‚;‹¬Åÿè<ùè´²÷Ås‚ùp2]°’wâ!,Í38qÓað]vÏ›„Óz•"ÆÉPÂRBNadÎ/Z •‚Ç•”áìTk¤ —°cÏŽ› D3H¾Wi>âú/Â~|c.Ç?ôfÁj¢€çý9ý³p–z$Öš0˜»´Òþ.rM‹yË …Π̈Œv¡Ú€Pt-Ëw-¤uÛïÎ/%b9zHš¥T•껞’ÂWÈQå¾Þµâ›ö¦ýæfÍÚ±çÝœV²eº.öÍ­r B)\·v F.Ýugù»€BßL·kN£oUkôþÌ\˜Â¥©; Š—5ŒŠ'\5vÍ”–,BQúAasŸ ,›¿s`¯o¡Û¾Õz/îʳÞ"zñ~¯½u- s¤ ’/e+@˃#ÜÍm þëÔë1WN‰k«3YìÏÒ—Am¶×¦TÝý~“±0þõÑ^1r áPP «Ý«ô¨§ˆ}>rò(“X§WêX9 7±¶2 ?Ö•´ÜøùßSñ”£†:E炘:î’¡ˆÇÐù÷l¾¡ZÍT-‘ë†Õé¯p7=_XD|7„¬>]é¿í@ž”ZK E©öBçubu°Ó/‹À0íý²Uü½ïk³Â³^õžœGõç{U¼ð6hç«ô“aVË1ëü¿Œ¬ÕVÝa\{rþÇІ#4öVhDZ¢VÀðyÔ¨ö¹“ž§'¤;óÇ‚^÷Ï_±hÔà`Âkí‘ùSSÎ4>Âíe¶ ©ñlqpV6),q”¯SÓQ­TÃtÏ:•e7tlœÕS 3-`]Ú#0¨X_p{Ã32_¬L©ô”óVx—²Á¦/¯~2 A.“) ~ •Iza½ìÔ-ɱ—ðñ1 i–éÑ%‹Ø=»¯Ò§Œ·3•»àEÝ6LAo)ƒ+ Séã†Xà×=|—â[Å¢*ªÿWÄP~%}‹cÀlP²ÌøítY È4B›¾ºj2”VbX„¢H`ÚK‘™¦ítk*ùc õª'ëš‚J/xÒ®•Å "ªô‰•Z Û¿Œò‹ÿM^Ù^÷ƒ¾>3#„ˆ8¼¦ÂxsÌ`Õ€qÇ"—4WUטgêj¢†d'Ǽvh¼Á€Ö’ü¨—Ë*ÑÕ¸„wl‹ó‹$p›2;Õ•±²´ð›Ò.¢ë^–½ZiÜ=áÜó ¢ ¬•‡ ÆãòŸ3w¤ÛMôóû¼ ±pûÆ6»²13ûžÃþf²+þúêç“ØÁpçÏÕ¢±¤9øE ØD³³qÞ?HKÎ0‡[ø²nsÐëJtë_ÒT8bfBÏ8rƒ>­eTR¢}âæ¯:¾bÍoMy£ŽÕ¨QƒÝ.YD™JzwØfyùžPZpÈ_^ 2ëm1 æñ€\1]õ"5ÂÆ;6_ø<¶Ll…&~^V'åÔ NÒªÒ^_SØ 仼š«ÿ9V”›FŒÝKØG=ý›­â;wX{QíÂ,k?Þˆ€Ò û’È5c™Š‘ÞÛM3&L°»;S¥ò÷¸í“™ÙFVOr÷Ên#ÍÔãzkMø¥"Âí½Û~Gm€9’x'ýxû²›uQ·¶˜£›âÎÔPp÷íŸ.µ+ƒ¶!Ô2YŒ<+èÕ0í+y6ºa)¥¯ëª×Ü'ƒ³â? †ž‘<˜6Ç€À­m+vF¯“–Ä©LJƒïBñ¦9›¡k¾·BöÄô7`~Þ†O–þ¶"™0Ÿà0÷6ã ÇhëÅW‘<Ôrå•[¬eÞdSˆüña>í@—Bov bŽ•)Ó©< wòã ~¤Z³½‡Ùð4=Ro§(he\“œ~v}% ‹À …laWûè3„æëÎVEŠ¡ÜÞQëíQwLùn!(Ÿï Úðö3ïh;'<'è +Ïú{äÚ£¿E+ê0ÉW­s6s;m%XÁŒ¬Ÿ`.+(æ¨P§H‹ BÅ{6.6ÿPH3ÎMw%ný¾ÚÉ!S0*³·ü%Á‘ÊvÍ‚á{~3À¢ú»ßÒ‰$\M/ë„1c6F›HФR%XB·4¤SoçÚ’-²Ó°Ý|PЃ_×jÊÆ"Ý2ž³C- ˜ŽQ¹Ù»j>÷™íä.œ+?kbq²´†åWFÄZqÙÚ»÷~Œ)ÿtM›!B#d¼V²*béœ$ž á®/f¢*&ÇhF/Òõ½6óÎ-ZºOMùh^h‡h½Ó (½@Ðî¿[ýæõ¿=‘ˆ›aá JF?ôœôDHúÔäü¶Î‚çßÑ‹©?CòË$ôqÁsJ*É1Àt»í0{ÏÀ\O¼y¡4STpp¼ŠO-ÚË¢¶5à¬WJµF6¾cXlƒ#(5n®U&†C×¹ì¿NF‹_¡Õó‰ Ç<ÿ òÖò`Ž6_Ä­ÌmuÞsÛrjÚ^Eöï”ñõ=× ºaêÎÆ,hÃå"çåzø¿z¾1Œ…h5ÐóWq*ºÙ\h\Cp ²#—«äæzéÙ<©÷ÇŠ,yt‰Õ`|V^-#6–>Á“ý™¿âò`IWØKï€OÜçB#ÐÛˆn9²|i!oÍEXØ ¿Éo :I¥DÈOè¶ag‚Ð/Œ §§oB‰b½y@òiúÖÏÈÉÊI› š"In‘«&ξ—ÏÒ㎛õyËׯ9¤¤Wù´aFÕ!¾½äjϼnÏr€Y_׃©DìPÌŒ‚®1‹×éú]gÂ͉"Î+ç\à0DJò@=v-Ÿ%àp£ñ49A!³QXV‡A NþßÙAc}–±Š Çy›…ª×°ªÉ³Ï‚Òî04å‡ììà|LÕO@æ1¥Î ðaZΧßân i°—ñga.wºaÿ;˜¢u4WÃJKK„Ü gäîûK:ͺ¦¼Í :íˆ0ö´É')bÉîÏÕkœ~txS*A’SM«²Xµ½L¸¹™ŽnvåyZK¸½¯¨;ZˆÏ%ÿÄÔóÛÒ²¼šu’ © nõŠnDûíi÷÷‚ËI¾`VT˜÷ñ¸:M‚¥þóøïÞûå ²“UȯAiÆö+yC\„Û[‰(Ǿ)cæÝP·u;|x­7ðë“ÉJiÄocc£(›[‚9G“%ÂÿMK±d(ø®D`ÃØÛçÙLIm_¬N4IÛùaZ"jåHvjB ÜÞèü„tIX݈=–^ûAÅÛú2#ý`wÁ~”ý#ôÌcôTt ?¶”TDšŒÐ{B÷¼zïezÿ, ¾=0Õ~›öà5/QY¾øÖ¿ÆAÛú}XéÅݶ#ñáð:Å_ùY 1‡Pã%-"ölxµÁ± '〶֪þ+qv„‰ Qs²V»H²©uJáné >}Tà¡Ø’ÉTä¶œvÆÙ¬Ç8_àéžõ¯¾¯{é†îÏïkûì'¾¯`zßC}÷kßg@õDS.a×x}¹Ý% ä ùÚfΓøç·¤S‘üò¼å˜Èrõ½ñªi&¦èú368¡)R¦èMWÛAzïí uÃc¡8¾c`Buð'Óð=±05±p°Ô&Éy1VùG]´Pã=÷©¼*˦4–vŒï 橸œ|[»„=SÓÆ|}GäQ¨)uc«Ý+xØ#¤ M,rá: mTÊCb»ø Ð0nsºÞÔ´Õ ß ýIQªŒð†Où’öí:b‚wál+åĦö½ ë+BÔ®þNbYšÂV5g”Gé÷@dÛ逿ÈV u‡¿®´…]ƒ\úàŒ™wv“!¢´«æA ];‰Â’ù•îÞÇ[8ãЭþÀ7M*wÔËÙª–Mçe^Q'{)·ßH·¾;€à ñÖˆæ_ ‰Íó)î¶% Z”eå¶QÎ'(¡ÂØÖH%#!¼iÅÞ_¤‰„Ím›—Í ;þ™3ûCèéc!½Ü©Ûúñ¿8mφw>5…‚V!âyýú¶ºfΉ¬éÈ,K{ÒSñióûE e§Î¢*‡_`K÷¡ÛôY*–/ÍFO®›Ì¦”#ª¶{ºª#Pµ”IHÁqPqKÜ'=ô2¯—2e‹‹´É¯& ¤`Í$Ag¨Rî±0ÀU–”ÊH[¦û«’è‘§ü)é¶‹±^¶crTÿ/Ú›%÷Ã⻇¥Äs¦Úöá:<œ¥ÒcÔêЧƒ2oªuK¬ÙBô3DÕõc‹0éBçÅñˆ´aJ™O.E«ÛÁF§D=™h q†´J,=„“ «ê¾ä{„'ø?ê=Äeùd!F|ñL5½ü5Ì­y_äÏû´?hE•làÏvÁR!$ºÎ¦úŸŸ'nŒ ˆ¢®Þ”ñmDõ/†GF¼`¦XrhVpëƒ”ßØó;ÌÎñÜO¸?T@^m‘É #L_çB:–gb\']JwúXÅ›ÏÚAÒ’ËF(|Iñ& Ù ¢u¦«¨¦VIÇ<˜)rŸPHq‡édŒbº¹(šq{;{ ²ÃL™V‡”ÚÔÎ0 Æ—+y’«ë $ã(v“Ëð$lÃ`wAàÏ„ ‰üÆð\Û¹‚ƒwwVºÈŒAµPBk[ø§{H_zëeB –“ù£À­6‡'‡%âq9¹”Â;玱èIi¬*:Ñb(N˜åT/½Žï`pß¡dT [ ¹sÎúù†@uîÑ÷2}˜Ä’ä ½·ÓõC]…ÓMd‚×ó–*ºå5rl³€G=&6Q'ôSÞ8ô+å÷Ñ‘'^LΫ1=vØ[Vó‰5òœKÂÚn ´m‡Ôh–Ô&•Åñ~§¿˜¬À"Â}va0}àÿ"çQK,]Ì2°ÑÄ~˜§Kñš¤rêÛ€ªÏ5ââúMΛ¿Ñ ^W’t,µ`ºVèæ¾„z£;±è7©äÃ/,bëOìS†îã«oÑXÍá}‘á‚ E~H@¤Ýi WºVº[ª¶?*Ozv…—Ï‹úÐ˘„Duøÿaº!^פ¼@:ÈQ¾:x™q>.?»†ùê»;i¶Éÿ•.ѹI¥–Øi„|T¼ÑÏL…ÂùU§6Ë{Þz¼§´~bI_©¼Lgýëø¢©Ö#¡Ÿ_q»:p®£iu|Ä&úËý_lÐðñ;2@vgçÏéצÉ8L{håÄÞó)æÀ|n’¬qx™3d`)>Fë[ùK^ñìxÓ9Üsaù9Àl ÍïC@ë}? ÑÈ; rÅ:£<ޝ鉒H眫ÏcíáNDuÂiåüüñM“óe&GC,^Í--ËÿZÔr™bÒ5­(kö,O¢¬8°Ñ•]ºøð4€ÜF!=E|‰§IA7-ý›"ëR¼ËE«\K+'2ÓÎ!;uçÑûæANóZk/ó.U„I"SÌj Ý€–d×*EEzGÜyæl¢"CÏò"š«wøÂœº{ƺ^Ÿ"4ÃoOxßÇ H3´»kù»”Øfª¿jaøòßPãðØc¯nóí¢þB„5ˆˆºîÝz÷èYÖhùs™4‰N†öe9Lû~îë!x/»‹íDŠüš0Ê^°s-ÞŸ~—îCzô[(___D \eóëŸOhÒå½›S×Ê—«"ŸÛ­„ gð•4-$‡]Ýê)”×hG'±}ûèÓË*¨sž|z8F6‡.â|ÏŸºTŽ}wÝ Eº@éÆ Öø¨\[c<“«K7^|Õ}Ö ËèWþeO³¾×âI[óŸVY¹‰p¹‘SVJ4ÎUXC›ŒÉÌíäÀÔ%)˜>9ùžú`{ÑE¿wY€#!–=1&>ÉåzÔÍáx ðÖƒ®ì h¸Ú¢,ôòA$óšˆár† äÖã#t$²Óû‚îŽ[fQRW‚‘‘ÆTêמ×%Qn0½,/h ­WîYNö$rÄa>oJ, †ùú s¨yÏ—`I›°jdHËp]ì×!Òw µkùŠ´grú•cq+[·£ñCîô ËaA[ùÁiû m^b{ñ³ º³¢É²º3ªùZSϺ?TBY¯_…êQ¸ûò0?Ïz²;šÅ‹uøÆ`}Õ=QÀt'þèuˆ·ÿ)Ì3˜˜%„ƒˆe ‹¨h5c?ÕT°6[F+hË®€6pt#øJ 7­¶Þóç}ªÃ”ðŒÃX=sä.JÁilî"žöÊòºìëiyž¶6íú³X£í:²Äß¹=`KÛdĤX§Ž…¡”Êš,Í5®pØCNrÞ,s×ðÎP$2^–ü¯†¶¥±JdNÙ ‡Æ ƒŽ\ ÁÙ¿- úl,·&©áö²Ék­ÄŽýUžì†ðŽ€ÄÕË 'ys(H9õI±5œ<”ÜL`ÏÑ"•Ÿô`Ò¿#§ÆižÞ‡†€÷Ø‰ÔÆã"SS©™Å9WjŒxy ÉÀóß)í6)Ò½²¢¹©Õ7²ÙKo#šÔÙ§ÀÛƒyˆŠx‰Š¡ œÁ€K˜Ñ‡ñH ¼ÒÏRá_ÝgÞTR1¾\²¡¯[n=Îs°Îðvl.V)Ž  ŠIº9ªî(’àþ ;uÕ²,'þGXüpqUMî™­6änw;0ë¦!¹òÕLc¤Ÿû—9Íï¶«aÁ‚a'ŽŒƒŸ7~*ºµ?¼f …Ž01ií*©Ã@ e7-ãénM­Ü'JéMeúáï/TMF§B­î°V'+¯¡áúºÚý]2þÎC¾¾aúºéý]-C¾ÔþÒ`ý]pß?§hø}û‡}ç¨ÿkOÄä~}@/&y$IÝyJã@¶ÈÃIXºèYUäQGŠZ“!øö2/¬‰Â¦Ó3ûtZ Ì•™ç€Àõ)¹±žUðj< Ÿ”%ß‚ž®Úà<ä°©‚pJ 1‡Uo RŽ„qß!¦F é™Ï³é·aš±Ú‹~Mz˜³3žE©H?f¦,¡pÙÖA•d9ˆeƒ’ÎÚÎR\ÙC1}ç{|Ôž=”üu¥ÒEëm lûaîÿPÜÒ÷­Ð6¿Nñ•ö&2÷’äý«8[~¬ˆ¬$:þ‚ÒÍSë#W.j¥m£ž³ÞHZò‚QR;¬ÈâX aL™˜Éä7R7tÝ‹´?ÒÌ¡h®Ä@v.=¨ß $µ¾ú1%ñ+UK7ÿAìÄó޲Í?ç3‚¨üBn{a0Ë b”Þ*O;Ö;ðrJv3^WG¶a<(*úfƒÎyXÛ±<íÉC39Zæù§(/m¶&yëmwqÅh)s¾J—®)»×³u°’hŠg-ÛjõlÞ6C€µkV}hÇ!ÓPʦùsÄÒb¸F‡/q;°M˃oc;XôÅ+øÝ !úòîÍQùüQŸ¶1Œð }˜¼ÆoW芡¦¯<¹öíéT'õhŽ|2Å-Á»wç)"»GØE0ÓÓÁ¤÷pû €²Óëâ䣧ª@e HòËæœ¾_C²µ¾ËP‰§AÅûÓÎmûaŠEØCT¶ ß‚}‡žÎñοÃg-sÖd¥.$Z­`Ò(¼8q°—T¡§Ÿ´Š¯Ëƒ,ãŸæÕÓ×8¨ gYs 5‡å1ß®M3ZlGª|*kNSÏA0"a©Ôeí®ø>¼£i6/¥ü˜Ê±6îd•$ ¿lLޤV F…Wì4ò¤ˆ6è½¶T±9l\ÔU¥Y‹‘wG×X ЧÁBkxàï+})¨úáXq"½•æT‰t·’%ÀéØv‚Ë%êºy¿4ým ƒüB½úÁvç‚ôhV"­ËFxZÊ|ŽBÆ  Ö‘i»ÓT€I曀¬œ½ŸñL7$ˆê`žëêû€Xl6»ÿ>“C¯PbòV*•´ºÅÉâ7ËŽ—Uºq“«ä8ûƒìÙ~±0‘?@”aÛŠ\ÞD|(jBó=NØæÄæ†÷£;šÌ/­HÎõfqNôõ²£?—uBaõg¦g\á#ª^Á>à d=ogÒûßöcO~]©ýÜ=G@KðEøîÑÜï2>¿Dv°qΖ£'[ôR®‘Y½9úsmÉ­¦ •1ËÓ×¹U½ e>3¾ŽØVo³/¸Ld-Ë€èÅ;«ÑYóS%ƒ–9Ÿ Ý%¼,â«Í’×1Ò™ÀÕÜ-ÁñF=êQg%Eè­ë‚(ì8À׿ª;}ÕJ?˜ˆu ¨øê”f™QÆØð|Îåéêä—ñn ×X’òÝûQ‰PGÆŽ\D /»™Ú` .¿39ÓTz­)•Á´J½{è}OaÈm%¾_©D¾A$¼œfõ|ÂÒhˆáVÙßœoœß¾ºùb¹@´K'`ìÛ:9›“¸]þ[ÄLá÷Qâ;ŽÓËD„‰QÉ|Í Z—*óObå8´Ë„ˆÍ6×8þV}]´‹?þ¿èI~úuÖ R˜doùžõåV`[Éÿ³–3ЃãƒãÀÍË“ÛwÞ»¨8Δ˜o»îðŠFâ*·@2„òOtI&Ủ°¾O±©.ÔórÉE{BEᢅÁÿ9ÝO¯r°4ùð^Lù¹°):±iÛÃAÕ· …w ËM:ê²§ý#-ÙÏÌ‘Lz“è_k½#Ôµä™8é†îÿá&ds‰lÇÕƒöŽ{O±¾Ã2yANòR¹õÀ“EˆLg]ÏyÃM«q×ê²[$ËÙmá®À ûQ$Üùj\¯ÃEÇÞ–ÞbC/Ù´Ê”]?ø?zÖB‹dØä’ßY„¬ÌãoaMdvlüQWÛ@ºa‡ecºÞUÚîˆOB݃Öù/¾3É:÷>š0S€žF¨± µp¹0:ÎÚºê¨hm;Õå™ûl$#Ûrã»RÃðFýo5fýÌ-µ¶çíqûÍ÷eñ_ñ¿Ê§kÎåD-äXµu5Œ 1pÐo¿"ƒËÐqÕM% WØ!¦Ë1Ò´¬bEÞ¿¨IyDX~Åéã¹Ów¢{nÕÙó<Ö Ìè“xôLø÷çTüĵc>‡ãóCòÁ÷æ#s~ûá yÁÔ*ÒÂj×WýÎ\ºŒIqº­úp“س'ý±œL¾; ç€c«Ü²îÃÆzzæÐß©òVYžê°Ü iÒõùŃù,Q lôæ Ÿ! ô˜Ãe©{q%g©ükÃÖ^þé9äqD¤`ëÄ“˜ƒyDð¾+Úÿf8¯³vß2º÷à‡Ä ǦƒqÏ£2ÛîÀ%Y|Á(¾Ù)Kiפ!;Zà†b>åÓaÜ—6¼ýl=’:iL~Òó?3< tÚÑÇ”|6OÒXÕã¼ø×Zº\#æÐïöØr~å¥zT+Þ\>ŠmÓ)à`?ܧùÇ1´=A³€šá]ŸóÆäêã1>Ò•„ÍGäè!̲à`hgñiùᎠïg] «|kÙ@¨½0[ÄRØPMôDe—›ê"ÛPÙLDjæ}ÿS÷‰‘ÿE2–9®QBIFË©Ø4ÁÙ¿×iàòðÉå"³GàuÏ2(Fµ˜ zlDÃY{¢™)m—Ù.¢oôãÜ™´íˆŸú\²³¦…9:–J^XTŸÐ–ñ{Ü$¡øÐOü6‹ßñÛiù¿_X€¾$öÚÅ@$4Z2 FD\}STPž»™Zµ´uNiž¸í;‘S©àM(âÊ}+´ y¦ |¬û‚"j…u7q¿ÃÜ×l҃»‹í«š¥Iõð–Û¨Ô`¶ÞiÀIS˜wºˆ “I[±×Y©T_Ÿ™º¸2Ê{ÿWüØ{e(Gƒ{&ŽWB8QŒåJþç•n’äv/‚ÁÂÉvlèvæc›ŸŒ­YFÖXE;Ë:‡^GÞø8õeâÂpó4¦š®|iöÖ$BR.ê8w 1B`[ œoM?$ÙÉâ]Ô&‡…h´O{–ãô§Öú†·iTeh…DN=%Î8Ä7û#‹“üamÁ…¡‘Ç1øÕ:ø$»³Ô©¨0)!>ãAô®~Dæcgk%ó`n·Qu{Oõ© ÆuÇ€•œ9i°æ)äzwf>ÔJþ´½ Ô«‰Bò€÷U6mÉÝcÇÃkËió Í»µÎÖX|Rp©pDÕØ›‡Qf „Ý•„öµƒdò=o}à—¥S~òó«pIN¹ù P‚uEÛN•çæ8 ¢O®&!›Zsà݃Á/îëi™(ÑP¨ˆúéðÙk÷¾ÛÔÊ)mgk¾…kU‚‘‡¹P5d—ô³€îoǯyoƒXj„þzUÝǃ™ ~¨s»º]€Gåˆå‡£!…×AãŽÙ“…ŒªÃtÔz”¦—ž™2¿Š@¯ȘۘÖˆÞ2»÷çö;0HUð—dŒ÷ßÊ”­)ŒF´%Öi+Z7i{†ìL-l(ç<ãó8Kú|E rtà¯ÀÝK¶7CdÖ„Óƒ‚Aíd ư’Jéh‚òp“XÔד·6I©§Ûn÷͵Z…x¿¤¶•pû&’E>¬ÝõY¡€¶ }ù4Ì@LÿneY7"Ö©¥EÝ]z¸ŒÑŸ¯JÿYQêè¾( ÙÊkí‹Wâq¸Ü3b”kÿaÖáD°ùž½ =råž{;B¶ÝÑŽëÎgµÊënÏN¡€ÄìæW0ß®j=ØÚ{3|}î ôÁ§ý”ÙP ˆs]5–¨-„«²FâÕ…46Õ‘Í%‹×ÚÇ yPvÝ–¾¨BD®ä´[SPNdñ«ñ{§+£ðÜ8àyAbyºûÕw”E>Ñ4]«ßÛ/«2bI Tõ2uÆ­ÆR®yŠBOæ5þpZîXë`Ìp¨¯m¹ _ߑчGfgo¬ â ܱ (aa]öãßÑ'?üsáÿ»¬ø1æØ™"LXº÷Q€YÛiGhÕ®öî½(É™±É®´zÛ† R`œY½yÑå£jÎÁ >±¡ÖÍ ¶1• î=qøFSéóS*rÐ.¼&ÍÜ}V¦¢T^«,úÊÎ^€õxʬ@Œ#ë«v²¥]{ASmÈÍcUvpÆöé±dÌ):wô æÙäí–ÞôÉêÅ],ûülâlÆÎm¶ m¸¼Éœ²S'p ãr/£…Õ54utõå{‚–E>“yHYÊ Z^bÚKX=l¸¾`Ðû0‡GwÂ^íä‹%³Ë—E”ÿK;/!øÿq…·ò‹·* ËQÛ3Ÿ`«wE¿#(=7¢Ž‹òŒ­JF_:Ÿÿj/)ÌUR×ÚóÄ2iö¬Tqœ)KŒöû­=ô¸_ÌÊvfc¤ TaKAsüªòtâ• $iv’–;R¾ö7Ê•™læLüóñ³åÚízpzeSUà“J@ꦨ¦hâ±|J2½yé\K-CŒ×Ì‚ê~rü_ã?c7ÕXL¥ÿv¢Z³°øS¬‚¤v£ãýó+¥»÷ÓvÇ .Ì0ûS—¥¯+窿—N˜UþMÇ@½†ÔôÍË‚³ºØÀìmdÞ¸=ÚTíSêsÙPÑð™¸OÔéô"{<Ô$÷xgÙüÄ úTNìB²ðˆ(ƒ´¥Œùu„*+¼MHÊ3¡ƒþÔ1ýŒl!èÑ‹¬+´Ø'JÁ.{ÖÏæÂ5KË7=(c­LÁªÉëEnãÊ×£æh¶74&ÓØhýìÄ#î½¢ê;ªMÏn×ÏÕ~`ô+PðGtœq©±É”Zס©™»%Yš>=DÑî²°S÷Ã4„ÐÕFÑõ"sq1b qÇâPx½‡¿ á‘Ï8¤Ð?þ"„4֮˧ýØsPgtz¼#.NEÝl¦«k´âËô{Ö¼ *¥Ç+ Àÿ8 Mž*Wìcp¿œ¿2C—´eBe=AK8¾Gô}8¥m„º9‰>8~m¹©Eìhgß•‚cD”?›Ï g›QÐɶð‡6uF?ªœßÌsz/±i[@¾4ÌMñ¯© ÷­ b¾%1N_"òØþÊú*ewñÿvIÌ -ä¯ì“^K˜ØcHtì¹7çýãŸ=6å,)öà¬Ý­%¨PC2}·ß=VžÂgФ„'Û¦]L;rf”úHº€Î©Þw£ ”3âBá–º/×á}œW«å%õ.LMÈhMË:-'½‹pC[­Fl?ûHðø†u8ybvÜ/ÁéÚ—la2ŸˆG¹é `õ–}N'° Â‹Âv¤ûû’߅ψãIŠ~,bsepy˜ìàÈqF"#ˆ¦º(r Á¼^Þ0s~G.Ê×èÿ&·Ü•>kÕòPêÒfB¨Ãªdïˆë„¨¡™§xØþ2ÍŒ J$šÀãzÓã}g VB༆ÜÿZ‘ “°k'C«öÀÈñ¬‘tõ¹¢£9×ÛÛóÙÚ>PË‘¸\ ø¡cˆð=¥•¹.“X6o.ÙáµñÎã*ÝIŸæ·5u£­îe²µ{"²Væ0IýÒ÷[­퇋 8u¦®ˆ¹‹èé‘HG;@ÒÞîùDïLä½Ö±ëÌX^mˆZ‰B»YCb#£(—`ÅŸ™DH„M%¸­{`kwO'|2ô¶ˆæñœÊ‰ßŒË1GÚtíøëÒÄ(oao]¥ù ßÁ𴆦ø»S‰Gy-Œ:Ö½ tc*P©˜N.OI)]ùßü̘!H“ÕTGÙÁBF³çCkjÞœ{ôËñûÉäƒ[iâÄO)ÿ4¾¯¢QÂÑËïÜgÐÂmú½6a¯ôZyBÝú”&Ñ~öoZe‘ø¹^’T â†r“:_µ¶ªÿN*_ñÄ!K»\Œ9º‡ÔÞáŸö8‹@ðBN‹k˜ôVÇêJ«XŒè ‹QßbY‰". öc$1ïóÕ­º Ó–X0wâ?½wøÀ“î´—B ¹õ»{ÓOâªñ%îLÊFN·µi•ß«Ÿ´²\'Œ&õòo“õ¿VŽ8pëj3ž”°p÷ÈÿZ€jœåõâ —Ù2p‰úøõ~IÅ–“RHE½)Ù¹è}Ò H´ÿCjÑñ¹jĸjx΢¤ *þ~¸ýªË3„“NB8˜oD‘EW×,ÚÏM®·cRÑVJÓÓ…¥sö²ò_“‘×›-'\A‚à<åâ ¥ìT·jα–÷½ïãiÚ^fiÎGÑ)ˆÄè7Ün01¿„©íŒu•ù¶“åÚ‡ð7’ºGŽã9½•¯ÊóBdê™2 w¸põÀ^bÆcDSî‡zwéü˜6¾"®ÂÔO°\Ó§^÷e³Ñ.LÎî»×Ù+1޺Η ôâeÑ¡ yŠA(qEþr‰çGˆÔNÔíÍuæ­"™×,Ðí®Öõz(™ þN]ÙïüÐñE¸LìÈ7k̉Ì=©ò¼¡'_—ýž¾„,=ZµÈõ<¸ØÜÿA!ö¼@תª¢§<[ËP«Ã_ú[\»mÀG Fö`R`@‹7χrÆ~ŽaŒòcé [ìŒÈ‹/Q˜ŸÆYùõñ z¤œžãg¾]+æBÓOmõÑz@róÁý& /ÜdLÍ´dêb¼‡G>ç UVxJëWªòdÔ}ûû¦è¢þD#NnôN‰£›A@fç$j¯·Æì%ÕùÍIÿ5ÞÞØ+FcD¥`­&õà;R°Î1ŸaÅáњ܀¥êˆÉ™ùÚ"~Üþ´tõ®àö¶T5ˆ=D¥†¦h™•õ0:¾˜GÅy†Õ¹L‡¬5í&oI!²EÓV'"ÇOÇžûê3õ#ÿs Ëhɾ 3Í£ Q»áŠh&x#[âv¡ßE¤5tR¥+I`ý’NT}vPœŸ5}GL3x¼:Qƒå˜¯ngrLËT]@çk€ìà}ôd·ßcw?žVð’w{LB‰W¹”œé @‚ ²6¿„›ò}Æ›Ðj>!flsö¯ªf쬻h‹ÙK{™Sn¾˜kJÄeG™œÔ[7Ç@z¢þKc¾šƒ(ÕYåKÙ=‘¹l5ñEfð‚~Û©©ËËŠ9í S_í”ý¾˜âÞ7Úñ “O—õfúÎå»L2 ‚VçþÐÊËÀ© C_u*øÉá¸ê08jLWÑâ…çeŒW+ }ÖD,+b"È>/~G¦yØdÐÃóq¶]æ˜;(fœ;Ò­¡Þ%B­zë¸eÈH’%CoƒÀ u(þ„_P(äkg|eláúÖZ‚_ X@ùÛrºä`ü}•Vµ_¯úmGXS'×Ásópß÷:¢mÁP¨ c~m½÷F`qÀïöüÖÑ+?Á(ûb©*ój;u“‡n´T»`¡Î!è(”íVÛ©„õ)1ÆÞ*Íæ0žTŸ“žkÃAÑg$h%þ3k¢MzÂE&µàjhCº[عä¨4ñ)H`‘HFôä„!ÕýÃrz@+(Z¿5~eç/Xn™ˆÅ[Ba8ð/ë¿í …×53dg§Ï=¡ž×ÁÅ´—5¯Û{²ì¡ð·wµ6¥6 O>\úNKˆÂ‹UÌÁ!YŽÈ(U03ojK-k\—ï^‚IÈJ5oúŸ2†_ºV.A“E„lpK»K!˜™^Å–²­y*{ŽâvÇ@Ïcñ$¼î³ÑfÆQBãöì—íÓûêÑoÛ¡Xý»‰ûu*|û’|ý/Ãöì¯íÓùóîOÛ¡`ìé¥ùÓ’.G nØMnÕdgù¦Mö'EÞáÛ’ý¦fØg%ôTƒÚ¥ ÀwºÆšÂ5¢¢§÷­ƒèÂPWipã€þº#ˆèñ9ËãÂ02£¦·ª#§[±Ø/oÿlñ±_{ÇÜšðïÜŠ‹˜L8!u˜ÆF^‰Ñ`ìî'¸l,ä¢ù¸öÍ+Èùj£§Þue%‹ñ›ÈçrøU¬ÕîÆŒ1¶pn¯õ]Ðç3ƒ}÷Ç!iAæ×-zv0eXÇ8´€ôøZ…gHçXi“ö  £Û!E>~j‘™õ!Ã`[Ϊ$HH¸pì¢h3Œlºº|Ù`·Kfªý@óÎ<ü&ôžxb1™4Zê4Ï';cpz¹õÎ]G"I^,l9€ñ#ü5à$úC RO¿P ¨G 'íõøè7+2¨@ôlEìÁ›êö¸JŒ¶,f½#sgùÔY8‰jþy]C1&“ÞSŽ+ጔ\YÖaNoȵ[Cæ ~yB±ëP(ºÂÜ5ð&Á01s·©·6R^]N|û~,†«„QKhóôqL|4Юe—|h÷-›èÜl…ªô³¼§´¢‹;m¶û¼‡ÕAsÔ/‚Z²Ü¿7Z ¨Ñ&AÑqý×i;/Þ[b4úY÷J®h8ª4s2’$›0ºC˜Õhò™Î^‘£2Ý”ïê /Ý*æÊ±vë—˜¡mÿ^>]iÐ ð˜r&þYsà`PµÙ×M¼=nëø¿GÅâYìGG‰„‘Ñ  °J¤P˜µLë£Çg³]W\eÆ¡¥î6U£“b¨ôô6ÜÝÉ_Óë; êÓÄ@`¿ÍF±Cî¸3ªÇ'Þ“•¦¥÷ºkž0y?¢CüK\NÐÏwÞvæàc—mç8$zô­J #÷ųÚñ[ýF-îZå1„^K­ ñž^IRÙõ0)>cÌõsè‡Ö24n°!ümº´ +%بú®÷芾#§­¥ÞsŽ{ +^¢¦÷Uýp­þߌ³üÂe$æéî³÷ŠÎ»Jɽë¿¶þ‡G5…-(ÙÑþû·ëò”ûlj"Ñšõ#]J½ùÛŸÎ^åÇ„™?‘óÿ a|ýÐ ù+!/ì©/fO¯Þ®w¨Ÿ'¬¢·=ÈÀ®Ò:Ål¿{£Ï`4nDˆÿ`²îï|®N’ÙDìÔzÞºÑÓúý@…F 7îóq^ %ƒ®ç•\@Û¥A.ðé^|.r£¾îq»«@@Óe€5ï¸ ×úxgw<¢RÞ&³¢ ®D÷TæYÂAº>C– aH@ÉÝ‹¬´~)Ý™ ”ŒBY.ºvŽÄR­æˆë¦ Ë/áØdéXÍI饷x†žFp¨®U(Èr¾)‰ãõ?»!B‹—v¢CF¢Ýü ÍÕ:QÏÑ^)lk1(«[WÖGßÉt?y±¹ó¹®‡M¥ P+V‡iÏbÝ,΋§þÎWu±#‹XìTqtº…F,5ïpäúÊxt‰Ît‰>.õ‰z€ ÛNqWP™kn®¢-æjGPð•Q#ó?®ˆ¶µðP]<+¨C7]j å2¿Iú ë‡Èú>H_®•zWÇóEgM}ˆzÓ­Èèù»rùG•'è7ûÊŸ«?B”] q³¥ ÈËo0Šã³½ìÏåÙ½BpråwL‰ìþˆ#‘ ;%w«%‚Kf—ø­Ž)sFµ•3Ã@‡¯{ÆcxQújeôª9‚L^n¥^âíêE}…c3}ͺŸ+ÛËCAæê±4â&¸3–{ôÉ ÕÓ8äO¥¹_Ât.¹#Æã6!_và‚)ÃGýªú ÷%ô–Ên's”h‹„¡/¼S4\~ݧaÍaƒ’£jßóû„ñï]ÌêÇò·c.2Ãoýò/& Ÿø2•§ d A¿gå ŸÂ„)sïŠ3æu¥LúFÍI{ìG€ÑAÝ ¹g—Å<…òÔ³¾e$ˆ÷Åò%¼¥ð鋆›å‡8|%P5ìhy˵Ueô Šê¢s1;ˆ¯¯—ì ãþLN1‘MT$Xi'µ‰Ã@pòŸšåpÖGb]qL’ÌOîÚÞĹöLEY-¢Ñ%¸òãc‰=_úµ"BåÙ+È ™ÑÀâߨÛþ šStz{Ý3Ô]†FÁ§Œ7à¾$ÚQD’‘ó`Á2×âÃ*4@µçÊZ.4«&%EÈM/Rß¿ö8ð [†cJkH"‡u•‡µE(øVV×6Ê%É]ÄËï"‰`e™ê@¶B]â`†ˆTÛƒ¼Å$‘$;3â¥:K«ðÖ¿ÖÑ9I=’|7ûçµÏ½…ßZ †*×ÄØ5Ê`÷.ÕØ÷°Âæ·¿¨Û=kgëLã*`7{ æDP`"ßPº8¤ÇÁï©„a×L^îŒrÛ¡Œ–;ê"‹aéã䉎ÉúþÉÇ1%­ª‘R,³eÿ@é~B PÕÔ–)ú¤dÅX²Ü¼¹¨|2QžûU9ÁDïù6Nì?dn£‚ ñÎê”`öÕþ\FäRAÎT5«føWâdÝé4(¸Úu‚¯2ݭ˽¶äþÌnÇ(Ö‡hHb/šüOeXîuµ®Æ$8@½›Ù\%<¿Ù°Ú±<¤šÒf[Þ°ì5 `[ûKl’Ñš>ÁÁ= é™þe༠–Rd[a°öBB 8=bRÄÁœÈõÊb[¤Žt-Táh¯Áú}¼þLÖ]´ÏënÓëN"ükóÔ1ã§bÆÊ›u=óV5 ™Ës7&H°‡¿™1S©ÓJ1}*$†Ô8åö¥y Õ##Ç(an÷«ü8æê+>|“V#㤠k "¹{ôèJƒ8….ÿˆÌ0¿q3¡„ûŸ-ýqÝ>º ý£¦ŠKèF±DÚ­XøÙë•ÕÏ¢tx‹9rï¶ ´[“Ñ/g¯Õœ¼K>q7)OXÃÛ;yþná\ZaØflº`߇èTì¾£ij£ãbvï^[Ùó.;¢Nø´¶)’-S$O|B®:æÈ²Ž ™°eä7Äë¹ËÊuªálä{èSÿ"‡Ú¬—»þk¾£IÅ7€›À0"Þž€Xªu:ýr}Éæ¡â¹Psaå’“¬¿Ñ="™à¸Œ©‹‰^®gfêˆ]¥ñ- Æú‡òDlâ3¨óê%Áö1eÚÖžböö¤ËˆE€²³Íßž. Ò$ñ¬\E¶—ä .Ä”6t“9 <Ö°ÎñåÙ5¿oîæ2†nJên¶,u^6-Hp€wîyXô×üaZc‰H0òŸZåY ÃÖ“CY½µCIšd‘F…”Ì¿Pß9úƒ,üf^¯ffdzŸÆ®%æë,!í¯KJ¥Iúø˜J‡5®,¾T¶8¬FË~½NäèÇØ­j§ä³8õbö¨“Uê)©š»ñc›íXýÖž…ƶ=UtûôÑì¯"ÚØMÜ€6Wo]‰·D¨ó#Z‰Ùåm3¤ëÿ ‹¬Ö þƒÂlhߘ¸zò¸(ÓÕª¨@Ó.î_êöO&ŒG‡ós¬íꇅàÓéäFŒÅžÕr¹UéU5™Ú•`€mÊ+kÚßø±&lMÝÚ3Ù­©ÍãQ©ñ9'¢AbHÖ˜¢9ÿ#»pû{ìpî8…©uM®bØœ|†XSÅln  DÁ‹"jÇê52³Dᨼ82’üŸ‰iÚ¹¶¬~ï6²Ù‰-j­]†Î¬JÈ…ð”ý‚ét® ^f›ÏŽ®Ýð ŠÍ@ŽÎ~ù‘ ¿=@s'çÆh¬aV°èH¬Zñ˲åcj6P„JÍ?Ï?ñu¾sKD>´dÃsgýÖV[`“.³èËÜ…œø0 ‰ŸKj¾ßâ /”Ó¬–Óú²*ìt.%2wŒéíaIaý  g+%–:hÍÓ¥ÏòÝî uån:øwò#ªo7E¯\e…öãbK“pØWbûc˜ö…s)騑+«‹$Ïs½pá^Y#†Á?ŒÊ•W{¢~¥HAØ2,DµdÖØ*1ö“ÀZÏÞÇŽì¤=ш™â½pNÎaò,áH”šs9¡ü¦ù!Sï…•{ü:´ûL›×@8ᡇ¡ª¥ò;µqÏòºÆc-Ë´Ù¢¢E·…l¸À1bsTŒôï8VDÍÈÒ‰³Ô2×Ö¶˜{\»m0Rp îî«HZ‰@‡Ü[% "cJõJFtÓÑ•Ç\åS½,ŒzA-bt¸"•–Æv5M‘Rµ3-†ÝPë¡0ü°¼ ŽÃ'‹‡Ãİ7§š †x¨½úba:‘¼ÅcšŽ½A`Ò”¼å7F*´³’çnМu‹|Ï´ü`çÂþŽk|84¯ÎYÍ›±êÊöªÜË£¸›wLÛn…G€¬Œÿ‚Š‹ºËÀ6‰€p–s"ú ƒ%ÿMêˆ.N(+Ìw<ì‘”ª4, “0p3"o ?K†äÒ]U7Bt»éisUp45 l/ñ;<|.¿)œ&vûÂ^¨$kmYƒ& G|RS@ba%Aq†èÇÌPd”ú`×U¤` %Ä_€·b›šþ×ÙPÓnÏ™­ºéýÆÁŽMr°,¡ü]ѳ-%SBŠ2 ­ä™ÊЇì*o~Ýl‹®ÿÙicnV BþTransGUI/setup/macosx/create_app.sh0000644000000000000000000000226711370562355016317 0ustar rootroot#!/bin/sh prog_ver=`cat ../../VERSION.txt` exename=../../transgui appname="Transmission Remote GUI" appfolder=../../$appname.app lazdir=$1 if [ ! "$lazdir" = "" ] then lazdir=LAZARUS_DIR=$lazdir fi # Building Intel version make -C ../.. clean CPU_TARGET=i386 $lazdir make -C ../.. CPU_TARGET=i386 $lazdir strip $exename mv $exename $exename.386 # Building PowerPC version make -C ../.. clean CPU_TARGET=powerpc $lazdir make -C ../.. CPU_TARGET=powerpc $lazdir strip $exename mv $exename $exename.ppc # Creating universal executable lipo -create $exename.ppc $exename.386 -output $exename rm $exename.386 rm $exename.ppc if ! [ -e $exename ] then echo "$exename does not exist" exit 1 fi if [ -e "$appfolder" ] then rm -r "$appfolder" fi echo "Creating $appfolder..." mkdir "$appfolder" mkdir "$appfolder/Contents" mkdir "$appfolder/Contents/MacOS" mkdir "$appfolder/Contents/Resources" mv $exename "$appfolder/Contents/MacOS" mkdir "$appfolder/Contents/MacOS/lang" cp ../../lang/transgui.* "$appfolder/Contents/MacOS/lang" cp PkgInfo "$appfolder/Contents" cp transgui.icns "$appfolder/Contents/Resources" cat Info.plist | sed -e "s/@prog_ver@/$prog_ver/" > "$appfolder/Contents/Info.plist" TransGUI/setup/macosx/Info.plist0000644000000000000000000000333512027111732015612 0ustar rootroot CFBundleDevelopmentRegion English CFBundleExecutable transgui CFBundleIconFile transgui CFBundleInfoDictionaryVersion 6.0 CFBundleLocalizations en CFBundleName Transmission Remote GUI CFBundlePackageType APPL CFBundleShortVersionString Transmission Remote GUI @prog_ver@ CFBundleIdentifier com.transgui CFBundleSignature transgui CFBundleVersion @prog_ver@ CSResourcesFileMapped NSHighResolutionCapable CFBundleDocumentTypes CFBundleTypeName Torrent File CFBundleTypeIconFile transgui.icns CFBundleTypeRole Viewer CFBundleTypeExtensions torrent CFBundleTypeOSTypes torrent CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLIconFile magnet CFBundleURLName magnet CFBundleURLSchemes magnet TransGUI/setup/macosx/package.plist0000644000000000000000000000052211466644405016323 0ustar rootroot CFBundleIdentifier com.transgui IFPkgFlagAuthorizationAction NoAuthorization TransGUI/setup/macosx/create_package.sh0000644000000000000000000000241511466644405017130 0ustar rootroot#!/bin/sh prog_ver=`cat ../../VERSION.txt` pm="/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker" proot=../../proot echo Preparing package contents... if [ -e "$proot" ] then rm -r "$proot" fi mkdir $proot mkdir $proot/Applications #mv "../../Transmission Remote GUI.app" $proot/Applications cp -R "../../Transmission Remote GUI.app" $proot/Applications # fix permissions # everyone can read, group can write find $proot -exec chmod a+r,g+w {} \; # what is executable should be executable by everyone find $proot -perm +o+x -exec chmod a+x {} \; # everyone can access directories find $proot -type d -exec chmod a+x {} \; mkdir ./Resources cp ../../LICENSE.txt ./Resources/License.txt echo Creating package... mkdir ./image $pm --root $proot --info ./package.plist --version $prog_ver --title "Transmission Remote GUI $prog_ver" --resources ./Resources --target 10.4 --no-relocate --out ./image/transgui.pkg rm -r ./Resources rm -r "$proot" cp ../../history.txt ./image cp ../../readme.txt ./image echo Creating disk image... if [ ! -e "../../Release" ] then mkdir "../../Release" fi hdiutil create -ov -anyowners -volname transgui-$prog_ver -imagekey zlib-level=9 -format UDZO -srcfolder ./image ../../Release/transgui-$prog_ver.dmg rm -r ./image TransGUI/setup/win/0000755000000000000000000000000012261774331013153 5ustar rootrootTransGUI/setup/win/setup.iss0000644000000000000000000001445112261767725015051 0ustar rootroot#ifndef AppVersion #define AppVersion GetFileVersion(SourcePath+'..\..\transgui.exe') #define AppVersion Copy(AppVersion, 1, RPos('.', AppVersion) - 1) #define tmpvar Copy(AppVersion, RPos('.', AppVersion) + 1, 3) #if tmpvar == "0" #define AppVersion Copy(AppVersion, 1, RPos('.', AppVersion) - 1) #endif #undef tmpvar ; #define AppVersion AppVersion+'-beta' #endif #define AppName "Transmission Remote GUI" #define AppVerName AppName + " " + AppVersion #define AppPublisher "Yury Sidorov" #define AppURL "http://code.google.com/p/transmisson-remote-gui/" #define AppExeName "transgui.exe" #define CurYear GetDateTimeString('yyyy', '', '') [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" Name: "pt_br"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl" Name: "cs"; MessagesFile: "compiler:Languages\Czech.isl" Name: "da"; MessagesFile: "compiler:Languages\Danish.isl" Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl" Name: "fi"; MessagesFile: "compiler:Languages\Finnish.isl" Name: "fr"; MessagesFile: "compiler:Languages\French.isl" Name: "de"; MessagesFile: "compiler:Languages\German.isl" Name: "hu"; MessagesFile: "compiler:Languages\Hungarian.isl" Name: "it"; MessagesFile: "compiler:Languages\Italian.isl" Name: "no"; MessagesFile: "compiler:Languages\Norwegian.isl" Name: "pl"; MessagesFile: "compiler:Languages\Polish.isl" Name: "ru"; MessagesFile: "compiler:Languages\Russian.isl" Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl" [Setup] AppId=transgui AppName={#AppName} AppVerName={#AppVerName} AppCopyright=Copyright (c) 2008-{#CurYear} by Yury Sidorov AppPublisher={#AppPublisher} AppPublisherURL={#AppURL} AppSupportURL={#AppURL} AppUpdatesURL={#AppURL} UninstallDisplayIcon={app}\transgui.exe VersionInfoVersion={#GetFileVersion(SourcePath+'..\..\transgui.exe')} VersionInfoTextVersion={#GetFileVersion(SourcePath+'..\..\transgui.exe')} DefaultDirName={pf}\{#AppName} DefaultGroupName={#AppName} AllowNoIcons=yes LicenseFile=..\..\LICENSE.txt InfoAfterFile=..\..\history.txt OutputDir=..\..\Release OutputBaseFilename=transgui-{#AppVersion}-setup Compression=lzma/max InternalCompressLevel=max SolidCompression=no PrivilegesRequired=poweruser ChangesAssociations=yes WizardImageFile=compiler:\WizModernImage-IS.bmp WizardSmallImageFile=compiler:\WizModernSmallImage-IS.bmp LanguageDetectionMethod=locale ShowLanguageDialog=yes #if GetEnv("CODECERT") != "" #define CODECERT GetEnv("CODECERT") SignTool=signtool sign /d "{#AppName} Setup" /du "{#AppURL}" /f "{#CODECERT}" /v $f #endif [Types] Name: "full"; Description: "Full installation" Name: "compact"; Description: "Compact installation" Name: "custom"; Description: "Custom installation"; Flags: iscustom [Components] Name: "app"; Description: "Main application files"; Types: full compact custom; Flags: fixed Name: "lang"; Description: "Language files"; Types: full custom [Tasks] Name: regfileext; Description: "{cm:AssocFileExtension,{#AppName},.torrent}"; Flags: unchecked Name: regmagnet; Description: "Handle magnet links by {#AppName}"; Flags: unchecked Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}" Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: "..\..\transgui.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: app Source: "..\..\LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion; Components: app Source: "..\..\readme.txt"; DestDir: "{app}"; Flags: ignoreversion; Components: app Source: "..\..\history.txt"; DestDir: "{app}"; Flags: ignoreversion; Components: app Source: "..\..\lang\transgui.*"; DestDir: "{app}\lang"; Flags: ignoreversion; Components: lang ; OpenSSL #if FileExists("libeay32.dll") && FileExists("ssleay32.dll") Source: "libeay32.dll"; DestDir: "{app}"; Components: app Source: "ssleay32.dll"; DestDir: "{app}"; Components: app #endif [Icons] Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"; WorkingDir: {app} Name: "{group}\History"; Filename: "{app}\history.txt"; WorkingDir: {app} Name: "{group}\Read me"; Filename: "{app}\readme.txt"; WorkingDir: {app} Name: "{group}\License"; Filename: "{app}\LICENSE.txt"; WorkingDir: {app} Name: "{group}\Home page"; Filename: "{#AppURL}"; WorkingDir: {app} Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}"; WorkingDir: {app} Name: "{userdesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; WorkingDir: {app}; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#AppName}"; Filename: "{app}\{#AppExeName}"; WorkingDir: {app}; Tasks: quicklaunchicon [Registry] Root: HKCU; Subkey: "Software\Classes\.torrent"; ValueType: string; ValueName: ""; ValueData: "{#AppName}"; Flags: uninsdeletevalue; Tasks: regfileext Root: HKCU; Subkey: "Software\Classes\{#AppName}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: """{app}\{#AppExeName}"",0"; Flags: uninsdeletevalue; Tasks: regfileext Root: HKCU; Subkey: "Software\Classes\{#AppName}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#AppExeName}"" ""%1"""; Flags: uninsdeletevalue; Tasks: regfileext Root: HKCU; Subkey: "Software\Classes\Magnet"; ValueType: string; ValueName: ""; ValueData: "Magnet URI"; Flags: createvalueifdoesntexist uninsdeletevalue; Tasks: regmagnet Root: HKCU; Subkey: "Software\Classes\Magnet"; ValueType: string; ValueName: "Content Type"; ValueData: "application/x-magnet"; Flags: uninsdeletevalue; Tasks: regmagnet Root: HKCU; Subkey: "Software\Classes\Magnet"; ValueType: string; ValueName: "URL Protocol"; ValueData: ""; Flags: uninsdeletevalue; Tasks: regmagnet Root: HKCU; Subkey: "Software\Classes\Magnet\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: """{app}\{#AppExeName}"",0"; Flags: uninsdeletevalue; Tasks: regmagnet Root: HKCU; Subkey: "Software\Classes\Magnet\shell"; ValueType: string; ValueName: ""; ValueData: "open"; Flags: uninsdeletevalue; Tasks: regmagnet Root: HKCU; Subkey: "Software\Classes\Magnet\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#AppExeName}"" ""%1"""; Flags: uninsdeletevalue; Tasks: regmagnet [Run] Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#AppName}}"; Flags: nowait postinstall skipifsilent [UninstallDelete] Type: filesandordirs ; Name: "{localappdata}\{#AppName}" TransGUI/setup/win/make_setup.bat0000644000000000000000000000100312000523045015753 0ustar rootroot@echo off if (%1) == () goto usage if (%2) == () goto usage make -C ../.. clean all LAZARUS_DIR="%1" if errorlevel 1 goto err if not (%CODECERT%) == () ( signtool.exe sign /d "Transmission Remote GUI" /du "http://code.google.com/p/transmisson-remote-gui/" /f "%CODECERT%" /v ..\..\transgui.exe if errorlevel 1 goto err ) set ISC=%~2 "%ISC%\iscc.exe" "/ssigntool=signtool.exe $p" setup.iss if errorlevel 1 goto err exit /b 0 :usage echo "Usage: %~nx0 " :err pause exit /b 1 TransGUI/setup/create_src.sh0000644000000000000000000000041311367553701015024 0ustar rootroot#!/bin/sh prog_ver=`cat ../VERSION.txt` zipfile=../Release/transgui-$prog_ver-src.zip if [ ! -e "../Release" ] then mkdir "../Release" fi if [ -e TransGUI ] then rm -r TransGUI fi svn export .. TransGUI rm $zipfile zip -9 -r $zipfile TransGUI rm -r TransGUI TransGUI/COPYING.FPC0000644000000000000000000000227512231246163012660 0ustar rootrootThis is the file COPYING.FPC, it applies to the Free Pascal Run-Time Library (RTL) and packages (packages) distributed by members of the Free Pascal Development Team. The source code of the Free Pascal Runtime Libraries and packages are distributed under the Library GNU General Public License (see the file COPYING) with the following modification: As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you didn't receive a copy of the file COPYING, contact: Free Software Foundation 675 Mass Ave Cambridge, MA 02139 USA TransGUI/addlink.pas0000644000000000000000000000373112261763702013335 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit AddLink; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ButtonPanel, ExtCtrls, BaseForm; resourcestring SNoLink = 'No link was specified.'; type { TAddLinkForm } TAddLinkForm = class(TBaseForm) Buttons: TButtonPanel; edLink: TEdit; Panel1: TPanel; txLink: TLabel; procedure btOKClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } end; implementation uses main; { TAddLinkForm } procedure TAddLinkForm.btOKClick(Sender: TObject); begin edLink.Text:=Trim(edLink.Text); if edLink.Text = '' then begin edLink.SetFocus; MessageDlg(SNoLink, mtError, [mbOK], 0); exit; end; ModalResult:=mrOK; end; procedure TAddLinkForm.FormCreate(Sender: TObject); begin Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@btOKClick; end; initialization {$I addlink.lrs} end. TransGUI/ipresolver.pas0000644000000000000000000001232112261763702014114 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit IpResolver; {$mode objfpc}{$H+} interface uses Classes, SysUtils, GeoIP, syncobjs; type PHostEntry = ^THostEntry; THostEntry = record IP: string; HostName: string; CountryName: string; CountryCode: string; ImageIndex: integer; end; TResolverOption = (roResolveIP, roResolveCountry); TResolverOptions = set of TResolverOption; { TIpResolverThread } TIpResolver = class(TThread) private FLock: TCriticalSection; FResolveEvent: TEvent; FCache: TList; FResolveIp: TStringList; FGeoIp: TGeoIP; FOptions: TResolverOptions; FGeoIpCounryDB: string; protected procedure Execute; override; function NewEntry(const IpAddress: string): PHostEntry; function FindEntry(const IpAddress: string): PHostEntry; public constructor Create(const GeoIpCounryDB: string; AOptions: TResolverOptions); reintroduce; destructor Destroy; override; function Resolve(const IpAddress: string): PHostEntry; end; implementation uses synsock; { TIpResolver } procedure TIpResolver.Execute; var ip, s: string; c: PHostEntry; begin try while not Terminated do begin if FResolveEvent.WaitFor(200) = wrSignaled then begin FResolveEvent.ResetEvent; while True do begin FLock.Enter; try ip:=''; if FResolveIp.Count > 0 then begin ip:=FResolveIp[0]; FResolveIp.Delete(0); end; UniqueString(ip); finally FLock.Leave; end; if ip = '' then break; if roResolveIP in FOptions then begin s:=synsock.ResolveIPToName(ip, AF_INET, IPPROTO_IP, 0); c:=FindEntry(ip); FLock.Enter; try c^.HostName:=s; UniqueString(c^.HostName); finally FLock.Leave; end; end; end; end; end; except end; Sleep(20); end; function TIpResolver.NewEntry(const IpAddress: string): PHostEntry; begin FLock.Enter; try New(Result); Result^.ImageIndex:=0; Result^.IP:=IpAddress; UniqueString(Result^.IP); Result^.HostName:=IpAddress; UniqueString(Result^.HostName); FCache.Add(Result); finally FLock.Leave; end; end; function TIpResolver.FindEntry(const IpAddress: string): PHostEntry; var i: integer; begin FLock.Enter; try for i:=0 to FCache.Count - 1 do begin Result:=FCache[i]; if Result^.IP = IpAddress then exit; end; Result:=nil; finally FLock.Leave; end; end; constructor TIpResolver.Create(const GeoIpCounryDB: string; AOptions: TResolverOptions); begin FOptions:=AOptions; FLock:=TCriticalSection.Create; FResolveEvent:=TEvent.Create(nil, True, False, ''); FCache:=TList.Create; FResolveIp:=TStringList.Create; FGeoIpCounryDB:=GeoIpCounryDB; if (roResolveCountry in FOptions) and (FGeoIpCounryDB <> '') then FGeoIp:=TGeoIP.Create(GeoIpCounryDB); inherited Create(not (roResolveIP in FOptions)); end; destructor TIpResolver.Destroy; var i: integer; begin Terminate; if not Suspended then WaitFor; FResolveIp.Free; FResolveEvent.Free; FLock.Free; FGeoIp.Free; for i:=0 to FCache.Count - 1 do Dispose(PHostEntry(FCache[i])); FCache.Free; inherited Destroy; end; function TIpResolver.Resolve(const IpAddress: string): PHostEntry; var GeoCountry: TGeoIPCountry; begin Result:=FindEntry(IpAddress); if Result <> nil then exit; Result:=NewEntry(IpAddress); if roResolveIP in FOptions then begin FLock.Enter; try if FResolveIp.IndexOf(IpAddress) < 0 then begin FResolveIp.Add(IpAddress); FResolveEvent.SetEvent; end; finally FLock.Leave; end; end; if FGeoIp <> nil then try if FGeoIp.GetCountry(IpAddress, GeoCountry) = GEOIP_SUCCESS then begin FLock.Enter; try Result^.CountryName:=GeoCountry.CountryName; UniqueString(Result^.CountryName); Result^.CountryCode:=AnsiLowerCase(GeoCountry.CountryCode); UniqueString(Result^.CountryCode); finally FLock.Leave; end; end; except FreeAndNil(FGeoIp); DeleteFile(FGeoIpCounryDB); Result:=nil; end; end; end. TransGUI/maclocale.pas0000644000000000000000000001772312231246521013645 0ustar rootroot{ This file is part of the Free Pascal packages library. Copyright (c) 2013 by Yury Sidorov, member of the Free Pascal development team This unit is used to get format settings of the current Mac OS UI locale. Works on Mac OS X 10.3 or later. Function CFStringToStr() has been taken from CarbonProc LCL unit. See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} unit MacLocale; {$mode objfpc}{$H+} interface uses SysUtils, MacOSAll; procedure GetMacFormatSettings(var ASettings: TFormatSettings); implementation {------------------------------------------------------------------------------ Name: CFStringToStr Params: AString - Core Foundation string ref Encoding - Result data encoding format Returns: UTF-8 string Converts Core Foundation string to string ------------------------------------------------------------------------------} function CFStringToStr(AString: CFStringRef; Encoding: CFStringEncoding = kCFStringEncodingUTF8): String; var Str: Pointer; StrSize: CFIndex; StrRange: CFRange; begin if AString = nil then begin Result := ''; Exit; end; // Try the quick way first Str := CFStringGetCStringPtr(AString, Encoding); if Str <> nil then Result := PChar(Str) else begin // if that doesn't work this will StrRange.location := 0; StrRange.length := CFStringGetLength(AString); CFStringGetBytes(AString, StrRange, Encoding, Ord('?'), False, nil, 0, StrSize{%H-}); SetLength(Result, StrSize); if StrSize > 0 then CFStringGetBytes(AString, StrRange, Encoding, Ord('?'), False, @Result[1], StrSize, StrSize); end; end; function StrOfChar(c: AnsiChar; Count: integer): ansistring; begin SetLength(Result, Count); FillChar(PAnsiChar(Result)^, Count, c); end; function ConvertFormatStr(const fmt: ansistring): ansistring; var cnt: integer; c, q: AnsiChar; p: PAnsiChar; s: ansistring; begin Result:=''; q:=#0; cnt:=1; p:=PAnsiChar(fmt); while p^ <> #0 do begin s:=''; c:=p^; if c in ['''', '"'] then begin if q = #0 then q:=c else if c = q then begin q:=#0; cnt:=1; end; s:=c; end else if q <> #0 then s:=c else begin if (p + 1)^ = c then Inc(cnt) else begin case c of 'y', 'Y': begin c:='y'; if cnt > 2 then cnt:=4 else cnt:=2; end; 'M', 'L': begin c:='m'; if cnt > 4 then cnt:=3; end; 'd': if cnt > 2 then cnt:=2; 'E', 'e', 'c': begin c:='d'; if (cnt < 3) or (cnt > 4) then cnt:=3; end; 'a': begin cnt:=0; s:='ampm'; end; 'h', 'H', 'k', 'K': begin c:='h'; if cnt > 2 then cnt:=2; end; 'm': begin c:='n'; if cnt > 2 then cnt:=2; end; 's': if cnt > 2 then cnt:=2; 'S': begin c:='z'; cnt:=1; end; 'G','u','Q','q','w','W','D','F','g','A','z','Z','v': cnt:=0; end; if cnt > 0 then s:=StrOfChar(c, cnt); cnt:=1; end; end; Inc(p); if s <> '' then Result:=Result + s; end; end; procedure GetMacFormatSettings(var ASettings: TFormatSettings); var loc: CFLocaleRef; function _GetFormat(dateStyle: CFDateFormatterStyle; timeStyle: CFDateFormatterStyle; const DefFormat: string): string; var fmt: CFDateFormatterRef; begin Result:=''; fmt:=CFDateFormatterCreate(nil, loc, dateStyle, timeStyle); if fmt <> nil then begin Result:=ConvertFormatStr(CFStringToStr(CFDateFormatterGetFormat(fmt))); CFRelease(fmt); end; if Result = '' then Result:=DefFormat; end; function _DateToStr(fmt: CFDateFormatterRef; const AFormat: ansistring; AYear: integer; AMonth, ADay, AHour: byte; const ADefault: ansistring): ansistring; var cs: CFStringRef; gd: CFGregorianDate; at: CFAbsoluteTime; tz: CFTimeZoneRef; begin cs:=CFStringCreateWithCString(nil, Pointer(PAnsiChar(AFormat)), kCFStringEncodingUTF8); CFDateFormatterSetFormat(fmt, cs); CFRelease(cs); FillChar(gd, SIzeOf(gd), 0); gd.year:=AYear; gd.month:=AMonth; gd.day:=ADay; gd.hour:=AHour; tz:=CFTimeZoneCopyDefault; at:=CFGregorianDateGetAbsoluteTime(gd, tz); CFRelease(tz); cs:=CFDateFormatterCreateStringWithAbsoluteTime(nil, fmt, at); Result:=CFStringToStr(cs); CFRelease(cs); if Result = '' then Result:=ADefault; end; function _GetSeparator(dateStyle: CFDateFormatterStyle; timeStyle: CFDateFormatterStyle; DefSep: char): char; var fmt: CFDateFormatterRef; s: ansistring; p: PAnsiChar; begin Result:=DefSep; fmt:=CFDateFormatterCreate(nil, loc, dateStyle, timeStyle); if fmt <> nil then begin s:=_DateToStr(fmt, CFStringToStr(CFDateFormatterGetFormat(fmt)), 2000, 1, 1, 0, DefSep); s:=Trim(s); p:=PAnsiChar(s); while p^ <> #0 do if (p^ > ' ') and (p^ < 'A') and not (p^ in ['0'..'9']) then begin Result:=p^; break; end else Inc(p); CFRelease(fmt); end; end; var s: string; fmt: CFDateFormatterRef; i: integer; begin with ASettings do begin loc:=CFLocaleCopyCurrent; if loc = nil then exit; s:=CFStringToStr(CFLocaleGetValue(loc, kCFLocaleDecimalSeparator)); if Length(s) = 1 then DecimalSeparator:=s[1]; s:=CFStringToStr(CFLocaleGetValue(loc, kCFLocaleGroupingSeparator)); if Length(s) = 1 then ThousandSeparator:=s[1] else ThousandSeparator:=' '; // Unicode char has been returned. Probably it is a whitespace CurrencyString:=CFStringToStr(CFLocaleGetValue(loc, kCFLocaleCurrencySymbol)); DateSeparator:=_GetSeparator(kCFDateFormatterShortStyle, kCFDateFormatterNoStyle, DateSeparator); TimeSeparator:=_GetSeparator(kCFDateFormatterNoStyle, kCFDateFormatterShortStyle, TimeSeparator); LongDateFormat:=_GetFormat(kCFDateFormatterLongStyle, kCFDateFormatterNoStyle, LongDateFormat); ShortDateFormat:=_GetFormat(kCFDateFormatterShortStyle, kCFDateFormatterNoStyle, ShortDateFormat); LongTimeFormat:=_GetFormat(kCFDateFormatterNoStyle, kCFDateFormatterLongStyle, LongTimeFormat); ShortTimeFormat:=_GetFormat(kCFDateFormatterNoStyle, kCFDateFormatterShortStyle, ShortTimeFormat); fmt:=CFDateFormatterCreate(nil, loc, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle); if fmt <> nil then begin for i:=1 to 12 do begin LongMonthNames[i]:=_DateToStr(fmt, 'LLLL', 2006, i, 1, 0, LongMonthNames[i]); ShortMonthNames[i]:=_DateToStr(fmt, 'LLL', 2006, i, 1, 0, ShortMonthNames[i]); end; for i:=1 to 7 do begin LongDayNames[i]:=_DateToStr(fmt, 'cccc', 2006, 1, i, 0, LongDayNames[i]); ShortDayNames[i]:=_DateToStr(fmt, 'ccc', 2006, 1, i, 0, ShortDayNames[i]); end; TimeAMString:=_DateToStr(fmt, 'a', 2006, 1, 1, 1, TimeAMString); TimePMString:=_DateToStr(fmt, 'a', 2006, 1, 1, 13, TimePMString); CFRelease(fmt); end; CFRelease(loc); end; end; initialization GetMacFormatSettings(DefaultFormatSettings); end. TransGUI/torrprops.lfm0000644000000000000000000001165412256577645014014 0ustar rootrootinherited TorrPropsForm: TTorrPropsForm Left = 364 Height = 270 Top = 185 Width = 517 HorzScrollBar.Page = 349 HorzScrollBar.Range = 40 VertScrollBar.Page = 155 VertScrollBar.Range = 22 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Torrent properties' ClientHeight = 270 ClientWidth = 517 Constraints.MinHeight = 186 Constraints.MinWidth = 350 OnCreate = FormCreate Position = poMainFormCenter object Buttons: TButtonPanel[0] Left = 8 Height = 26 Top = 236 Width = 501 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end object Page: TPageControl[1] Left = 8 Height = 220 Top = 8 Width = 501 ActivePage = tabGeneral Align = alClient BorderSpacing.Around = 8 TabIndex = 0 TabOrder = 0 object tabGeneral: TTabSheet Caption = 'General' ClientHeight = 194 ClientWidth = 493 object txKbs1: TLabel Left = 420 Height = 14 Top = 37 Width = 22 Anchors = [akTop, akRight] Caption = 'KB/s' ParentColor = False end object txKbs2: TLabel Left = 420 Height = 14 Top = 69 Width = 22 Anchors = [akTop, akRight] Caption = 'KB/s' ParentColor = False end object txName: TLabel Left = 10 Height = 14 Top = 10 Width = 32 Caption = 'Name:' ParentColor = False ShowAccelChar = False end object txPeerLimit: TLabel Left = 10 Height = 14 Top = 101 Width = 48 Caption = 'Peer limit:' ParentColor = False end object txMinutes: TLabel Left = 420 Height = 14 Top = 163 Width = 38 Anchors = [akTop, akRight] Caption = 'minutes' ParentColor = False end object cbMaxDown: TCheckBox Left = 10 Height = 17 Top = 36 Width = 147 Caption = 'Maximum download speed:' OnClick = cbMaxDownClick TabOrder = 0 end object edMaxDown: TSpinEdit Left = 340 Height = 21 Top = 34 Width = 66 Anchors = [akTop, akRight] Increment = 10 MaxValue = 999999 TabOrder = 1 end object cbMaxUp: TCheckBox Left = 10 Height = 17 Top = 68 Width = 133 Caption = 'Maximum upload speed:' OnClick = cbMaxUpClick TabOrder = 2 end object edMaxUp: TSpinEdit Left = 340 Height = 21 Top = 66 Width = 66 Anchors = [akTop, akRight] Increment = 10 MaxValue = 999999 TabOrder = 3 end object edPeerLimit: TSpinEdit Left = 340 Height = 21 Top = 98 Width = 66 Anchors = [akTop, akRight] MaxValue = 999 MinValue = 1 TabOrder = 4 Value = 1 end object cbSeedRatio: TCheckBox Left = 10 Height = 17 Top = 131 Width = 71 AllowGrayed = True Caption = 'Seed ratio:' OnClick = cbSeedRatioClick TabOrder = 5 end object edSeedRatio: TFloatSpinEdit Left = 340 Height = 21 Top = 129 Width = 66 Anchors = [akTop, akRight] Increment = 0.1 MaxValue = 9999 MinValue = 0 TabOrder = 6 Value = 0 end object cbIdleSeedLimit: TCheckBox Left = 10 Height = 17 Top = 162 Width = 170 AllowGrayed = True Caption = 'Stop seeding when inactive for:' OnClick = cbIdleSeedLimitClick TabOrder = 7 end object edIdleSeedLimit: TSpinEdit Left = 340 Height = 21 Top = 160 Width = 66 Anchors = [akTop, akRight] MaxValue = 999999 MinValue = 1 TabOrder = 8 Value = 1 end end object tabAdvanced: TTabSheet Caption = 'Advanced' ClientHeight = 194 ClientWidth = 488 object edTrackers: TMemo Left = 8 Height = 153 Top = 30 Width = 471 Anchors = [akTop, akLeft, akRight, akBottom] ScrollBars = ssAutoBoth TabOrder = 0 WordWrap = False end object txTrackers: TLabel Left = 8 Height = 14 Top = 10 Width = 46 Caption = 'Trackers:' ParentColor = False end end end end TransGUI/addtorrent.lfm0000644000000000000000000001153712256577645014110 0ustar rootrootinherited AddTorrentForm: TAddTorrentForm Left = 376 Height = 440 Top = 186 Width = 497 HorzScrollBar.Page = 505 VertScrollBar.Page = 404 VertScrollBar.Range = 92 AutoSize = True BorderIcons = [biSystemMenu] Caption = 'Add new torrent' ClientHeight = 440 ClientWidth = 497 Constraints.MinHeight = 300 Constraints.MinWidth = 470 OnCreate = FormCreate OnShow = FormShow Position = poMainFormCenter object gbSaveAs: TGroupBox[0] Left = 8 Height = 132 Top = 8 Width = 481 Align = alTop BorderSpacing.Left = 8 BorderSpacing.Top = 8 BorderSpacing.Right = 8 Caption = ' ' ClientHeight = 114 ClientWidth = 477 TabOrder = 0 object txDestFolder: TLabel Left = 10 Height = 14 Top = 0 Width = 90 Caption = 'Destination folder:' ParentColor = False end object txPeerLimit: TLabel Left = 340 Height = 14 Top = 87 Width = 48 Alignment = taRightJustify Anchors = [akTop, akRight] Caption = 'Peer limit:' ParentColor = False end object cbDestFolder: TComboBox Left = 10 Height = 21 Top = 16 Width = 360 Anchors = [akTop, akLeft, akRight] DropDownCount = 20 ItemHeight = 13 OnChange = cbDestFolderChange TabOrder = 0 end object cbStartTorrent: TCheckBox Left = 10 Height = 17 Top = 85 Width = 79 Caption = 'Start torrent' Checked = True State = cbChecked TabOrder = 3 end object edPeerLimit: TSpinEdit Left = 404 Height = 21 Top = 84 Width = 62 Anchors = [akTop, akRight] MaxValue = 999 MinValue = 1 TabOrder = 4 Value = 1 end object btBrowse: TButton Left = 375 Height = 23 Top = 15 Width = 91 Anchors = [akTop, akRight] Caption = 'Browse...' OnClick = btBrowseClick TabOrder = 1 end object txSaveAs: TLabel Left = 10 Height = 14 Top = 40 Width = 43 Caption = 'Save as:' ParentColor = False end object edSaveAs: TEdit Left = 10 Height = 21 Top = 56 Width = 456 Anchors = [akTop, akLeft, akRight] OnChange = edSaveAsChange TabOrder = 2 end end object gbContents: TGroupBox[1] Left = 8 Height = 254 Top = 144 Width = 481 Align = alClient BorderSpacing.Left = 8 BorderSpacing.Top = 4 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 Caption = 'Torrent contents' ClientHeight = 236 ClientWidth = 477 TabOrder = 1 object txDiskSpace: TLabel Left = 10 Height = 14 Top = 20 Width = 79 Caption = 'Free disk space:' ParentColor = False end object txSize: TLabel Left = 10 Height = 1 Top = 2 Width = 1 ParentColor = False end object lvFiles: TVarGrid Left = 10 Height = 183 Top = 40 Width = 456 Anchors = [akTop, akLeft, akRight, akBottom] Columns = < item ReadOnly = True Title.Caption = 'File name' Width = 350 end item ReadOnly = True Title.Caption = 'Size' Width = 80 end> FixedCols = 0 Options = [goFixedVertLine, goFixedHorzLine, goColSizing, goEditing, goRowSelect, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook] RowCount = 1 TabOrder = 0 Images = MainForm.ImageList16 MultiSelect = True SortColumn = 0 HideSelection = True end object btSelectAll: TButton Left = 206 Height = 23 Top = 4 Width = 126 Anchors = [akTop, akRight] Caption = 'Select all' Constraints.MinWidth = 75 OnClick = btSelectAllClick TabOrder = 1 end object btSelectNone: TButton Left = 340 Height = 23 Top = 4 Width = 126 Anchors = [akTop, akRight] Caption = 'Select none' Constraints.MinWidth = 75 OnClick = btSelectNoneClick TabOrder = 2 end end object Buttons: TButtonPanel[2] Left = 8 Height = 26 Top = 406 Width = 481 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True OKButton.OnClick = OKButtonClick HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 2 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end object DiskSpaceTimer: TTimer[3] Enabled = False OnTimer = DiskSpaceTimerTimer left = 44 top = 248 end end TransGUI/addtracker.pas0000644000000000000000000000213511652054747014035 0ustar rootrootunit AddTracker; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, ButtonPanel, StdCtrls, ExtCtrls, BaseForm; resourcestring STrackerProps = 'Tracker properties'; SNoTracker = 'No tracker URL was specified.'; type { TAddTrackerForm } TAddTrackerForm = class(TBaseForm) Buttons: TButtonPanel; edTracker: TEdit; Panel1: TPanel; txTrackerURL: TLabel; procedure FormCreate(Sender: TObject); procedure OKButtonClick(Sender: TObject); private { private declarations } public { public declarations } end; implementation uses main; { TAddTrackerForm } procedure TAddTrackerForm.OKButtonClick(Sender: TObject); begin edTracker.Text:=Trim(edTracker.Text); if edTracker.Text = '' then begin edTracker.SetFocus; MessageDlg(SNoTracker, mtError, [mbOK], 0); exit; end; ModalResult:=mrOk; end; procedure TAddTrackerForm.FormCreate(Sender: TObject); begin Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@OKButtonClick; end; initialization {$I addtracker.lrs} end. TransGUI/transgui.png0000644000000000000000000000544511434456407013572 0ustar rootroot‰PNG  IHDR00Wù‡ ìIDATxœÕ™ëo\ÇuÀóØÝ»wï>¸KJ”D‘2M‹t%9¶L«r£ˆ8’~ $†AƒÄh€  ‚q* ¨­Q´IQÀÈÈ_ ©Bä‹›¢ 䤆­ÚrM+zÄ¢*Ú²eÈ—Ü÷ò>fúaIŠÝ%%Ù r’÷òÞ9çüfΜ93WXkùCùûvàNå@Ô øÃziþpWþß"šñü³Ï<÷ÂGiOlfüXˆƒÀþ(‚í¶Ýè Aãù#þ0&2ëb>2ÖZ._¾LЦœH{)Ïõ°sçN÷ÜÙsß¿-`8é&›azår™X<ÆSŸû,Rµ/š&sÅRG…ÆX>ùØ£­½Âºg†ùÒ<¯:Ò !…B!„;1y(ü艶%Ew€¤‹­R¸\ªpøË_¢§'×±Áô¥ºM!@©8ƒC;;¼1DLǘzó žç!¥Äqœz½^ß ¼zË©¤ë€hMÌ(```G׉Ò­4ZÅÐZ£µ^ü_ŒÙÙ9ÒiR¹Ò¹ynÌ™-Î17_¢\©R.×H{.ó¥J/êP‹?:ÖÒ¥5ýÛ¶áûã8h­ÍÄä¡í›`8•JùBG+Eò…åºÙi7ûp~¾„’JµÆÌ;ïRÈ÷.E2ÁÌ»W(U* 9Jå*ƒƒzqבµôõõqáüôb&J§Ó~±XÜ \Ý,À®l6£ ”b¡Ñ¤°BKá³búPœ-bm„Μ=‹”²•µl ÔCÊMbMHµRYÕvMHöoí§ÑX@)…r¹],G€“›BÜ—Îd]„@*E½^§¯¯o…Q»¢ÇZ¿JåÖ&ë½£(­Y›p-¥R™Ò|‰X,¶ h¬Å®¨¶ní§R)£´Æ Èår®”r_;_Ûh­÷fÒi„E8 'á¬r¦·§o9›„QHàÔj5üæ—ckJ°ÐlÐß·c¹½1†k7>@,êNy7é>’Ùl¥Ô›RŽxi0 ñý€\OÏrøXÛ2øÒoNrúôkDQ´xt¨—î¼ÊÌÍφÿü/?,ñxœ‡zˆ±±û‘R.mï–F“lÖ’Ëõ´M¥m”RƒIÇ¡ÑhàûM }…›¹»õœýû÷322Â/ùï\½z•×ßx“Z­Î‚ß¾Ò\’f³Éï.N3¶{„‘‘ûxôÑGqœÖ讘Ólß6@µZA A6“! Ã-“‡äñ£'VÕâë&&¹étÚ‰Çø~@³¹ÀÖ-[Û:“Éd8|x‚ çÏ“L:\¼t™ó.vÜ7lé+ð'£¿¿Ÿ'Ÿx’|¡ÐtÇŽíLMM!¥Bë®ëúµZmð]€]™l¶)„ð”VøA@¡·³!€±ûïgphˆ_ýê¿Ø9°ƒ—_y³7«ß´—âÙOÿ–^>ýé'ÙuÏ=]õlíßFý•WPZ„|O>¬Õj#›Îe³H)ÑJ³ÐX ïàº._üâŸòÎÌ n2É¥Ë3¼9u޽{F¹wxˆGÆðàƒ.W™IÿÖ-”+åVQg½}½Î•÷®ìþcC€|>·Ö¢”Â}Ä& íÚÅ×¾þu~ýÒK íÜÁðð½|êSãºîÆWHÒu ü , ··7®µþÄÚ÷V•B©”ÚÓ“ë‰K)ÑZ³­/¿üß,,,lÚx<ç3Ÿù,O?ýç<õÔçnÙyk-ÿsê}}[ЋÆ=¹|ÛTºjöíÛ—‘Rí_:PJ1:º›S§Nñì³ß#é$±¬`-VX–ןÅ{,‹€Ör¥c­¦‹×‚µ_ˆ–Ví¾¾-Œ?ÒZ…$×ÓCdÌÈ}f.¾úêòÎi`ïÞ½q•LXk±‹Åeåûö=ÀèèXë¸Ð „°­Åjå5ÀÒåâ »´Ò.-Ë+ï×^/qˆÅû¥´=;;‹RŠL&C†¹L">0>>þö믿®Èd2²E9cŒ®TÊÌ͵vï%å¦6öCšÍ&ç/œC+½ü½!flþZ:­i}R[BÖZi­¥\*qíÃkXk¹öáµ»ïùÑZ·>QY‹ C¾év»ý€ñŸx­þ‘öÎqmš»IEND®B`‚TransGUI/options.lfm0000644000000000000000000001755412261612465013424 0ustar rootrootinherited OptionsForm: TOptionsForm Left = 313 Height = 331 Top = 230 Width = 564 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Application options' ClientHeight = 331 ClientWidth = 564 OnCreate = FormCreate OnDestroy = FormDestroy OnShow = FormShow Position = poMainFormCenter object Page: TPageControl[0] Left = 8 Height = 281 Top = 8 Width = 548 ActivePage = tabGeneral Align = alClient BorderSpacing.Around = 8 TabIndex = 0 TabOrder = 0 object tabGeneral: TTabSheet Caption = 'General' ClientHeight = 255 ClientWidth = 540 object gbNewTorrent: TGroupBox Left = 8 Height = 95 Top = 118 Width = 523 Anchors = [akTop, akLeft, akRight] Caption = 'Add torrent' ClientHeight = 77 ClientWidth = 519 TabOrder = 1 object cbShowAddTorrentWindow: TCheckBox Left = 10 Height = 17 Top = 4 Width = 289 Caption = 'Prompt for download options when adding a new torrent' TabOrder = 0 end object cbDeleteTorrentFile: TCheckBox Left = 10 Height = 17 Top = 28 Width = 245 Caption = 'Delete a .torrent file after a successful addition' TabOrder = 1 end object cbLinksFromClipboard: TCheckBox Left = 10 Height = 17 Top = 52 Width = 253 Caption = 'Automatically add torrent links from the clipboard' TabOrder = 2 end end object gbData: TGroupBox Left = 8 Height = 106 Top = 6 Width = 523 Anchors = [akTop, akLeft, akRight] Caption = 'Data display' ClientHeight = 88 ClientWidth = 519 TabOrder = 0 object txSeconds: TLabel Left = 438 Height = 14 Top = 6 Width = 40 Anchors = [akTop, akRight] Caption = 'seconds' ParentColor = False end object txRefreshInterval: TLabel Left = 10 Height = 14 Top = 5 Width = 105 Caption = 'Data refresh interval:' ParentColor = False end object txRefreshIntervalMin: TLabel Left = 10 Height = 14 Top = 33 Width = 182 Caption = 'Data refresh interval when minimized:' ParentColor = False end object txSeconds2: TLabel Left = 438 Height = 14 Top = 34 Width = 40 Anchors = [akTop, akRight] Caption = 'seconds' ParentColor = False end object edRefreshInterval: TSpinEdit Left = 358 Height = 21 Top = 2 Width = 70 Anchors = [akTop, akRight] MaxValue = 999 MinValue = 1 TabOrder = 0 Value = 1 end object edRefreshIntervalMin: TSpinEdit Left = 358 Height = 21 Top = 30 Width = 70 Anchors = [akTop, akRight] MaxValue = 999 MinValue = 1 TabOrder = 1 Value = 1 end object cbCalcAvg: TCheckBox Left = 10 Height = 17 Top = 60 Width = 274 Caption = 'Average out transfer speeds to eliminate fluctuations' TabOrder = 2 end end end object tabAdvanced: TTabSheet Caption = 'Advanced' ClientHeight = 255 ClientWidth = 540 object txLanguage: TLabel Left = 8 Height = 14 Top = 148 Width = 52 Caption = 'Language:' ParentColor = False end object gbTray: TGroupBox Left = 8 Height = 73 Top = 6 Width = 523 Anchors = [akTop, akLeft, akRight] Caption = 'Tray icon' ChildSizing.LeftRightSpacing = 10 ChildSizing.TopBottomSpacing = 4 ChildSizing.VerticalSpacing = 6 ChildSizing.EnlargeHorizontal = crsHomogenousChildResize ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.ControlsPerLine = 2 ClientHeight = 55 ClientWidth = 519 TabOrder = 0 object cbTrayMinimize: TCheckBox Left = 10 Height = 17 Top = 4 Width = 234 Caption = 'Minimize to tray' TabOrder = 0 end object cbTrayClose: TCheckBox Left = 244 Height = 17 Top = 4 Width = 265 Caption = 'Close to tray' TabOrder = 1 end object cbTrayIconAlways: TCheckBox Left = 10 Height = 17 Top = 27 Width = 234 Caption = 'Tray icon always visible' TabOrder = 2 end object cbTrayNotify: TCheckBox Left = 244 Height = 17 Top = 27 Width = 265 Caption = 'Show notifications in tray icon' TabOrder = 3 end end object cbLanguage: TComboBox Left = 304 Height = 21 Top = 146 Width = 227 Anchors = [akTop, akLeft, akRight] DropDownCount = 20 ItemHeight = 13 OnEnter = cbLanguageEnter OnMouseDown = cbLanguageMouseDown Style = csDropDownList TabOrder = 4 end object txIntfScale: TLabel Left = 8 Height = 14 Top = 121 Width = 48 Caption = 'Font size:' ParentColor = False end object edIntfScale: TSpinEdit Left = 304 Height = 21 Top = 118 Width = 70 Increment = 5 MaxValue = 200 MinValue = 75 TabOrder = 3 Value = 100 end object txPerc: TLabel Left = 384 Height = 14 Top = 121 Width = 12 Caption = '%' ParentColor = False end object cbCheckNewVersion: TCheckBox Left = 8 Height = 17 Top = 92 Width = 160 Caption = 'Check for new version every:' OnClick = cbCheckNewVersionClick TabOrder = 1 end object edCheckVersionDays: TSpinEdit Left = 304 Height = 21 Top = 90 Width = 70 MaxValue = 999 MinValue = 1 TabOrder = 2 Value = 5 end object txDays: TLabel Left = 384 Height = 14 Top = 93 Width = 24 Caption = 'days' ParentColor = False end object gbSysInt: TGroupBox Left = 8 Height = 71 Top = 172 Width = 523 Anchors = [akTop, akLeft, akRight] Caption = 'System integration' ClientHeight = 53 ClientWidth = 519 TabOrder = 5 Visible = False object cbRegExt: TCheckBox Left = 10 Height = 17 Top = 4 Width = 148 Caption = 'Handle .torrent files by %s' TabOrder = 0 end object cbRegMagnet: TCheckBox Left = 10 Height = 17 Top = 28 Width = 147 Caption = 'Handle magnet links by %s' TabOrder = 1 end end end end object Buttons: TButtonPanel[1] Left = 8 Height = 26 Top = 297 Width = 548 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end end TransGUI/lang/0000755000000000000000000000000012261774330012136 5ustar rootrootTransGUI/lang/transgui.nl0000644000000000000000000003272412261612465014335 0ustar rootrootTranslationLanguage=Nederlands "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Remote naar lokale path mappings.~~Voorbeelden:~/share=\\pch\share~/var/downloads/muziek=Z:\muziek" %d x %s (have %d)=%d x %s (heb %d) %ds=%ds %s (%d hashfails)=%s (%d hashfails) %s (%s done)=%s (%s gereed) '%s' has finished downloading='%s' is klaar met downloaden %s%s%d downloading, %d seeding%s%s, %s=%s%s%d downloaden, %d seeden%s%s, %s &All=&Alles &Close=&Sluiten &Help=&Help &Ignore=&Negeren &No=&Nee &OK=&OK &Open=&Open &Retry=&Opnieuw &Save=&Opslaan &Unlock=&Ontgrendel &Yes=&Ja /s=/s Abort=Afbreken About=Info Active=Actief Add new torrent=Nieuwe torrent toevoegen &Add torrent=Torrent toevoegen Added on=Toegevoegd op Are you sure to remove torrent '%s' and all associated DATA?=Weet je zeker dat je de torrent '%s' en alle bijbehorende data wil verwijderen? Are you sure to remove torrent '%s'?=Weet je zeker dat je torrent '%s' wil verwijderen? Authentication=Authenticatie b=b Cancel=Cancel Client=Client Close to tray=Sluiten naar tray Comment=Opmerkingen Completed on=Voltooid op Completed=Voltooid Confirmation=Bevestiging connected=verbonden Connecting to daemon=Verbinden met server Connection error occurred=Verbindingsfout opgetreden Copy=Kopieëren Country=Land Created on=Gemaakt op D: %s/s=D: %s/s Destination folder=Doel map Disconnected=Niet verbonden Donate to support further development=Doneer om verdere ontwikkeling te steunen Donate via PayPal,WebMoney,Credit card=Doneer via PayPal,WebMoney,Credit card Done=Klaar Don't download=Niet downloaden Down limit=Download limiet Down speed=Down snelheid Down=Down Download complete=Download gereed Download speed=Download snelheid Downloaded=Gedownload Downloading=Downloaden Enable DHT=DHT Enable Peer Exchange=Peer Uitwisseling Enable port forwarding=Poort forwarding aan Encryption disabled=Encryptie uit Encryption enabled=Encryptie aan Encryption required=Encryptie vereist Encryption=Encryptie Error=Fout ETA=ETA E&xit=Afsluiten File name=Bestandsnaam Files=Bestanden Finished=Gereed Flag images archive is needed to display country flags.~Download this archive now?=Vlag iconen zijn nodig om de vlaggen van landen te tonen.~Wil je deze nu downloaden? Flags=Vlaggen GB=GB General=Algemeen Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP database is nodig om het land te vinden op basis van het ip adres.~Wil je deze database nu downloaden? Global bandwidth settings=Algemene bandbreedte instellingen Global peer limit=Algemene peer limiet Hash=Hash Have=Heeft Hide=Verbergen High priority=Hoge prioriteit high=hoog Host=Host in swarm=in zwerm Inactive=Niet actief Incoming port is closed. Check your firewall settings=Binnenkomende poort is gesloten. Controleer je firewall instellingen Incoming port tested successfully=Inkomende poort is goed getest Incoming port=Inkomende port Information=Informatie KB/s=KB/s KB=KB Language=Taal Last active=Laatst actief License=Licentie Low priority=Lage prioriteit low=laag Max peers=Max peers Maximum download speed=Maximum download snelheid Maximum upload speed=Maximum upload snelheid MB=MB Minimize to tray=Minimaliseren naar tray Name=Naam No host name specified=Geen hostname opgegeven No proxy server specified=Geen proxyserver opgegeven No to all=Nee op alles Normal priority=Normale prioriteit normal=normaal of=of Open containing folder=Map openen Open=Open Password=Wachtwoord Paths=Paden Peer limit=Peer limiet Peers=Peers Pieces=Delen Port=Poort Priority=Prioriteit Properties=Eigenschappen Proxy password=Proxy wachtwoord Proxy port=Proxy poort Proxy server=Proxy server Proxy user name=Proxy gebruikersnaam Ratio=Ratio Reconnect in %d seconds=Opnieuw verbinden in %d seconden Remaining=Resterend Remote host=Machine Remove torrent and Data=Torrent en data verwijderen Remove torrent=Torrent verwijderen Remove=Verwijderen Resolve country=Land ophalen Resolve host name=Hostnaam ophalen seconds=seconden Seed ratio=Seed ratio Seeding=Seeden Seeds=Seeds Select a .torrent to open=Selecteer een .torrent bestand om te openen Select all=Alles selecteren Select none=Niet selecteren Setup columns=Kolommen instellen Share ratio=Deel ratio Show country flag=Vlaggen tonen Show=Tonen Size=Grootte skip=overslaan Start all torrents=Start alle torrents Start torrent=Start torrent Start=Start Status=Status Stop all torrents=Stop alle torrents Stop all=Stop alles Stop torrent=Stop torrent Stop=Stop Stopped=Gestopt TB=TB Test port=Test poort T&ools=Instellingen Torrent contents=Torrent inhoud Torrent properties=Torrent eigenschappen Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrent verificatie kan lang duren.~Weet je zeker dat je de torrent '%s' wil controleren? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Alle bestanden (*.*)|*.* Total size=Totale grootte Tracker status=Tracker status Tracker update on=Tracker update aan Tracker=Tracker Trackers=Trackers Transfer=Overdracht Transmission options=Transmission opties Transmission%s at %s:%s=Transmission%s van %s:%s Tray icon always visible=Tray icoon altijd zichtbaar Tray icon=Tray icoon U: %s/s=U: %s/s Unable to extract flag image=Uitpakken van vlag iconen is mislukt Unable to get files list=Filelijst ophalen mislukt Unknown=Onbekend Up limit=Upload limiet Up speed=Upload snelheid Up=Up Update complete=Update gereed Update GeoIP database=GeoIP database bijwerken Update in=Update over Updating=Updaten Upload speed=Upload snelheid Uploaded=Geupload User name=Gebruikersnaam Verify torrent=Torrent verifieren &Verify=Verifieren Verifying=Verifieren Version %s=Versie %s Waiting=Wachten Warning=Waarschuwing Wasted=Verspild Working=Bezig Yes to &All=Ja op &Alles No tracker=Geen tracker %s downloaded=%s gedownload %s of %s downloaded=%s of %s gedownload %d torrents=%d torrents Add .part extension to incomplete files=Voeg de .part extensie toe aan incomplete bestanden Add torrent link=Voeg torrent link toe Are you sure to remove %d selected torrents and all their associated DATA?=Weet u zeker dat u %d geselecteerde torrents en alle DATA wilt verwijderen? Are you sure to remove %d selected torrents?=Weet u zeker dat u %d geselecteerde torrents wilt verwijderen? Bandwidth=Bandbreedte Directory for incomplete files=Locatie voor incomplete bestanden Download=Download Enable blocklist=Pas Blocklist toe ID=ID Move torrent data from current location to new location=Verplaats torrent data van huidige locatie naar nieuwe locatie New location for torrent data=Nieuwe locatie voor torrent data No link was specified=Er is geen link gespecificeerd No torrent location was specified=Er is geen torrent locatie gespecificeerd Path=Pad Reannounce (get more peers)=Reannounce (Krijg meer peers) Set data location=Stel data locatie in Size to download=Download formaat The block list has been updated successfully.~The list entries count: %d=De block lijst is geupdate.~De lijst bevat: %d regels The directory for incomplete files was not specified=De locatie voor incomplete bestanden is niet gespecificeerd The downloads directory was not specified=De download locatie is niet gespecificeerd Torrent data location=Torrent data locatie Torrents=Torrents Unable to execute "%s"=Kan "%s" niet uivoeren Update blocklist=Update blocklijst URL of a .torrent file or a magnet link=URL van een .torrent of een magnet link Columns setup=Kolommen instellen Add torrent=Voeg torrent toe Delete a .torrent file after a successful addition=.torrent bestand verwijderen na succesvol toevoegen Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrents verifieren kan lang duren.~Weet je zeker dat je %d torrents wil verifieren? Unable to load OpenSSL library files: %s and %s=Fout bij laden OpenSSL library bestanden: %s en %s Use SSL=Gebruik SSL Add tracker=Voeg tracker toe Alternate bandwidth settings=Alternatieve bandbreedte instellingen Apply alternate bandwidth settings automatically=Pas alternatieve bandreedte snelheid automatish toe Are you sure to delete connection '%s'?=Weet u zeker dat u de verbinding '%s' wilt verwijderen? Are you sure to remove tracker '%s'?=Weet u zeker dat u de tracker '%s' wilt verwijderen? Days=Dagen Delete=Verwijderen Disk cache size=Disk cache formaat Download speeds (KB/s)=Download snelheid (KB/s) Edit tracker=Tracker wijzigen Enable Local Peer Discovery=Local Peer Discovery inschakelen Free disk space=Vrije schijf ruimte Free: %s=Vrij: %s From=Van minutes=minuten Misc=Overig New connection=Nieuwe verbinding New=Nieuw No tracker URL was specified=Er is geen tracker URL gespecificeerd Proxy=Proxy Remove tracker=Verwijder tracker Rename=Hernoem Speed limit menu items=Snelheidlimiet menu items Stop seeding when inactive for=Stop seeden wanneer inactief gedurende The invalid time value was entered=De verkeerde tijd waarde is ingevoerd to=naar Tracker announce URL=Tracker announce URL Tracker properties=Tracker eigenschappen Unlimited=Onbeperkt Upload speeds (KB/s)=Upload snelheden (KB/s) Use alternate bandwidth settings=Gebruik alternatieve bandbreedte instellingen average=gemiddelde Browse=Bladeren Select a folder for download=Selecteer een map voor download Select torrent location=Selecteer torrent locatie A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Er is een nieuwe versie van %s beschikbaar.~Je huidige versie: %s~De nieuwe versie: %s~~Wil je de Download pagina openen? Advanced=Geavanceerd Check for new version every=Controleer op nieuwe versies iedere Check for updates=Controleer op updates Consider active torrents as stalled when idle for=Beschouw actiever torrents als inactief als ze niks doen gedurende Do you wish to enable automatic checking for a new version of %s?=Wil je automatisch controleren op nieuwe versies van %s? Donate!=Doneer! Download queue size=Download wachtrij grootte Error checking for new version=Fout bij het controleren voor nieuwe versie Folder grouping=Map groep Force start=Forceer start Home page=Home page Modify trackers=Trackers aanpassen Move bottom=Verplaats, Onderaan Move down queue=Verplaats omlaag wachtrij Move down=Verplaats omlaag Move top=Verplaats, Bovenaan Move up queue=Verplaats omhoog wachtrij Move up=Verplaats omhoog No updates have been found.~You are running the latest version of %s=Er zijn geen updates gevonden.~Je gebruikt de meest resente vresie van %s Queue position=Wachtrij positie Queue=Wachtrij Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents die niks doen gedurende N minuten worden niet meegeteld bij de Download wachtrijd of Upload wachtrij Tracker grouping=Tracker groep Upload queue size=Upload wachtrij grootte View=Weergave Visit home page=Bezoek home page days=Dagen Active time=Actief gedurende Automatically add torrent links from the clipboard=Automatisch torrents toevoegen van het plakbord Copy file path to clipboard=Copieer bestands pad naar plakbord Cumulative=Cumulatief Current=Huidig Files added=Bestanden toegevoegd Filter pane=Filter paneel Global statistics=Globale statestieken Info pane=Info paneel Statistics=Statistieken Status bar=Status balk %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Enable µTP=Enable µTP Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.template0000644000000000000000000003150212261612465015530 0ustar rootrootTranslationLanguage=??? "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (have %d) %ds=%ds %s (%d hashfails)=%s (%d hashfails) %s (%s done)=%s (%s done) '%s' has finished downloading='%s' has finished downloading %s%s%d downloading, %d seeding%s%s, %s=%s%s%d downloading, %d seeding%s%s, %s &All=&All &Close=&Close &Help=&Help &Ignore=&Ignore &No=&No &OK=&OK &Open=&Open &Retry=&Retry &Save=&Save &Unlock=&Unlock &Yes=&Yes /s=/s Abort=Abort About=About Active=Active Add new torrent=Add new torrent &Add torrent=Add torrent Added on=Added on Are you sure to remove torrent '%s' and all associated DATA?=Are you sure to remove torrent '%s' and all associated DATA? Are you sure to remove torrent '%s'?=Are you sure to remove torrent '%s'? Authentication=Authentication b=b Cancel=Cancel Client=Client Close to tray=Close to tray Comment=Comment Completed on=Completed on Completed=Completed Confirmation=Confirmation connected=connected Connecting to daemon=Connecting to daemon Connection error occurred=Connection error occurred Copy=Copy Country=Country Created on=Created on D: %s/s=D: %s/s Destination folder=Destination folder Disconnected=Disconnected Donate to support further development=Donate to support further development Donate via PayPal,WebMoney,Credit card=Donate via PayPal,WebMoney,Credit card Done=Done Don't download=Don't download Down limit=Down limit Down speed=Down speed Down=Down Download complete=Download complete Download speed=Download speed Downloaded=Downloaded Downloading=Downloading Enable DHT=Enable DHT Enable Peer Exchange=Enable Peer Exchange Enable port forwarding=Enable port forwarding Encryption disabled=Encryption disabled Encryption enabled=Encryption enabled Encryption required=Encryption required Encryption=Encryption Error=Error ETA=ETA E&xit=Exit File name=File name Files=Files Finished=Finished Flag images archive is needed to display country flags.~Download this archive now?=Flag images archive is needed to display country flags.~Download this archive now? Flags=Flags GB=GB General=General Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP database is needed to resolve country by IP address.~Download this database now? Global bandwidth settings=Global bandwidth settings Global peer limit=Global peer limit Hash=Hash Have=Have Hide=Hide High priority=High priority high=high Host=Host in swarm=in swarm Inactive=Inactive Incoming port is closed. Check your firewall settings=Incoming port is closed. Check your firewall settings Incoming port tested successfully=Incoming port tested successfully Incoming port=Incoming port Information=Information KB/s=KB/s KB=KB Language=Language Last active=Last active License=License Low priority=Low priority low=low Max peers=Max peers Maximum download speed=Maximum download speed Maximum upload speed=Maximum upload speed MB=MB Minimize to tray=Minimize to tray Name=Name No host name specified=No host name specified No proxy server specified=No proxy server specified No to all=No to all Normal priority=Normal priority normal=normal of=of Open containing folder=Open containing folder Open=Open Password=Password Paths=Paths Peer limit=Peer limit Peers=Peers Pieces=Pieces Port=Port Priority=Priority Properties=Properties Proxy password=Proxy password Proxy port=Proxy port Proxy server=Proxy server Proxy user name=Proxy user name Ratio=Ratio Reconnect in %d seconds=Reconnect in %d seconds Remaining=Remaining Remote host=Remote host Remove torrent and Data=Remove torrent and Data Remove torrent=Remove torrent Remove=Remove Resolve country=Resolve country Resolve host name=Resolve host name seconds=seconds Seed ratio=Seed ratio Seeding=Seeding Seeds=Seeds Select a .torrent to open=Select a .torrent to open Select all=Select all Select none=Select none Setup columns=Setup columns Share ratio=Share ratio Show country flag=Show country flag Show=Show Size=Size skip=skip Start all torrents=Start all torrents Start torrent=Start torrent Start=Start Status=Status Stop all torrents=Stop all torrents Stop all=Stop all Stop torrent=Stop torrent Stop=Stop Stopped=Stopped TB=TB Test port=Test port T&ools=Tools Torrent contents=Torrent contents Torrent properties=Torrent properties Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|All files (*.*)|*.* Total size=Total size Tracker status=Tracker status Tracker update on=Tracker update on Tracker=Tracker Trackers=Trackers Transfer=Transfer Transmission options=Transmission options Transmission%s at %s:%s=Transmission%s at %s:%s Tray icon always visible=Tray icon always visible Tray icon=Tray icon U: %s/s=U: %s/s Unable to extract flag image=Unable to extract flag image Unable to get files list=Unable to get files list Unknown=Unknown Up limit=Up limit Up speed=Up speed Up=Up Update complete=Update complete Update GeoIP database=Update GeoIP database Update in=Update in Updating=Updating Upload speed=Upload speed Uploaded=Uploaded User name=User name Verify torrent=Verify torrent &Verify=Verify Verifying=Verifying Version %s=Version %s Waiting=Waiting Warning=Warning Wasted=Wasted Working=Working Yes to &All=Yes to &All No tracker=No tracker %s downloaded=%s downloaded %s of %s downloaded=%s of %s downloaded %d torrents=%d torrents Add .part extension to incomplete files=Add .part extension to incomplete files Add torrent link=Add torrent link Are you sure to remove %d selected torrents and all their associated DATA?=Are you sure to remove %d selected torrents and all their associated DATA? Are you sure to remove %d selected torrents?=Are you sure to remove %d selected torrents? Bandwidth=Bandwidth Directory for incomplete files=Directory for incomplete files Download=Download Enable blocklist=Enable blocklist ID=ID Move torrent data from current location to new location=Move torrent data from current location to new location New location for torrent data=New location for torrent data No link was specified=No link was specified No torrent location was specified=No torrent location was specified Path=Path Reannounce (get more peers)=Reannounce (get more peers) Set data location=Set data location Size to download=Size to download The block list has been updated successfully.~The list entries count: %d=The block list has been updated successfully.~The list entries count: %d The directory for incomplete files was not specified=The directory for incomplete files was not specified The downloads directory was not specified=The downloads directory was not specified Torrent data location=Torrent data location Torrents=Torrents Unable to execute "%s"=Unable to execute "%s" Update blocklist=Update blocklist URL of a .torrent file or a magnet link=URL of a .torrent file or a magnet link Columns setup=Columns setup Add torrent=Add torrent Delete a .torrent file after a successful addition=Delete a .torrent file after a successful addition Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrents verification may take a long time.~Are you sure to start verification of %d torrents? Unable to load OpenSSL library files: %s and %s=Unable to load OpenSSL library files: %s and %s Use SSL=Use SSL Add tracker=Add tracker Alternate bandwidth settings=Alternate bandwidth settings Apply alternate bandwidth settings automatically=Apply alternate bandwidth settings automatically Are you sure to delete connection '%s'?=Are you sure to delete connection '%s'? Are you sure to remove tracker '%s'?=Are you sure to remove tracker '%s'? Days=Days Delete=Delete Disk cache size=Disk cache size Download speeds (KB/s)=Download speeds (KB/s) Edit tracker=Edit tracker Enable Local Peer Discovery=Enable Local Peer Discovery Free disk space=Free disk space Free: %s=Free: %s From=From minutes=minutes Misc=Misc New connection=New connection New=New No tracker URL was specified=No tracker URL was specified Proxy=Proxy Remove tracker=Remove tracker Rename=Rename Speed limit menu items=Speed limit menu items Stop seeding when inactive for=Stop seeding when inactive for The invalid time value was entered=The invalid time value was entered to=to Tracker announce URL=Tracker announce URL Tracker properties=Tracker properties Unlimited=Unlimited Upload speeds (KB/s)=Upload speeds (KB/s) Use alternate bandwidth settings=Use alternate bandwidth settings average=average Browse=Browse Enable µTP=Enable µTP Select a folder for download=Select a folder for download Select torrent location=Select torrent location A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.fr0000644000000000000000000003577612261612465014345 0ustar rootrootTranslationLanguage=Français "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Correspondances entre système de fichier distant et local.~~Exemples:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (%d obtenus) %ds=%ds %s (%d hashfails)=%s (%d corrompus) %s (%s done)=%s (%s terminés) '%s' has finished downloading='%s' a fini de télécharger %s%s%d downloading, %d seeding%s%s, %s=%s%s%d en téléchargement, %d en partage %s%s, %s &All=&Tout &Close=&Fermer &Help=&Aide &Ignore=&Ignorer &No=&Non &OK=&OK &Open=&Ouvrir &Retry=&Réessayer &Save=&Enregistrer &Unlock=&Déverrouiller &Yes=&Oui /s=/s Abort=Abandonner About=À propos Active=Actifs Add new torrent=Ajouter un nouveau torrent &Add torrent=&Ajouter un torrent Added on=Ajouté le Are you sure to remove torrent '%s' and all associated DATA?=Êtes-vous sûr de vouloir enlever le torrent '%s' ainsi que toutes les DONNÉES correspondantes? Are you sure to remove torrent '%s'?=Êtes-vous sûr de vouloir enlever le torrent '%s'? Authentication=Authentification b=o Cancel=Annuler Client=Client Close to tray=Fermer vers l'icône de notification Comment=Commentaire Completed on=Terminé le Completed=Terminés Confirmation=Confirmation connected=connecté Connecting to daemon=Connexion au démon Connection error occurred=Erreur de connexion survenue Copy=Copier Country=Pays Created on=Créé le D: %s/s=T: %s/s Destination folder=Dossier de destination Disconnected=Déconnecté Donate to support further development=Faire un don pour soutenir les futurs développements Donate via PayPal,WebMoney,Credit card=Faire un don via PayPal,WebMoney,Carte bancaire Done=Accompli Don't download=Ne pas télécharger Down limit=Limite tél Down speed=Vitesse tél Down=&Bas Download complete=Téléchargement terminé Download speed=Vitesse de téléchargement Downloaded=Téléchargé Downloading=Téléchargement Enable DHT=Activer la DHT Enable Peer Exchange=Activer l'Échange de Pairs Enable port forwarding=Activer la redirection de ports Encryption disabled=Chiffrage désactivé Encryption enabled=Chiffrage activé Encryption required=Chiffrage nécessaire Encryption=Chiffrage Error=Erreur ETA=Temps restant estimé E&xit=Q&uitter File name=Nom de fichier Files=Fichiers Finished=Terminé Flag images archive is needed to display country flags.~Download this archive now?=L'archive des images de drapeaux est requise pour afficher les drapeaux de pays..~Télécharger cette archive maintenant? Flags=Drapeau GB=Go General=Général Geo IP database is needed to resolve country by IP address.~Download this database now?=La base de données Geo IP est requise pour déterminer la localisation par adresse IP.~Télécharger cette archive maintenant? Global bandwidth settings=Paramètres généraux de bande passante Global peer limit=Limite globale de pairs Hash=Hachage Have=Possède Hide=Cacher High priority=Priorité haute high=haut Host=Hôte in swarm=dans l'essaim Inactive=Inactifs Incoming port is closed. Check your firewall settings=Le port entrant est fermé. Vérifiez votre pare-feu Incoming port tested successfully=Port entrant testé avec succès Incoming port=Port entrant Information=Information KB/s=ko/s KB=ko Language=Langue Last active=Dernière fois actif License=Licence Low priority=Priorité basse low=bas Max peers=Pairs max Maximum download speed=Vitesse maximum de téléchargement Maximum upload speed=Vitesse maximum d'envoi MB=Mo Minimize to tray=Réduire vers l'icône de notification Name=Nom No host name specified=Pas de nom d'hôte spécifié No proxy server specified=Pas de serveur proxy spécifié No to all=Non pour tout Normal priority=Priorité normale normal=normal of=de Open containing folder=Ouvrir le dossier de destination Open=Ouvrir Password=Mot de passe Paths=Chemins Peer limit=Limite de pairs Peers=Pairs Pieces=Parts Port=Port Priority=Priorité Properties=Propriétés Proxy password=Mot de passe proxy Proxy port=Port proxy Proxy server=Serveur proxy Proxy user name=Nom d'utilisateur proxy Ratio=Ratio Reconnect in %d seconds=Reconnexion dans %d secondes Remaining=Restant Remote host=Hôte distant Remove torrent and Data=Enlever le torrent et les données Remove torrent=Enlever le torrent Remove=Enlever Resolve country=Déterminer le pays Resolve host name=Déterminer le nom d'hôte seconds=secondes Seed ratio=Taux de partage Seeding=Partage Seeds=Sources Select a .torrent to open=Sélectionnez un .torrent à ouvrir Select all=Sélectionner tout Select none=Sélectionner aucun Setup columns=Configurer les colonnes Share ratio=Ratio de partage Show country flag=Afficher le drapeau du pays Show=Afficher Size=Taille skip=passer Start all torrents=Démarrer tous les torrents Start torrent=Démarrer le torrent Start=Démarrer Status=Statut Stop all torrents=Arrêter tous les torrents Stop all=Tout arrêter Stop torrent=Arrêter le torrent Stop=Arrêter Stopped=Arrêté TB=To Test port=Tester le port T&ools=O&utils Torrent contents=Contenu du torrent Torrent properties=Propriétés du torrent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=La vérification du torrent peut prendre longtemps.~Êtes-vous sûr de vouloir vérifier le torrent '%s'? &Torrent=&Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Tous les fichiers (*.*)|*.* Total size=Taille totale Tracker status=État du tracker Tracker update on=Prochaine mise à jour tracker Tracker=Tracker Trackers=Trackers Transfer=Transfert Transmission options=Options de Transmission Transmission%s at %s:%s=Transmission%s sur %s:%s Tray icon always visible=Icône de notification toujours visible Tray icon=Icône de notification U: %s/s=E: %s/s Unable to extract flag image=Échec à l'extraction de l'image du drapeau Unable to get files list=Échec d'obtention de la liste de fichiers Unknown=Inconnu Up limit=Limite env Up speed=Vitesse env Up=&Haut Update complete=Mise à jour terminée Update GeoIP database=Mettre à jour la base GeoIP Update in=Mettre à jour dans Updating=Mise à jour Upload speed=Vitesse d'envoi Uploaded=Envoyé User name=Nom d'utilisateur Verify torrent=Vérifier le torrent &Verify=&Vérifier Verifying=Vérification Version %s=Version %s Waiting=En attente Warning=Attention Wasted=Perdu Working=En fonctionnement Yes to &All=Oui à &Tout No tracker=Pas de tracker %s downloaded=%s téléchargés %s of %s downloaded=%s de %s téléchargés %d torrents=%d torrents Add .part extension to incomplete files=Ajouter l'extension .part aux fichiers incomplets Add torrent link=Ajouter le lien du torrent Are you sure to remove %d selected torrents and all their associated DATA?=Êtes-vous sûr d'enlever les %d torrents sélectionnés et toutes les DONNÉES correspondantes? Are you sure to remove %d selected torrents?=Êtes-vous sûr d'enlever les %d torrents sélectionnés? Bandwidth=Bande passante Directory for incomplete files=Dossier des fichiers incomplet Download=Télécharger Enable blocklist=Activer la liste de blocage ID=ID Move torrent data from current location to new location=Déplacer les données du torrent de l'emplacement actuel vers le nouveau New location for torrent data=Nouvel emplacement pour les données du torrent No link was specified=Aucun lien n'a été spécifié No torrent location was specified=Aucun emplacement n'a été spécifié Path=Chemin Reannounce (get more peers)=Relancer (obtenir plus de pairs) Set data location=Définir l'emplacement des données Size to download=Taille à télécharger The block list has been updated successfully.~The list entries count: %d=La liste de blocage a été mise à jour avec succès.~Nombre d'entrées: %d The directory for incomplete files was not specified=Le dossier pour les fichiers incomplets n'a pas été spécifié The downloads directory was not specified=Le dossier de téléchargement n'a pas été spécifié Torrent data location=Emplacement des données du torrent Torrents=Torrents Unable to execute "%s"=Impossible d'exécuter "%s" Update blocklist=Mettre à jour la liste de blocage URL of a .torrent file or a magnet link=URL d'un fichier .torrent ou magnet link Columns setup=Configurer les colonnes Add torrent=Ajouter torrent Delete a .torrent file after a successful addition=Supprimer le fichier torrent après l'avoir ajouté avec succès Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=La vérification des torrents peut prendre un certain temps.~Êtes-vous sur de démarrer la vérification de %d torrents ? Unable to load OpenSSL library files: %s and %s=Impossible de charger les fichiers de bibliothèque OpenSSL : %s et %s Use SSL=Utiliser SSL Add tracker=Ajouter un tracker Alternate bandwidth settings=Paramètres alternatifs de bande passante Apply alternate bandwidth settings automatically=Appliquer automatiquement les paramètres alternatifs de bande passante Are you sure to delete connection '%s'?=Êtes-vous sur de supprimer la connexion '%s'? Are you sure to remove tracker '%s'?=Êtes-vous sur d'enlever le tracker '%s'? Days=Jours Delete=Supprimer Disk cache size=Taille du cache disque Download speeds (KB/s)=Vitesses de téléchargement (ko/s) Edit tracker=Éditer le tracker Enable Local Peer Discovery=Activer la Découverte Locale des Pairs Free disk space=Espace disque libre Free: %s=Libre: %s From=De minutes=minutes Misc=Divers New connection=Nouvelle connexion New=Nouveau No tracker URL was specified=Aucune URL de tracker spécifiée Proxy=Proxy Remove tracker=Supprimer le tracker Rename=Renommer Speed limit menu items=Valeurs des vitesses limites Stop seeding when inactive for=Arreter le partage si inactif depuis The invalid time value was entered=Une valeur de temps incorrecte a été entrée to=à Tracker announce URL=URL d'annonce tracker Tracker properties=Propriété du tracker Unlimited=Illimité Upload speeds (KB/s)=Vitesses d'envoi (ko/s) Use alternate bandwidth settings=Utiliser les paramètres alternatifs de bande passante average=moyenne Browse=Parcourir Enable µTP=Activer µTP Select a folder for download=Choisir un dossier de téléchargement Select torrent location=Choisir l'emplacement d'un torrent A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Une nouvelle version de %s est disponible.~Votre version actuelle: %s~La nouvelle version: %s~~Voulez vous ouvrir la page web de téléchargement? Advanced=Avancé Check for new version every=Vérifier une nouvelle version tous les Check for updates=Vérifier les mises à jours Consider active torrents as stalled when idle for=Considérer les torrents actifs comme au point mort en cas d'inactivité pour Do you wish to enable automatic checking for a new version of %s?=Souhaitez vous activer la vérification automatique des nouvelles versions de %s? Donate!=Donnez ! Download queue size=Taille de la file de téléchargement Error checking for new version=Erreur durant la vérification d'une nouvelle version Folder grouping=Regroupement par dossier Force start=Forcer le démarrage Home page=Page d'accueil Modify trackers=Modifier les trackers Move bottom=Déplacer en bas Move down queue=Déplacer la file vers le bas Move down=Déplacer vers le bas Move top=Déplacer en haut Move up queue=Déplacer la file vers le haut Move up=Déplacer vers le haut No updates have been found.~You are running the latest version of %s=Aucune mise à jour trouvée.~Vous utilisez la dernière version de %s Queue position=Position dans la file Queue=File Torrents that are idle for N minutes aren't counted toward the Download queue or Upload queue=Les torrents qui sont inactifs pendant N minutes ne sont pas comptabilisés vers la file de téléchargement ou vers la file d'envoi Tracker grouping=Regroupement par tracker Upload queue size=Taille de la file d'envoi View=Vue Visit home page=Visiter la page d'accueil days=jours Active time=Durée d'activité Automatically add torrent links from the clipboard=Ajouter automatiquement les liens torrent depuis le presse-papier Copy file path to clipboard=Copier chemin dans le presse-papier Cumulative=Cumulées Current=Session courante Files added=Fichiers ajoutés Filter pane=Panneau de filtres Global statistics=Statistiques globales Info pane=Panneau d'informations Statistics=Statistiques Status bar=Barre de statut %dd=%dj %dh=%dh %dm=%dmin All torrents=Tous les torrents Application options=Options de Transmission Remote GUI Ask for password=Demander le mot de passe Authentication required=Authentication nécessaire Average out transfer speeds to eliminate fluctuations=Faire une moyenne des vitesses de transfert pour éliminer les fluctuations Connect to %s=Se connecter à %s Connect to Transmission using proxy server=Se connecter à Transmission en utilisant un serveur proxy Connect to Transmission=Se connecter à Transmission Connection name=Nom de la connexion Could not connect to tracker=Impossible de se connecter au tracker Data display=Affichage des données Data refresh interval when minimized=Intervalle de rafraichissement lorsque la fenêtre est réduite Data refresh interval=Intervalle de rafraichissement des données Default download folder on remote host=Dossier de téléchargement par défaut sur l'hôte distant Disconnect from Transmission=Déconnecter de Transmission Downloading torrent file=Fichier torrent en cours de téléchargement Font size=Taille de la police Handle .torrent files by %s=Prendre en charge les fichiers .torrent avec %s Handle magnet links by %s=Prendre en charge les liens magnet avec %s Invalid name specified=Nom invalide spécifié Manage connections to Transmission=Gérer les connections à Transmission Manage connections=Gérer les connexions Network (WAN)=Réseau (WAN) New connection to Transmission=Nouvelle connexion à Transmission Pick random port on Transmission launch=Utiliser un port aléatoire au lancement de Transmission Please enter a password to connect to %s=Veuillez entrer un mot de passe pour vous connecter à %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Veuillez préciser comment %s va se connecter à un hôte distant exécutant le démon Transmission(service) Prompt for download options when adding a new torrent=Demander les options de téléchargement lors de l'ajout d'un nouveau torrent RPC path=Chemin RPC Save as=Enregistrer sous Seeding time=Temps de Seed Show advanced options=Montrer les options avancées Show notifications in tray icon=Afficher les notifications dans l'icône de notification System integration=Intégration système Torrent already exists in the list=Le torrent existe déjà dans la liste Torrent not registered with this tracker=Torrent non enregistré sur ce tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Utilisez les Options de Transmission Remote GUI pour définir la correspondance des chemins Update trackers for the existing torrent?=Mettre à jour les trackers pour le torrent existant? You need to restart the application to apply changes=Vous devez redémarrer Transmission Remote GUI pour appliquer les changementsTransGUI/lang/transgui.zh0000644000000000000000000003103412261612465014336 0ustar rootrootTranslationLanguage=简体中文 "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="远程路径映射.~~比如:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (下载了 %d) %ds=%ds %s (%d hashfails)=%s (%d 丢弃的å—) %s (%s done)=%s (%s 完æˆ) '%s' has finished downloading='%s' 完æˆä¸‹è½½ %s%s%d downloading, %d seeding%s%s, %s=%s%s%d 下载中, %d åšç§ä¸­%s%s, %s &All=&所有 &Close=&关闭 &Help=&帮助 &Ignore=&忽略 &No=&å–æ¶ˆ &OK=&确认 &Open=&打开 &Retry=&é‡è¯• &Save=&ä¿å­˜ &Unlock=&è§£é” &Yes=&确认 /s=/s Abort=中止 About=关于 Active=活动 Add new torrent=添加新ç§å­ &Add torrent=添加ç§å­ Added on=添加于 Are you sure to remove torrent '%s' and all associated DATA?=你确定移除ç§å­ '%s' 和其所有数æ®å—? Are you sure to remove torrent '%s'?=你确定è¦ç§»é™¤ç§å­å— '%s'? Authentication=è®¤è¯ b=b Cancel=关闭 Client=客户端 Close to tray=关闭到托盘 Comment=评论 Completed on=下载完毕于 Completed=å®Œæˆ Confirmation=è®¤è¯ connected=已连接 Connecting to daemon=连接中 Connection error occurred=连接å‘生错误 Copy=å¤åˆ¶ Country=国家 Created on=创建于 D: %s/s=D: %s/s Destination folder=目标目录 Disconnected=离线 Donate to support further development=ææ¬¾ Donate via PayPal,WebMoney,Credit card=通过PayPal,WebMoney,Credit cardææ¬¾ Done=å®Œæˆ Don't download=ä¸è¦ä¸‹è½½ Down limit=下载é™é€Ÿ Down speed=下载速度 Down=下移 Download complete=ä¸‹è½½å®Œæˆ Download speed=下载速度 Downloaded=已下载 Downloading=下载中 Enable DHT=打开DHT Enable Peer Exchange=æ‰“å¼€ç”¨æˆ·äº¤æ¢ Enable port forwarding=打开端å£è½¬å‘ Encryption disabled=加密关闭 Encryption enabled=åŠ å¯†å¼€å¯ Encryption required=需è¦åР坆 Encryption=加密 Error=错误 ETA=剩余时间 E&xit=退出 File name=文件åç§° Files=文件 Finished=å®Œæˆ Flag images archive is needed to display country flags.~Download this archive now?=需è¦ä¸‹è½½ip之剿˜¾ç¤ºçš„国旗å—? Flags=çŠ¶æ€ GB=GB General=æ¦‚è¦ Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP æ•°æ®åº“å¯ä»¥å¤„ç†ipæ¥è‡ªäºŽä½•方,下载它å—? Global bandwidth settings=全局带宽设置 Global peer limit=全局连接é™åˆ¶ Hash=校验 Have=å·²æŠ“å– Hide=éšè— High priority=高优先 high=高 Host=主机 in swarm=in swarm Inactive=䏿´»åЍ Incoming port is closed. Check your firewall settings=端å£å…³é—­ï¼Œè¯·æ£€æŸ¥é˜²ç«å¢™è®¾ç½® Incoming port tested successfully=ç«¯å£æµ‹è¯•æˆåŠŸ Incoming port=ç«¯å£ Information=ä¿¡æ¯ KB/s=KB/s KB=KB Language=语言 Last active=æœ€åŽæ´»åЍ License=License Low priority=低优先 low=低 Max peers=最大连接数 Maximum download speed=最大下载速度 Maximum upload speed=最大上传速度 MB=MB Minimize to tray=最å°åŒ–到托盘 Name=åç§° No host name specified=没有指定主机åç§° No proxy server specified=没有指定代ç†ä¸»æœºåç§° No to all=å…¨å¦ Normal priority=正常优先 normal=正常 of=of Open containing folder=打开包å«çš„æ–‡ä»¶å¤¹ Open=打开 Password=å¯†ç  Paths=路径 Peer limit=连接é™åˆ¶ Peers=连接 Pieces=å— Port=ç«¯å£ Priority=优先级 Properties=属性 Proxy password=代ç†å¯†ç  Proxy port=代ç†ç«¯å£ Proxy server=代ç†ä¸»æœº Proxy user name=代ç†ç”¨æˆ·å Ratio=分享率 Reconnect in %d seconds=在%dç§’å†…é‡æ–°è¿žæŽ¥ Remaining=剩余 Remote host=远程主机 Remove torrent and Data=删除ç§å­å’Œæ•°æ® Remove torrent=删除ç§å­ Remove=删除 Resolve country=解释国家 Resolve host name=解释主机åç§° seconds=ç§’ Seed ratio=åšç§åˆ†äº«çއ Seeding=åšç§ä¸­ Seeds=ç§å­ Select a .torrent to open=选则一个ç§å­æ‰“å¼€ Select all=选择所有 Select none=å…¨ä¸é€‰æ‹© Setup columns=设置显示选项 Share ratio=共享分享率 Show country flag=显示国家气质 Show=显示 Size=尺寸 skip=跳过 Start all torrents=开始所有ç§å­ Start torrent=开始ç§å­ Start=开始 Status=çŠ¶æ€ Stop all torrents=åœæ­¢æ‰€æœ‰ç§å­ Stop all=åœæ­¢æ‰€æœ‰ Stop torrent=åœæ­¢ç§å­ Stop=åœæ­¢ Stopped=å·²åœæ­¢ TB=TB Test port=æµ‹è¯•ç«¯å£ T&ools=工具 Torrent contents=ç§å­å†…容 Torrent properties=ç§å­å±žæ€§ Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=æ•ˆéªŒæ—¶é—´å¾ˆé•¿ï¼Œæ˜¯å¦æ•ˆéªŒ'%s'? &Torrent=ç§å­ Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=ç§å­æ–‡ä»¶ (*.torrent)|*.torrent|所有类型文件 (*.*)|*.* Total size=总体积 Tracker status=æœåŠ¡å™¨çŠ¶æ€ Tracker update on=æœåŠ¡å™¨æ›´æ–°äºŽ Tracker=æœåС噍 Trackers=æœåС噍 Transfer=äº¤æ¢ Transmission options=Transmission选项 Transmission%s at %s:%s=Transmission%s at %s:%s Tray icon always visible=托盘图标总是å¯è§ Tray icon=托盘图标 U: %s/s=U: %s/s Unable to extract flag image=æ— æ³•è§£åŽ‹ç¼©æ——å¸œå›¾åƒ Unable to get files list=无法得到文件列表 Unknown=未知 Up limit=上传é™åˆ¶ Up speed=上传速度 Up=上移 Update complete=更新完毕 Update GeoIP database=æ›´æ–°åœ°ç†æ•°æ®åº“ Update in=å³å°†æ›´æ–°äºŽ Updating=更新中 Upload speed=上传速度 Uploaded=上传 User name=用户å Verify torrent=效验ç§å­ &Verify=效验 Verifying=效验中 Version %s=版本 %s Waiting=等待 Warning=警告 Wasted=浪费 Working=工作中 Yes to &All=全部确认 No tracker=æ— æœåС噍 %s downloaded=下载了%s %s of %s downloaded=下载了 %s / %s %d torrents=%d ç§å­ Add .part extension to incomplete files=添加.partåˆ°æœªå®Œæˆæ–‡ä»¶ Add torrent link=添加ç§å­é“¾æŽ¥ Are you sure to remove %d selected torrents and all their associated DATA?=你想删除选中ç§å­ %d 和其数æ®å—? Are you sure to remove %d selected torrents?=你想删除ç§å­ %d å—? Bandwidth=æµé‡ Directory for incomplete files=æœªå®Œæˆæ–‡ä»¶ç›®å½• Download=下载 Enable blocklist=å¼€å¯é»‘åå• ID=ID Move torrent data from current location to new location=移动ç§å­æ•°æ®ä»Žå½“å‰ä½ç½®åˆ°æ–°ä½ç½® New location for torrent data=ç§å­æ•°æ®æ–°ä½ç½® No link was specified=没有指定链接 No torrent location was specified=没有指定ç§å­ä½ç½® Path=路径 Reannounce (get more peers)=釿–°é€šå‘Š Set data location=设置数æ®ä½ç½® Size to download=下载体积 The block list has been updated successfully.~The list entries count: %d=黑å啿›´æ–°æˆåŠŸ.~列表总长: %d The directory for incomplete files was not specified=æœªæŒ‡å®šæœªå®Œæˆæ–‡ä»¶çš„目录 The downloads directory was not specified=未指定下载目录 Torrent data location=ç§å­æ–‡ä»¶ä½ç½® Torrents=ç§å­ Unable to execute "%s"=无法执行 "%s" Update blocklist=更新黑åå• URL of a .torrent file or a magnet link=ç§å­é“¾æŽ¥ Columns setup=设置列 Add torrent=添加ç§å­ Delete a .torrent file after a successful addition=æˆåŠŸæ·»åŠ ä»»åŠ¡ä¹‹åŽåˆ é™¤ç§å­ Torrent=ç§å­ Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=æ•ˆéªŒæ—¶é—´å¾ˆé•¿ï¼Œæ˜¯å¦æ•ˆéªŒ %d? Unable to load OpenSSL library files: %s and %s=无法载入 OpenSSL 库文件: %s and %s Use SSL=使用 SSL Add tracker=添加站点 Alternate bandwidth settings=备用带宽设置 Apply alternate bandwidth settings automatically=自动应用备用带宽设置 Are you sure to delete connection '%s'?=你确认删除连接 '%s'? Are you sure to remove tracker '%s'?=你确认移除站点 '%s'? Days=æ—¥ Delete=删除 Disk cache size=ç£ç›˜é«˜é€Ÿç¼“å­˜çš„å¤§å° Download speeds (KB/s)=下载速度 (KB/s) Edit tracker=编辑站点 Enable Local Peer Discovery=开坿œ¬åœ°ç”¨æˆ·å‘现 Free disk space=剩余空间 Free: %s=剩余: %s From=æ¥è‡ª minutes=分 Misc=æ‚项 New connection=新的连接 New=新建 No tracker URL was specified=没有指定站点连接 Proxy=ä»£ç† Remove tracker=移除站点 Rename=é‡å‘½å Speed limit menu items=èœå•项é™é€Ÿ Stop seeding when inactive for=当ç§å­ä¸æ´»åŠ¨æ—¶åœæ­¢åšç§ The invalid time value was entered=输入了无效的时间 to=到 Tracker announce URL=站点通告链接 Tracker properties=站点选项 Unlimited=æ— é™åˆ¶ Upload speeds (KB/s)=上传速度 (KB/s) Use alternate bandwidth settings=使用备用带宽设置 A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=%s 的更新å¯ç”¨ã€‚~当å‰ç‰ˆæœ¬å·ï¼š %s~新版本å·ï¼š %s~~你想打开下载页å—? Advanced=高级 average=å¹³å‡ Browse=æµè§ˆ Check for new version every=è‡ªåŠ¨æ£€æŸ¥æ›´æ–°ï¼Œæ¯ Check for updates=检查更新 Consider active torrents as stalled when idle for=当活动的ç§å­é—²ç½®æ— ä¸‹è½½è¾¾åˆ°æ‰€è®¾å®šçš„æ—¶é—´åŽè‡ªåŠ¨åœæ­¢ Do you wish to enable automatic checking for a new version of %s?=你想自动检查 %s? 的更新å—? Donate!=ææ¬¾ï¼ Download queue size=åŒæ—¶ä¸‹è½½çš„任务数 Enable µTP=å¼€å¯ ÂµTP Error checking for new version=检查更新å‘生错误 Folder grouping=文件夹组 Force start=强制开始 Home page=主页 Modify trackers=修改 Tracker Move bottom=ç§»åˆ°æœ€åŽ Move down queue=队列下移 Move down=往下移 Move top=ç§»åˆ°æœ€å‰ Move up queue=队列上移 Move up=往上移 No updates have been found.~You are running the latest version of %s=未å‘现更新。~你正在使用最新的 %s。 Queue position=队列ä½ç½® Queue=队列 Select a folder for download=选择一个目录æ¥å­˜æ”¾ä¸‹è½½çš„æ–‡ä»¶ Select torrent location=选择ç§å­è·¯å¾„ Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=当ç§å­é—²ç½®æ— ä¸‹è½½ N 分钟åŽï¼Œé‚£ä¹ˆä»–们将ä¸ä¼šè®¡å…¥ä¸‹è½½é˜Ÿåˆ—或上传队列 Tracker grouping=Tracker 组 Upload queue size=åŒæ—¶ä¸Šä¼ çš„任务数 View=视图 Visit home page=访问主页 days=天 Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.pl0000644000000000000000000003366312261612465014342 0ustar rootrootTranslationLanguage=Polski "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Mapowanie Å›cieżek zdalnych do lokalnych.~~PrzykÅ‚ady:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (pobrano %d) %ds=%ds %s (%d hashfails)=%s (%d błędów bufora) %s (%s done)=%s (%s pobrane) '%s' has finished downloading=ZakoÅ„czono pobieranie '%s' %s%s%d downloading, %d seeding%s%s, %s=%s%s%d pobieranie, %d seedowanie%s%s, %s &All=&Wszystkie &Close=&Zamknij &Help=&Pomoc &Ignore=&Ignoruj &No=&Nie &OK=&OK &Open=&Otwórz &Retry=&Ponów &Save=&Zapisz &Unlock=&Odblokuj &Yes=&Tak /s=/s Abort=Przerwij About=O programie Active=Aktywne Add new torrent=Dodaj nowy torrent &Add torrent=Dodaj torrent Added on=Dodane o Are you sure to remove torrent '%s' and all associated DATA?=Czy na pewno chcesz usunąć plik torrent '%s' oraz wszystkie pobrane dane? Are you sure to remove torrent '%s'?=Czy na pewno chcesz usunąć plik torrent '%s'? Authentication=Autoryzacja b=b Cancel=Anuluj Client=Klient Close to tray=Zamknij do tray'a Comment=Komentarz Completed on=ZakoÅ„czono o Completed=UkoÅ„czono Confirmation=Potwierdzenie connected=połączono Connecting to daemon=ÅÄ…czenie do demona Connection error occurred=WystÄ…piÅ‚ błąd połączenia Copy=Kopiuj Country=Kraj Created on=Utworzono o D: %s/s=D: %s/s Destination folder=Folder docelowy Disconnected=Rozłącz Donate to support further development=Darowizna na wsparcie dalszego rozwoju Donate via PayPal,WebMoney,Credit card=Dotuj przez PayPal,WebMoney,Credit card Done=PostÄ™p Don't download=Nie pobieraj Down limit=Limit pobierania Down speed=Pobieranie Down=W dół Download complete=ZakoÅ„czono pobieranie Download speed=PrÄ™dkość pobierania Downloaded=Pobrane Downloading=Pobieranie Enable DHT=Włącz DHT Enable Peer Exchange=Włącz Peer Exchange Enable port forwarding=Włącz przekierowywanie portów Encryption disabled=Szyfrowanie wyłączone Encryption enabled=Szyfrowanie włączone Encryption required=Szyfrowanie wymagane Encryption=Szyfrowanie Error=Błąd ETA=ETA E&xit=Wyjdź File name=Nazwa pliku Files=Pliki Finished=UkoÅ„czono Flag images archive is needed to display country flags.~Download this archive now?=Obrazki flag sÄ… potrzebne, aby wyÅ›wietlać je w karcie peerów.~Czy chcesz je teraz pobrać? Flags=Flagi GB=GB General=Ogólne Geo IP database is needed to resolve country by IP address.~Download this database now?=Baza Geo IP jest potrzebna aby wykryć kraj peer'a.~Czy chcesz jÄ… teraz pobrać? Global bandwidth settings=Ogólne ustawienia prÄ™dkoÅ›ci Global peer limit=Ogólny limit peerów Hash=Hash Have=PobraÅ‚ Hide=Ukryj High priority=Wysoki priorytet high=Wysoki Host=Host in swarm=w kolejce Inactive=Nieaktywne Incoming port is closed. Check your firewall settings=Port połączeÅ„ przychodzÄ…cych jest zablokowany. Sprawdź ustawienia swojego firewalla. Incoming port tested successfully=Test portu połączeÅ„ przychodzÄ…cych wypadÅ‚ pomyÅ›lnie Incoming port=Port połączeÅ„ przychodzÄ…cych Information=Informacja KB/s=KB/s KB=KB Language=JÄ™zyk Last active=Ostatnio aktywny License=Licencja Low priority=Niski priorytet low=niski Max peers=Maksymalna ilość peerów Maximum download speed=Maksymalna prÄ™dkość pobierania Maximum upload speed=Maksymalna prÄ™dkość wysyÅ‚ania MB=MB Minimize to tray=Schowaj do tray'a Name=Nazwa No host name specified=Nie zdefiniowano hostname No proxy server specified=Nie zdefiniowano serwera proxy No to all=Nie na wszystko Normal priority=Normalny priorytet normal=normalny of=z Open containing folder=Otwórz folder zawierajÄ…cy Open=Otwórz Password=HasÅ‚o Paths=Åšcieżki Peer limit=Limit peerów Peers=Peery Pieces=Części Port=Port Priority=Priorytet Properties=WÅ‚aÅ›ciwoÅ›ci Proxy password=HasÅ‚o proxy Proxy port=Port proxy Proxy server=Serwer proxy Proxy user name=Nazwa użytkownika proxy Ratio=Ratio Reconnect in %d seconds=Ponawianie próby za %d sekund Remaining=PozostaÅ‚o Remote host=Zdalny host Remove torrent and Data=UsuÅ„ torrenta razem z danymi Remove torrent=UsuÅ„ torrenta Remove=UsuÅ„ Resolve country=Sprawdzaj kraj Resolve host name=Sprawdzaj hostname seconds=sekund Seed ratio=Ratio seedowania Seeding=Seedowanie Seeds=Seedy Select a .torrent to open=Wybierz plik .torrent aby otworzyć Select all=Zaznacz wszystko Select none=Odznacz wszystko Setup columns=Ustawienia kolumn Share ratio=Ratio współdzielenia Show country flag=WyÅ›wietlaj flagi paÅ„stw Show=Pokaż Size=Rozmiar skip=pomiÅ„ Start all torrents=Rozpocznij pobieranie wszystkiego Start torrent=Rozpocznij pobieranie Start=Start Status=Stan Stop all torrents=Zatrzymaj wszystkie torrenty Stop all=Zatrzymaj wszystko Stop torrent=Zatrzymaj torrent Stop=Stop Stopped=Zatrzymane TB=TB Test port=Testuj port T&ools=NarzÄ™dzia Torrent contents=Treść torrenta Torrent properties=WÅ‚aÅ›ciwoÅ›ci torrenta Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Weryfikacja torrenta może trwać bardzo dÅ‚ugo.~Czy na pewno chcesz zweryfikować torrent '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenty (*.torrent)|*.torrent|Wszystkie pliki (*.*)|*.* Total size=ÅÄ…czny rozmiar Tracker status=Status trackera Tracker update on=Aktualizacja trackera o Tracker=Tracker Trackers=Trackery Transfer=Transfer Transmission options=Opcje Transmission Transmission%s at %s:%s=Transmission%s na %s:%s Tray icon always visible=Ikonka w trayu zawsze widoczna Tray icon=Ikonka w trayu U: %s/s=U: %s/s Unable to extract flag image=Nie można rozpakować obrazków flag Unable to get files list=Nie można pobrać listy plików Unknown=Nieznane Up limit=Limit wysyÅ‚ania Up speed=WysyÅ‚anie Up=W górÄ™ Update complete=Uaktualnianie zakoÅ„czone Update GeoIP database=Uaktualnij bazÄ™ GeoIP Update in=Aktualizacja za Updating=Aktualizacja Upload speed=PrÄ™dkość wysyÅ‚ania Uploaded=WysÅ‚ane User name=Nazwa użytkownika Verify torrent=Zweryfikuj torrenta &Verify=Weryfikuj Verifying=Sprawdzanie Version %s=Wersja %s Waiting=Oczekiwanie Warning=Ostrzeżenie Wasted=Zmarnowane Working=DziaÅ‚a Yes to &All=Tak na &Wszystkie No tracker=Brak trackera %s downloaded=%s pobrano %s of %s downloaded=%s z %s pobrano %d torrents=%d torrenty Add .part extension to incomplete files=Dodaj koÅ„cówkÄ™ .part do plików niedokoÅ„czonych Add torrent link=Dodaj link do torrenta Are you sure to remove %d selected torrents and all their associated DATA?=Czy na pewno chcesz usunąć %d wybranych torrentów razem z danymi? Are you sure to remove %d selected torrents?=Czy na pewno chcesz usunąć %d wybranych torrentów? Bandwidth=ÅÄ…cze Directory for incomplete files=Folder na niedokoÅ„czone pliki Download=Pobierz Enable blocklist=Włącz blocklistÄ™ ID=Nr Move torrent data from current location to new location=PrzenieÅ› dane tego torrenta do innej lokalizacji New location for torrent data=Nowa lokalizacja na pobrane dane No link was specified=Link nie zostaÅ‚ podany No torrent location was specified=Nie zostaÅ‚a podana lokalizacja torrenta Path=Åšcieżka Reannounce (get more peers)=Pobierz wiÄ™cej peerów Set data location=Ustaw lokalizacjÄ™ pobieranych plików Size to download=Do pobrania The block list has been updated successfully.~The list entries count: %d=Lista blokowanych adresów zostaÅ‚a pomyÅ›lnie zaktualizowana.~Liczba wpisów: %d The directory for incomplete files was not specified=Katalog na niedokoÅ„czone pliki nie zostaÅ‚ wskazany The downloads directory was not specified=Katalog na pobierane pliki nie zostaÅ‚ wskazany Torrent data location=Lokalizacja danych torrenta Torrents=Torrenty Unable to execute "%s"=Nie można uruchomić "%s" Update blocklist=Uaktualnij blocklistÄ™ URL of a .torrent file or a magnet link=URL pliku .torrent lub magnet linka Columns setup=Ustawienia kolumn Add torrent=Dodaj torrent Delete a .torrent file after a successful addition=UsuÅ„ plik .torrent po dodaniu Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Weryfikacja torrentów może trwać bardzo dÅ‚ugo.~Czy na pewno chcesz zweryfikować %d torrentów? Unable to load OpenSSL library files: %s and %s=Nie można zaÅ‚adować biblioteki OpenSSL: %s i %s Use SSL=Używaj SSL Add tracker=Dodaj tracker Alternate bandwidth settings=Alternatywne ustawienia łącza Apply alternate bandwidth settings automatically=Automatycznie włącz alternatywne ustawienia łącza Are you sure to delete connection '%s'?=Czy na pewno chcesz usunąć połączenie '%s'? Are you sure to remove tracker '%s'?=Czy na pewno chcesz usunąć tracker '%s'? Days=Dni Delete=UsuÅ„ Disk cache size=Wielkość cache dysku Download speeds (KB/s)=PrÄ™dkoÅ›ci pobierania (KB/s) Edit tracker=Edytuj tracker Enable Local Peer Discovery=Włącz Local Peer Discovery Free disk space=Wolne miejsce na dysku Free: %s=Wolne: %s From=Od minutes=minut Misc=Inne New connection=Nowe połączenie New=Nowy No tracker URL was specified=URL trackera nie zostaÅ‚ podany Proxy=Proxy Remove tracker=UsuÅ„ tracker Rename=ZmieÅ„ nazwÄ™ Speed limit menu items=Elementy menu ograniaczania prÄ™dkoÅ›ci Stop seeding when inactive for=ZakoÅ„cz seedowanie gdy nieaktywny przez The invalid time value was entered=ZostaÅ‚ podany niepoprawny czas to=do Tracker announce URL=URL Trackera Tracker properties=WÅ‚aÅ›ciwoÅ›ci trackera Unlimited=Bez limitów Upload speeds (KB/s)=PrÄ™dkoÅ›ci wysyÅ‚ania (KB/s) Use alternate bandwidth settings=Używaj alternatywnych ustawieÅ„ Å‚acza average=Å›rednio Browse=PrzeglÄ…daj Enable µTP=Włącz µTP Select a folder for download=Wybierz folder pobierania Select torrent location=Wybierz lokalizacjÄ™ torrenta A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Nowa wersja %s jest dostÄ™pna.~Twoja obecna wersja: %s~Nowa wersja: %s~~Czy chcesz otworzyć stronÄ™ pobierania? Advanced=Zaawansowane Check for new version every=Sprawdź dostÄ™pność nowych wersji co Check for updates=Sprawdź dostÄ™pność aktualizacji Consider active torrents as stalled when idle for=Uznaj za wstrzymane, aktywne torrenty, gdy sÄ… bezczynne przez Do you wish to enable automatic checking for a new version of %s?=Czy chcesz włączyć automatyczne sprawdzanie nowej wersji %s? Donate!=Dotacje! Download queue size=Rozmiar kolejki pobierania Error checking for new version=Błąd sprawdzania nowej wersji Folder grouping=Grupowanie folderów Force start=WymuÅ› start Home page=Strona domowa Modify trackers=Modyfikuj trackery Move bottom=PrzenieÅ› na dół Move down queue=PrzenieÅ› niżej w kolejce Move down=Niżej Move top=PrzenieÅ› na górÄ™ Move up queue=PrzenieÅ› wyżej w kolejce Move up=Wyżej No updates have been found.~You are running the latest version of %s=Aktualizacja nie zostaÅ‚a znaleziona.~Używasz najnowszej wersji %s Queue position=Pozycja w kolejce Queue=Kolejka Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrenty, które sÄ… bezczynne przez N minut, sÄ… pominiÄ™te z kolejki pobierania lub wysyÅ‚ania Tracker grouping=Grupowanie trackerów Upload queue size=Rozmiar kolejki wysyÅ‚ania View=Widok Visit home page=Odwiedź stronÄ™ domowÄ… days=dni Active time=Czas pracy Automatically add torrent links from the clipboard=Automatycznie dodaj linki torrentów ze schowka Copy file path to clipboard=Skopiuj Å›cieżkÄ™ pliku do schowka Cumulative=ÅÄ…czny Current=Bieżący Files added=Pliki dodane Filter pane=Panel filtrów Global statistics=Globalna statystyka Info pane=Panel informacji Statistics=Statystyka Status bar=Pasek stanu %dd=%dd %dh=%dh %dm=%dm All torrents=Wszystkie torrenty Application options=Opcje aplikacji Ask for password=PoproÅ› o hasÅ‚o Authentication required=Uwierzytelnianie wymagane Average out transfer speeds to eliminate fluctuations=UÅ›rednij prÄ™dkość transferu do wyeliminowania wahaÅ„ Connect to %s=Połącz z %s Connect to Transmission using proxy server=Połącz z Transmission przy użyciu serwera proxy Connect to Transmission=Połącz z Transmission Connection name=Nazwa połączenia Could not connect to tracker==Nie można połączyć siÄ™ z trackerem Data display=WyÅ›wietlaj dane Data refresh interval when minimized=InterwaÅ‚ odÅ›wieżania danych gdy zminimalizowane Data refresh interval=InterwaÅ‚ odÅ›wieżania danych Default download folder on remote host=DomyÅ›lny folder pobrania na zdalnym hoÅ›cie Disconnect from Transmission=Odłącz od Transmission Downloading torrent file=Pobieranie pliku torrent Font size=Rozmiar czcionki Handle .torrent files by %s=ObsÅ‚uga plików .torrent przez %s Handle magnet links by %s=ObsÅ‚uga linków magnet przez %s Invalid name specified=OkreÅ›lona nazwa jest nieprawidÅ‚owa Manage connections to Transmission=ZarzÄ…dzaj połączeniami do Transmission Manage connections=ZarzÄ…dzaj połączeniami Network (WAN)=Sieć (WAN) New connection to Transmission=Nowe połączenie do Transmission Pick random port on Transmission launch=Wybierz losowy port przy uruchomieniu Transmission Please enter a password to connect to %s=Wprowadź hasÅ‚o, aby połączyć siÄ™ z %s Please specify how %s will connect to a remote host running Transmission daemon (service)=ProszÄ™ okreÅ›lić, jak %s połączy siÄ™ usÅ‚ugÄ… Transmission (daemonem) uruchomionym na zdalnym hoÅ›cie Prompt for download options when adding a new torrent=Pytaj o opcje pobierania podczas dodawania nowego torrenta RPC path=Å›cieżka RPC Save as=Zapisz jako Seeding time=Czas seedowania Show advanced options=Pokaż opcje zaawansowane Show notifications in tray icon=Pokaż powiadomienia w ikonie na pasku System integration=Integracja systemu Torrent already exists in the list=Torrent już istnieje na liÅ›cie Torrent not registered with this tracker==Torrent nie jest zarejestrowany na tym trackerze Unable to find path mapping.~Use the application's options to setup path mappings=Nie można znaleźć mapowania Å›cieżki.~Użyj opcji w aplikacji do konfiguracji mapowania Å›cieżki Update trackers for the existing torrent?=Uaktualnij trackery dla istniejÄ…cego torrenta? You need to restart the application to apply changes=Musisz ponownie uruchomić aplikacjÄ™, aby zastosować zmianyTransGUI/lang/transgui.da0000644000000000000000000003164112261612465014305 0ustar rootrootTranslationLanguage=Dansk "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Fjernadgang til lokale stier og mapper.~~Eks:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (downloaded %d) %ds=%ds %s (%d hashfails)=%s (%d hashfails) %s (%s done)=%s (%s færdig) '%s' has finished downloading='%s' færdig med at downloade %s%s%d downloading, %d seeding%s%s, %s=%s%s%d downloader, %d seeder%s%s, %s &All=&Alt &Close=&Luk &Help=&Hjælp &Ignore=&Ignorer &No=&Nej &OK=&OK &Open=&Ã…ben &Retry=&Prøv igen &Save=&Gem &Unlock=&OplÃ¥s &Yes=&Ja /s=/s Abort=Stop About=Om Active=Aktiv Add new torrent=Tilføj ny torrent &Add torrent=Tilføj torrent Added on=Tilføjet Are you sure to remove torrent '%s' and all associated DATA?=Sikker pÃ¥ at slette torrent '%s' og alt tilhørende DATA? Are you sure to remove torrent '%s'?=Sikker pÃ¥ at slette torrent '%s'? Authentication=Godkendelse b=b Cancel=Cancel Client=Klient Close to tray=Luk vindue Comment=Kommentar Completed on=Fuldendt om Completed=Fuldendt Confirmation=Bekræft connected=Forbundet Connecting to daemon=Forbinder til daemon Connection error occurred=Forbindelsesfejl opstod Copy=Kopier Country=Land Created on=Oprettet den D: %s/s=D: %s/s Destination folder=Destinations mappe Disconnected=Ikke forbundet Donate to support further development=Doner penge Donate via PayPal,WebMoney,Credit card=Doner via PayPal,WebMoney,Credit card Done=Færdig Don't download=Download ikke Down limit=Download grænse Down speed=Download hastighed Down=Download Download complete=Download færdig Download speed=Download hastighed Downloaded=Downloadet Downloading=Downloader Enable DHT=Aktiver DHT Enable Peer Exchange=Aktiver Peer Exchange Enable port forwarding=Aktiver port forwarding Encryption disabled=Kryptering deaktiveret Encryption enabled=Kryptering aktiveret Encryption required=Kryptering kræves Encryption=Kryptering Error=Fejl ETA=ETA E&xit=Afslut File name=Filnavn Files=Filer Finished=Færdig Flag images archive is needed to display country flags.~Download this archive now?=Mangler flagarkiv for at vise flag.~Download arkivet nu? Flags=Flag GB=GB General=Generelt Geo IP database is needed to resolve country by IP address.~Download this database now?=Mangler Geo IP databasen for at vise IP addressers oprindelsesland.~Download databasen nu? Global bandwidth settings=Indstillinger for global bÃ¥ndbredde Global peer limit=Global peer grænse Hash=Hash Have=Modtaget Hide=Skjul High priority=Høj prioritet high=høj Host=Host in swarm=i sværm Inactive=Inaktiv Incoming port is closed. Check your firewall settings=IndgÃ¥ende port er lukket. Kontroller firewall'ens indstillinger Incoming port tested successfully=IndgÃ¥ende port blev succesfuldt testet Incoming port=IndgÃ¥ende port Information=Information KB/s=KB/s KB=KB Language=Sprog Last active=Sidst aktiv License=License Low priority=Lav prioritet low=lÃ¥v Max peers=Max peers Maximum download speed=Maximal download hastighed Maximum upload speed=Maximal upload hastighed MB=MB Minimize to tray=Minimer Name=Navn No host name specified=Intet specificeret host nacn No proxy server specified=Ingen proxyserver specificeret No to all=Nej til alt Normal priority=Normal prioritet normal=normal of=af Open containing folder=Ã…ben mappe med indhold Open=Ã…ben Password=Kodeord Paths=Paths Peer limit=Peer grænse Peers=Peers Pieces=Andele Port=Port Priority=Prioritet Properties=Egenskaber Proxy password=Proxy kodeord Proxy port=Proxy port Proxy server=Proxy server Proxy user name=Proxy brugernavn Ratio=Ratio Reconnect in %d seconds=Forbinder igen om %d sekunder Remaining=Tilbage Remote host=Fjern host Remove torrent and Data=Fjern torrent og data Remove torrent=Fjern torrent Remove=Fjern Resolve country=Find country Resolve host name=Find host name seconds=sekunder Seed ratio=Seed ratio Seeding=Seeder Seeds=Seeds Select a .torrent to open=Vælg a .torrent som skal Ã¥bnes Select all=Vælg alt Select none=vælg intet Setup columns=Indstil kolonner Share ratio=Dele ratio Show country flag=Vis flag Show=Vis Size=Størrelse skip=spring over Start all torrents=Start alle torrents Start torrent=Start torrent Start=Start Status=Status Stop all torrents=Stop alle torrents Stop all=Stop alle Stop torrent=Stop torrent Stop=Stop Stopped=Stoppet TB=TB Test port=Test port T&ools=Værktøjer Torrent contents=Torrentindhold Torrent properties=Torrentindstillinger Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Verificering af torrent verification kan tage lang tid.~vil du verificere '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Alle filer (*.*)|*.* Total size=Samlet størelse Tracker status=Tracker status Tracker update on=Tracker update om Tracker=Tracker Trackers=Trackers Transfer=Overfører Transmission options=Transmission indstillinger Transmission%s at %s:%s=Transmission%s pÃ¥ %s:%s Tray icon always visible=Altid synlig bakkeikon Tray icon=Bakkeikon U: %s/s=U: %s/s Unable to extract flag image=Kan ikke udpakke flag Unable to get files list=Kan ikke finde liste over filer Unknown=Ukendt Up limit=Opload grænse Up speed=Opload hastighed Up=Opload Update complete=Opdatering færdig Update GeoIP database=Opdater GeoIP database Update in=Opdateres om Updating=Updaterer Upload speed=Upload hastigehed Uploaded=Oploadet User name=Brugernavn Verify torrent=Verificer torrent &Verify=Verificer Verifying=Verificerer Version %s=Version %s Waiting=Venter Warning=Advarsel Wasted=Mistet Working=Arbejder Yes to &All=Ja til &Alt No tracker=Ingen tracker %s downloaded=%s downloaded %s of %s downloaded=%s of %s downloaded %d torrents=%d torrents Add .part extension to incomplete files=Add .part extension to incomplete files Add torrent link=Add torrent link Are you sure to remove %d selected torrents and all their associated DATA?=Are you sure to remove %d selected torrents and all their associated DATA? Are you sure to remove %d selected torrents?=Are you sure to remove %d selected torrents? Bandwidth=Bandwidth Directory for incomplete files=Directory for incomplete files Download=Download Enable blocklist=Enable blocklist ID=ID Move torrent data from current location to new location=Move torrent data from current location to new location New location for torrent data=New location for torrent data No link was specified=No link was specified No torrent location was specified=No torrent location was specified Path=Path Reannounce (get more peers)=Reannounce (get more peers) Set data location=Set data location Size to download=Size to download The block list has been updated successfully.~The list entries count: %d=The block list has been updated successfully.~The list entries count: %d The directory for incomplete files was not specified=The directory for incomplete files was not specified The downloads directory was not specified=The downloads directory was not specified Torrent data location=Torrent data location Torrents=Torrents Unable to execute "%s"=Unable to execute "%s" Update blocklist=Update blocklist URL of a .torrent file or a magnet link=URL of a .torrent file or a magnet link Columns setup=Columns setup Add torrent=Add torrent Delete a .torrent file after a successful addition=Delete a .torrent file after a successful addition Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrents verification may take a long time.~Are you sure to start verification of %d torrents? Unable to load OpenSSL library files: %s and %s=Unable to load OpenSSL library files: %s and %s Use SSL=Use SSL Add tracker=Add tracker Alternate bandwidth settings=Alternate bandwidth settings Apply alternate bandwidth settings automatically=Apply alternate bandwidth settings automatically Are you sure to delete connection '%s'?=Are you sure to delete connection '%s'? Are you sure to remove tracker '%s'?=Are you sure to remove tracker '%s'? Days=Days Delete=Delete Disk cache size=Disk cache size Download speeds (KB/s)=Download speeds (KB/s) Edit tracker=Edit tracker Enable Local Peer Discovery=Enable Local Peer Discovery Free disk space=Free disk space Free: %s=Free: %s From=From minutes=minutes Misc=Misc New connection=New connection New=New No tracker URL was specified=No tracker URL was specified Proxy=Proxy Remove tracker=Remove tracker Rename=Rename Speed limit menu items=Speed limit menu items Stop seeding when inactive for=Stop seeding when inactive for The invalid time value was entered=The invalid time value was entered to=to Tracker announce URL=Tracker announce URL Tracker properties=Tracker properties Unlimited=Unlimited Upload speeds (KB/s)=Upload speeds (KB/s) Use alternate bandwidth settings=Use alternate bandwidth settings average=average Browse=Browse Enable µTP=Enable µTP Select a folder for download=Select a folder for download Select torrent location=Select torrent location A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.hr0000644000000000000000000003244612261612465014336 0ustar rootrootTranslationLanguage=Hrvatski "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Unesite put do odrediÅ¡ne mape.~~Primjer:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (have %d) %ds=%ds %s (%d hashfails)=%s (%d hashfails) %s (%s done)=%s (%s done) '%s' has finished downloading='%s' je dovrÅ¡io sa skidanjem %s%s%d downloading, %d seeding%s%s, %s=%s%s%d skida, %d Å¡alje%s%s, %s &All=&Sve &Close=&Zatvori &Help=&Pomoć &Ignore=&Ignoriraj &No=&Ne &OK=&OK &Open=&Otvori &Retry=&PokuÅ¡aj ponovno &Save=&Spremi &Unlock=&OtkljuÄaj &Yes=&Da /s=/s Abort=Prekini About=O programu Active=Aktivnih Add new torrent=Dodaj novi torrent &Add torrent=Dodaj torrent Added on=Dodano Are you sure to remove torrent '%s' and all associated DATA?=Da li ste sigurni da želite ukloniti torrent '%s' i sve pridružene podatke? Are you sure to remove torrent '%s'?=Da li ste sigurni da želite ukloniti torrent '%s'? Authentication=Autentifikacija b=b Cancel=Odustani Client=Client Close to tray=Zatvori u tray Comment=Komentar Completed on=ZavrÅ¡eno Completed=ZavrÅ¡eno Confirmation=Provjera connected=povezano Connecting to daemon=Povezujem se s daemonom Connection error occurred=Pojavila se greÅ¡ka prilikom povezivanja Copy=Kopiraj Country=Zemlja Created on=Kreirano D: %s/s=D: %s/s Destination folder=OdrediÅ¡na mapa Disconnected=Nije povezano Donate to support further development=Donirajte kako bi pomogli daljnji razvoj ove aplikacije Donate via PayPal,WebMoney,Credit card=Donirajte putem PayPal,WebMoney,Credit card Done=DovrÅ¡eno Don't download=Nemoj downloadati Down limit=OgraniÄenja skidanja Down speed=Brzina skidanja Down=Skidanje Download complete=Download dovrÅ¡en Download speed=Brzina skidanja Downloaded=Skinuto Downloading=Skidam Enable DHT=Omogući DHT Enable Peer Exchange=Omogući Peer Exchange Enable port forwarding=Omogući port forwarding Encryption disabled=Enkripcija iskljuÄena Encryption enabled=Enkripcija ukljuÄena Encryption required=Enkripcija je zahtijevana Encryption=Enkripcija Error=GreÅ¡ka ETA=Preostalo vrijeme E&xit=IzaÄ‘i File name=Ime datoteke Files=Datoteke Finished=DovrÅ¡eno Flag images archive is needed to display country flags.~Download this archive now?=Arhiva zastava je potrebna, kako bi one bile prkazane.~Želite li skinuti tu arhivu? Flags=Zastave GB=GB General=Općenito Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP baza podataka je potrebna, kako bi se utvrdila lokacija IP adrese.~Želite li skinuti tu arhivu? Global bandwidth settings=Općenite postavke Å¡irokopojasne veze Global peer limit=Općenito ograniÄenje broja peerova Hash=Hash Have=Posjeduje Hide=Sakrij High priority=Visok prioritet high=visok Host=Posljužitelj in swarm=u roju Inactive=Neaktinih Incoming port is closed. Check your firewall settings=Ulazni port je zatvoren. Provjerite postavke svojega frewalla Incoming port tested successfully=Ulazni port je uspjeÅ¡no testiran Incoming port=Ulazni port Information=Informacije KB/s=KB/s KB=KB Language=Jezik Last active=Posljednji puta aktivan License=Licenca Low priority=Niski prioritet low=nizak Max peers=Maksimalan broj peerova Maximum download speed=Maksimalna brzina downloada Maximum upload speed=Maksimalna brzina slanaja MB=MB Minimize to tray=Spusti u tray Name=Ime No host name specified=Ime poslužitelja nije specificirano No proxy server specified=Proxy server nije specificiran No to all=Ne za sve Normal priority=Normalan prioritet normal=normalan of=of Open containing folder=Otvori sadržajnu mapu Open=Otvori Password=Zaporka Paths=Paths Peer limit=Peer limit Peers=Peerovi Pieces=Dijelova Port=Port Priority=Prioritet Properties=PodeÅ¡enja Proxy password=Zaporka proxyja Proxy port=Proxy port Proxy server=Proxy server Proxy user name=KorisniÄko ime proxyja Ratio=Omjer Reconnect in %d seconds=PokuÅ¡aj ponovnog spajanja za %d sekundi Remaining=Preostalo Remote host=Udaljeni poslužitelj Remove torrent and Data=Ukloni torrent i sve podatke Remove torrent=Ukloni torrent Remove=Ukloni Resolve country=Dohvati country Resolve host name=Dohvati naziv poslužitelja seconds=sekundi Seed ratio=Omjer Seeding=Å aljem Seeds=Seederi Select a .torrent to open=Otvori .torrent Select all=Odaberi sve Select none=Nemoj odabrati nijednog Setup columns=Uredi kolone Share ratio=Omjer razmjene Show country flag=Prikaži zastavu Show=Prikaži Size=VeliÄina skip=PreskoÄi Start all torrents=Pokreni sve torrente Start torrent=Pokreni torrent Start=Pokreni Status=Status Stop all torrents=Zaustavi sve torrente Stop all=Stop all Stop torrent=Zaustavi torrent Stop=Zastavi Stopped=Zaustavljeno TB=TB Test port=Testiraj port T&ools=Alati Torrent contents=Sadržaj torrenta Torrent properties=Osobine torrenta Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Provjera torrenta bi mogla potrajati neko vrijeme.~Da li ste sigurni da želite pokrenuti provjeru torrenta '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenti (*.torrent)|*.torrent|Sve datoteke (*.*)|*.* Total size=Ukupna veliÄina Tracker status=Status trackera Tracker update on=Zadnje osvježenje trackera Tracker=Tracker Trackers=Trackeri Transfer=Transfer Transmission options=Opcije Transmissiona Transmission%s at %s:%s=Transmission%s u %s:%s Tray icon always visible=Tray ikona uvijek vidljiva Tray icon=Tray ikona U: %s/s=U: %s/s Unable to extract flag image=Nemoguće raspakirati zastavu Unable to get files list=Unable to get files list Unknown=Unknown Up limit=OgraniÄenja slanja Up speed=Brzina slanja Up=Up Update complete=Upload dovrÅ¡en Update GeoIP database=Osvježi GeoIP bazu Update in=Sljedeće osvježenje Updating=Osvježavam Upload speed=Brzina slanja Uploaded=Poslano User name=KorisniÄko ime Verify torrent=Provjeri torrent &Verify=Provjeri Verifying=Provjeravam Version %s=InaÄica %s Waiting=ÄŒekam Warning=ÄŒekam Wasted=Wasted Working=IzvrÅ¡avam Yes to &All=Da za &sve No tracker=No tracker %s downloaded=%s downloaded %s of %s downloaded=%s of %s downloaded %d torrents=%d torrents Add .part extension to incomplete files=Add .part extension to incomplete files Add torrent link=Add torrent link Are you sure to remove %d selected torrents and all their associated DATA?=Are you sure to remove %d selected torrents and all their associated DATA? Are you sure to remove %d selected torrents?=Are you sure to remove %d selected torrents? Bandwidth=Bandwidth Directory for incomplete files=Directory for incomplete files Download=Download Enable blocklist=Enable blocklist ID=ID Move torrent data from current location to new location=Move torrent data from current location to new location New location for torrent data=New location for torrent data No link was specified=No link was specified No torrent location was specified=No torrent location was specified Path=Path Reannounce (get more peers)=Reannounce (get more peers) Set data location=Set data location Size to download=Size to download The block list has been updated successfully.~The list entries count: %d=The block list has been updated successfully.~The list entries count: %d The directory for incomplete files was not specified=The directory for incomplete files was not specified The downloads directory was not specified=The downloads directory was not specified Torrent data location=Torrent data location Torrents=Torrents Unable to execute "%s"=Unable to execute "%s" Update blocklist=Update blocklist URL of a .torrent file or a magnet link=URL of a .torrent file or a magnet link Columns setup=Columns setup Add torrent=Add torrent Delete a .torrent file after a successful addition=Delete a .torrent file after a successful addition Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrents verification may take a long time.~Are you sure to start verification of %d torrents? Unable to load OpenSSL library files: %s and %s=Unable to load OpenSSL library files: %s and %s Use SSL=Use SSL Add tracker=Add tracker Alternate bandwidth settings=Alternate bandwidth settings Apply alternate bandwidth settings automatically=Apply alternate bandwidth settings automatically Are you sure to delete connection '%s'?=Are you sure to delete connection '%s'? Are you sure to remove tracker '%s'?=Are you sure to remove tracker '%s'? Days=Days Delete=Delete Disk cache size=Disk cache size Download speeds (KB/s)=Download speeds (KB/s) Edit tracker=Edit tracker Enable Local Peer Discovery=Enable Local Peer Discovery Free disk space=Free disk space Free: %s=Free: %s From=From minutes=minutes Misc=Misc New connection=New connection New=New No tracker URL was specified=No tracker URL was specified Proxy=Proxy Remove tracker=Remove tracker Rename=Rename Speed limit menu items=Speed limit menu items Stop seeding when inactive for=Stop seeding when inactive for The invalid time value was entered=The invalid time value was entered to=to Tracker announce URL=Tracker announce URL Tracker properties=Tracker properties Unlimited=Unlimited Upload speeds (KB/s)=Upload speeds (KB/s) Use alternate bandwidth settings=Use alternate bandwidth settings average=average Browse=Browse Enable µTP=Enable µTP Select a folder for download=Select a folder for download Select torrent location=Select torrent location A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.cs0000644000000000000000000003331512261612465014326 0ustar rootrootTranslationLanguage=ÄŒesky "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Mapování vzdálené cesty na lokalní.~~Příklady:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (hotových %d) %ds=%ds %s (%d hashfails)=%s (%d hash chyb) %s (%s done)=%s (%s Hotovo) '%s' has finished downloading='%s' dokonÄil stahování %s%s%d downloading, %d seeding%s%s, %s=%s%s%d stahování, %d seedování%s%s, %s &All=&VÅ¡e &Help=&NápovÄ›da &Ignore=&Ignorovat &No=&Ne &OK=&OK &Open=&Otevřít &Retry=&Znovu &Save=&Uložit &Unlock=&Odemknout &Yes=&Ano /s=/s Abort=ZruÅ¡it About=O programu Active=Aktivní Add new torrent=PÅ™idat nový torrent &Add torrent=PÅ™idat torrent Added on=PÅ™idáno Are you sure to remove torrent '%s' and all associated DATA?=Opravdu chcete odstranit torrent '%s' a vÅ¡echna s ním spojená DATA? Are you sure to remove torrent '%s'?=Opravdu chcete odstranit torrent '%s'? Authentication=Autentizace b=b Cancel=ZruÅ¡it Client=Klient Close to tray=Zavřít do tray Comment=Komentář Completed on=DokonÄeno Completed=DokonÄeno Confirmation=Potvrzení connected=pÅ™ipojeno Connecting to daemon=PÅ™ipojování k démonovi Connection error occurred=Chyba pÅ™ipojení Copy=Kopírovat Country=Stát Created on=VytvoÅ™eno D: %s/s=D: %s/s Destination folder=Cílový adresář Disconnected=Odpojeno Donate to support further development=PodpoÅ™te další vývoj Donate via PayPal,WebMoney,Credit card=Darujte skrze PayPal,WebMoney,Credit card Done=Hotovo Don't download=Nestahovat Down limit=Limit stahování Down speed=Rychlost stahování Down=Stahování Download complete=Stahování dokonÄeno Download speed=Rychlost stahování Downloaded=Staženo Downloading=Stahování Enable DHT=Povolit DHT Enable Peer Exchange=Povolit Peer Exchange Enable port forwarding=Povolit port forwarding Encryption disabled=Å ifrování zakázáno Encryption enabled=Å ifrování povoleno Encryption required=Å ifrování požadováno Encryption=Å ifrování Error=Chyba ETA=Odhad Äasu E&xit=UkonÄit File name=Jméno souboru Files=Soubory Finished=DokonÄeno Flag images archive is needed to display country flags.~Download this archive now?=Archív vlajek je potÅ™eba k zobrazení vlajek států.~Stáhnout nyní tento archív? Flags=Vlajky GB=GB General=Hlavní Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP databáze je tÅ™eba k rozpoznání zemÄ› podle IP adresy.~Stáhnout nyní tuto databázi? Global bandwidth settings=Globální nastavní rychlostí Global peer limit=Globální limit peerů Hash=Hash Have=Hotovo Hide=Skrýt High priority=Vysoká priorita high=vysoká Host=Host in swarm=ve swarmu Inactive=Neaktivní Incoming port is closed. Check your firewall settings=Příchozí port je zavÅ™ený. Zkontrolujte vaÅ¡e nastavení firewallu Incoming port tested successfully=Příchozí port úspěšnÄ› otestován Incoming port=Příchozí port Information=Informace KB/s=KB/s KB=KB Language=Jazyk Last active=Naposledy aktivní License=Licence Low priority=Nízká priorita low=nízká Max peers=Maximum peerů Maximum download speed=Maximální rychlost stahování Maximum upload speed=Maximální rychlost uploadu MB=MB Minimize to tray=Minimalizovat do tray Name=Jméno No host name specified=Nenastevené jméno hosta No proxy server specified=Nenastavnená proxy No to all=Ne vÅ¡em Normal priority=Normální priorita normal=normální of=z Open containing folder=Otevřít cílový adesář Open=Otevřít Password=Heslo Paths=Cesty Peer limit=Limit peerů Peers=Peery Pieces=Částí Port=Port Priority=Priorita Properties=Vlastnosti Proxy password=Heslo proxy Proxy port=Port proxy Proxy server=Proxy server Proxy user name=Uživatelské jméno proxy Ratio=Ratio Reconnect in %d seconds=ZnovupÅ™ipojení za %d sekund Remaining=Zbývá Remote host=Vzdálený host Remove torrent and Data=Smazat torrent a data Remove torrent=Smazat torrent Remove=Smazat Resolve country=Rozpoznat stát Resolve host name=Rozpoznat hostname seconds=sekund Seed ratio=Ratio seedování Seeding=Seedování Seeds=Seedy Select a .torrent to open=Vyberte .torrent k otevÅ™ení Select all=OznaÄit vÅ¡e Select none=OdznaÄit vÅ¡e Setup columns=Nastavit sloupce Share ratio=PomÄ›r sdílení Show country flag=Zobrazit vlajky států Show=Zobrazit Size=Velikost skip=pÅ™eskoÄit Start all torrents=Start vÅ¡ech torrentů Start torrent=Start torrentu Start=Start Status=Status Stop all torrents=Zastavit vÅ¡echny torrenty Stop all=Zastavit vÅ¡echny Stop torrent=Zastavit torrent Stop=Zastavit Stopped=Zastaveno TB=TB Test port=Test portu T&ools=Nástroje Torrent contents=Torrent contents Torrent properties=Vlastnosti torrentu Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Ověření torrentu může nÄ›jkou doby trvat.~Opravdu chcete ověřit torrent '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenty (*.torrent)|*.torrent|VÅ¡echny soubory (*.*)|*.* Total size=Celková velikost Tracker status=Status trackeru Tracker update on=Update trackeru za Tracker=Tracker Trackers=Trackery Transfer=PÅ™enos Transmission options=Možnosti transmission Transmission%s at %s:%s=Transmission%s v %s:%s Tray icon always visible=Tray ikona stále zobrazená Tray icon=Tray ikona U: %s/s=U: %s/s Unable to extract flag image=Není možné rozbalit obrázek vlajky Unable to get files list=Není možné získat seznam souborů Unknown=Neznámo Up limit=Limit uploadu Up speed=Rychlost uploadu Up=Up Update complete=Obnovení dokonÄeno Update GeoIP database=Obnovit databázi GeoIP Update in=Obnovení za Updating=Obnovuji Upload speed=Rychlost uploadu Uploaded=Uploadováno User name=Uživatelské jméno Verify torrent=Ověřit torrent &Verify=Ověřit Verifying=Ověřuji Version %s=Verze %s Waiting=ÄŒekání Warning=Varování Wasted=Zahozeno Working=Běží Yes to &All=Ano &VÅ¡em No tracker=Žádný tracker %s downloaded=%s staženo %s of %s downloaded=%s z %s staženo %d torrents=%d torrentů Add .part extension to incomplete files=PÅ™idat příponu .part nedokonÄeným souborům Add torrent link=PÅ™idat URL Are you sure to remove %d selected torrents and all their associated DATA?=Opravdu chcete odstranit %d vybraných torrentů a vÅ¡echna jejich DATA? Are you sure to remove %d selected torrents?=Opravdu chcete odstranit %d vybraných torrentů? Bandwidth=Rychlost Directory for incomplete files=Adresář pro nedokonÄené soubory Download=Stažené Enable blocklist=Povolit blocklist ID=ID Move torrent data from current location to new location=PÅ™esunout data torrentu z aktuálního umístÄ›ní na nové New location for torrent data=Nové umístÄ›ní pro data torrentu No link was specified=Není uveden žádný link No torrent location was specified=Není uvedeno žádné umístÄ›ní Path=Cesta Reannounce (get more peers)=Obnovit (získá více peerů) Set data location=Nastavit umístÄ›ní dat Size to download=Velikost ke stažení The block list has been updated successfully.~The list entries count: %d=Blocklist úspěšne aktualizován.~PoÄet položek: %d The directory for incomplete files was not specified=Adresář pro nedokonÄené soubory není uveden The downloads directory was not specified=Adresář pro stahování není uveden Torrent data location=UmístÄ›ní dat torrentu Torrents=Torrenty Unable to execute "%s"=Není možné spustit "%s" Update blocklist=Aktualizovat blocklist URL of a .torrent file or a magnet link=URL .torrent souboru nebo magnet link Columns setup=Nastavení sloupců Add torrent=PÅ™idat torrent Delete a .torrent file after a successful addition=Odstranit .torrent soubor po úspěšném pÅ™idání Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Ověřování torrentu může trvat delší dobu.~Opravdu chcete ověřit %d torrentů? Unable to load OpenSSL library files: %s and %s=Nebylo možné otevřít soubory knihovny OpenSSL: %s a %s Use SSL=Použít SSL Add tracker=PÅ™idat tracker Alternate bandwidth settings=Alternativní nastavení rychlostí Apply alternate bandwidth settings automatically=Automaticky použít alternativní nastavení rychlostí Are you sure to delete connection '%s'?=Opravdu chcete smazat spojení '%s'? Are you sure to remove tracker '%s'?=Opravdu chcete odstranit tracker '%s'? Days=Dny Delete=Smazat Disk cache size=Velikost vyrovnávací pamÄ›ti Download speeds (KB/s)=Rychlost stahování (KB/s) Edit tracker=Upravit tracker Enable Local Peer Discovery=Povolit Hledání Místních Peerů Free disk space=Volné místo na disku Free: %s=Volno: %s From=Od minutes=minut Misc=Další New connection=Nové spojení New=Nové No tracker URL was specified=Nebyla zadána URL trackeru Proxy=Proxy Remove tracker=Odstranit tracker Rename=PÅ™ejmenovat Speed limit menu items=Položky menu omezení rychlosti Stop seeding when inactive for=PÅ™estat seedovat pokud je neaktivní déle jak The invalid time value was entered=Byl zadán chybný Äasový údaj to=do Tracker announce URL=Announce URL trackeru Tracker properties=Vlastnosti trackeru Unlimited=NeomezenÄ› Upload speeds (KB/s)=Rychlost uploadu (KB/s) Use alternate bandwidth settings=Použít alternativní nastavené rychlostí average=average Browse=Procházet Enable µTP=Povolit µTP Select a folder for download=Vybrat adresář pro stahování Select torrent location=Vybrat umístÄ›ní torrentu &Close=&Zavřít A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Je dostupná nová verze %s.~SouÄasná verze: %s~Nová verze: %s~~ChtÄ›li byste otevřít stránky se stažením? Advanced=PokroÄilé Check for new version every=Kontrolovat nové verze jednou za Check for updates=Kontrola updatů Consider active torrents as stalled when idle for=Považovat aktivní torrenty za pozastavené pokud jsou neÄinné Do you wish to enable automatic checking for a new version of %s?=ChtÄ›li byste povolit automatickou kontrolu nových verzí %s? Donate!=PodpoÅ™te! Download queue size=Velikost fronty stahování Error checking for new version=Chyba pÅ™i kontrole nové verze Folder grouping=Filtr adresářů Force start=Vynucený start Home page=Domovská stránka Modify trackers=Upravit trackery Move bottom=Posunout dospod Move down queue=Posunout frontou dolů Move down=Posunout dolů Move top=Posunout navrch Move up queue=Posunout frontou nahoru Move up=Posunout nahoru No updates have been found.~You are running the latest version of %s=Žádné updaty nenalezeny~Používáte nejnovÄ›jší verzi %s Queue position=Pozice ve frontÄ› Queue=Fronta Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrenty neÄinné alespoň N minut se nezpoÄítavají do Stahovací nebo Odesílací fronty Tracker grouping=Filtr trackerů Upload queue size=Velikost fronty odesílání View=Zobrazit Visit home page=NavÅ¡tívit domovskou stránku days=dny Active time=Doba bÄ›hu Automatically add torrent links from the clipboard=Automaticky pÅ™idávat torrent linky ze schránky Copy file path to clipboard=Kopírovat cestu k souboru do schránky Cumulative=Cumulative Current=SouÄasný Files added=PÅ™idáno souborů Filter pane=Filtry Global statistics=Globální statistiky Info pane=Informace Statistics=Statistiky Status bar=Stavový řádek %dd=%dd %dh=%dh %dm=%dm All torrents=VÅ¡echny torrenty Application options=Možnosti aplikace Ask for password=Zeptat se na heslo Authentication required=Vyžadovat ověření Average out transfer speeds to eliminate fluctuations=ZprůmÄ›rovat rychlosti pÅ™enosu k odstranÄ›ní kolísání Connect to %s=PÅ™ipojit k %s Connect to Transmission using proxy server=PÅ™ipojit k Transmission pomocí proxy serveru Connect to Transmission=PÅ™ipojit k Transmission Connection name=Jméno spojení Could not connect to tracker=Nelze se spojit s trackerem Data display=Zobrazení informací Data refresh interval when minimized=Interval obnovení informací pÅ™i minimalizaci Data refresh interval=Interval obnovení informací Default download folder on remote host=Výchozí složka pro stahování na vzdáleném hostiteli Disconnect from Transmission=Odpojit od Transmission Downloading torrent file=Stahuji torrent soubor Font size=Velikost fontu Handle .torrent files by %s=Otevírat .torrent soubory pomocí %s Handle magnet links by %s=Otevírat magnet linky pomocí %s Invalid name specified=Zadáno neplatné jméno Manage connections to Transmission=Spravovat spojení s Transmission Manage connections=Správa spojení Network (WAN)=Síť (WAN) New connection to Transmission=Nové spojení s Transmission Pick random port on Transmission launch=Vybrat náhodný port pÅ™i spuÅ¡tÄ›ní Transmission Please enter a password to connect to %s=Pro spojení s %s prosím vložte heslo Please specify how %s will connect to a remote host running Transmission daemon (service)=UrÄete jak se bude %s pÅ™ipojovat ke vzdálenému hostiteli s běžícím demonem (službou) Transmission Prompt for download options when adding a new torrent=Dotázat se na možnosti stahováni pÅ™i pÅ™idání nového torrentu RPC path=cesta RPC Save as=Uložit jako Seeding time=Doba seedování Show advanced options=Zobrazit pokroÄilé možnosti Show notifications in tray icon=Zobrazit notifikace v tray ikonÄ› System integration=Systémová integrace Torrent already exists in the list=Torrent už je v seznamu Torrent not registered with this tracker=Torrent není na tomto trackeru registrován Unable to find path mapping.~Use the application's options to setup path mappings=Není možné najít mapování cest. ~Nastavte mapování cest pÅ™es možnosti aplikace Update trackers for the existing torrent?=Aktualizovat trackery pro existující torrent? You need to restart the application to apply changes=Aby se projevily zmÄ›ny, je nutné aplikaci restartovat TransGUI/lang/transgui.fi0000644000000000000000000003351112261612465014315 0ustar rootrootTranslationLanguage=Suomi "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Etäsijaintien määrittäminen paikallisten kanssa.~~Esimerkkejä:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (valmiina %d) %ds=%ds %s (%d hashfails)=%s (%d tarkistusvirhettä) %s (%s done)=%s (%s ladattu) '%s' has finished downloading='%s' lataus on suoritettu. %s%s%d downloading, %d seeding%s%s, %s=%s%s%d ladataan, %d jaetaan%s%s, %s &All=&Kaikki &Close=&Sulje &Help=&Apua &Ignore=&Hylkää &No=&Ei &OK=&OK &Open=&Avaa &Retry=&Yritä uudelleen &Save=&Tallenna &Unlock=&Avaa lukitus &Yes=&Kyllä /s=/s Abort=Keskeytä About=Tietoa Active=Aktiiviset Add new torrent=Lisää uusi torrentti &Add torrent=Lisää torrentti Added on=Lisätty Are you sure to remove torrent '%s' and all associated DATA?=Haluatko varmasti poistaa torrentin '%s' sekä sen kaikki TIEDOSTOT? Are you sure to remove torrent '%s'?=Haluatko varmasti poistaa torrentin '%s'? Authentication=Todennus b=t Cancel=Sulje Client=Ohjelma Close to tray=Sulje tehtäväpalkkiin Comment=Kommentti Completed on=Valmistui Completed=Valmiit Confirmation=Vahvistus connected=yhteydessä Connecting to daemon=Yhdistetään Transmissioniin Connection error occurred=Yhteysvirhe Copy=Kopioi Country=Maa Created on=Luotu D: %s/s=Lataus: %s/s Destination folder=Kohdekansio Disconnected=Yhteys katkaistu Donate to support further development=Tee lahjoitus tukeaksesi ohjelman kehitystä Donate via PayPal,WebMoney,Credit card=Tee lahjoitus PayPal, WebMoney tai luottokortin avulla Done=Tila Don't download=Älä lataa Down limit=Latausraja Down speed=Latausnopeus Down=Alas Download complete=Lataaminen valmistunut Download speed=Latausnopeus Downloaded=Ladattu Downloading=Ladataan Enable DHT=Käytä DHT Enable Peer Exchange=Käytä Peer Exchange Enable port forwarding=Käytä porttienvälitystä Encryption disabled=Älä salli salausta Encryption enabled=Salli salaus Encryption required=Salaus vaaditaan Encryption=Salaus Error=Virhe ETA=ETA E&xit=Lopeta File name=Tiedostonimi Files=Tiedostot Finished=Valmis Flag images archive is needed to display country flags.~Download this archive now?=Lippuarkisto täytyy ladata jotta maiden liput voidaan näyttää.~Ladataanko arkisto nyt? Flags=Flags GB=Gt General=Yleinen Global bandwidth settings=Yleiset nopeusrajoitukset Global peer limit=Lataajien enimmäismäärä Hash=Hash Have=Saatavilla Hide=Piilota High priority=Korkea high=korkea Host=IP in swarm=ryhmässä Inactive=Toimettomat Incoming port is closed. Check your firewall settings=Saapuvien yhteyksien portti on kiinni. Tarkista palomuurisi asetukset. Incoming port tested successfully=Saapuvien yhteyksien portti testattu onnistuneesti Incoming port=Saapuvien yhteyksien portti Information=Tietoa KB/s=kt/s KB=kt Language=Kieli Last active=Viimeksi aktiivisena License=Lisenssi Low priority=Matala low=matala Max peers=Lataajien enimmäismäärä Maximum download speed=Latausraja Maximum upload speed=Lähetysraja MB=Mt Minimize to tray=Pienennä tehtäväpalkkiin Name=Nimi No host name specified=Palvelimen IP-osoitetta tai nimeä ei ole määritetty No proxy server specified=Välityspalvelinta ei ole määritetty No to all=Ei kaikkiin Normal priority=Normaali normal=normaali of= / Open containing folder=Avaa latauskansio Open=Avaa Password=Salasana Paths=Sijainnit Peer limit=Lataajien enimmäismäärä Peers=Lataajia Pieces=Osia Port=Portti Priority=Prioriteetti Properties=Ominaisuudet Proxy password=Salasana Proxy port=Portti Proxy server=Palvelin Proxy user name=Käyttäjätunnus Ratio=Suhde Reconnect in %d seconds=Yhdistetään uudelleen %d sekunnin kuluttua Remaining=Jäljellä Remote host=Palvelin Remove torrent and Data=Poista torrent ja sen tiedostot Remove torrent=Poista torrentti Remove=Poista Resolve country=Näytä käyttäjän sijainti Resolve host name=Selvitä palvelimen nimi seconds=sekuntia Seed ratio=Lopeta jakaminen suhteessa Seeding=Jaetaan Seeds=Jakajia Select a .torrent to open=Valitse avattava .torrent Select all=Valitse kaikki Select none=Tyhjennä valinnat Setup columns=Sarakkeiden määritys Share ratio=Jakosuhde Show country flag=Näytä maan lippu Show=Näytä Size=Koko skip=ohita Start all torrents=Aloita kaikki Start torrent=Aloita heti Start=Aloita Status=Tila Stop all torrents=Pysäytä kaikki Stop all=Pysäytä kaikki Stop torrent=Pysäytä Stop=Pysäytä Stopped=Pysäytetty TB=Tt Test port=Testaa portti T&ools=Työkalut Torrent contents=Torrentin sisältö Torrent properties=Torrentin ominaisuudet Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrentin todentaminen voi kestää kauan.~Haluatko varmasti aloittaa torrentin '%s' todentamisen? &Torrent=Torrentti Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrentit (*.torrent)|*.torrent|Kaikki tiedostot (*.*)|*.* Total size=Koko yhteensä Tracker status=Seurantapalvelimen tila Tracker update on=Seurantapalvelimen päivitys Tracker=Seurantapalvelin Trackers=Palvelimet Transfer=Siirto Transmission options=Transmission asetukset Transmission%s at %s:%s=Transmission%s @ %s:%s Tray icon always visible=Tehtäväpalkin kuvake aina näkyvissä Tray icon=Tehtäväpalkin kuvake U: %s/s=Lähetys: %s/s Unable to extract flag image=Lippukuvan purkaminen epäonnistui Unable to get files list=Tiedostolistan noutaminen epäonnistui Unknown=Tuntematon Up limit=Lähetysraja Up speed=Lähetysnopeus Up=Ylös Update complete=Päivitys valmis Update GeoIP database=Päivitä GeoIP tietokanta Update in=Päivitys Updating=Päivitetään Upload speed=Lähetysnopeus Uploaded=Lähetetty User name=Käyttäjätunnus Verify torrent=Todenna torrent &Verify=Todenna Verifying=Todennetaan Version %s=Versio %s Waiting=Odotetaan Warning=Varoitus Wasted=Hylätty Working=Toiminnassa Yes to &All=Kyllä &kaikkiin No tracker=Ei seurantapalvelinta %s downloaded=%s ladattu %s of %s downloaded=%s / %s ladattu %d torrents=%d torrentia Add .part extension to incomplete files=Lisää .part pääte keskeneräisille tiedostoille Add torrent link=Avaa .torrent URL-osoitteesta Are you sure to remove %d selected torrents and all their associated DATA?=Haluatko varmasti poistaa %d valittua torrenttia ja myös niiden TIEDOSTOT? Are you sure to remove %d selected torrents?=Haluatko varmasti poistaa %d valittua torrenttia? Bandwidth=Kaistankäyttö Directory for incomplete files=Tallenna keskeneräiset lataukset kansioon Download=Lataaminen Enable blocklist=Käytä estolistaa ID=ID Move torrent data from current location to new location=Siirrä torrentin tiedostot nykyisestä sijainnista uuteen sijaintiin. New location for torrent data=Uusi sijainti torrentille No link was specified=Linkkiä ei määritetty No torrent location was specified=Torrentin sijaintia ei määritetty Path=Sijainti Reannounce (get more peers)=Päivitä seurantapalvelin Set data location=Aseta sijainti Size to download=Ladattava määrä The block list has been updated successfully.~The list entries count: %d=Estolista päivitetty onnistuneesti.~Sääntöjen määrä listalla: %d The directory for incomplete files was not specified=Keskeneräisten torrenttien kansiota ei ole määritetty The downloads directory was not specified=Latauskansiota ei ole määritetty Torrent data location=Aseta sijainti Torrents=Torrentit Unable to execute "%s"=Suorittaminen epäonnistui "%s" Update blocklist=Päivitä estolista URL of a .torrent file or a magnet link=Anna .torrent tiedoston URL-osoite tai magnet -linkki Columns setup=Sarakkeiden asetukset Add torrent=Lisää torrent Delete a .torrent file after a successful addition=Poista .torrent tiedosto onnistuneen lisäämisen jälkeen Torrent=Torrentti Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrentien todentaminen voi kestää kauan.~Haluatko varmasti aloittaa %d torrentin todentamisen? Unable to load OpenSSL library files: %s and %s=OpenSSL kirjaston tiedostojen lataaminen epäonnistui: %s and %s Use SSL=Käytä SSL Add tracker=Lisää seurantapalvelin Alternate bandwidth settings=Vaihtoehtoiset nopeusrajoitukset Apply alternate bandwidth settings automatically=Ota vaihtoehtoiset nopeusrajoitukset käyttöön automaattisesti Are you sure to delete connection '%s'?=Haluatko varmasti poistaa yhteyden '%s'? Are you sure to remove tracker '%s'?=Haluatko varmasti poistaa seurantapalvelimen '%s'? Days=Päivinä Delete=Poista Disk cache size=Levyn välimuisti Download speeds (KB/s)=Latausnopeudet (kt/s) Edit tracker=Muokkaa seurantapalvelinta Enable Local Peer Discovery=Käytä Lähikäyttäjien hakua (LPD) Free disk space=Vapaata levytilaa Free: %s=Vapaana: %s From=Mistä minutes=minuuttia Misc=Muuta New connection=Uusi yhteys New=Uusi No tracker URL was specified=Seurantapalvelimen osoitetta ei ole määritetty Proxy=Välityspalvelin Remove tracker=Poista seurantapalvelin Rename=Nimeä uudelleen Speed limit menu items=Nopeuslistat Stop seeding when inactive for=Lopeta jakaminen kun ollut toimettomana The invalid time value was entered=Antamasi kellonajat eivät kelpaa to=mihin Tracker announce URL=Tracker announce URL Tracker properties=Seurantapalvelimen ominaisuudet Unlimited=Rajoittamaton Upload speeds (KB/s)=Lähetysnopeudet (kt/s) Use alternate bandwidth settings=Käytä vaihtoehtoisia nopeusrajoituksia average=keskiarvo Browse=Selaa Enable µTP=Käytä µTP Select a folder for download=Valitse kansio johon torrentti ladataan Select torrent location=Valitse uusi sijainti torrentille A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Uusi versio %s on saatavilla.~Nykyinen versio: %s~Uusi versio: %s~~Haluatko avata lataussivun? Advanced=Edistyneet Check for new version every=Tarkista päivitykset joka Check for updates=Tarkista päivitykset Consider active torrents as stalled when idle for=Katso latausten olevan pysähtyneitä kun ne ovat olleet toimettomina Do you wish to enable automatic checking for a new version of %s?=Sallitaanko %s päivitysten automaattinen tarkistaminen? Donate!=Lahjoita! Download queue size=Aktiivisten latausten enimmäismäärä jonossa Error checking for new version=Virhe tarkistaessa uutta versiota Folder grouping=Kansioiden ryhmittely Force start=Pakoita aloitus Home page=Kotisivu Modify trackers=Muokka seurantapalvelimia Move bottom=Siirrä alimmaiseksi Move down queue=Siirrä alemmaksi jonossa Move down=Siirrä alemmas Move top=Siirrä ylimmäiseksi Move up queue=Siirrä ylemmäksi jonossa Move up=Siirrä ylemmäksi No updates have been found.~You are running the latest version of %s=Uusia päivityksiä ei löytynyt.~Käytössäsi on jo uusin versio %s Queue position=Sijainti jonossa Queue=Jono Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrentit jotka olleet toimettomina X minuuttia, eivät nouse Latausjonossa tai Lähetysjonossa ylemmäksi Tracker grouping=Seurantapalvelinten ryhmittely Upload queue size=Aktiivisten lähetysten enimmäismäärä jonossa View=Näytä Visit home page=Vieraile kotisivuilla days=päivä Active time=Active time Automatically add torrent links from the clipboard=Lisää torrent automaattisesti leikepöydän linkistä Copy file path to clipboard=Kopioi tiedoston polku leikepöydälle Cumulative=Yhteensä Current=Nykyinen Files added=Tiedostoja Filter pane=Suodattimet Global statistics=Kokonaistilastot Info pane=Tilastot Statistics=Tilastot Status bar=Tilarivi %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP database is needed to resolve country by IP address.~Download this database now? Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.it0000644000000000000000000003477112261612465014344 0ustar rootrootTranslationLanguage=Italiano "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Associazione delle risorse locali con una cartella remota.~~Esempio:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (già prelevati %d) %ds=%ds %s (%d hashfails)=%s (%d corrotto) %s (%s done)=%s (%s fatto) '%s' has finished downloading='%s' è stato prelevato %s%s%d downloading, %d seeding%s%s, %s=%s%s%d in prelievo, %d in invio%s%s, %s &All=&Tutto &Close=&Chiudi &Help=&Aiuto &Ignore=&Ignora &No=&No &OK=&OK &Open=&Apri &Retry=&Riprova &Save=&Salva &Unlock=&Sblocca &Yes=&Si /s=/s Abort=Abbandona About=Informazioni su Active=Attivi Add new torrent=Aggiungi nuovo torrent &Add torrent=Aggiungi torrent Added on=Aggiunto il Are you sure to remove torrent '%s' and all associated DATA?=Sei sicuro di voler cancellare il torrent '%s' e tutti i dati associati? Are you sure to remove torrent '%s'?=Sei sicuro di voler cancellare il torrent '%s'? Authentication=Autenticazione b=b Cancel=Cancella Client=Cliente Close to tray=Minimizza nella barra di sistema Comment=Commento Completed on=Completato il Completed=Completati Confirmation=Conferma connected=connessi Connecting to daemon=In connessione al demone Connection error occurred=Avvenuto errore connessione Copy=Copia Country=Nazione Created on=Creato il D: %s/s=P: %s/s Destination folder=Cartella di destinazione Disconnected=Disconnesso Donate to support further development=Dona per supportare futuri aggiornamenti Donate via PayPal,WebMoney,Credit card=Dona via PayPal, WebMoney, Carta di Credito Done=Completato Don't download=Non scaricare Down limit=Limite prelievo Down speed=Velocità prelievo Down=Giù Download complete=Prelievo completo Download speed=Velocità prelevamento Downloaded=Ricevuto Downloading=In prelievo Enable DHT=Abilita DHT Enable Peer Exchange=Abilita scambio fonti Enable port forwarding=Abilita apertura porte TCP Encryption disabled=Crittografazione disabilitata Encryption enabled=Crittografazione abilitata Encryption required=Crittografazione necessaria Encryption=Crittografazione Error=In errore ETA=Tempo stimato al completamento E&xit=Esci File name=Nome File Files=File Finished=Finito Flag images archive is needed to display country flags.~Download this archive now?=Le bandiere servono per indicare la nazione.~Vuoi scaricarne l'archivio ? Flags=Bandiere GB=GB General=Generale Geo IP database is needed to resolve country by IP address.~Download this database now?=La base dati di geolocalizzazione associa l'indirizzo IP alla nazione.~Vuoi scaricare l' archivio? Global bandwidth settings=Configurazione globale banda Global peer limit=Limite globale di prelevanti Hash=Firma (hash) Have=Prelevato Hide=Nascondi High priority=Priorità alta high=alta Host=Sistema remoto in swarm=in totale Inactive=Inattivi Incoming port is closed. Check your firewall settings=La porta per la connessione in ingresso è chiusa. Controlla la configurazione del firewall. Incoming port tested successfully=La porta per le connessioni in ingresso è stata verificata con successo. Incoming port=Porta in ingresso Information=Informazioni KB/s=KB/s KB=KB Language=Lingua Last active=Ultima attività License=Licenza Low priority=Priorità bassa low=basso Max peers=Massimo prelevanti Maximum download speed=Massima velocità Prelievo Maximum upload speed=Massima velocità Invio MB=MB Minimize to tray=Minimizza nella barra delle applicazioni Name=Nome No host name specified=Nessun nome host specificato No proxy server specified=Nessun servente proxy specificato No to all=No a tutti Normal priority=Priorità normale normal=normale of=di Open containing folder=Apri la cartella che contiene Open=Apri Password=Parola chiave Paths=Percorsi Peer limit=Limite numero prelevanti Peers=Prelevanti Pieces=Parti Port=Porta Priority=Priorità Properties=Proprietà Proxy password=Parola chiave Proxy Proxy port=Porta Proxy Proxy server=Servente Proxy Proxy user name=Nome utente Proxy Ratio=Rapporto di trasmissione Reconnect in %d seconds=Riconnessione in %d secondi Remaining=Rimangono Remote host=Indirizzo dispositivo remoto Remove torrent and Data=Rimuovi torrent e Dati Remove torrent=Rimuovi torrent Remove=Rimuovi Resolve country=Risolvi nazione Resolve host name=Risolvi nome Sistema remoto seconds=secondi Seed ratio=Ferma quando il rapporto distribuzione raggiunge Seeding=In distribuzione Seeds=Ditributori Select a .torrent to open=Seleziona un .torrent da aprire Select all=Seleziona tutti Select none=Cancella selezione Setup columns=Configura colonne Share ratio=Rapporto condivisione Show country flag=Visualizza bandiere Show=Mostra Size=Dimensione skip=Tralascia Start all torrents=Avvia tutti i torrent Start torrent=Avvia torrent Start=Avvia Status=Stato Stop all torrents=Ferma tutti i torrent Stop all=Ferma tutti Stop torrent=Ferma torrent Stop=Ferma Stopped=Fermato TB=TB Test port=Testa porte T&ools=Utilità Torrent contents=Contenuto Torrent Torrent properties=Proprietà Torrent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=La verifica del torrent impiega tempo.~Sei sicuro di voler procedere alla verifica di '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenti (*.torrent)|*.torrent|Tutti i file (*.*)|*.* Total size=Dimensione totale Tracker status=Stato del tracker Tracker update on=Aggiornamento tracker tra Tracker=Tracker Trackers=Tracker Transfer=Trasferimento Transmission options=Opzioni di Transmission Transmission%s at %s:%s=Transmission %s a %s:%s Tray icon always visible=L'icona nella barra di sistema è sempre visibile Tray icon=Icona U: %s/s=I: %s/s Unable to extract flag image=Impossibile estrarre le bandiere Unable to get files list=Impossibile trovare la lista file Unknown=Sconosciuto Up limit=Limite Invio Up speed=Velocità Invio Up=Su Update complete=Aggiornamento completato Update GeoIP database=Aggiornamento base dati GeoIP Update in=Aggiornamento tra Updating=In aggiornamento Upload speed=Velocità Invio Uploaded=Inviato User name=Nome utente Verify torrent=Verifica torrent &Verify=Verifica Verifying=Sto verificando Version %s=Versione %s Waiting=Attendi Warning=Attenzione Wasted=Sprecato Working=In funzione Yes to &All=Si a &tutti No tracker=Nessun tracker %s downloaded=%s prelevato %s of %s downloaded=%s di %s prelevato %d torrents=%d torrent Add .part extension to incomplete files=Aggiungi l'estensione .part ai files incompleti Add torrent link=Aggiungi collegamento torrent Are you sure to remove %d selected torrents and all their associated DATA?=Sei sicuro di voler rimuovere i %d torrent selezionati e tutti i dati ad essi associati? Are you sure to remove %d selected torrents?=Sei sicuro di voler rimuovere i %d torrent selezionati ? Bandwidth=Larghezza di banda Directory for incomplete files=Cartella per i file incompleti Download=Impostazioni di Prelievo Enable blocklist=Abilita lista blocco ID=ID Move torrent data from current location to new location=Sposta i dati dei torrente dalla cartella corrente ad una nuova New location for torrent data=Nuova cartella per i dati dei torrent No link was specified=Nessun collegamento specificato No torrent location was specified=Nessuna cartella specificata per i torrent Path=Destinazione Reannounce (get more peers)=Riannuncia (Trova più distributori) Set data location=Seleziona cartella per i dati Size to download=Dimensione da prelevare The block list has been updated successfully.~The list entries count: %d=Lista indirizzi IP bloccati aggiornata con successo.~La lista contiene %d voci The directory for incomplete files was not specified=La cartella per i file incompleti non è specificata The downloads directory was not specified=La cartella per i file completati non è specificata Torrent data location=Cartella dei dati Torrents=Torrent Unable to execute "%s"=Impossibile eseguire "%s" Update blocklist=Aggiorna lista di blocco URL of a .torrent file or a magnet link=URL di un file .torrent o un collegamento magnetico Columns setup=Configura colonne Add torrent=Aggiungi torrent Delete a .torrent file after a successful addition=Cancella un file .torrent dopo dopo averlo aggiunto Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=La verifica dei torrent potrebbe richiedere tempo.~Sei sicuro di voler verificare %d torrent? Unable to load OpenSSL library files: %s and %s=Impossibile caricare la libreria OpenSSL: %s e %s Use SSL=Usa SSL Add tracker=Aggiungi tracker Alternate bandwidth settings=Configurazione alternativa della banda passante Apply alternate bandwidth settings automatically=Applica automaticamente la configurazione alternativa della banda passante Are you sure to delete connection '%s'?=Vuoi eliminare la connessione '%s'? Are you sure to remove tracker '%s'?=Vuoi eliminare il tracker '%s'? Days=Giorno Delete=Cancella Disk cache size=Dimensione della memoria temporanea su disco Download speeds (KB/s)=Velocità Prelievo (KB/s) Edit tracker=Modifica il tracker Enable Local Peer Discovery=Abilita scoperta distributori locali Free disk space=Spazio libero su disco Free: %s=Libero: %s From=Dalle minutes=minuti Misc=Varie New connection=Nuova connessione New=Nuovo No tracker URL was specified=Nessun URL di tracker specificato Proxy=Proxy Remove tracker=Rimuovi tracker Rename=Rinomina Speed limit menu items=Voci del menu per la limitazione della velocità: Stop seeding when inactive for=Ferma la distribuzione quando inattivo per The invalid time value was entered=Valore di tempo non valido to=alle Tracker announce URL=URL annuncio Tracker Tracker properties=Proprietà tracker Unlimited=Non limitato Upload speeds (KB/s)=Velocità Invio (KB/s) Use alternate bandwidth settings=Usa la configurazione alternativa della banda passante average=medio Browse=Cerca Enable µTP=Abilita µTP Select a folder for download=Scegli una cartella per il prelievo Select torrent location=Scegli una cartella per i torrent A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Una nuona versione di %s è disponibile.~La tua versione corrente è: %s~La nuova versione è: %s~~Vuoi aprire la pagina web per il prelievo? Advanced=Avanzate Check for new version every=Controlla l'esistenza di nuove versioni ogni Check for updates=Controlla aggiornamenti Consider active torrents as stalled when idle for=Considera i torrent attivi come in stallo se sono fermi per Do you wish to enable automatic checking for a new version of %s?=Vuoi abilitare il controllo automatico per nuove versioni di %s? Donate!=Dona! Download queue size=Lunghezza della coda di Prelievo Error checking for new version=Si è verificato un errore controllando l'esistenza di una nuova versione Folder grouping=Mostra raggruppamento per cartelle Force start=Forza l'avvio Home page=Pagina principale Modify trackers=Modifica i trackers Move bottom=Sposta in fondo Move down queue=Sposta in fondo alla coda Move down=Sposta giù Move top=Sposta in cima Move up queue=Sposta in cima alla coda Move up=Sposta su No updates have been found.~You are running the latest version of %s=Non sono stati trovati aggiornamenti.~Stai usando l'ultima versione di %s Queue position=Posizione nella coda Queue=Coda Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=I torrent che sono stati fermi per N minuti non saranno calcolati nella coda di prelevo o invio Tracker grouping=Mostra raggruppamento per Tracker Upload queue size=Lunghezza della coda di Invio View=Mostra Visit home page=Vai alla pagina principale days=giorni Active time=Tempo di attività Automatically add torrent links from the clipboard=Aggiungi in automatico i collegamenti dei Torrent dagli appunti Copy file path to clipboard=Copia il percorso del file negli Appunti Cumulative=Cumulativo Current=Corrente Files added=File aggiunti Filter pane=Pannello filtri Global statistics=Statistiche globali Info pane=Pannello informazioni Statistics=Statistiche Status bar=Barra di stato %dd=%dd %dh=%dh %dm=%dm All torrents=Tutti i torrent Application options=Opzioni di questa applicazione Ask for password=Chiedi la Parola chiave Authentication required=Autenticazione richiesta Average out transfer speeds to eliminate fluctuations=Stabilizza la velocità di invio per eliminare le fluttuazioni Connect to %s=Connetti a %s Connect to Transmission using proxy server=Connetti a Transmission utilizzando il servente proxy Connect to Transmission=Connetti a Transmission Connection name=Nome connessione Could not connect to tracker==Impossibile connettersi al tracker Data display=Visualizza dati Data refresh interval when minimized=Intervallo aggiornamento dati quando minimizzato Data refresh interval=Intervallo aggiornamento dati Default download folder on remote host=Cartella predefinita di Prelievo sul sistema remoto Disconnect from Transmission=Disconnetti da Transmission Downloading torrent file=Preleva file torrent Font size=Dimensione Carattere Handle .torrent files by %s=Gestione del file .torrent da parte di %s Handle magnet links by %s=Gestione collegamento magnetico da parte di %s Invalid name specified=Nome specificato non valido Manage connections to Transmission=Gestisci le connessioni a Transmission Manage connections=Gestisci connessioni Network (WAN)=Rete globale (WAN) New connection to Transmission=Nuova connessione a Transmission Pick random port on Transmission launch=Scegliere una porta casuale all'avvio di Transmission Please enter a password to connect to %s=Prego inserire una Parola chiave per connettersi a %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Prego specificare come %s si connetterà al sistema remoto su cui è in funzione Transmission (servizio) Prompt for download options when adding a new torrent=Richiedi le opzioni di prelievo quando aggiungi un nuovo torrent RPC path=Percorso RPC Save as=Salva come Seeding time=Tempo Distribuzione Show advanced options=Visualizza le opzioni avanzate Show notifications in tray icon=Visualizza le notifiche se è minimizzato System integration=Integrazione sistema Torrent already exists in the list=Il torrent esiste già nell'elenco Torrent not registered with this tracker==Torrent non registrato su questo tracker Unable to find path mapping.~Use the application's options to setup path mappings=Impossibile trovare la mappa dei percorsi.~Utilizza le opzioni dell'applicazione per impostare la mappa dei percorsi Update trackers for the existing torrent?=Aggiorna i tracker per il torrent esistente? You need to restart the application to apply changes=Devi riavviare l'applicativo per applicare le modifiche TransGUI/lang/transgui.ko0000644000000000000000000003457312261612465014341 0ustar rootrootTranslationLanguage=한국어 "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="리모트와 ë¡œì»¬ê°„ì˜ ê²½ë¡œì„¤ì •.~~사용예:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (%d 소유) %ds=%dì´ˆ %s (%d hashfails)=%s (%d 해쉬실패) %s (%s done)=%s (%s ë) '%s' has finished downloading='%s' 다운로드 종료 %s%s%d downloading, %d seeding%s%s, %s=%s%s%d 다운로드, %d 시딩%s%s, %s &All=&모든 &Close=&닫다 &Help=&ë„ì›€ë§ &Ignore=&무시 &No=&아니 &OK=&í™•ì¸ &Open=&열기 &Retry=&다시 &Save=&저장 &Unlock=&잠금풀기 &Yes=&예 /s=/s Abort=취소 About=알아보기 Active=활성화 Add new torrent=새 토렌트 추가 &Add torrent=토렌트 추가 Added on=추가 Are you sure to remove torrent '%s' and all associated DATA?='%s'토렌트와 ë°ì´í„°ë¥¼ ì§€ì›ë‹ˆê¹Œ? Are you sure to remove torrent '%s'?='%s' 토렌트를 ì§€ì›ë‹ˆê¹Œ? Authentication=ì¸ì¦ b=b Cancel=취소 Client=í´ë¼ì´ì–¸íЏ Close to tray=트레ì´ë¡œ 닫기 Comment=설명 Completed on=완료 Completed=ì™„ë£Œë¨ Confirmation=í™•ì¸ connected=ì—°ê²°ë¨ Connecting to daemon=ì„œë²„ì— ì—°ê²°ì¤‘ Connection error occurred=ì—°ê²° 오류 Copy=복사 Country=êµ­ê°€ Created on=ìƒì„± D: %s/s=다운: %s/s Destination folder=ëŒ€ìƒ í´ë” Disconnected=연결종료 Donate to support further development=개발 ì§€ì›ì„ 위한 기부 Donate via PayPal,WebMoney,Credit card=PayPal,WebMoney,Credit card를 통한 기부 Done=완료 Don't download=다운로드 í•˜ì§€ì•ŠìŒ Down limit=다운 제한 Down speed=다운 ì†ë„ Down=아래로 Download complete=다운로드 완료 Download speed=다운로드 ì†ë„ Downloaded=다운로드 Downloading=다운로드중 Enable DHT=DHT사용 Enable Peer Exchange=Peer Exchange 사용 Enable port forwarding=í¬íЏ í¬ì›Œë”© 사용 Encryption disabled=암호화 비활성화 Encryption enabled=암호화 활성화 Encryption required=암호화 필수 Encryption=암호화 Error=ì—러 ETA=ETA E&xit=종료 File name=íŒŒì¼ ì´ë¦„ Files=íŒŒì¼ Finished=ë Flag images archive is needed to display country flags.~Download this archive now?=국기를 표시하기 위해 국기 ì´ë¯¸ì§€ëª¨ìŒì´ 필요합니다..~지금 다운로드 할까요? Flags=ìƒíƒœ GB=GB General=종합 Geo IP database is needed to resolve country by IP address.~Download this database now?=IP로 국가를 ë¶„ì„하기 위해 Geo IP ë°ì´í„° ë² ì´ìŠ¤ê°€ 필요합니다.~ë°ì´í„° ë² ì´ìŠ¤ë¥¼ 지금 ë°›ì„까요? Global bandwidth settings=ì „ì²´ ëŒ€ì—­í­ ì„¤ì • Global peer limit=ì „ì²´ 피어 제한 Hash=해쉬 Have=완료 Hide=숨김 High priority=ë†’ì€ ìš°ì„ ê¶Œ high=ë†’ìŒ Host=호스트 in swarm=피어 Inactive=비활성화 Incoming port is closed. Check your firewall settings=들어오는 í¬íЏ 닫힘. 방화벽 ì„¤ì •ì„ ì ê²€í•˜ì„¸ìš” Incoming port tested successfully=들어오는 í¬íЏ 시험 성공 Incoming port=들어오는 í¬íЏ Information=ì •ë³´ KB/s=KB/s KB=KB Language=언어 Last active=최근 활성화 License=저작권 Low priority=ë‚®ì€ ìš°ì„ ê¶Œ low=ë‚®ìŒ Max peers=최대 피어수 Maximum download speed=최대 다운로드 ì†ë„ Maximum upload speed=최대 업로드 ì†ë„ MB=MB Minimize to tray=트레ì´ë¡œ 최소화 Name=ì´ë¦„ No host name specified=호스트 ì´ë¦„ 명시ë˜ì§€ ì•ŠìŒ No proxy server specified=프ë½ìФ 서버 명시ë˜ì§€ ì•ŠìŒ No to all=No to all Normal priority=보통 ìš°ì„ ê¶Œ normal=보통 of=중 Open containing folder=ì €ìž¥ëœ í´ë” 열기 Open=열기 Password=비밀번호 Paths=경로 Peer limit=피어 제한 Peers=피어 Pieces=ì¡°ê° Port=í¬íЏ Priority=ìš°ì„ ê¶Œ Properties=설정 Proxy password=프ë¡ì‹œ 비밀번호 Proxy port=프ë¡ì‹œ í¬íЏ Proxy server=프ë¡ì‹œ 서버 Proxy user name=프ë¡ì‹œ 유저 ì´ë¦„ Ratio=비율 Reconnect in %d seconds=%d ì´ˆ ì•ˆì— ìž¬ì ‘ì† Remaining=ë‚¨ìŒ Remote host=ì›ê²© 호스트 Remove torrent and Data=토렌트와 ë°ì´í„° 제거 Remove torrent=토렌트 제거 Remove=제거 Resolve country=êµ­ê°€ í™•ì¸ Resolve host name=호스트 ì´ë¦„ í™•ì¸ seconds=ì´ˆ Seed ratio=시드 비율 Seeding=시딩중 Seeds=시드 Select a .torrent to open=ì„ íƒëœ .torrent 열기 Select all=ëª¨ë‘ ì„ íƒ Select none=ì„ íƒ í•˜ì§€ ì•ŠìŒ Setup columns=세로줄 설정 Share ratio=공유 비율 Show country flag=국기 표시 Show=ë³´ì´ê¸° Size=í¬ê¸° skip=취소 Start all torrents=모든 토렌트 시작 Start torrent=토렌트 시작 Start=시작 Status=ìƒíƒœ Stop all torrents=모든 토렌트 멈춤 Stop all=ëª¨ë‘ ë©ˆì¶¤ Stop torrent=토렌트 멈춤 Stop=멈춤 Stopped=멈춤 TB=TB Test port=í¬íЏ 시험 T&ools=ë„구 Torrent contents=토렌트 ë‚´ìš© Torrent properties=토렌트 설정 Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=토렌트 ê²€ì¦ì€ ì‹œê°„ì´ ì˜¤ëž˜ê±¸ë¦´ 수 있습니다.~'%s'토렌트 ê²€ì¦ì„ 시작할 까요? &Torrent=토렌트 Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=토렌트 (*.torrent)|*.torrent|모든 íŒŒì¼ (*.*)|*.* Total size=ì „ì²´ í¬ê¸° Tracker status=트레커 ìƒíƒœ Tracker update on=트레커 ì—…ë°ì´íЏ 중 Tracker=트레커 Trackers=트레커 Transfer=전송 Transmission options=트렌스미션 옵션 Transmission%s at %s:%s=트랜스미션%s at %s:%s Tray icon always visible=íŠ¸ë ˆì´ ì•„ì´ì½˜ í•­ìƒ ë³´ì´ê¸° Tray icon=íŠ¸ë ˆì´ ì•„ì´ì½˜ U: %s/s=ì—…: %s/s Unable to extract flag image=국기 ì´ë¯¸ì§€ë¥¼ 추출 í•  수 없습니다. Unable to get files list=íŒŒì¼ ëª©ë¡ì„ 가져올 수 ì—†ìŒ Unknown=ì•Œìˆ˜ì—†ìŒ Up limit=업로드 제한 Up speed=ì—… ì†ë„ Up=위로 Update complete=ì—…ë°ì´íЏ 완료 Update GeoIP database=GeoIP database ì—…ë°ì´íЏ Update in=ì—…ë°ì´íЏ Updating=ì—…ë°ì´íŠ¸ì¤‘ Upload speed=업로드 ì†ë„ Uploaded=업로드 User name=ì‚¬ìš©ìž ì´ë¦„ Verify torrent=토렌트 ê²€ì¦ &Verify=ê²€ì¦ Verifying=ê²€ì¦ì¤‘ Version %s=버전 %s Waiting=대기 Warning=경고 Wasted=ë²„ë ¤ì§ Working=ë™ìž‘중 Yes to &All=ëª¨ë‘ ì˜ˆ No tracker=트레커 아님 %s downloaded=%s 다운로드 %s of %s downloaded=%s 중 %s 다운로드 ë¨ %d torrents=%d 토렌트 Add .part extension to incomplete files=ì§„í–‰ì¤‘ì´ íŒŒì¼ì— .part ì—°ê²° 추가 Add torrent link=토렌트 ì—°ê²° 추가 Are you sure to remove %d selected torrents and all their associated DATA?=%d ì„ íƒëœ 토렌트와 ë°ì´í„°ë¥¼ ëª¨ë‘ ì‚­ì œ 하시겠습니까? Are you sure to remove %d selected torrents?=%d ì„ íƒëœ 토렌트를 ì‚­ì œ 하시겠습니까? Bandwidth=ëŒ€ì—­í­ Directory for incomplete files=ì§„í–‰ì¤‘ì¸ íŒŒì¼ ë””ë ‰í† ë¦¬ Download=다운로드 Enable blocklist=차단 ëª©ë¡ ì‚¬ìš©í•˜ê¸° ID=ì•„ì´ë”” Move torrent data from current location to new location=토렌트 ë°ì´í„° 새 위치로 ì´ë™ New location for torrent data=토렌트 ë°ì´í„° 새 위치 No link was specified=ì—°ê²°ì´ ëª…ì‹œë˜ì§€ ì•ŠìŒ No torrent location was specified=토렌트 위치가 명시ë˜ì§€ ì•ŠìŒ Path=경로 Reannounce (get more peers)=다시알림 (피어 추가) Set data location=ë°ì´í„° 위치 설정 Size to download=다운로드 í¬ê¸° The block list has been updated successfully.~The list entries count: %d=차단 목ë¡ì´ ì—…ë°ì´íЏ ë˜ì—ˆìŠµë‹ˆë‹¤.~ëª©ë¡ ê°¯ìˆ˜: %d The directory for incomplete files was not specified=ì§„í–‰ì¤‘ì¸ íŒŒì¼ì˜ 디렉토리가 명시ë˜ì§€ 않았습니다. The downloads directory was not specified=다운로드 디렉토리가 명시ë˜ì§€ 않았습니다. Torrent data location=토렌트 ë°ì´í„° 위치 Torrents=토렌트 Unable to execute "%s"=%s Update blocklist=blocklist ì—…ë°ì´íЏ URL of a .torrent file or a magnet link=URL of a .torrent file or a magnet link Columns setup=세로줄 설정 Add torrent=토렌트 추가 Delete a .torrent file after a successful addition=추가 성공 후 .torrent 파ì¼ì„ 지움 Torrent=토렌트 Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=토렌트 íŒŒì¼ í™•ì¸ì€ ì‹œê°„ì´ ì˜¤ëž˜ 걸릴 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.~ %d 토렌트 파ì¼ì„ í™•ì¸ í•˜ì‹œê² ìŠµë‹ˆê¹Œ? Unable to load OpenSSL library files: %s and %s=OpenSSL library 파ì¼ì„ ì—´ 수 ì—†ìŒ: %s and %s Use SSL=SSL 사용 Add tracker=트레커 추가 Alternate bandwidth settings=êµì°¨ ëŒ€ì—­í­ ì„¤ì • Apply alternate bandwidth settings automatically=êµì°¨ ëŒ€ì—­í­ ìžë™ 설정 ì ìš© Are you sure to delete connection '%s'?='%s'ì—°ê²°ì„ ì‚­ì œ 하시겠습니까? Are you sure to remove tracker '%s'?='%s'트레커를 제거 하시겠습니까? Days=Days Delete=ì‚­ì œ Disk cache size=ë””ìŠ¤í¬ ìºì‰¬ í¬ê¸° Download speeds (KB/s)=다운로드 ì†ë„ (KB/s) Edit tracker=트레커 편집 Enable Local Peer Discovery=지역 피어 발견 사용 Free disk space=ë‚¨ì€ ë””ìŠ¤í¬ ê³µê°„ Free: %s=Free: %s From=From minutes=ë¶„ Misc=기타 New connection=새 ì—°ê²° New=새로만들기 No tracker URL was specified=트레커 URLì´ ëª…ì‹œë˜ì§€ ì•ŠìŒ Proxy=프ë¡ì‹œ Remove tracker=트레커 제거 Rename=ì´ë¦„ 바꾸기 Speed limit menu items=ì†ë„제한 메뉴 항목 Stop seeding when inactive for=다ìŒì‹œê°„ ë™ì•ˆ 비활성화시 시딩 멈춤 The invalid time value was entered=ìž˜ëª»ëœ ì‹œê°„ê°’ì´ ìž…ë ¥ë¨. to=to Tracker announce URL=트레커 알림 URL Tracker properties=트레커 설정 Unlimited=제한 ì—†ìŒ Upload speeds (KB/s)=업로드 ì†ë„ (KB/s) Use alternate bandwidth settings=êµì°¨ 대역í­ì„¤ì • 사용 average=í‰ê·  Browse=찾아보기 Select a folder for download=다운로드할 í´ë”를 지정하세요 Select torrent location=토렌트 위치를 지정하세요 A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=%sì˜ ìƒˆ ë²„ì „ì´ ë‚˜ì™”ìŠµë‹ˆë‹¤.~현재 버젼: %s~새 버젼: %s~~다운로드 페ì´ì§€ë¡œ ì ‘ì†í•˜ì‹œê² ìŠµë‹ˆê¹Œ? Advanced=고급 Check for new version every=새 ë²„ì „ì„ í™•ì¸. 매 Check for updates=ì—…ë°ì´íЏ í™•ì¸ Consider active torrents as stalled when idle for=í™œì„±í™”ëœ í† ë ŒíŠ¸ê°€ 멈춘것으로 간주합니다. ë‹¤ìŒ ì‹œê°„ë™ì•ˆ 유휴ìƒíƒœì¼ 경우 Do you wish to enable automatic checking for a new version of %s?=%sì˜ ìƒˆ 버전확ì¸ì„ 사용하시겠습니까? Donate!=기부하기! Download queue size=다운로드 í í¬ê¸° Error checking for new version=새 버전 í™•ì¸ ì˜¤ë¥˜ Folder grouping=í´ë” 그룹화 Force start=강제로 시작 Home page=홈페ì´ì§€ Modify trackers=트레커 수정 Move bottom=맨 하단으로 Move down queue=í 순위 아래로 Move down=아래로 Move top=맨 ìƒë‹¨ìœ¼ë¡œ Move up queue=í 순위 위로 Move up=위로 No updates have been found.~You are running the latest version of %s=ì—…ë°ì´íŠ¸ê°€ 없습니다.~%sì˜ ìµœì‹  ë²„ì „ì„ ì‚¬ìš©ì¤‘ìž…ë‹ˆë‹¤. Queue position=í 순위 Queue=í Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=N ë¶„ê°„ 유휴ìƒíƒœì¸ 토렌트는 다운로드나 업로드 íì— ë„£ì§€ 않습니다. Tracker grouping=트레커 그룹화 Upload queue size=업로드 í í¬ê¸° View=보기 Visit home page=홈페ì´ì§€ 방문 days=ì¼ Active time=활성화 시간 Automatically add torrent links from the clipboard=í´ë¦½ë³´ë“œì— 있는 토렌트 ë§í¬ ìžë™ìœ¼ë¡œ 추가 Copy file path to clipboard=íŒŒì¼ ê²½ë¡œë¥¼ í´ë¦½ë³´ë“œì— 복사 Cumulative=ëˆ„ì  Current=현재 Files added=ì¶”ê°€ëœ íŒŒì¼ Filter pane=í•„í„° íŒ¨ë„ Global statistics=ì „ì²´ 통계 Info pane=ì •ë³´ íŒ¨ë„ Statistics=통계 Status bar=ìƒíƒœ ë°” %dd=%dì¼ %dh=%d시간 %dm=%dë¶„ All torrents=모든 토렌트 Application options=트렌스미션 Remote GUI 옵션 Ask for password=암호를 물어봄 Authentication required=ì¸ì¦ì´ 필요함 Average out transfer speeds to eliminate fluctuations=급격한 ì†ë„ 변경 현ìƒì„ 막기위해 전송ì†ë„를 í‰ê·  처리 합니다 Connect to %s=%s ì— ì—°ê²° Connect to Transmission using proxy server=íŠ¸ë ŒìŠ¤ë¯¸ì…˜ì— í”„ë¡ì‹œë¥¼ 사용하여 연결합니다 Connect to Transmission=íŠ¸ë ŒìŠ¤ë¯¸ì…˜ì— ì—°ê²° Connection name=ì—°ê²° ì´ë¦„ Could not connect to tracker==íŠ¸ë ˆì»¤ì— ì—°ê²°í•  수 없습니다 Data display=ë°ì´í„° 표시 Data refresh interval when minimized=최소화시 ë°ì´í„° 갱신 주기 Data refresh interval=ë°ì´í„° 갱신 주기 Default download folder on remote host=ì›ê²© í˜¸ìŠ¤íŠ¸ì˜ ê¸°ë³¸ 다운로드 í´ë” Disconnect from Transmission=íŠ¸ë ŒìŠ¤ë¯¸ì…˜ìœ¼ë¡œì˜ ì—°ê²° í•´ì œ Downloading torrent file=ë‹¤ìš´ë¡œë“œì¤‘ì¸ í† ëŸ°íŠ¸ íŒŒì¼ Enable μTP=μTP 사용 Font size=í°íЏ í¬ê¸° Handle .torrent files by %s=%s로 .torrent íŒŒì¼ ì²˜ë¦¬ Handle magnet links by %s=%s로 마그넷 ë§í¬ 처리 Invalid name specified=비정ìƒì ì¸ ì´ë¦„ì´ ì§€ì •ë˜ì—ˆìŠµë‹ˆë‹¤ Manage connections to Transmission=íŠ¸ë ŒìŠ¤ë¯¸ì…˜ê³¼ì˜ ì—°ê²° 관리하기 Manage connections=ì—°ê²° 관리하기 Network (WAN)=ë„¤íŠ¸ì›Œí¬ (WAN) New connection to Transmission=새로운 íŠ¸ë ŒìŠ¤ë¯¸ì…˜ê³¼ì˜ ì—°ê²° Pick random port on Transmission launch=트렌스미션 ë°ëª¬ 시작시 ëžœë¤ìœ¼ë¡œ 고름 Please enter a password to connect to %s=%s ì— ì—°ê²°í•˜ê¸° 위해 비밀번호를 입력하세요 Please specify how %s will connect to a remote host running Transmission daemon (service)=%s ê°€ 트렌스미션 ë°ëª¬ (서비스)ì´ ê°€ë™ì¤‘ì¸ ì›ê²© í˜¸ìŠ¤íŠ¸ì— ì–´ë–»ê²Œ ì ‘ì†í•  지를 지정해주세요 Prompt for download options when adding a new torrent=새로운 토렌트 추가시 다운로드 ì˜µì…˜ì„ ë¬¼ì–´ë´…ë‹ˆë‹¤ RPC path=RPC 경로 Save as=다른 ì´ë¦„으로 저장 Seeding time=시딩 시간 Show advanced options=고급 ì„¤ì •ì„ ë³´ì´ê¸° Show notifications in tray icon=트레ì´ì— 알림 ì•„ì´ì½˜ ë³´ì´ê¸° System integration=ì‹œìŠ¤í…œê³¼ì˜ í†µí•© Torrent already exists in the list=목ë¡ì— 토렌트가 ì´ë¯¸ 존재합니다 Torrent not registered with this tracker==ì´ íŠ¸ë ˆì»¤ì— í•´ë‹¹ 토렌트 ì •ë³´ê°€ 등ë¡ë˜ì–´ 있지 않습니다 Unable to find path mapping.~Use the application's options to setup path mappings=ì—°ê²°ëœ ê²½ë¡œë¥¼ ì°¾ì„수 없습니다.~트렌스미션 Remote GUI 옵션 í˜¹ì€ ì—°ê²° 관리하기를 통해 경로를 설정하십시요 Update trackers for the existing torrent?=기존 í† ëŸ°íŠ¸ì˜ íŠ¸ë ˆì»¤ë¥¼ ì—…ë°ì´íЏ 하시겠습니까? You need to restart the application to apply changes=변경 ì‚¬í•­ì„ ì ìš©í•˜ë ¤ë©´ í”„ë¡œê·¸ëž¨ì„ ë‹¤ì‹œ 시작해야 합니다 TransGUI/lang/transgui.tr0000644000000000000000000003366212261612465014353 0ustar rootrootTranslationLanguage=Turkish "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Uzak klasöre yerel yol atama.~~Örnek:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (%d sahip) %ds=%ds %s (%d hashfails)=%s (%d hash hatası) %s (%s done)=%s (%s tamam) '%s' has finished downloading='%s' indirmesi tamamlandı %s%s%d downloading, %d seeding%s%s, %s=%s%s%d indiriliyor, %d gönderiliyor%s%s, %s &All=&Tümü &Close=&Kapat &Help=&Yardım &Ignore=&Yoksay &No=&Hayır &OK=&Tamam &Open=&Aç &Retry=&Tekrar &Save=&Kaydet &Unlock=&Kilit aç &Yes=&Evet /s=/s Abort=İptal About=Hakkında Active=Aktif Add new torrent=Yeni torrent ekle &Add torrent=Torrent ekle Added on=EklendiÄŸi tarih Are you sure to remove torrent '%s' and all associated DATA?='%s'torrentini ve tüm DOSYALARINI silmek istediÄŸinize emin misiniz? Are you sure to remove torrent '%s'?='%s' torrentini silmek istediÄŸinize emin misiniz? Authentication=Kimlik doÄŸrulama b=b Cancel=İptal Client=İstemci Close to tray=Simge durumuna kapat Comment=Yorum Completed on=Tamamlandığı tarih Completed=Tamamlandı Confirmation=Onay connected=baÄŸlandı Connecting to daemon=Sunucuya baÄŸlanılıyor Connection error occurred=BaÄŸlantı problemi oluÅŸtu Copy=Kopyala Country=Ülke Created on=OluÅŸturulduÄŸu tarih D: %s/s=↓↓ : %s/s Destination folder=Hedef klasör Disconnected=BaÄŸlantı koptu Donate to support further development=Gelecekte ki geliÅŸtirmeler için bağışta bulunun Donate via PayPal,WebMoney,Credit card=PayPal,WebMoney,Kredi kartı ile bağışta bulunun Done=Tamamlanan Don't download=İndirme Down limit=İndirme limiti Down speed=İndirme hızı Down=İndir Download complete=İndirme tamamlandı Download speed=İndirme hızı Downloaded=İndirilen Downloading=İndiriliyor Enable DHT=DHT'yi aktif et Enable Peer Exchange=EÅŸ deÄŸiÅŸimini aktif et Enable port forwarding=Port yönlendirmesini aktif et Encryption disabled=Åžifreleme devre dışı Encryption enabled=Åžifreleme aktif Encryption required=Åžifreleme gerekli Encryption=Åžifreleme Error=Hatalı ETA=Tahmini Süre E&xit=Çıkış File name=Dosya adı Files=Dosyalar Finished=Tamamlandı Flag images archive is needed to display country flags.~Download this archive now?=Ülke bayrak paketi, ülke bayraklarının gösterilmesi için gereklidir.~Paketi indirilsin mi? Flags=Bayraklar GB=GB General=Genel Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP veritabanı ülke bayrakları çözümlemesi için gereklidir.~Veritabanını indirilsin mi? Global bandwidth settings=Genel bant geniÅŸliÄŸi ayarları Global peer limit=Genel eÅŸ limiti Hash=Hash Have=Sahip Hide=Gizle High priority=Yüksek öncelik high=yüksek Host=Kaynak in swarm=yığın içerisinde Inactive=Pasif Incoming port is closed. Check your firewall settings=GeliÅŸ portu kapalı. Firewall ayarlarını kontrol edin Incoming port tested successfully=GeliÅŸ portu testi baÅŸarıyla tamamlandı Incoming port=GeliÅŸ portu Information=Bilgi KB/s=KB/s KB=KB Language=Dil Last active=Son aktif zamanı License=Lisans Low priority=Düşük öncelik low=düşük Max peers=Azami eÅŸ sayısı Maximum download speed=Azami indirme hızı Maximum upload speed=Azami gönderme hızı MB=MB Minimize to tray=Simge durumuna küçült Name=İsim No host name specified=Sunucu ismi tanımlanmamış No proxy server specified=Proxy sunucu tanımlanmamış No to all=Hepsine hayır Normal priority=Normal öncelik normal=normal of=of Open containing folder=İçerdiÄŸi klasörü aç Open=Aç Password=Åžifre Paths=Yollar Peer limit=EÅŸ limiti Peers=EÅŸler Pieces=Parçalar Port=Port Priority=Öncelik Properties=Özellikler Proxy password=Proxy ÅŸifresi Proxy port=Proxy portu Proxy server=Proxy sunucusu Proxy user name=Proxy kullanıcı adı Ratio=Oran Reconnect in %d seconds=%d saniye içerisinde tekrar baÄŸlan Remaining=Kalan Remote host=Uzak sunucu Remove torrent and Data=Torrenti ve dosyalarını sil Remove torrent=Torrenti sil Remove=Sil Resolve country=Ülkeri çözümle Resolve host name=Sunucu isimlerini çözümle seconds=saniye Seed ratio=Kaynak kalma oranı Seeding=Gönderiliyor Seeds=Kaynaklar Select a .torrent to open=Açmak için .torrent dosyası seçin Select all=Hepsini seç Select none=Hiçbirini seçme Setup columns=Sütunları ayarla Share ratio=Paylaşım oranı Show country flag=Ülke bayraklarını göster Show=Göster Size=Boyut skip=geç Start all torrents=Tüm torrentleri baÅŸlat Start torrent=Torrenti baÅŸlat Start=BaÅŸlat Status=Durum Stop all torrents=Tüm torrentleri durdur Stop all=Hepsini durdur Stop torrent=Torrenti durdur Stop=Dur Stopped=DurdurulmuÅŸ TB=TB Test port=Portu test et T&ools=Araçlar Torrent contents=Torrent içeriÄŸi Torrent properties=Torrent özellikleri Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrent kontolü uzun sürebilecek bir iÅŸlemdir.~'%s' torrentini kontrol ettirmek istiyor musun? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrentler (*.torrent)|*.torrent|Tüm dosyalar (*.*)|*.* Total size=Toplam boyut Tracker status=İzleyici durumu Tracker update on=İzleyici güncellemesi Tracker=İzleyici Trackers=İzleyiciler Transfer=Aktarım Transmission options=Transmission ayarları Transmission%s at %s:%s=Transmission%s at %s:%s Tray icon always visible=Simge her zaman gözüksün Tray icon=Simge U: %s/s=↑↑ : %s/s Unable to extract flag image=Bayrak görüntüsü açılmıyor Unable to get files list=Dosya listesi alınamadı Unknown=Bilinmiyor Up limit=Gönderme limiti Up speed=Gönderme hızı Up=Gönderme Update complete=Güncelleme tamamlandı Update GeoIP database=GeoIP veritabanını güncelle Update in=Güncelleme Updating=Güncelleniyor Upload speed=Gönderme hızı Uploaded=Gönderilen User name=Kullanıcı adı Verify torrent=Torrenti kontrol et &Verify=Kontrol et Verifying=Kontrol ediliyor Version %s=Sürüm %s Waiting=Bekliyor Warning=Uyarı Wasted=BoÅŸa giden Working=Çalışıyor Yes to &All=&All evet de No tracker=İzleyici yok %s downloaded=%s indirildi %s of %s downloaded=%s of %s indirildi %d torrents=%d torrent Add .part extension to incomplete files=Tamamlanmamış dosyalara .part dosya eki ekle Add torrent link=Magnet baÄŸlantısı ekle Are you sure to remove %d selected torrents and all their associated DATA?=%d seçilmiÅŸ torrentlerini ve tüm DOSYALARINI silmek istediÄŸinize emin misiniz? Are you sure to remove %d selected torrents?=%d torrentlerini silmek istediÄŸinize emin misiniz? Bandwidth=Bant geniÅŸliÄŸi Directory for incomplete files=Tamamlanmamış dosya klasörü Download=İndirme Enable blocklist=Engelleme listesini aktif et ID=Kimlik Move torrent data from current location to new location=Torrent dosyalarını yeni bir klasöre taşı New location for torrent data=Torrent dosyaları için yeni dizin No link was specified=Hiçbir link tanımlanmamış No torrent location was specified=Hiçbir torrent dizini tanımlanmamış Path=Yol Reannounce (get more peers)=Tekrar anons et (daha fazla eÅŸ al) Set data location=Dosya dizinini ayarla Size to download=İndirme için boyut The block list has been updated successfully.~The list entries count: %d=Engelleme listesi baÅŸarıyla güncellendi.~Engelleme listesinde ki içerik sayısı: %d The directory for incomplete files was not specified=Tamamlanmamış dosya klasörü tanımlanmamış The downloads directory was not specified=İndirme klasörü tanımlanmamış Torrent data location=Torrent dosyalarının yeri Torrents=Torrentler Unable to execute "%s"=%s Update blocklist=Engelleme listesini güncelle URL of a .torrent file or a magnet link=.torrent dosyası adresi veya magnet baÄŸlantısı Columns setup=Sütun ayarları Add torrent=Torrent ekle Delete a .torrent file after a successful addition=Torrent baÅŸarıyla eklendikten sonra .torrent dosyasını sil Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrentlerin kontolü uzun sürebilecek bir iÅŸlemdir.~%d torrentlerini kontrol ettirmek istiyor musun? Unable to load OpenSSL library files: %s and %s=OpenSSL kütüphanesinden dosyalar yüklenemedi: %s ve %s Use SSL=SSL kullan Add tracker=İzleyici ekle Alternate bandwidth settings=Alternatif bant geniÅŸliÄŸi ayarları Apply alternate bandwidth settings automatically=Alternatif bant geniÅŸliÄŸi ayarlarını otomatik uygula Are you sure to delete connection '%s'?='%s' baÄŸlantısını silmek istiyor musun? Are you sure to remove tracker '%s'?='%s' izleyicisini silmek istiyor musun? Days=Günler Delete=Sil Disk cache size=Disk önbellek boyutu Download speeds (KB/s)=İndirme hızı (KB/s) Edit tracker=İzleyiciyi düzenle Enable Local Peer Discovery=Yerel eÅŸ bulmayı etkinleÅŸtir Free disk space=BoÅŸ disk alanı Free: %s=BoÅŸ alan: %s From=Kimden minutes=dakika Misc=DiÄŸer New connection=Yeni baÄŸlantı New=Yeni No tracker URL was specified=İzleyici adresi tanımlanmamış Proxy=Proxy Remove tracker=İzleyiciyi sil Rename=Tekrar isimlendir Speed limit menu items=Menü araçlarına ki hız limiti seçenekleri Stop seeding when inactive for=Belli bir süre aktif olmayan göndermeyi durdur The invalid time value was entered=Geçersiz zaman deÄŸeri girildi to=buna Tracker announce URL=İzleyici anons adresi Tracker properties=İzleyici ayarları Unlimited=Limitsiz Upload speeds (KB/s)=Gönderme hızı (KB/s) Use alternate bandwidth settings=Alternatif bant geniÅŸliÄŸi ayarlarını kullan average=ortalama Browse=Tara Select a folder for download=İndirmeler için dizin seç Select torrent location=Torrent dizinini seç A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=%s yeni bir sürümü kullanılabilinir.~Sizin sürümünüz: %s~Yeni sürüm: %s~~İndirme sayfasını açmak ister misiniz? Advanced=GeliÅŸmiÅŸ Check for new version every=Yeni sürüm kaç günde bir kontrol edilsin Check for updates=Güncellemeleri kontrol et Consider active torrents as stalled when idle for=Belli bir süre beklemede kalmış aktif torrentleri durdur Do you wish to enable automatic checking for a new version of %s?=%s için otomatik sürüm güncellemeleri aktifleÅŸtirilsin mi? Donate!=Bağışta bulun! Download queue size=İndirme sırası boyutu Error checking for new version=Yeni sürümü kontrol ederken hata oluÅŸtu Folder grouping=Klasör guruplama Force start=Zorla baÅŸlat Home page=Ana sayfa Modify trackers=İzleyicileri deÄŸiÅŸtir Move bottom=Alta çek Move down queue=Sırada aÅŸağı çek Move down=AÅŸağı çek Move top=Tepeye çek Move up queue=Sırada yukarı çek Move up=Yukarı çek No updates have been found.~You are running the latest version of %s=Hiçbir güncelleme bulunamadı.~%s ile en son sürümü kullanıyorsunuz Queue position=Sıra Queue=Sıralama Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=N dakika beklemede kalan torrentler, İndirme veya Yükleme sırasına kabul edilmez Tracker grouping=İzleyici guruplaması Upload queue size=Gönderme sırası boyutu View=Göster Visit home page=Ana sayfamızı ziyaret et days=gün Active time=Aktif zaman Automatically add torrent links from the clipboard=Panodan torrent linklerini oku ve ekle Copy file path to clipboard=Dosya yolunu panoya kopyala Cumulative=Toplam Current=Oturum Files added=Eklenen dosyalar Filter pane=Filtreleme paneli Global statistics=Genel istatistikler Info pane=Bilgi paneli Statistics=İstatistikler Status bar=Durum çubuÄŸu %dd=%dd %dh=%dh %dm=%dm All torrents=Tüm torrentler Application options=Uygulama ayarları Ask for password=Parola doÄŸrulama Authentication required=Kimlik doÄŸrulama gerekli Average out transfer speeds to eliminate fluctuations=Hat kalitesini düşürmemesi için baÄŸlantıyı yönet Connect to %s=BaÄŸlan %s Connect to Transmission using proxy server=Transmission'a Proxy sunucusu kullanarak baÄŸlan Connect to Transmission=Transmission'a baÄŸlan Connection name=BaÄŸlantı adı Could not connect to tracker==İzleyiciye baÄŸlanılamıyor Data display=Veri görünümü Data refresh interval when minimized=Görev çubuÄŸuna küçültüldüğünde ki uygulama yenileme hızı Data refresh interval=Uygulama yenileme hızı Default download folder on remote host=Uzak sunucuda ki indirme klasörü Disconnect from Transmission=Transmission'dan baÄŸlantıyı kes Downloading torrent file=Torrent dosyası indiriliyor Enable µTP=µTP kullan Font size=Font boyutu Handle .torrent files by %s=.torrent dosyalarını %s ile çalıştır Handle magnet links by %s=Magnet baÄŸlantılarını %s ile çalıştır Invalid name specified=Geçersiz isim girildi Manage connections to Transmission=Transmission'a giden baÄŸlantılarını yönet Manage connections=BaÄŸlantıları yönet Network (WAN)=AÄŸ New connection to Transmission=Transmission'a yeni baÄŸlantı Pick random port on Transmission launch=Transmission baÅŸlangıcında rastgele port seç Please enter a password to connect to %s=%s baÄŸlanmak için lütfen bir parola girin Please specify how %s will connect to a remote host running Transmission daemon (service)=Uzak transmission daemon servisine, %s ile nasıl baÄŸlanacağını ayarlayın Prompt for download options when adding a new torrent=Yeni bir torrent eklendiÄŸinde indirme seçeneklerini göster RPC path=RPC yolu Save as=Farklı kaydet Seeding time=Kaynak zamanı Show advanced options=GeliÅŸmiÅŸ ayarları göster Show notifications in tray icon=Görev çubuÄŸu simgesinde bildirimleri göster System integration=Sistem ile bütünleÅŸme Torrent already exists in the list=Torrent zaten liste de mevcut Torrent not registered with this tracker==Torrent bu izleyiciye kayıtlı deÄŸil Unable to find path mapping.~Use the application's options to setup path mappings=Uzak yol bulunamadı.~Uzak yol ayarlamak için uygulama ayarlarını kullanın Update trackers for the existing torrent?=Mevcut torrentlerin izleyicileri güncelleÅŸtirilsin mi? You need to restart the application to apply changes=DeÄŸiÅŸikliklerin aktif olabilmesi için uygulamayı tekrar baÅŸlatın TransGUI/lang/transgui.ru0000644000000000000000000004777512261612465014366 0ustar rootrootTranslationLanguage=РуÑÑкий %ds=%dÑ %d x %s (have %d)=%d x %s (в наличии %d) %s (%d hashfails)=%s (%d ошибок Ñ…Ñша) %s (%s done)=%s (%s готово) '%s' has finished downloading=Загрузка '%s' завершена %s%s%d downloading, %d seeding%s%s, %s=%s%s%d загружаетÑÑ, %d раздаетÑÑ%s%s, %s &All=Ð’Ñе &Close=Закрыть &Help=Справка &Ignore=Игнорировать &No=Ðет &OK=&OK &Open=&Oткрыть &Retry=Повтор &Save=Сохранить &Unlock=&Unlock &Yes=Да Abort=Abort About=О программе Active=Ðктивные Add new torrent=Добавление нового торрента &Add torrent=Добавить торрент Added on=Добавлен Are you sure to remove torrent '%s' and all associated DATA?=Ð’Ñ‹ дейÑтвительно хотите удалить торрент~'%s'~и вÑе данные, ÑвÑзанные Ñ Ð½Ð¸Ð¼? Are you sure to remove torrent '%s'?=Ð’Ñ‹ дейÑтвительно хотите удалить торрент~'%s' ? Authentication=ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Cancel=Отмена Client=Клиент Close to tray=Закрывать в трей Comment=Комментарий Completed on=Завершен Completed=Завершены Confirmation=Подтверждение connected=подключен Connecting to daemon=Подключение к Transmission Connection error occurred=Ошибка Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Copy=Копировать Country=Страна Created on=Создан D: %s/s=Загр: %s/Ñ Destination folder=Папка Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Disconnected=Отключен Donate to support further development=Поддержите финанÑово дальнейшее развитие Donate via PayPal,WebMoney,Credit card=ПеречиÑлить ÑредÑтва через PayPal,WebMoney,Кредитку Done=Готово Don't download=Ðе загружать Down limit=Лимит загрузки Down speed=Загрузка Down=Вниз Download complete=Загрузка завершена Download speed=СкороÑть загрузки Downloaded=Загружено Downloading=ЗагружаетÑÑ Enable DHT=Разрешить DHT Enable Peer Exchange=Разрешить обмен пирами Enable port forwarding=Разрешить проброÑку порта Encryption=Шифрование Error=Ошибка ETA=ОÑталоÑÑŒ E&xit=Выход File name=Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Files=Файлы Finished=Завершен Flag images archive is needed to display country flags.~Download this archive now?=Ð”Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ„Ð»Ð°Ð³Ð¾Ð² Ñтран необходим архив изображений флагов.~Загрузить Ñтот архив ÑейчаÑ? Flags=Флаги General=Общие Geo IP database is needed to resolve country by IP address.~Download this database now?=Ð”Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñтраны по IP адреÑу необходима база данных Geo IP.~Загрузить Ñту базу данных ÑейчаÑ? Global bandwidth settings=Глобальные наÑтройки ÑкороÑти Global peer limit=Глобальный лимит пиров Hash=Ð¥Ñш Have=в наличии Hide=Скрыть High priority=Ð’Ñ‹Ñокий приоритет high=выÑокий Host=Узел in swarm=в рое Inactive=Ðеактивные Incoming port=ВходÑщий порт Information=Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ KB/s=КБ/Ñ Last active=ПоÑледнÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñть License=Ð›Ð¸Ñ†ÐµÐ½Ð·Ð¸Ñ Low priority=Ðизкий приоритет low=низкий Max peers=Лимит пиров Maximum download speed=Лимит ÑкороÑти загрузки Maximum upload speed=Лимит ÑкороÑти отдачи Minimize to tray=Сворачивать в трей Name=Ðазвание No to all=Ðет Ð´Ð»Ñ Ð²Ñех Normal priority=Ðормальный приоритет normal=нормальный Open containing folder=Открыть папку Ñодержащую объект Open=Открыть Password=Пароль Paths=Пути Peer limit=Лимит пиров Peers=Пиры Pieces=ЧаÑтей Port=Порт Priority=Приоритет Properties=СвойÑтва Proxy password=Пароль прокÑи Proxy port=Порт прокÑи Proxy server=ПрокÑи-Ñервер Proxy user name=Пользователь прокÑи Ratio=КоÑффициент Reconnect in %d seconds=Повтор Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñ‡ÐµÑ€ÐµÐ· %d Ñекунд Remaining=ОÑталоÑÑŒ Remote host=Удаленный узел "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="СопоÑтавление локальных путей удаленным.~~Ðапример:~/share=\\pch\share~/var/downloads/music=Z:\music" Remove torrent and Data=Удалить торрент и файлы Remove torrent=Удалить торрент Remove=Удалить Resolve country=ОпределÑть Ñтрану Resolve host name=ОпределÑть Ð¸Ð¼Ñ ÑƒÐ·Ð»Ð° seconds=Ñекунд Seed ratio=КоÑффициент отдачи Seeding=РаздаетÑÑ Seeds=Сиды Select a .torrent to open=Выберите торрент-файл Select all=Выбрать вÑе Select none=Отменить выбор Setup columns=ÐаÑтроить Ñтолбцы Share ratio=КоÑффициент Show country flag=Показывать флаг Ñтраны Show=Показать Size=Размер skip=пропуÑтить Start all torrents=ЗапуÑтить вÑе торренты Start torrent=ЗапуÑтить торрент Start=ЗапуÑтить Status=СоÑтоÑние Stop all torrents=ОÑтановить вÑе торренты Stop all=ОÑтановить вÑе Stop torrent=ОÑтановить торрент Stop=ОÑтановить Stopped=ОÑтановлено Test port=ТеÑÑ‚ порта T&ools=ИнÑтрументы Torrent contents=Содержимое торрента Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Проверка торрента может занÑть много времени.~Ð’Ñ‹ уверены, что хотите проверить торрент '%s'? &Torrent=Торрент Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Торренты (*.torrent)|*.torrent|Ð’Ñе файлы (*.*)|*.* Total size=Общий размер Tracker status=СоÑтоÑние трекера Tracker update on=Обновление трекера Tracker=Трекер Trackers=Трекеры Transfer=Передача Transmission options=Параметры Transmission Tray icon always visible=Ð’Ñегда отображать значок в трее Tray icon=СиÑтемный трей U: %s/s=Разд: %s/Ñ Unable to extract flag image=Ðевозможно извлечь изображение флага Unable to get files list=Ðевозможно получить ÑпиÑок файлов Unknown=ÐеизвеÑтно Up limit=Лимит отдачи Up speed=Отдача Up=Вверх Update complete=Обновление завершено Update GeoIP database=Обновить базу данных GeoIP Updating=ОбновлÑетÑÑ Update in=Обновление через Upload speed=СкороÑть отдачи Uploaded=Отдано User name=Пользователь Verify torrent=Проверить торрент &Verify=Проверить Verifying=Проверка Version %s=ВерÑÐ¸Ñ %s Waiting=Ожидание Warning=Предупреждение Wasted=Загружено Ð·Ñ€Ñ Working=Работает Yes to &All=Да Ð´Ð»Ñ Ð²Ñех Transmission%s at %s:%s=Transmission%s на %s:%s Torrent properties=СвойÑтва торрента /s=/Ñ Encryption disabled=Шифрование отключено Encryption enabled=Шифрование включено Encryption required=Ð’Ñегда требуетÑÑ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ðµ Incoming port is closed. Check your firewall settings=ВходÑщий порт закрыт. Проверьте наÑтройки вашего файрвола. Incoming port tested successfully=ВходÑщий порт проверен уÑпешно of=из b=б GB=ГБ KB=КБ MB=МБ TB=ТБ No host name specified=Ðе указан узел. No proxy server specified=Ðе указан прокÑи-Ñервер. Language=Язык No tracker=Ðет трекера %s downloaded=%s загружено %s of %s downloaded=%s из %s загружено %d torrents=%d торрентов Add .part extension to incomplete files=ДобавлÑть раÑширение .part к не полноÑтью загруженным файлам Add torrent link=Добавить ÑÑылку на торрент Are you sure to remove %d selected torrents and all their associated DATA?=Ð’Ñ‹ дейÑтвительно хотите удалить %d отмеченных торрентов и вÑе данные, ÑвÑзанные Ñ Ð½Ð¸Ð¼Ð¸? Are you sure to remove %d selected torrents?=Ð’Ñ‹ дейÑтвительно хотите удалить %d отмеченных торрентов? Bandwidth=СкороÑть Directory for incomplete files=Папка Ð´Ð»Ñ Ð½Ðµ полноÑтью загруженных файлов Download=Загрузка Enable blocklist=Разрешить ÑпиÑок блокировок ID=ID No link was specified=Ðе указана ÑÑылка No torrent location was specified=Ðе указано размещение торрента Path=Путь Reannounce (get more peers)=ÐнонÑировать (получить больше пиров) Set data location=Задать раÑположение Size to download=Размер Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ The block list has been updated successfully.~The list entries count: %d=СпиÑок блокировок обновлен уÑпешно.~КоличеÑтво Ñлементов в ÑпиÑке: %d The directory for incomplete files was not specified=Ðе указана папка Ð´Ð»Ñ Ð½Ðµ полноÑтью загруженных файлов The downloads directory was not specified=Ðе указана папка Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ файлов Torrents=Торренты Unable to execute "%s"=Ðевозможно выполнить "%s" Update blocklist=Обновить ÑпиÑок блокировок URL of a .torrent file or a magnet link=URL Ð´Ð»Ñ .torrent файла или magnet ÑÑылки Move torrent data from current location to new location=ПеремеÑтить данные торрента из текущего меÑта в новое New location for torrent data=Ðовое раÑположение данных торрента Torrent data location=РаÑположение данных торрента Columns setup=ÐаÑтройка Ñтолбцов Add torrent=Добвление торрента Torrent=Торрент Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Проверка торрентов может занÑть много времени.~Ð’Ñ‹ уверены, что хотите проверить %d торрентов? Unable to load OpenSSL library files: %s and %s=Ðевозможно загрузить файлы библиотеки OpenSSL: %s и %s Delete a .torrent file after a successful addition=УдалÑть .torrent файл поÑле уÑпешного Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Use SSL=ИÑпользовать SSL Add tracker=Добавить трекер Alternate bandwidth settings=Ðльтернативные наÑтройки ÑкороÑти Apply alternate bandwidth settings automatically=ÐвтоматичеÑки уÑтанавливать альтернативные ÑкороÑти Are you sure to delete connection '%s'?=Ð’Ñ‹ дейÑтвительно хотите удалить Ñоединение '%s'? Are you sure to remove tracker '%s'?=Ð’Ñ‹ дейÑтвительно хотите удалить трекер '%s'? Days=Дни Delete=Удалить Disk cache size=Размер диÑкового кеша Download speeds (KB/s)=СкороÑти загрузки (КБ/Ñ) Edit tracker=Редактировать трекер Enable Local Peer Discovery=Разрешить Local Peer Discovery Free disk space=Свободно на диÑке Free: %s=Своб: %s From=С minutes=минут Misc=Разное New connection=Ðовое подключение New=Создать No tracker URL was specified=Ðе указан URL трекера Proxy=ПрокÑи Remove tracker=Удалить трекер Rename=Переименовать Speed limit menu items=Элементы меню Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ ÑкороÑти Stop seeding when inactive for=Прекратить раздачу еÑли не активен The invalid time value was entered=Введено неправильное значение времени to=по Tracker announce URL=Ðnnounce URL трекера Tracker properties=СвойÑтва трекера Unlimited=Ðе ограничено Upload speeds (KB/s)=СкороÑти отдачи (КБ/Ñ) Use alternate bandwidth settings=ИÑпользовать альтернативные ÑкороÑти average=ÑреднÑÑ Browse=Обзор Enable µTP=Разрешить µTP Select a folder for download=Выберите папку Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Select torrent location=Выберите раÑположение данных торрента A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=ДоÑтупна Ð½Ð¾Ð²Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ %s.~Ваша Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ€ÑиÑ: %s~ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ€ÑиÑ: %s~~Ð’Ñ‹ хотите перейти на Ñтраницу загрузки? Advanced=Дополнительно Check for new version every=ПроверÑть наличие обновлений каждые Check for updates=Проверить наличие обновлений Consider active torrents as stalled when idle for=Считать активные торренты завиÑшими, еÑли нет активноÑти Do you wish to enable automatic checking for a new version of %s?=Ð’Ñ‹ хотите включить автоматичеÑкую проверку обновлений %s? Donate!=ПеречиÑлить ÑредÑтва! Download queue size=Размер очереди загрузки Error checking for new version=Ошибка проверки обновлений Folder grouping=Группировка по папкам Force start=ЗапуÑтить принудительно Home page=ДомашнÑÑ Ñтраница Modify trackers=Изменить трекеры Move bottom=Сдвинуть в конец Move down queue=Вниз в очереди Move down=Сдвинуть вниз Move top=Сдвинуть в начало Move up queue=Вверх в очереди Move up=Сдвинуть вверх No updates have been found.~You are running the latest version of %s=ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ найдены.~У Ð²Ð°Ñ ÑƒÑтановлена ÑÐ°Ð¼Ð°Ñ Ð½Ð¾Ð²Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ %s Queue position=ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð² очереди Queue=Очередь Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Торренты, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… не было активноÑти N минут, не учитываютÑÑ Ð² очереди загрузки или отдачи Tracker grouping=Группировка по трекерам Upload queue size=Размер очереди отдачи View=Вид Visit home page=ПоÑетить домашнюю Ñтраницу days=дней Active time=Ð’Ñ€ÐµÐ¼Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñти Automatically add torrent links from the clipboard=ÐвтоматичеÑки добавлÑть ÑÑылки на торренты из буфера обмена Copy file path to clipboard=Копировать путь к файлу в буфер обмена Cumulative=За вÑе Ð²Ñ€ÐµÐ¼Ñ Current=Эта ÑеÑÑÐ¸Ñ Files added=Добавлено файлов Filter pane=Панель фильтрации Global statistics=Ð“Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ ÑтатиÑтика Info pane=Панель информации Statistics=СтатиÑтика Status bar=Строка ÑоÑтоÑÐ½Ð¸Ñ %dd=%dд %dh=%dч %dm=%dм All torrents=Ð’Ñе торренты Application options=Параметры Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ask for password=Спрашивать пароль Authentication required=ТребуетÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ Average out transfer speeds to eliminate fluctuations=УÑреднÑть ÑкороÑти передачи данных Ð´Ð»Ñ ÑƒÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð»ÐµÐ±Ð°Ð½Ð¸Ð¹ Connect to %s=Подключение к %s Connect to Transmission using proxy server=ПодключатьÑÑ Ðº Transmission, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¿Ñ€Ð¾ÐºÑи-Ñервер Connect to Transmission=ПодключитьÑÑ Ðº Transmission Connection name=Ðазвание Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Could not connect to tracker==Ðевозможно подключитьÑÑ Ðº трекеру Data display=ÐžÑ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… Data refresh interval when minimized=Период Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… в Ñвернутом ÑоÑтоÑнии Data refresh interval=Период Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… Default download folder on remote host=Папка по умолчанию Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐµ на удаленном узле Disconnect from Transmission=ОтключитьÑÑ Ð¾Ñ‚ Transmission Downloading torrent file=Загрузка торрент файла Font size=Размер шрифта Handle .torrent files by %s=Обрабатывать .torrent файлы Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ %s Handle magnet links by %s=Обрабатывать magnet ÑÑылки Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ %s Invalid name specified=Указано неправильное Ð¸Ð¼Ñ Manage connections to Transmission=Управление подключениÑми к Transmission Manage connections=Управление подключениÑми Network (WAN)=Сеть (WAN) New connection to Transmission=Ðовое подключение к Transmission Pick random port on Transmission launch=ИÑпользовать Ñлучайный порт при запуÑке Transmission Please enter a password to connect to %s=ПожалуйÑта, введите пароль Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº %s Please specify how %s will connect to a remote host running Transmission daemon (service)=ПожалуйÑта, укажите как %s будет подключатьÑÑ Ðº удаленному узлу, где работает демон (Ñлужба) Transmission Prompt for download options when adding a new torrent=Запрашивать параметры загрузки при добавлении нового торрента RPC path=Путь RPC Save as=Сохранить как Seeding time=Ð’Ñ€ÐµÐ¼Ñ ÑÐ¸Ð´Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Show advanced options=Показать раÑширенные параметры Show notifications in tray icon=Показывать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸Ð· иконки в трее System integration=Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ñ ÑиÑтемой Torrent already exists in the list=Торрент уже еÑть в ÑпиÑке Torrent not registered with this tracker==Торрент не зарегиÑтрирован на Ñтом трекере Unable to find path mapping.~Use the application's options to setup path mappings=Ðевозможно найти ÑопоÑтавление путей.~ИÑпользуйте параметры Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð½Ð°Ñтройки ÑопоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿ÑƒÑ‚ÐµÐ¹ Update trackers for the existing torrent?=Обновить трекеры Ð´Ð»Ñ ÑущеÑтвующего торрента? You need to restart the application to apply changes=Ðеобходимо перезапуÑтить приложение Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ TransGUI/lang/transgui.es0000644000000000000000000003372212261612465014332 0ustar rootrootTranslationLanguage=Español "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Mapeado de rutas remotas a locales.~~Ejemplos:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (descargadas %d) %ds=%ds %s (%d hashfails)=%s (%d fallos de hash) %s (%s done)=%s (%s terminado) '%s' has finished downloading='%s' ha terminado de descargarse %s%s%d downloading, %d seeding%s%s, %s=%s%s%d sembrando, %d sirviendo%s%s, %s &All=&Todos &Close=&Cerrar &Help=&Ayuda &Ignore=&Ignorar &No=&No &OK=&Aceptar &Open=&Abrir &Retry=&Reintentar &Save=&Salvar &Unlock=&Desbloquear &Yes=&Sí /s=/s Abort=Cancelar About=Acerca de Active=Activo Add new torrent=Añadir nuevo torrent &Add torrent=Añadir torrent Added on=Añadido el Are you sure to remove torrent '%s' and all associated DATA?=¿Está seguro de querer eliminar el torrent '%s' y todos los DATOS asociados? Are you sure to remove torrent '%s'?=¿Está seguro de querer eliminar el torrent '%s'? Authentication=Authenticación b=b Cancel=Cancelar Client=Cliente Close to tray=Cerrar a la bandeja del sistema Comment=Comentario Completed on=Completado el Completed=Completado Confirmation=Confirmación connected=conectados Connecting to daemon=Connectando con el demonio Connection error occurred=Ocurrió un error al conectar Copy=Copiar Country=País Created on=Creado el D: %s/s=D: %s/s Destination folder=Carpeta destino Disconnected=Desconectado Donate to support further development=Dona para soportar el futuro desarrollo Donate via PayPal,WebMoney,Credit card=Donar via PayPal,WebMoney,Credit card Done=Completado Don't download=No descargar Down limit=Límite de descarga Down speed=Velocidad de descarga Down=Bajada Download complete=Descarga completada Download speed=Velocidad de descarga Downloaded=Descargado Downloading=Descargando Enable DHT=Activar DHT Enable Peer Exchange=Halilitar Intercambio de Pares Enable port forwarding=Habilitar Redirección de Puertos Encryption disabled=Cifrado deshabilitado Encryption enabled=Cifrado habilitado Encryption required=Cifrado requerido Encryption=Cifrado Error=Error ETA=Tiempo estimado E&xit=Salir File name=Nombre de archivo Files=Archivos Finished=Finalizado Flag images archive is needed to display country flags.~Download this archive now?=Es necesario el archivo de imágenes banderas para mostrar banderas de países.~¿Descargar este archivo ahora? Flags=Banderas GB=GB General=General Geo IP database is needed to resolve country by IP address.~Download this database now?=Se necesita la base de datos Geo IP para resolver el país a partir de la dirección IP.~¿Descargar esta base de datos ahora? Global bandwidth settings=Configuración global de ancho de banda Global peer limit=Límite global de pares Hash=Hash Have=Tengo Hide=Ocultar High priority=Alta prioridad high=alto Host=Host in swarm=en el enjambre Inactive=Inactivo Incoming port is closed. Check your firewall settings=El puerto entrante está cerrado. Comprueba la configuración de tu firewall Incoming port tested successfully=Probado el puerto entrante con éxito Incoming port=Puerto entrante Information=Informacion KB/s=KB/s KB=KB Language=Idioma Last active=Activo por última vez License=Licencia Low priority=Baja prioridad low=bajo Max peers=Pares máximos Maximum download speed=Velocidad máxima de descarga Maximum upload speed=Velocidad máxima de subida MB=MB Minimize to tray=Minimizar a la bandeja del sistema Name=Nombre No host name specified=No se especificó nombre del host No proxy server specified=No se especificó servidor proxy No to all=No a todo Normal priority=Prioridad normal normal=normal of=de Open containing folder=Abrir carpeta contenedora Open=Abierto Password=Contraseña Paths=Rutas Peer limit=Límite de pares Peers=Pares Pieces=Partes Port=Puerto Priority=Prioridad Properties=Propiedades Proxy password=Contraseña del proxy Proxy port=Puerto del proxy Proxy server=Servidor proxy Proxy user name=Usuario del proxy Ratio=Ratio Reconnect in %d seconds=Reconectar en %d segundos Remaining=Renombrado Remote host=Host remoto Remove torrent and Data=Eliminar torrent y datos Remove torrent=Eliminar torrent Remove=Eliminar Resolve country=Resolver país Resolve host name=Resolver nombre del host seconds=segundos Seed ratio=Ratio de siembra Seeding=Sembrando Seeds=Semillas Select a .torrent to open=Selecciona un .torrent a abrir Select all=Seleccionar todo Select none=Seleccionar nada Setup columns=Configurar columnas Share ratio=Ratio de compartición Show country flag=Mostrar bandera de país Show=Mostrar Size=Tamaño skip=saltar Start all torrents=Iniciar todos los torrents Start torrent=Iniciar torrent Start=Iniciar Status=Estado Stop all torrents=Detener todos los torrents Stop all=Detener todo Stop torrent=Detener torrent Stop=Detener Stopped=Detenido TB=TB Test port=Probar puerto T&ools=Herramientas Torrent contents=Contenido del torrent Torrent properties=Propiedades del torrent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Verificar el torrent puede tomar bastante tiempo..~¿Está seguro de que querer iniciar la verificación del torrent '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Todos los archivos (*.*)|*.* Total size=Tamaño total Tracker status=Estado del tracker Tracker update on=Tracker actualizado Tracker=Tracker Trackers=Trackers Transfer=Transferencia Transmission options=Opciones de Transmission Transmission%s at %s:%s=Transmission%s en %s:%s Tray icon always visible=Icono de la bandeja siempre visible Tray icon=Icono de la bandeja del sistema U: %s/s=U: %s/s Unable to extract flag image=No se pudo extraer imagen de bandera Unable to get files list=No se pudo obtener lista de archivos Unknown=Desconocido Up limit=Límite de subida Up speed=Velocidad de subida Up=Subida Update complete=Actualización completa Update GeoIP database=Actualizar base de datos GeoIP Update in=Actualización en Updating=Actualizando Upload speed=Velocidad de subida Uploaded=Subido User name=Nombre de usuario Verify torrent=Verificar torrent &Verify=Verificar Verifying=Verificando Version %s=Versión %s Waiting=Esperando Warning=Aviso Wasted=Desperdiciado Working=Trabajando Yes to &All=Sí a &Todo No tracker=Sin tracker %s downloaded=%s descargado %s of %s downloaded=%s de %s descargado %d torrents=%d torrents Add .part extension to incomplete files=Añadir extensión .part a los archivos incompletos Add torrent link=Añadir enlace a torrent Are you sure to remove %d selected torrents and all their associated DATA?=¿Está seguro de querer eliminar %d torrents seleccionados y sus DATOS asociados? Are you sure to remove %d selected torrents?=¿Está seguro de querer eliminar %d torrents seleccionados? Bandwidth=Ancho de banda Directory for incomplete files=Directorio para los archivos incompletos Download=Descarga Enable blocklist=Activar lista negra ID=Identificador Move torrent data from current location to new location=Mover los datos del torrent de la ruta actual a la nueva New location for torrent data=Nueva ruta para los datos del torrent No link was specified=No se especificó ningún enlace No torrent location was specified=No se especificó ninguna ruta de torrent Path=Ruta Reannounce (get more peers)=Reanunciar (conseguir más pares) Set data location=Definir ruta para los datos Size to download=Tamaño a descargar The block list has been updated successfully.~The list entries count: %d=La lista negra se ha actualizado correctamente.~Entradas en la lista: %d The directory for incomplete files was not specified=No se especificó directorio para los archivos incompletos The downloads directory was not specified=No se especificó directorio para las descargas Torrent data location=Ruta de los datos del torrent Torrents=Torrents Unable to execute "%s"=No se pudo ejecutar "%s" Update blocklist=Actualizar lista negra URL of a .torrent file or a magnet link=URL de archivo .torrent o enlace magnético Columns setup=Configurar columnas Add torrent=Añadir torrent Delete a .torrent file after a successful addition=Borrar fichero .torrent tras añadirlo exitosamente Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Verificar los torrents puede llevar bastante tiempo.~Estás seguro de que quieres verificar %d torrents? Unable to load OpenSSL library files: %s and %s=Incapaz de cargar los archivos de las bibliotecas OpenSSL: %s y %s Use SSL=Usar SSL Add tracker=Añadir tracker Alternate bandwidth settings=Configuración de ancho de banda alternativa Apply alternate bandwidth settings automatically=Aplicar configuración de ancho de banda alternativa automáticamente Are you sure to delete connection '%s'?=Estás seguro de querer eliminar la conexión '%s'? Are you sure to remove tracker '%s'?=Estás seguro de querer eliminar el tracker '%s'? Days=Días Delete=Borrar Disk cache size=Tamaño de caché de disco Download speeds (KB/s)=Velocidad de descarga (KB/s) Edit tracker=Editar tracker Enable Local Peer Discovery=Activar descubrimiento de pares locales Free disk space=Espacio libre en disco Free: %s=Libre: %s From=Desde minutes=minutos Misc=Misc New connection=Nueva conexión New=Nuevo No tracker URL was specified=No se especificó URL de tracker Proxy=Proxy Remove tracker=Borrar tracker Rename=Renombrar Speed limit menu items=Elementos del menú de límite de velocidad Stop seeding when inactive for=Dejar de compartir cuando esté inactivo The invalid time value was entered=El valor de tiempo introducido no es válido to=a Tracker announce URL=URL de anuncio del tracker Tracker properties=Propiedades del Tracker Unlimited=Ilimitado Upload speeds (KB/s)=Velocidad de subida (KB/s) Use alternate bandwidth settings=Usar configuración de ancho de banda alternativa average=average Browse=Browse Enable µTP=Enable µTP Select a folder for download=Select a folder for download Select torrent location=Select torrent location A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.el0000644000000000000000000005106512261612465014323 0ustar rootrootTranslationLanguage=Ελληνικά "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="ΧαÏτογÏάφηση Î¼Î¿Î½Î¿Ï€Î±Ï„Î¹Î¿Ï Î±Ï€Î¿Î¼Î±ÎºÏυσμένο σε τοπικό.~~ΠαÏαδείγματα:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (έχω %d) %ds=%ds %s (%d hashfails)=%s (%d hashfails) %s (%s done)=%s (%s έτοιμο) '%s' has finished downloading='%s' ολοκληÏώθηκε η λήψη %s%s%d downloading, %d seeding%s%s, %s=%s%s%d λήψη, %d αποστολή%s%s, %s &All=&Όλα &Close=&Κλείσιμο &Help=&Βοήθεια &Ignore=&Αγνόησε &No=&Όχι &OK=&OK &Open=&Άνοιγμα &Retry=&Επανάληψη &Save=&Αποθήκευση &Unlock=&Ξεκλείδωμα &Yes=&Îαι /s=/s Abort=Ματαίωση About=ΠεÏί Active=ΕνεÏγά Add new torrent=ΠÏοσθήκη νέου torrent &Add torrent=ΠÏοσθήκη torrent Added on=ΠÏοστέθηκε την Are you sure to remove torrent '%s' and all associated DATA?=Είστε σίγουÏοι για τη διαγÏαφή του torrent '%s' και όλων των ΑΡΧΕΙΩÎ; Are you sure to remove torrent '%s'?=Είστε σίγουÏοι για τη διαγÏαφή του torrent '%s'; Authentication=Πιστοποίηση b=b Cancel=ΑκÏÏωση Client=Πελάτης Close to tray=Κλείσιμο στη γÏαμμή εÏγασιών Comment=Σχόλια Completed on=ΟλοκληÏώθηκε την Completed=ΟλοκληÏωμένα Confirmation=ΕπικÏÏωση connected=συνδεδεμένοι Connecting to daemon=Συνδέεται στο δαίμονα Connection error occurred=Σφάλμα σÏνδεσης Copy=ΑντιγÏαφή Country=ΧώÏα Created on=ΔημιουÏγήθηκε την D: %s/s=Λ: %s/s Destination folder=Φάκελος Ï€ÏοοÏÎ¹ÏƒÎ¼Î¿Ï Disconnected=Αποσυνδέθηκε Donate to support further development=ΥποστηÏίξτε την πεÏαιτέÏω ανάπτυξη μέσω ΔΩΡΕΑΣ Donate via PayPal,WebMoney,Credit card=ΔωÏίστε μέσω PayPal,WebMoney,Πιστωτικής ΚάÏτας Done=Έχω Don't download=Μην κατεβάσεις Down limit=ÎŒÏιο λήψης Down speed=ΤαχÏτητα λήψης Down=Κάτω Download complete=ΟλοκλήÏωση λήψης Download speed=ΤαχÏτητα λήψης Downloaded=Λήφθηκαν Downloading=Λαμβάνονται Enable DHT=ΕνεÏγοποίηση DHT Enable Peer Exchange=ΕνεÏγοποίηση Ανταλλαγής Κόμβων Enable port forwarding=ΕνεÏγοποίηση Ï€Ïοώθησης θυÏών Encryption disabled=ΚÏυπτογÏάφηση απενεÏγοποιημένη Encryption enabled=ΚÏυπτογÏάφηση ενεÏγοποιημένη Encryption required=Απαιτείται ΚÏυπτογÏάφηση Encryption=ΚÏυπτογÏάφηση Error=Σφάλμα ETA=Απομένουν E&xit=Έξοδος File name=Όνομα αÏχείου Files=ΑÏχεία Finished=ΟλοκληÏωμένα Flag images archive is needed to display country flags.~Download this archive now?=ΑπαιτοÏνται αÏχεία εικόνας για Ï€Ïοβολή της σημαίας κάθε χώÏας.~Îα ξεκινήσει η λήψη των αÏχείων εικόνας τώÏα; Flags=Σημαίες GB=GB General=Γενικά Geo IP database is needed to resolve country by IP address.~Download this database now?=Απαιτείται η βάση δεδομένων Geo IP για την αναγνώÏιση της χώÏας από την διεÏθυνση IP.~Îα ξεκινήσει η λήψη της βάσης δεδομένων τώÏα; Global bandwidth settings=Γενικές Ïυθμίσεις εÏÏους ζώνης Global peer limit=Γενικό ÏŒÏιο κόμβων Hash=Hash Have=Έχει Hide=ΑπόκÏυψη High priority=Υψηλή Ï€ÏοτεÏαιότητα high=υψηλή Host=ΧÏήστης in swarm=στο νέφος Inactive=ΑνενεÏγά Incoming port is closed. Check your firewall settings=Η θÏÏα εισεÏχομένων συνδέσεων είναι κλειστή.Ελέγξτε τις Ïυθμίσεις του τείχους Ï€Ïοστασίας Incoming port tested successfully=Η θÏÏα εισεÏχομένων συνδέσεων δοκιμάστηκε με επιτυχία Incoming port=ΘÏÏα εισεÏχομένων συνδέσεων Information=ΠληÏοφοÏίες KB/s=KB/s KB=KB Language=Γλώσσα Last active=Τελευταία δÏαστηÏιότητα License=Άδεια Low priority=Χαμηλή Ï€ÏοτεÏαιότητα low=χαμηλή Max peers=Μέγιστος αÏιθμός κόμβων Maximum download speed=Μέγιστη ταχÏτητα λήψης Maximum upload speed=Μέγιστη ταχÏτητα αποστολής MB=MB Minimize to tray=Ελαχιστοποίηση στη γÏαμμή εÏγασιών Name=Όνομα No host name specified=Δεν έχει οÏιστεί όνομα εξυπηÏετητή No proxy server specified=Δεν έχει οÏιστεί proxy server No to all=Όχι σε όλα Normal priority=Κανονική Ï€ÏοτεÏαιότητα normal=κανονική of=από Open containing folder=Άνοιγμα φακέλου Open=Άνοιγμα Password=Συνθηματικό Paths=Μονοπάτια Peer limit=ÎŒÏιο κόμβων Peers=ΧÏήστες Pieces=Τμήματα Port=ΘÏÏα Priority=ΠÏοτεÏαιότητα Properties=Ιδιότητες Proxy password=Συνθηματικό proxy Proxy port=ΘÏÏα Proxy Proxy server=Proxy server Proxy user name=Όνομα χÏήστη Proxy Ratio=Αναλογία Reconnect in %d seconds=ΕπανασÏνδεση σε %d δευτεÏόλεπτα Remaining=Υπολείπονται Remote host=Δ/νση υπολογιστή Remove torrent and Data=ΔιαγÏαφή του torrent και των ΑÏχείων Remove torrent=ΔιαγÏαφή μόνο του torrent Remove=ΔιαγÏαφή Resolve country=ΑναγνώÏιση χώÏας Resolve host name=ΑναγνώÏιση ονόματος απομακÏυσμένου υπολογιστή seconds=δευτεÏόλεπτα Seed ratio=Αναλογία αποστολής Seeding=Αποστολή Seeds=Αποστέλλουν Select a .torrent to open=Επέλεξε ένα αÏχείου Ï„Ïπου .torrent για άνοιγμα Select all=Επιλογή όλων Select none=Αποεπιλογή όλων Setup columns=Επιλογή στηλών Share ratio=Αναλογία διαμοιÏÎ±ÏƒÎ¼Î¿Ï Show country flag=ΠαÏουσίασε τη σημαία της χώÏας Show=Άνοιγμα Size=Μέγεθος skip=Ï€ÏοσπέÏασε Start all torrents=ΈναÏξη όλων των torrents Start torrent=ΈναÏξη του torrent Start=ΈναÏξη Status=Κατάσταση Stop all torrents=ΠαÏση όλων των torrents Stop all=ΠαÏση όλων Stop torrent=ΠαÏση του torrent Stop=ΠαÏση Stopped=Σταματημένα TB=TB Test port=Δοκιμή θÏÏας T&ools=ΕÏγαλεία Torrent contents=ΠεÏιεχόμενα torrent Torrent properties=Ιδιότητες torrent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Η επαλήθευση του torrent μποÏεί να διαÏκέσει αÏκετό χÏόνο.~Είστε σίγουÏοι ότι θέλετε να ξεκινήσετε την επαλήθευση του torrent '%s'; &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Όλα τα αÏχεία (*.*)|*.* Total size=Συνολικό μέγεθος Tracker status=Κατάσταση ιχνηλάτη Tracker update on=ΕνημέÏωση ιχνηλάτη σε Tracker=Ιχνηλάτης Trackers=Ιχνηλάτες Transfer=ΜεταφόÏτωση Transmission options=Επιλογές του transmission Transmission%s at %s:%s=Transmission%s στο %s:%s Tray icon always visible=Εικονίδιο γÏαμμής εÏγασιών πάντα οÏατό Tray icon=Εικονίδιο γÏαμμής εÏγασιών U: %s/s=Α: %s/s Unable to extract flag image=Αδυναμία ανεÏÏεσης εικονιδίου σημαίας Unable to get files list=Αδυναμία λήψης της λίστας αÏχείων Unknown=Άγνωστο Up limit=ÎŒÏιο αποστολής Up speed=ΤαχÏτητα αποστολής Up=Πάνω Update complete=ΟλοκλήÏωση ενημέÏωσης Update GeoIP database=ΕνημέÏωση βάσης δεδομένων GeoIP Update in=ΕνημέÏωση σε Updating=ΕνημέÏωση Upload speed=ΤαχÏτητα αποστολής Uploaded=Απεστάλησαν User name=Όνομα χÏήστη Verify torrent=Επαλήθευση torrent &Verify=Επαλήθευση Verifying=Υπό επαλήθευση Version %s=Έκδοση %s Waiting=Αναμονή Warning=ΠÏοειδοποίηση Wasted=Χάθηκαν Working=ΛειτουÏγεί Yes to &All=Îαι σε &Όλα No tracker=Κανένας ιχνηλάτης %s downloaded=%s λήφθηκαν %s of %s downloaded=%s από %s λήφθηκαν %d torrents=%d torrents Add .part extension to incomplete files=ΠÏοσθήκη κατάληξης .part σε μη ολοκληÏωμένα αÏχεία Add torrent link=ΠÏοσθήκη συνδέσμου torrent Are you sure to remove %d selected torrents and all their associated DATA?=Είστε σίγουÏοι για τη διαγÏαφή %d επιλεγμένων torrents και όλων των σχετικών ΑΡΧΕΙΩÎ; Are you sure to remove %d selected torrents?=Είστε σίγουÏοι για τη διαγÏαφή %d επιλεγμένων torrents; Bandwidth=ΕÏÏος ζώνης Directory for incomplete files=Φάκελος για μη ολοκληÏωμένα αÏχεία Download=Λήψη Enable blocklist=ΕνεÏγοποίηση της λίστας Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï ID=ΑΑ Move torrent data from current location to new location=ΜεταφοÏά των αÏχείων από την Ï„Ïέχουσα θέση σε νέα θέση New location for torrent data=Îέα θέση για τα αÏχεία No link was specified=Δέν οÏίστηκε σÏνδεση No torrent location was specified=Δεν οÏίστηκε η θέση των torrent Path=Μονοπάτι Reannounce (get more peers)=Επανεκφώνηση (φέÏε επιπλέον χÏήστες) Set data location=ÎŒÏισε θέση αÏχείων Size to download=Μέγεθος για λήψη The block list has been updated successfully.~The list entries count: %d=Η λίστα Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï ÎµÎ½Î·Î¼ÎµÏώθηκε επιτυχώς.~Η λίστα πεÏιέχει %d καταχωÏήσεις The directory for incomplete files was not specified=Δεν οÏίστηκε ο φάκελος για μη ολοκληÏωμένα αÏχεία The downloads directory was not specified=Δεν οÏίστηκε ο φάκελος για τις λήψεις Torrent data location=Θέση αÏχείων Torrents=Torrents Unable to execute "%s"=Αδυναμία εκτέλεσης "%s" Update blocklist=ΕνημέÏωση λίστας Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï URL of a .torrent file or a magnet link=URL ενός αÏχείου .torrent ή ένα magnet link Columns setup=Επιλογή στηλών Add torrent=ΠÏοσθήκη torrent Delete a .torrent file after a successful addition=ΔιαγÏαφή αÏχείου .torrent μετά από επιτυχή Ï€Ïοσθήκη Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Η επαλήθευση των torrent μποÏεί να διαÏκέσει αÏκετό χÏόνο.~Είστε σίγουÏοι ότι θέλετε να ξεκινήσετε την επαλήθευση των %d torrents? Unable to load OpenSSL library files: %s and %s=Αδυναμία φόÏτωσης των αÏχείων βιβλιοθήκης του OpenSSL: %s και %s Use SSL=ΧÏήση SSL Add tracker=ΠÏοσθήκη ιχνηλάτη Alternate bandwidth settings=Εναλλακτικές Ïυθμίσεις εÏÏους ζώνης Apply alternate bandwidth settings automatically=Αυτόματη εφαÏμογή εναλλακτικών Ïυθμίσεων εÏÏους ζώνης Are you sure to delete connection '%s'?=Είστε σίγουÏοι για τη διαγÏαφή της σÏνδεσης '%s'? Are you sure to remove tracker '%s'?=Είστε σίγουÏοι για τη διαγÏαφή του ιχνηλάτη '%s'? Days=ΗμέÏες Delete=ΔιαγÏαφή Disk cache size=Μέγεθος cache του δίσκου Download speeds (KB/s)=ΤαχÏτητες λήψης (KB/s) Edit tracker=ΕπεξεÏγασία ιχνηλάτη Enable Local Peer Discovery=ΕνεÏγοποίηση ανεÏÏεσης τοπικών κόμβων Free disk space=ΕλεÏθεÏος χώÏος στο δίσκο Free: %s=Κενά: %s From=από minutes=λεπτά Misc=ΔιάφοÏα New connection=Îέα σÏνδεση New=Îέα No tracker URL was specified=Δεν καθοÏίστηκε URL ιχνηλάτη Proxy=Proxy Remove tracker=ΔιαγÏαφή ιχνηλάτη Rename=Μετονομασία Speed limit menu items=Στοιχεία Î¼ÎµÎ½Î¿Ï Î¿Ïίων ταχÏτητας Stop seeding when inactive for=Διακοπή αποστολής ανενεÏÎ³Î¿Ï The invalid time value was entered=Εισήχθη μη έγκυÏη τιμή ÏŽÏας to=έως Tracker announce URL=URL εκφώνησης ιχνηλάτη Tracker properties=Ιδιότητες ιχνηλάτη Unlimited=ΧωÏίς ÏŒÏιο Upload speeds (KB/s)=ΤαχÏτητες αποστολής (KB/s) Use alternate bandwidth settings=ΧÏήση εναλλακτικών Ïυθμίσεων εÏÏους ζώνης average=μέσος ÏŒÏος Browse=Αναζήτηση Enable µTP=ΕνεÏγοποίηση µTP Select a folder for download=Επιλέξτε ένα φάκελο για λήψη Select torrent location=Επιλέξτε τη θέση του αÏχείου torrent A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Μια νέα έκδοση του %s είναι διαθέσιμη.~Εγκατεστημένη έκδοση : %s~Îέα έκδοση: %s~~Îα ανοίξει η ιστοσελίδα λήψης της νέας έκδοσης; Advanced=Για Ï€ÏοχωÏημένους Check for new version every=Έλεγχος για νέα έκδοση κάθε Check for updates=Έλεγχος για ενημεÏώσεις Consider active torrents as stalled when idle for=ΘεώÏησε τα ενεÏγά torrent ως στάσιμα εάν είναι ανενεÏγά για Do you wish to enable automatic checking for a new version of %s?=ΕνεÏγοποίηση αυτόματου ελέγχου για νέες εκδόσεις of %s; Donate!=ΔωÏεές! Download queue size=Μέγεθος ουÏάς λήψεων Error checking for new version=Σφάλμα ελέγχου νέας έκδοσης Folder grouping=Ομαδοποίηση καταλόγων Force start=Εξαναγκασμός έναÏξης Home page=ΑÏχική σελίδα Modify trackers=ΤÏοποποίηση ιχνηλατών Move bottom=Μετακίνηση στο τέλος Move down queue=Μετακίνηση κάτω στην ουÏά Move down=Μετακίνηση κάτω Move top=Μετακίνηση στην αÏχή Move up queue=Μετακίνηση επάνω στην ουÏά Move up=Μετακίνηση επάνω No updates have been found.~You are running the latest version of %s=Δεν βÏέθηκαν ενημεÏώσεις.~ΧÏησιμοποιείτε τη νεώτεÏη έκδοση του %s Queue position=Θέση ΟυÏάς Queue=ΟυÏά Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Τα ανενεÏγά torrents για N λεπτά δεν υπολογίζονται στην ΟυÏά Λήψεων ή την ΟυÏά Αποστολών Tracker grouping=Ομαδοποίηση ιχνηλατών Upload queue size=Μέγεθος ουÏάς αποστολών View=ΠÏοβολή Visit home page=ΑÏχική Σελίδα days=ημέÏες Active time=ΕνεÏγός χÏόνος Automatically add torrent links from the clipboard=Αυτόματη Ï€Ïοσθήκη συνδέσμων torrent από το Ï€ÏόχειÏο Copy file path to clipboard=ΑντιγÏαφή θέσης αÏχείου στο Ï€ÏόχειÏο Cumulative=ΑθÏοιστικά Current=ΤÏέχοντα Files added=ΑÏχεία που Ï€Ïοστέθηκαν Filter pane=ΠαÏάθυÏο φίλτÏων Global statistics=Συνολικά στατιστικά Info pane=ΠαÏάθυÏο πληÏοφοÏιών Statistics=Στατιστικά Status bar=ΜπάÏα κατάστασης %dd=%dd %dh=%dh %dm=%dm All torrents=Όλα τα torrents Application options=Επιλογές εφαÏμογής Ask for password=Îα ζητείται το συνθηματικό Authentication required=Απαιτείται πιστοποίηση Average out transfer speeds to eliminate fluctuations=ΧÏήση μέσου ÏŒÏου ταχÏτητας μεταφοÏάς για αποφυγή διακυμάνσεων Connect to %s=ΣÏνδεση σε %s Connect to Transmission using proxy server=ΣÏνδεση στον Transmission μέσω proxy server Connect to Transmission=ΣÏνδεση στο Transmission Connection name=Όνομα σÏνδεσης Could not connect to tracker==ΑδÏνατη η σÏνδεση στον ιχνηλάτη Data display=ΠÏοβολή δεδομένων Data refresh interval when minimized=Ανανέωση δεδομένων υπό ελαχιστοποίηση Data refresh interval=Διάστημα ανανέωσης δεδομένων Default download folder on remote host=ΠÏοεπιλεγμένος φάκελος λήψεων στον απομακÏυσμένο υπολογιστή Disconnect from Transmission=ΑποσÏνδεση από το Transmission Downloading torrent file=Λήψη αÏχείου torrent Font size=Μέγεθος γÏαμματοσειÏάς Handle .torrent files by %s=ΔιαχείÏιση των αÏχείων .torrent από το %s Handle magnet links by %s=ΔιαχείÏιση magnet link από το %s Invalid name specified=Μη έγκυÏο όνομα Manage connections to Transmission=ΔιαχείÏιση συνδέσεων στο Transmission Manage connections=ΔιαχείÏιση συνδέσεων Network (WAN)=Δίκτυο (WAN) New connection to Transmission=Îέα σÏνδεση στο Transmission Pick random port on Transmission launch=Επιλογή τυχαίας θÏÏας κατά την εκκίνηση Please enter a password to connect to %s=ΠαÏακαλώ εισάγετε το συνθηματικό για να συνδεθείτε στο %s Please specify how %s will connect to a remote host running Transmission daemon (service)=ΠαÏακαλώ καθοÏίστε πως το %s θα συνδέεται σε έναν απομακÏυσμένο υπολογιστή όπου εκτελείται ο δαίμονας του Transmission (υπηÏεσία) Prompt for download options when adding a new torrent=ΠÏοτÏοπή για επιλογές λήψης κατά την Ï€Ïοσθήκη νέου torrent RPC path=Μονοπάτι RPC Save as=Αποθήκευση ως Seeding time=ΧÏόνος αποστολής Show advanced options=ΠÏοβολή Ï€ÏοχωÏημένων επιλογών Show notifications in tray icon=ΠÏοβολή ειδοποιήσεων στο εικονίδιο γÏαμμής εÏγασιών System integration=Ενσωμάτωση στο λειτουÏγικό σÏστημα Torrent already exists in the list=Το torrent υπάÏχει ήδη στη λίστα Torrent not registered with this tracker==Το torrent δεν είναι εγγεγÏαμμένο στον ιχνηλάτη Unable to find path mapping.~Use the application's options to setup path mappings=ΑδÏνατη η εÏÏεση του μονοπατιοÏ.~ΧÏησιμοποιήστε τις επιλογές της εφαÏμογής για καθοÏισμό των μονοπατιών Update trackers for the existing torrent?=ΕνημέÏωση ιχνηλατών για το υπάÏχον torrent? You need to restart the application to apply changes=Απαιτείται επανεκκίνηση για εφαÏμογή των αλλαγών TransGUI/lang/transgui.hu0000644000000000000000000003441612261612465014340 0ustar rootrootTranslationLanguage=Magyar "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Távoli és helyi elérési utak kapcsolata.~~Példák:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (megvan %d) %ds=%ds %s (%d hashfails)=%s (%d hibás) %s (%s done)=%s (%s kész) '%s' has finished downloading='%s' letöltése befejezve %s%s%d downloading, %d seeding%s%s, %s=%s%s%d letöltés, %d megosztás%s%s, %s &All=&Minden &Close=&Bezár &Help=&Súgó &Ignore=&Elutasít &No=&Nem &OK=&OK &Open=&Megnyitás &Retry=&Újra &Save=&Mentés &Unlock=&Kiold &Yes=&Igen /s=/s Abort=Leállít About=Névjegy Active=Aktív Add new torrent=Új torrent hozzáadás &Add torrent=Torrent hozzáadás Added on=Hozzáadva Are you sure to remove torrent '%s' and all associated DATA?=Biztos,hogy törlöd a '%s' torrentet és minden hozzá tartozó adatot? Are you sure to remove torrent '%s'?=Biztos,hogy törlöd a '%s' torrentet? Authentication=Azonosítás b=b Cancel=Mégsem Client=Kliens Close to tray=Bezárás tálcára Comment=Megjegyzés Completed on=Befejezve ekkor Completed=Befejezve Confirmation=MegerÅ‘sítés connected=csatlakozva Connecting to daemon=Csatlakozás a démonhoz folyamatban Connection error occurred=Csatlakozási hiba Copy=Másolás Country=Ország Created on=Létrehozva ekkor D: %s/s=D: %s/s Destination folder=Célmappa Disconnected=Kapcsolat bontva Donate to support further development=Adományoddal támogasd a további fejlesztést! Donate via PayPal,WebMoney,Credit card=Támogatás PayPal,WebMoney,Credit card keresztül Done=Kész Don't download=Ne töltsd le Down limit=Korlát Le Down speed=Sebesség Le Down=Le Download complete=Letöltés kész Download speed=Letöltési sebesség Downloaded=Letöltve Downloading=Letöltés alatt Enable DHT=DHT engedélyezés Enable Peer Exchange=Partner kapcsolat engedélyezése Enable port forwarding=Port forwarding engedélyezése Encryption disabled=Titkosítás letiltva Encryption enabled=Titkosítás engedélyezve Encryption required=Titkosítás szükséges Encryption=Titkosítás Error=Hiba ETA=Hátravan E&xit=Kilépés File name=Fájl név Files=Fájlok Finished=Befejezve Flag images archive is needed to display country flags.~Download this archive now?=Az országzászlók kijelzéséhez szükség van a Zászlókép csomagra.~Letöltsem ezt a csomagot most? Flags=JelzÅ‘k GB=GB General=Ãltalános Geo IP database is needed to resolve country by IP address.~Download this database now?=Geo IP adatbázis kell az ország megfejtéséhez az IP cím alapján.~Letöltsem ezt a csomagot most? Global bandwidth settings=Ãltalános sávszélesség beállítások Global peer limit=Globális partner korlát Hash=Hash Have=Megvan Hide=Elrejt High priority=Magas prioritás high=magas Host=Host in swarm=a rajban Inactive=Inaktív Incoming port is closed. Check your firewall settings=BejövÅ‘ port zárva. EllenÅ‘rízd a tűzfal beállításokat Incoming port tested successfully=BejövÅ‘ port próbája sikerült Incoming port=BejövÅ‘ port Information=Információ KB/s=KB/s KB=KB Language=Nyelv Last active=Utolsó aktív License=Licensz Low priority=Alacsony prioritás low=alacsony Max peers=Max peer szám Maximum download speed=Maximum letöltési sebesség Maximum upload speed=Maximum feltöltési sebesség MB=MB Minimize to tray=Minimalizálás tálcára Name=Név No host name specified=Host név nem adott No proxy server specified=Proxy server nincs beállítva No to all=Mindenre nem Normal priority=Normál prioritás normal=normál of=/ Open containing folder=tartalmazó mappa megnyitása Open=Megnyitás Password=Jelszó Paths=Elérési utak Peer limit=Partnerszám korlát Peers=Partnerek Pieces=Darabok Port=Port Priority=Prioritás Properties=Tulajdonságok Proxy password=Proxy jelszó Proxy port=Proxy port Proxy server=Proxy szerver Proxy user name=Proxy felhasználó név Ratio=Arány Reconnect in %d seconds=Újracsatlakozás %d mp-en belül Remaining=HátralévÅ‘ Remote host=Távoli host Remove torrent and Data=Torrent és adat eltávolítása Remove torrent=Torrent eltávolítása Remove=Eltávolítás Resolve country=Országnév feloldása Resolve host name=Host név feloldása seconds=mp Seed ratio=Megosztás eddig az arányig Seeding=Megosztva Seeds=Megosztók Select a .torrent to open=Válassz egy .torrent-et megnyitásra Select all=Mind kijelÅ‘lése Select none=Kijelölés megszüntetése Setup columns=Oszlopok beállítása Share ratio=Megosztási arány Show country flag=Mutasd az országzászlót Show=Megjelenít Size=Méret skip=Kihagy Start all torrents=Minden torrent indítása Start torrent=Torrent indítása Start=Indítás Status=Ãllapot Stop all torrents=Minden torrent leállítása Stop all=Mind leállítása Stop torrent=Torrent leállítása Stop=Leállítás Stopped=Leállítva TB=TB Test port=Port próba T&ools=Eszközök Torrent contents=Torrent tartalmak Torrent properties=Torrent tulajdonságok Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=A Torrent ellenÅ‘rzés sokáig tarthat.~Biztos, hogy indítod a '%s' torrent ellenÅ‘rzését? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Minden fájl (*.*)|*.* Total size=Teljes méret Tracker status=Tracker állapot Tracker update on=Tracker frissítve ekkor Tracker=Tracker Trackers=Trackerek Transfer=Ãtvitel Transmission options=Transmission beállítások Transmission%s at %s:%s=Transmission%s a %s:%s címen Tray icon always visible=Tálca ikon mindig látható Tray icon=Tálca ikon U: %s/s=U: %s/s Unable to extract flag image=Zászló kép nem kibontható Unable to get files list=Nem kapok fájl listát Unknown=Ismeretlen Up limit=Korlát Fel Up speed=Sebesség Fel Up=Fel Update complete=Frissítés kész Update GeoIP database=GeoIP adatbázis frissítése Update in=Frissítés Updating=Frissítek Upload speed=Feltöltési sebesség Uploaded=Feltöltve User name=Felhasználói név Verify torrent=Torrent ellenÅ‘rzés &Verify=EllenÅ‘rzés Verifying=EllenÅ‘rzés... Version %s=Verzió %s Waiting=Várakozás... Warning=Figyelmeztetés Wasted=Kárbaveszett Working=Működik Yes to &All=Igen &Mindenre No tracker=Nincs Tracker %s downloaded=%s letöltve %s of %s downloaded=%s a %s-ból letöltve %d torrents=%d torrent Add .part extension to incomplete files=Adj .part kiterjesztést a befejezetlen fájloknak Add torrent link=Torrent link hozzáadás Are you sure to remove %d selected torrents and all their associated DATA?=Biztos, hogy el akarod távolítani a %d kiválasztott torrentet és az összes hozzárendelt ADATot? Are you sure to remove %d selected torrents?=Biztos, hogy töllöd a %d kiválastott torrentet? Bandwidth=Sávszélesség Directory for incomplete files=A befejezetlen fájlok mappája Download=Letöltés Enable blocklist=Tiltólista engedélyezése ID=ID Move torrent data from current location to new location=A torrent adatok mozgatása új helyre New location for torrent data=Torrent adatok új helye No link was specified=Nincs link megadva No torrent location was specified=Nincs torrent hely megadva Path=Elérési út Reannounce (get more peers)=Tracker frissítése (több peer szerzése) Set data location=Adat hely beállítása Size to download=Letöltés mérete The block list has been updated successfully.~The list entries count: %d=A blokk lista sikeresen frissítve.~Lista elemek száma: %d The directory for incomplete files was not specified=A félkész fájlok mappája nincs megadva The downloads directory was not specified=Letöltések mappa nincs megadva Torrent data location=Torrent adat helye Torrents=Torrentek Unable to execute "%s"=Nem sikerült futtatni: "%s" Update blocklist=Tiltólista frissítése URL of a .torrent file or a magnet link=Egy .torrent fájl vagy magnet link URL-je Columns setup=Oszlopok beállítása Add torrent=Torrent hozzáadás Delete a .torrent file after a successful addition=A .torrent fájl törlése ha sikerült betölteni Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=A Torrent ellenÅ‘rzés sokáig tarthat.~Biztos, hogy indítod %d torrent ellenÅ‘rzését? Unable to load OpenSSL library files: %s and %s=Ezeket az OpenSSL library fájlokat nem sikerült betölteni: %s és %s Use SSL=SSL használata Add tracker=Tracker hozzáadás Alternate bandwidth settings=Alternatív sávszélesség beállítások Apply alternate bandwidth settings automatically=Az alternatív sávszélesség-beállítás használata automatikusan Are you sure to delete connection '%s'?=Biztos, hogy törlöd a '%s' kapcsolatot? Are you sure to remove tracker '%s'?=Biztos, hogy törlöd a '%s' tracker-t? Days=Napok Delete=Törlés Disk cache size=Gyorsítótár mérete Download speeds (KB/s)=Letöltési sebesség (KB/s) Edit tracker=Tracker módosítás Enable Local Peer Discovery=Local Peer Discovery engedélyezése Free disk space=Szabad lemez terület Free: %s=Szabad: %s From=EttÅ‘l minutes=perc Misc=Egyéb New connection=Új kapcsolat New=Új No tracker URL was specified=Nincs tracker URL megadva Proxy=Proxy Remove tracker=Tracker eltávolítása Rename=Ãtnevezés Speed limit menu items=Sebesség-korlát menüpontok Stop seeding when inactive for=Megosztás megszüntetése, ha nem használt The invalid time value was entered=Érvénytelen időérték van beírva to=eddig Tracker announce URL=Tracker bejelentÅ‘ URL Tracker properties=Tracker tulajdonságok Unlimited=Korlátlan Upload speeds (KB/s)=Feltöltési sebesség (KB/s) Use alternate bandwidth settings=Az alternatív sávszélesség-beállítások használata average=átlag Browse=Böngész Enable µTP=Engedd a µTP-t Select a folder for download=Letöltési könyvtár kiválasztása Select torrent location=A torrent helyének kiválasztása A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A %s új verziója elérhetÅ‘.~Jelenlegi verzió: %s~Az új verzió: %s~~Megnyissam a Downloads weboldalt? Advanced=Haladó Check for new version every=Új verzió keresése, gyakoriság Check for updates=Frissités keresése Consider active torrents as stalled when idle for=Tekintsd elakadtnak az aktív torrentet, ha tétlen több mint Do you wish to enable automatic checking for a new version of %s?=Engedélyezett a %s új verziójának automatikus keresése? Donate!=Adakozz! Download queue size=Letöltési sor hossza Error checking for new version=Hiba új verzió keresése közben Folder grouping=Mappa csoportosítás Force start=Eröltetett indítás Home page=Weblap Modify trackers=Trackerek módosítása Move bottom=Aljára Move down queue=Alsó sorba Move down=Lefelé Move top=Tetejére Move up queue=FelsÅ‘ sorba Move up=Felfelé No updates have been found.~You are running the latest version of %s=Nem találtam frissítést.~A %s legújabb verziója van használatban Queue position=Sor pozíció Queue=Sor Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Az N percig tétlen Torrentek nincsnek beleszámolva a Le- illetve Feltöltési sorba Tracker grouping=Tracker csoportosítás Upload queue size=Feltöltési sor hossza View=Nézet Visit home page=Látogasd meg a weblapot days=naponta Active time=Aktív idÅ‘ Automatically add torrent links from the clipboard=Torrent linkek automatikus hozzáadása a vágólapról Copy file path to clipboard=Elérési út másolása a vágólapra Cumulative=Göngyölt Current=Jelenlegi Files added=Hozzáadott fájlok száma Filter pane=SzűrÅ‘ panel Global statistics=Globális statisztikák Info pane=Infó panel Statistics=Statisztika Status bar=Ãllapotsor %dd=%dd %dh=%dh %dm=%dm All torrents=Minden torrent Application options=Alkalmazás beállításai Ask for password=Rákérdezés a jelszóra Authentication required=Autentikáció szükséges Average out transfer speeds to eliminate fluctuations=A fluktuáció elkerüléséhez szükséges átlagos feltöltési sebesség Connect to %s=Kapcsolódás ide: %s Connect to Transmission using proxy server=Kapcsolódás a Transmission-höz proxy szerveren keresztül Connect to Transmission=Kapcsolódás a Transmission-höz Connection name=Kapcsolat neve Could not connect to tracker==Nem tudok kapcsolódni a tracker-hez Data display=Adat kijelzése Data refresh interval when minimized=Adat frissítési gyakorisága, amikor a kliens le van kicsinyítve Data refresh interval=Adatfrissítési gyakoriság Default download folder on remote host=Letöltési könyvtár a távoli gépen Disconnect from Transmission=Kapcsolat bontása a Transmission-tÅ‘l Downloading torrent file=Torrent fájl letölése Font size=etű méret Handle .torrent files by %s=A.torrent kiterjesztésű fájlok kezelÅ‘je: %s Handle magnet links by %s=A magnet linkek kezelÅ‘je: %s Invalid name specified=Hibás név Manage connections to Transmission=Transmission-höz való kapcsolódás beállítása Manage connections=Kapcsolatok beállítása Network (WAN)=Hálózat (WAN) New connection to Transmission=Új kapcsolat a Transmission-höz Pick random port on Transmission launch=Véletlenszerű portválasztás Transmission elindításakor Please enter a password to connect to %s=Kérlek, add meg a jelszót a következÅ‘höz való kapcsolódáshoz: %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Add meg, hogyan fog a(z) %s kapcsolódni a távoli gépen futó Transmission démonhoz (szolgáltatáshoz) Prompt for download options when adding a new torrent=A letöltési opciók beállítása új torrent hozzáadásakor RPC path=RPC útvonal Save as=Mentés másként Seeding time=Seedelési idÅ‘tartam Show advanced options=Haladó beállítások megjelenítése Show notifications in tray icon=Jelentések mutatása a tálca ikonon System integration=Rendszer integráció Torrent already exists in the list=A torrent már létezik a listában Unable to find path mapping.~Use the application's options to setup path mappings=Nem található az útvonal.~ Használd az alkalmazás beállításai opciót az útvonalak beállításához Update trackers for the existing torrent?=A létezÅ‘ torrent trackereinek frissítése You need to restart the application to apply changes=Újra kell indítanod az alkalmazást a beállítások érvénybelépéséhez Torrent not registered with this tracker==A torrent nincs regisztrálva ezen a trackeren TransGUI/lang/transgui.no0000644000000000000000000003206112261612465014332 0ustar rootrootTranslationLanguage=Norsk (BokmÃ¥l) "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Knytt ekstern tjeners filbane mot en lokal filbane.~~For eksempel:~/deling=\\pch\deling~/var/nedlastinger/musikk=Z:\musikk" %d x %s (have %d)=%d x %s (har %d) %ds=%ds %s (%d hashfails)=%s (%d sjekksumfeil) %s (%s done)=%s (%s fullført) '%s' has finished downloading='%s' er lastet ned %s%s%d downloading, %d seeding%s%s, %s=%s%s%d laster ned, %d sender%s%s, %s &All=&Alle &Close=Lukk &Help=&Hjelp &Ignore=&Ignorer &No=&Nei &OK=&OK &Open=Ã…pne… &Retry=Prøv igjen &Save=Lagre &Unlock=LÃ¥s opp &Yes=Ja /s=/s Abort=Avbryt About=Om Active=Aktiv Add new torrent=Legg til en ny torrent &Add torrent=Legg til torrent Added on=Lagt til den Are you sure to remove torrent '%s' and all associated DATA?=Ønsker du Ã¥ fjerne '%s' og all tilhørende data? Are you sure to remove torrent '%s'?=Ønsker du Ã¥ fjerne '%s'? Authentication=Godkjennelse b=b Cancel=Avbryt Client=Klient Close to tray=Lukk til statusfeltet Comment=Kommentar Completed on=Fullført den Completed=Fullført Confirmation=Bekreftelse connected=tilkoblet Connecting to daemon=Kobler til tjeneste Connection error occurred=Feil under tilkobling Copy=Kopier Country=Land Created on=Opprettet D: %s/s=N: %s/s Destination folder=Destinasjonsmappe Disconnected=Frakoblet Donate to support further development=Støtt videre utvikling Donate via PayPal,WebMoney,Credit card=Doner med PayPal, WebMoney eller kredittkort Done=Fullført Don't download=Ikke last ned Down limit=Begrensning ned Down speed=Nedhastighet Down=Ned Download complete=Nedlasting fullført Download speed=Nedlastingshastighet Downloaded=Lastet ned Downloading=Laster ned Enable DHT=Aktiver DHT Enable Peer Exchange=Aktiver utveksling av delere Enable port forwarding=Aktiver portviderekobling Encryption disabled=Kryptering av Encryption enabled=Kryptering pÃ¥ Encryption required=Kryptering pÃ¥krevd Encryption=Kryptering Error=Feil ETA=Antatt fullført E&xit=Avslutt File name=Filnavn Files=Filer Finished=Fullført Flag images archive is needed to display country flags.~Download this archive now?=Et flaggarkiv er nødvendig for Ã¥ vise flagg.~Ønsker du Ã¥ laste det ned nÃ¥? Flags=Flagg GB=GB General=Generelt Geo IP database is needed to resolve country by IP address.~Download this database now?=For Ã¥ knytte en IP-adresse til et bestemt land sÃ¥ mÃ¥ en GeoIP database lastes ned.~Ønsker du Ã¥ laste den ned nÃ¥? Global bandwidth settings=Global bÃ¥ndbredde Global peer limit=Maksimalt antall delere Hash=Sjekksum Have=Tilgjengelig Hide=Skjul High priority=Høy prioritet high=høy Host=Vert in swarm=i sverm Inactive=Inaktiv Incoming port is closed. Check your firewall settings=Innkommende port er stengt, sjekk brannmuren din Incoming port tested successfully=Innkommende port er Ã¥pen Incoming port=Innkommende port Information=Informasjon KB/s=KB/s KB=KB Language=SprÃ¥k Last active=Sist aktiv License=Lisens Low priority=Lav prioritet low=lav Max peers=Maks delere Maximum download speed=Maksimal nedlastingshastighet Maximum upload speed=Maksimal opplastingshastighet MB=MB Minimize to tray=Minimer til statusfelt Name=Navn No host name specified=Mangler vertsnavn No proxy server specified=Mangler proxy-tjener No to all=Nei til alt Normal priority=Normal prioritet normal=normal of=av Open containing folder=Ã…pne tilhørende mappe Open=Ã…pne Password=Passord Paths=Baner Peer limit=Begrens antall delere Peers=Delere Pieces=Biter Port=Port Priority=Prioritet Properties=Innstillinger Proxy password=Passord Proxy port=Port Proxy server=Tjener Proxy user name=Brukernavn Ratio=Forhold Reconnect in %d seconds=Kobler til igjen om %d sekunder Remaining=GjenstÃ¥ende Remote host=Ekstern tjener Remove torrent and Data=Fjern torrent og tilhørende data Remove torrent=Fjern torrent Remove=Fjern Resolve country=SlÃ¥ opp land Resolve host name=SlÃ¥ opp vertsnavn seconds=sekunder Seed ratio=Senderforhold Seeding=Sender Seeds=Sendere Select a .torrent to open=Velg en .torrent-fil Select all=Velg alle Select none=Velg bort alle Setup columns=Kolonnevalg Share ratio=Delingsforhold Show country flag=Vis flagg Show=Vis Size=Størrelse skip=hopp over Start all torrents=Start samtlige torrenter Start torrent=Start torrent Start=Start Status=Status Stop all torrents=Stopp samtlige torrenter Stop all=Stopp alle Stop torrent=Stopp torrent Stop=Stopp Stopped=Stoppet TB=TB Test port=Sjekk port T&ools=Verktøy Torrent contents=Innhold Torrent properties=Egenskaper Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Kontroll av en torrent kan ta lang tid.~Er du sikker pÃ¥ at du vil starte kontrollen av '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenter (*.torrent)|*.torrent|Alle filer (*.*)|*.* Total size=Samlet størrelse Tracker status=Trackerstatus Tracker update on=Tracker oppdateres Tracker=Tracker Trackers=Trackere Transfer=Overførsel Transmission options=Valg for Transmission Transmission%s at %s:%s=Transmission%s pÃ¥ %s:%s Tray icon always visible=Systemstatusikon alltid synlig Tray icon=Systemstatusikon U: %s/s=O: %s/s Unable to extract flag image=Kunne ikke pakke ut flagg Unable to get files list=Kunne ikke fÃ¥ filliste Unknown=Ukjent Up limit=Begrensning opp Up speed=Opphastighet Up=Opp Update complete=Oppdatering fullført Update GeoIP database=Oppdater GeoIP database Update in=Oppdaterer om Updating=Oppdaterer Upload speed=Opplastingshastighet Uploaded=Lastet opp User name=Brukernavn Verify torrent=Kontroller torrent &Verify=Kontroll Verifying=Kontrollerer Version %s=Versjon %s Waiting=Venter Warning=Advarsel Wasted=Bortkastet Working=Fungerer Yes to &All=Ja til &Alt No tracker=Ingen tracker %s downloaded=%s lastet ned %s of %s downloaded=%s av %s lastet ned %d torrents=%d torrenter Add .part extension to incomplete files=Legg filendelsen ".part" til ufullstendige filer Add torrent link=Legg til lenke til torrent Are you sure to remove %d selected torrents and all their associated DATA?=Ønsker du Ã¥ fjerne %d valgte torrenter og all tilhørende data? Are you sure to remove %d selected torrents?=Ønsker du Ã¥ fjerne %d valgte torrenter? Bandwidth=BÃ¥ndbredde Directory for incomplete files=Mappe for ufullstendige filer Download=Last ned Enable blocklist=Aktiver svarteliste ID=ID Move torrent data from current location to new location=Flytt torrentdata fra nÃ¥værende til ny plassering New location for torrent data=Ny plassering for torrentdata No link was specified=Mangler lenke No torrent location was specified=Mangler plassering for torrent Path=Bane Reannounce (get more peers)=Annonser pÃ¥ nytt for flere delere Set data location=Velg plassering for data Size to download=Hvor mye skal lastes ned The block list has been updated successfully.~The list entries count: %d=Svartelisten er oppdatert.~Listen inneholder %d oppføringer The directory for incomplete files was not specified=Mangler mappen for ufullstendige filer The downloads directory was not specified=Mangler mappen for nedlastinger Torrent data location=Plassering for torrentdata Torrents=Torrenter Unable to execute "%s"=Kan ikke kjøre "%s" Update blocklist=Oppdater svarteliste URL of a .torrent file or a magnet link=Adresse til en .torrent-fil eller magnetlenke Columns setup=Kolonnevalg Add torrent=Legg til Delete a .torrent file after a successful addition=Slett .torrent-filen etter at den er lagt til tjeneren Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Kontroll av torrenter kan ta lang tid.~Er du sikker pÃ¥ at du vil starte Ã¥ kontrollere %d torrenter? Unable to load OpenSSL library files: %s and %s=Kunne ikke Ã¥pne OpenSSL-bibliotekene: %s and %s Use SSL=Bruk SSL Add tracker=Legg til tracker Alternate bandwidth settings=Alternativ bÃ¥ndbredde Apply alternate bandwidth settings automatically=Bruk automatisk alternativ bÃ¥ndbreddeinstilling Are you sure to delete connection '%s'?=Ønsker du Ã¥ slette forbindelsen '%s'? Are you sure to remove tracker '%s'?=Ønsker du Ã¥ fjerne tracker '%s'? Days=Dager Delete=Slett Disk cache size=Størrelse pÃ¥ diskbuffer Download speeds (KB/s)=Nedlastingshastighet (KB/s) Edit tracker=Endre tracker Enable Local Peer Discovery=Aktiver "Søk etter lokale delere" Free disk space=Ledig diskplass Free: %s=Ledig: %s From=Fra minutes=minutter Misc=Diverse New connection=Ny forbindelse New=Ny No tracker URL was specified=Mangler trackeradresse Proxy=Proxy Remove tracker=Fjern tracker Rename=Gi nytt navn Speed limit menu items=Hastighetsbegrensninger Stop seeding when inactive for=Avslutt sending uten aktivitet i The invalid time value was entered=Det ble angitt en ugyldig tid to=til Tracker announce URL=Trackerens annonseringsadresse Tracker properties=Tracker-innstillinger Unlimited=Ubegrenset Upload speeds (KB/s)=Opplastingshastighet (KB/s) Use alternate bandwidth settings=Bruk alternativ bÃ¥ndbreddeinstilling average=i snitt Browse=Bla gjennom Enable µTP=Aktiver µTP Select a folder for download=Velg en mappe for nedlasting Select torrent location=Velg plassering for torrent A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=En ny versjon av %s er tilgjengelig.~Din versjon er: %s~Den nye versjonen er: %s~~Ønsker du Ã¥ gÃ¥ til web-siden for nedlasting? Advanced=Avansert Check for new version every=Se etter en ny versjon hver Check for updates=Se etter oppdatering Consider active torrents as stalled when idle for=Anta at aktive torrenter har stoppet opp etter ingen aktivitet i Do you wish to enable automatic checking for a new version of %s?=Ønsker du Ã¥ se etter nye versjoner av %s automatisk? Donate!=Doner! Download queue size=Størrelse pÃ¥ nedlastingskø Error checking for new version=Feil under sjekk for ny versjon Folder grouping=Mappegruppering Force start=Tvunget start Home page=Hjemmeside Modify trackers=Endre trackere Move bottom=Flytt nederst Move down queue=Flytt ned i køen Move down=Flytt ned Move top=Flytt øverst Move up queue=Flytt opp i køen Move up=Flytt opp No updates have been found.~You are running the latest version of %s=Ingen oppdatering er funnet.~Du bruker allerede den siste versjonen av %s Queue position=Plass i køen Queue=Kø Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrenter som har stoppet opp i N minutter teller ikke mot ned- eller opplastingskøen Tracker grouping=Trackergruppering Upload queue size=Størrelse pÃ¥ opplastingskø View=Vis Visit home page=Besøk hjemmesiden days=dag Active time=Tid aktiv Automatically add torrent links from the clipboard=Legg automatisk til torrentlenker fra utklippstavlen Copy file path to clipboard=Kopier filbanen Cumulative=Kumulativ Current=NÃ¥værende Files added=Filer lagt til Filter pane=Filter pane Global statistics=Global statistikk Info pane=Info pane Statistics=Statistikk Status bar=Statuslinje %dd=%dd %dh=%dh %dm=%dm All torrents=Alle torrenter Application options=Innstillinger Ask for password=Spør etter passord Authentication required=Autentisering kreves Average out transfer speeds to eliminate fluctuations=Vis gjennomsnittshastighet Connect to %s=Koble til %s Connect to Transmission using proxy server=Koble til Transmission via mellomtjener Connect to Transmission=Koble til Transmission Connection name=Navn pÃ¥ tilkoblingen Could not connect to tracker==Greide ikke Ã¥ koble til tracker Data display=Datavisning Data refresh interval when minimized=Oppfriskningsrate nÃ¥r minimert Data refresh interval=Oppfriskningsrate Default download folder on remote host=Forvalgt nedlastingsmappe pÃ¥ tjener Disconnect from Transmission=Koble fra Transmission Downloading torrent file=Laster ned torrentfil Font size=Tekststørrelse Handle .torrent files by %s=Behandle .torrent-filer etter %s Handle magnet links by %s=Behandle magnetlinker etter %s Invalid name specified=Ugyldig filnavn Manage connections to Transmission=Behandle tilkolinger til Transmission Manage connections=Behandle tilkobling Network (WAN)=Nettverk (WAN) New connection to Transmission=Ny tilkobling til Transmission Pick random port on Transmission launch=Velg tilfeldig port nÃ¥r Transmission starter Please enter a password to connect to %s=Tast inn passordet for Ã¥ koble til %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Velg hvordan %s skal koble til Transmission Prompt for download options when adding a new torrent=Be om nedlastingsvalg nÃ¥r ny torrent legges til RPC path=RPC-bane Save as=Lagre som Seeding time=Delingstid Show advanced options=Vis avanserte innstillinger Show notifications in tray icon=Vis varsler i systemkurven System integration=Systemintegrasjon Torrent already exists in the list=Torrenten er allerede i lista Torrent not registered with this tracker==Torrenten finnes ikke pÃ¥ trackeren Unable to find path mapping.~Use the application's options to setup path mappings=Finner ikke tilknytning mellom lokal og tjeners filbaner.~Sett opp filbanetilknytninger i innstillinger Update trackers for the existing torrent?=Oppdater tracker? You need to restart the application to apply changes=Du mÃ¥ starte applikasjonen pÃ¥ nytt for at endringene skal tre i kraft TransGUI/lang/transgui.zh_tw0000644000000000000000000003162212261612465015053 0ustar rootrootTranslationLanguage=ç¹é«”中文 "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="é ç«¯è·¯å¾‘映射.~~例如:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (下載了 %d) %ds=%ds %s (%d hashfails)=%s (%d 丟棄的分塊) %s (%s done)=%s (%s 已完æˆ) '%s' has finished downloading='%s' 已完æˆä¸‹è¼‰ %s%s%d downloading, %d seeding%s%s, %s=%s%s%d 下載中, %d åšç¨®ä¸­%s%s, %s &All=所有(&A) &Close=關閉(&C) &Help=幫助(&H) &Ignore=忽略(&I) &No=å¦(&N) &OK=確èª(&O) &Open=開啟(&O) &Retry=é‡è©¦(&R) &Save=儲存(&S) &Unlock=解鎖(&U) &Yes=確èª(&Y) /s=/s Abort=中止 About=關於(&A) Active=活動 Add new torrent=新增Torrent &Add torrent=新增Torrent(&A) Added on=新增於 Are you sure to remove torrent '%s' and all associated DATA?=確定è¦åˆªé™¤ %s 個Torrent與所有相關資料嗎? Are you sure to remove torrent '%s'?=確定è¦ç§»é™¤ %s å—Ž? Authentication=èªè­‰ b=b Cancel=å–æ¶ˆ Client=客戶端 Close to tray=關閉到系統匣 Comment=附註 Completed on=下載完畢於 Completed=å®Œæˆ Confirmation=ç¢ºèª connected=å·²é€£çµ Connecting to daemon=連çµä¸­ Connection error occurred=連çµç™¼ç”ŸéŒ¯èª¤ Copy=複製 Country=國家 Created on=創建於 D: %s/s=D: %s/s Destination folder=目標資料夾 Disconnected=未連線 Donate to support further development=è«‹æåЩ以å”助後續程å¼é–‹ç™¼ Donate via PayPal,WebMoney,Credit card=通éŽPayPal,WebMoney,Credit CardæåŠ© Done=å®Œæˆ Don't download=ä¸è¦ä¸‹è¼‰ Down limit=下載é™é€Ÿ Down speed=下載速度 Down=下移 Download complete=ä¸‹è¼‰å®Œæˆ Download speed=下載速度 Downloaded=已下載 Downloading=下載中 Enable DHT=開啟DHT Enable Peer Exchange=é–‹å•Ÿç”¨æˆ¶äº¤æ› Enable port forwarding=開啟連çµåŸ è½‰ç™¼ Encryption disabled=加密關閉 Encryption enabled=加密開啟 Encryption required=需è¦åР坆 Encryption=加密 Error=錯誤 ETA=剩餘時間 E&xit=退出(&X) File name=檔案å稱 Files=檔案 Finished=å·²å®Œæˆ Flag images archive is needed to display country flags.~Download this archive now?=是å¦è¦ä¸‹è¼‰åœ‹æ——圖片檔案? Flags=旗標 GB=GB General=æ¦‚è¦ Geo IP database is needed to resolve country by IP address.~Download this database now?=地ç†IP資料庫å¯ä»¥ç”¨ä»¥è§£æžIPä½ç½®ï¼Œè¦ä¸‹è¼‰å—Ž? Global bandwidth settings=總體頻寬設定 Global peer limit=總體連çµé™åˆ¶ Hash=校驗值 Have=已下載 Hide=éš±è— High priority=高優先性 high=高 Host=主機 in swarm=於群集 Inactive=䏿´»å‹• Incoming port is closed. Check your firewall settings=連çµåŸ é—œé–‰ï¼Œè«‹æª¢æŸ¥é˜²ç«ç‰†è¨­å®š Incoming port tested successfully=連çµåŸ æ¸¬è©¦æˆåŠŸ Incoming port=連çµåŸ  Information=è¨Šæ¯ KB/s=KB/s KB=KB Language=語言 Last active=最後活動 License=授權 Low priority=低優先性 low=低 Max peers=最大Peersé€£çµæ•¸ Maximum download speed=最大下載速度 Maximum upload speed=最大上傳速度 MB=MB Minimize to tray=最å°åŒ–到系統匣 Name=å稱 No host name specified=未指定主機å稱 No proxy server specified=未指定代ç†ä¼ºæœå™¨å稱 No to all=å…¨å¦ Normal priority=一般優先性 normal=一般 of=of Open containing folder=開啟包å«çš„資料夾 Open=開啟 Password=密碼 Paths=路徑 Peer limit=Peers連接數é™åˆ¶ Peers=Peers Pieces=分塊 Port=連çµåŸ  Priority=優先性 Properties=屬性 Proxy password=代ç†ä¼ºæœå™¨å¯†ç¢¼ Proxy port=代ç†ä¼ºæœå™¨é€£çµåŸ  Proxy server=代ç†ä¼ºæœå™¨ä¸»æ©Ÿ Proxy user name=代ç†ä¼ºæœå™¨ä½¿ç”¨è€…å稱 Ratio=分享率 Reconnect in %d seconds=在%dç§’å¾Œé‡æ–°é€£çµ Remaining=剩餘 Remote host=é ç«¯ä¸»æ©Ÿ Remove torrent and Data=刪除Torrent和資料 Remove torrent=移除Torrent Remove=移除 Resolve country=è§£æžåœ‹å®¶å稱 Resolve host name=è§£æžä¸»æ©Ÿå稱 seconds=ç§’ Seed ratio=到é”è¨­å®šåˆ†äº«çŽ‡å¾Œåœæ­¢ä½œç¨® Seeding=åšç¨®ä¸­ Seeds=ç¨®å­ Select a .torrent to open=鏿“‡Torrent檔案開啟 Select all=鏿“‡å…¨éƒ¨ Select none=å…¨ä¸é¸æ“‡ Setup columns=設定顯示é¸é … Share ratio=分享率 Show country flag=顯示國家旗幟 Show=顯示 Size=æª”æ¡ˆå¤§å° skip=è·³éŽ Start all torrents=啟動所有Torrents Start torrent=啟動Torrent Start=é–‹å§‹ Status=狀態 Stop all torrents=åœæ­¢æ‰€æœ‰Torrent Stop all=åœæ­¢å…¨éƒ¨ Stop torrent=åœæ­¢Torrent Stop=åœæ­¢ Stopped=å·²åœæ­¢ TB=TB Test port=測試連çµåŸ  T&ools=工具(&O) Torrent contents=Torrent檔案內容 Torrent properties=Torrent檔案屬性 Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrent校驗時間å¯èƒ½æœƒå¾ˆé•·ï¼Œæ˜¯å¦æ ¡é©— %s? &Torrent=&Torrent檔案 Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrent檔案 (*.torrent)|*.torrent|所有檔案 (*.*)|*.* Total size=ç¸½é«”å¤§å° Tracker status=Tracker狀態 Tracker update on=Trackeræ›´æ–°æ–¼ Tracker=Tracker Trackers=Trackers Transfer=傳輸 Transmission options=Transmissioné¸é … Transmission%s at %s:%s=Transmission%s æ–¼ %s:%s Tray icon always visible=系統匣圖示總是å¯è¦‹ Tray icon=系統匣圖示 U: %s/s=U: %s/s Unable to extract flag image=旗幟圖片無法解壓縮 Unable to get files list=無法ç²å¾—檔案列表 Unknown=未知 Up limit=上傳é™åˆ¶ Up speed=上傳速度 Up=上移 Update complete=更新完畢 Update GeoIP database=更新地ç†IP資料庫 Update in=å³å°‡æ›´æ–°æ–¼ Updating=更新中 Upload speed=上傳速度 Uploaded=已上傳 User name=使用者å稱 Verify torrent=æ ¡é©—Torrent檔案 &Verify=æ ¡é©—(&V) Verifying=校驗中 Version %s=版本 %s Waiting=等待 Warning=警告 Wasted=æ¨æ£„ Working=工作中 Yes to &All=全部確èª(&A) No tracker=ç„¡Tracker %s downloaded=下載了%s %s of %s downloaded=下載了 %s / %s %d torrents=%d Torrent檔案 Add .part extension to incomplete files=æ–¼æœªå®Œæˆæª”案添加.part副檔å Add torrent link=添加Torrentæª”æ¡ˆé€£çµ Are you sure to remove %d selected torrents and all their associated DATA?=確定è¦åˆªé™¤ %d 個Torrent檔案與其資料嗎? Are you sure to remove %d selected torrents?=確定è¦ç§»é™¤ %d 個Torrent檔案嗎? Bandwidth=頻寬 Directory for incomplete files=æœªå®Œæˆæª”案 Download=下載 Enable blocklist=開啟黑åå–® ID=ID Move torrent data from current location to new location=從當å‰ä½ç½®ç§»å‹•Torrent檔案資料到新ä½ç½® New location for torrent data=Torrent檔案資料新ä½ç½® No link was specified=æœªæŒ‡å®šé€£çµ No torrent location was specified=Torrent檔案ä½ç½®æœªæŒ‡å®š Path=路徑 Reannounce (get more peers)=釿–°é€šå‘Š(å–得更多Peers) Set data location=設定資料ä½ç½® Size to download=ä¸‹è¼‰å¤§å° The block list has been updated successfully.~The list entries count: %d=黑å單更新æˆåŠŸ.~共有: %d é … The directory for incomplete files was not specified=æœªå®Œæˆæ–‡ä»¶ç›®éŒ„未指定 The downloads directory was not specified=下載目錄未指定 Torrent data location=Torrent資料ä½ç½® Torrents=Torrent檔案 Unable to execute "%s"=無法執行 "%s" Update blocklist=更新黑åå–® URL of a .torrent file or a magnet link=Torrentæª”æ¡ˆç¶²å€æˆ–是Magneté€£çµ Columns setup=欄ä½è¨­å®š Add torrent=添加Torrent檔案 Delete a .torrent file after a successful addition=æˆåŠŸæ·»åŠ ä»»å‹™ä¹‹å¾Œåˆªé™¤Torrent檔案 Torrent=Torrent檔案 Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrent校驗時間å¯èƒ½æœƒå¾ˆé•·ï¼Œæ˜¯å¦å° %d 個Torrents進行校驗? Unable to load OpenSSL library files: %s and %s=無法載入 OpenSSL 函å¼åº«æª”案: %s and %s Use SSL=使用 SSL Add tracker=添加Tracker Alternate bandwidth settings=替代頻寬設定 Apply alternate bandwidth settings automatically=自動套用替代頻寬設定 Are you sure to delete connection '%s'?=確èªåˆªé™¤é€£çµ '%s'? Are you sure to remove tracker '%s'?=確èªç§»é™¤Tracker '%s'? Days=æ—¥ Delete=刪除 Disk cache size=ç£ç¢Ÿå¿«å–å¤§å° Download speeds (KB/s)=下載速度 (KB/s) Edit tracker=編輯Tracker Enable Local Peer Discovery=開啟本地用戶探索 Free disk space=剩餘空間 Free: %s=剩餘: %s From=來自 minutes=分 Misc=雜項 New connection=æ–°é€£çµ New=新建 No tracker URL was specified=沒有指定Trackerç¶²å€ Proxy=代ç†ä¼ºæœå™¨ Remove tracker=移除Tracker Rename=釿–°å‘½å Speed limit menu items=é™é€Ÿé¸å–®é …ç›® Stop seeding when inactive for=æ–¼æŒ‡å®šæ™‚é–“å…§ä¸æ´»å‹•æ™‚åœæ­¢åšç¨® The invalid time value was entered=輸入了無效的時間 to=到 Tracker announce URL=Trackeré€šå‘Šé€£çµ Tracker properties=Trackeré¸é … Unlimited=ç„¡é™åˆ¶ Upload speeds (KB/s)=上傳速度 (KB/s) Use alternate bandwidth settings=使用替代頻寬設定 average=å¹³å‡ Browse=ç€è¦½ Enable µTP=開啟 µTP Select a folder for download=鏿“‡ä¸‹è¼‰è³‡æ–™å¤¾ Select torrent location=鏿“‡Torrent檔案ä½ç½® A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=新版本 %s 已經å¯ä»¥ä¸‹è¼‰~ç¾åœ¨çš„版本為: %s~新版本為: %s~~是å¦è¦é–‹å•Ÿä¸‹è¼‰ç¶²é ? Advanced=進階 Check for new version every=檢查新版本間隔時間 Check for updates=檢查更新 Consider active torrents as stalled when idle for=è¶…éŽå¤šä¹…閒置時間後,設定 Torrent ç‚ºæš«åœ Do you wish to enable automatic checking for a new version of %s?=你想è¦é–‹å•Ÿæ–°ç‰ˆæœ¬ %s 的自動檢查嗎? Donate!=æåŠ©! Download queue size=下載佇列 Error checking for new version=新版本檢查錯誤 Folder grouping=資料夾群組 Force start=強迫啟動 Home page=é¦–é  Modify trackers=變更 Trackers Move bottom=移到底部 Move down queue=佇列下移 Move down=下移 Move top=移到頂部 Move up queue=佇列上移 Move up=上移 No updates have been found.~You are running the latest version of %s=æœªç™¼ç¾æ–°ç‰ˆæœ¬~你正在使用最新版本的 %s Queue position=佇列ä½ç½® Queue=佇列 Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents é–’ç½® N 分é˜å¾Œä¸åˆ—入下載或上傳佇列 Tracker grouping=Tracker 群組 Upload queue size=ä¸Šå‚³ä½‡åˆ—å¤§å° View=檢視(&V) Visit home page=é€ è¨ªç¶²é  days=æ—¥ Active time=啟動時間 Automatically add torrent links from the clipboard=自動由剪貼簿中貼上Torrenté€£çµ Copy file path to clipboard=複製檔案路徑到剪貼簿 Cumulative=累計 Current=ç¾è¡ŒéšŽæ®µ Files added=增加檔案 Filter pane=分類窗格 Global statistics=整體統計 Info pane=資訊窗格 Statistics=統計 Status bar=狀態列 %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.de0000644000000000000000000003315512261612465014313 0ustar rootrootTranslationLanguage=German "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Pfadzuordnung Remote zu lokal.~~Beispiel:~/share=\\pch\share~/var/downloads/music=Z:\music" %d x %s (have %d)=%d x %s (noch %d) %ds=%ds %s (%d hashfails)=%s (%d Prüfsummenfehler) %s (%s done)=%s (%s fertig) '%s' has finished downloading='%s' erfolgreich heruntergeladen %s%s%d downloading, %d seeding%s%s, %s=%s%s%d Lädt, %d seedet%s%s, %s &All=&Alle &Close=&Schließen &Help=&Hilfe &Ignore=&Ignorieren &No=&Nein &OK=&OK &Open=&Öffnen &Retry=&Wiederholen &Save=&Speichern &Unlock=&Freigeben &Yes=&Ja /s=/s Abort=Abruch About=Über Active=Aktiv Add new torrent=Neuen Torrent hinzufügen &Add torrent=Torrent hinzufügen Added on=hinzugefügt Are you sure to remove torrent '%s' and all associated DATA?=Wollen sie wirklich den Torrent '%s' und alle zugehörigen Dateien löschen? Are you sure to remove torrent '%s'?=Wollen sie wirklich den Torrent '%s' löschen? Authentication=Anmeldung b=b Cancel=Abbruch Client=Client Close to tray=zur Symbolleiste schließen Comment=Kommentar Completed on=fertig Completed=Fertig Confirmation=Bestätigung connected=verbunden Connecting to daemon=Verbinden zum Server Connection error occurred=Verbindungsfehler Copy=Kopieren Country=Land Created on=Erstellt D: %s/s=D: %s/s Destination folder=Zielordner Disconnected=nicht verbunden Donate to support further development=Spenden um die Weiterentwicklung zu unterstützen Donate via PayPal,WebMoney,Credit card=Spenden über PayPal,WebMoney,Kreditkarte Done=Fertig Don't download=nicht herunterladen Down limit=Download Grenze Down speed=Download Geschwindigkeit Down=Herunter Download complete=Download fertig Download speed=Download Geschwindigkeit Downloaded=heruntergeladen Downloading=lade herunter Enable DHT=Erlaube DHT Enable Peer Exchange=Erlaube Peer Exchange Enable port forwarding=Erlaube Portweiterleitung Encryption disabled=Verschlüsselung aus Encryption enabled=Verschlüsselung ein Encryption required=Verschlüsselung erforderlich Encryption=Verschlüsselung Error=Fehler ETA=ETA E&xit=Beenden File name=Dateiname Files=Dateien Finished=Fertig Flag images archive is needed to display country flags.~Download this archive now?=Zum Anzeigen der Länderflaggen wird das Bilderarchiv benötigt.~Soll das Archiv jetzt heruntergeladen werden? Flags=Kennzeichen GB=GB General=General Geo IP database is needed to resolve country by IP address.~Download this database now?=Zur Zuordnung einer IP Adresse zu einem Land wird die Geo IP Datenbank benötigt.~Datenbank jetzt herunterladen? Global bandwidth settings=Globale Einstellungen Bandbreite Global peer limit=Globale Peer Grenze Hash=Prüfsumme Have=fertig Hide=versteckt High priority=hohe Priorität high=hoch Host=Host in swarm=in swarm Inactive=Inaktiv Incoming port is closed. Check your firewall settings=eingehender Port ist geschlossen. Kontrollieren Sie Ihre Firewall Incoming port tested successfully=eingehender Port erfolgreich getestet Incoming port=eingehender Port Information=Information KB/s=KB/s KB=KB Language=Sprache Last active=letzte Aktivität License=Lizenz Low priority=niedrige Priorität low=niedrig Max peers=Max Peers Maximum download speed=Maximale Downloadgeschwindigkeit Maximum upload speed=Maximale Uploadgeschwindigkeit MB=MB Minimize to tray=Minimieren zum Tray Name=Name No host name specified=keinen Hostnamen angegeben No proxy server specified=Keinen Proxyserver angegeben No to all=Nein für alle Normal priority=Normale Priorität normal=normal of=of Open containing folder=Beinhaltenden Ordner öffnen Open=Öffnen Password=Passwort Paths=Pfade Peer limit=Peer Grenze Peers=Peers Pieces=Teile Port=Port Priority=Priorität Properties=Einstellungen Proxy password=Proxy Passwort Proxy port=Proxy Port Proxy server=Proxy Server Proxy user name=Proxy Benutzername Ratio=Ratio Reconnect in %d seconds=neu verbinden in %d Sekunden Remaining=verbleibend Remote host=Remote Server Remove torrent and Data=Torrent und Dateien löschen Remove torrent=Torrent löschen Remove=löschen Resolve country=Länder anzeigen Resolve host name=Hostnamen anzeigen seconds=Sekunden Seed ratio=Seed Ratio Seeding=seeden Seeds=Seeds Select a .torrent to open=wähle einen .torrent zum öffnen Select all=alle auswählen Select none=keinen auswählen Setup columns=Spalten auswählen Share ratio=Share Ratio Show country flag=Länderflaggen anzeigen Show=Anzeigen Size=Größe skip=überspringen Start all torrents=Starte alle Torrents Start torrent=Starte Torrent Start=Start Status=Status Stop all torrents=Halte alle Torrents an Stop all=alle anhalten Stop torrent=Halte Torrent an Stop=Stop Stopped=angehalten TB=TB Test port=Port testen T&ools=Tools Torrent contents=Torrent contents Torrent properties=Torrent Einstellungen Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Die Üperprüfung kann sehr lange dauern.~Überprüfung von '%s' trotzdem starten? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Alle Dateien (*.*)|*.* Total size=gesamte Größe Tracker status=Tracker Status Tracker update on=Trackerupdate in Tracker=Tracker Trackers=Trackers Transfer=Übertragung Transmission options=Transmission Einstellungen Transmission%s at %s:%s=Transmission%s in %s:%s Tray icon always visible=Tray Icon immer sichtbar Tray icon=Tray Icon U: %s/s=U: %s/s Unable to extract flag image=Fehler beim Anzeigen der Flagge Unable to get files list=Fehler beim Empfangen der Dateiliste Unknown=Unbekannt Up limit=Upload Grenze Up speed=Upload Geschwindigkeit Up=hoch Update complete=Update fertig Update GeoIP database=Update GeoIP Datenbank Update in=Update in Updating=aktualisiere Upload speed=Upload Geschwindigkeit Uploaded=Hochgeladen User name=Benutzername Verify torrent=überprüfe torrent &Verify=Überprüfung Verifying=überprüfe Version %s=Version %s Waiting=warten Warning=Warnung Wasted=verworfen Working=arbeitet Yes to &All=Ja zu Allen No tracker=kein Tracker %s downloaded=%s heruntergeladen %s of %s downloaded=%s von %s heruntergeladen %d torrents=%d Torrents Add .part extension to incomplete files=unfertige Dateien mit der Erweiterung .part versehen Add torrent link=Torrent Link hinzufügen Are you sure to remove %d selected torrents and all their associated DATA?=Wollen Sie die %d markierten Torrents und deren Dateien wirklich löschen? Are you sure to remove %d selected torrents?=Wollen Sie die %d markierten Torrents wirklich löschen? Bandwidth=Bandbreite Directory for incomplete files=Ordner für unfertige Dateien Download=Download Enable blocklist=Erlaube Blockliste ID=ID Move torrent data from current location to new location=Dateien verschieben New location for torrent data=Neuer Speicherort No link was specified=kein Link angegeben No torrent location was specified=Es wurde kein Speicherort für Torrents angegegeben Path=Pfad Reannounce (get more peers)=Aktualisieren (für mehr Peers) Set data location=Speicherort festlegen Size to download=Downloadgröße The block list has been updated successfully.~The list entries count: %d=Die Blockliste wurde erfolgreich aktualisiert.~Die Anzahl der Einträge beträgt: %d The directory for incomplete files was not specified=Der Ordner für unfertige Dateien wurde nicht festgelegt The downloads directory was not specified=Der Download-Ordner wurde nicht festgelegt Torrent data location=Download-Ordner Torrents=Torrents Unable to execute "%s"=kann "%s" nicht ausführen Update blocklist=Update Blocklist URL of a .torrent file or a magnet link=URL zu einem .torrent File oder einen Magnetlink Columns setup=Spalteneinstellung Add torrent=Torrent hinzufügen Delete a .torrent file after a successful addition=.torrent Datei löschen wenn erfolgreich hinzugefügt Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrent-Überprüfung kann sehr lange dauern.~Überprüfung von %d Torrents trotzdem starten? Unable to load OpenSSL library files: %s and %s=Laden der OpenSSL Bibliotheken fehlgeschlagen: %s and %s Use SSL=SSL nutzen Add tracker=Tracker hinzufügen Alternate bandwidth settings=Alternative Bandbreiteneinstellung Apply alternate bandwidth settings automatically=Alternative Bandbreiteneinstellung automatisch verwenden Are you sure to delete connection '%s'?=Verbindung '%s' löschen? Are you sure to remove tracker '%s'?=Tracker '%s' entfernen? Days=Tage Delete=Löschen Disk cache size=Größe Festplattenpuffer Download speeds (KB/s)=Download Geschwindigkeit (KB/s) Edit tracker=Tracker bearbeiten Enable Local Peer Discovery=Local Peer Discovery einschalten Free disk space=Freier Festplattenspeicher Free: %s=Frei: %s From=Von minutes=Minuten Misc=Verschiedenes New connection=Neue Verbindung New=Neu No tracker URL was specified=Keine Tracker-URL angegeben Proxy=Proxy Remove tracker=Tracker entfernen Rename=Umbenennen Speed limit menu items=Menüpunkte Geschwindigkeitslimit Stop seeding when inactive for=Seeding stoppen wenn inaktiv für The invalid time value was entered=Ein ungültiger Zeitwert wurde eingegeben to=zu Tracker announce URL=Tracker announce URL Tracker properties=Tracker-Einstellungen Unlimited=Unbegrenzt Upload speeds (KB/s)=Upload Geschwindigkeit (KB/s) Use alternate bandwidth settings=Verwende alternative Bandbreiteneinstellung average=Durchschnitt Browse=Browse Enable µTP=µTP aktivieren Select a folder for download=Ordner für Download wählen Select torrent location=Ort für torrent wählen A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Eine neue Version von %s ist verfügbar.~Ihre derzeitige Version: %s~Die neue Version: %s~~Möchten Sie die Download-Seite öffnen? Advanced=Fortgeschritten Check for new version every=Auf neue Version prüfen Consider active torrents as stalled when idle for=Aktive Torrents gelten als blockiert, wenn inaktiv für Do you wish to enable automatic checking for a new version of %s?=Automatisch nach neuer Version von %s suchen? Donate!=Spenden! Download queue size=Größe der Download-Warteschlange Error checking for new version=Fehler beim prüfen auf neue Version Folder grouping=Ordner gruppieren Force start=Start erzwingen Home page=Homepage Modify trackers=Trackers ändern Move bottom=An das Ende schieben Move down queue=In Warteschlange nach hinten Move down=Nach hinten Move top=An den Anfang schieben Move up queue=In Warteschlange nach vorne Move up=Nach vorne No updates have been found.~You are running the latest version of %s=Keine Updates gefunden.~Sie nutzen bereits die letzte Version von %s Queue position=Position in Warteschlange Queue=Warteschlange Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents, die für N Minuten inaktiv sind, zählen nicht zur Download- oder Upload-Warteschlange Tracker grouping=Tracker gruppieren Upload queue size=Größe der Upload-Warteschlange View=Ansicht Visit home page=Homepage besuchen days=Tage Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Check for updates=Check for updates Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.ro0000644000000000000000000003327212261612465014343 0ustar rootrootTranslationLanguage=Română "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Maparea căilor de la distanţă către local.~~Exemple:~/share=\\pch\share~/var/downloads/muzica=Z:\muzica" %d x %s (have %d)=%d x %s (complet %d) %ds=%ds %s (%d hashfails)=%s (%d hashfails) %s (%s done)=%s (%s complet) '%s' has finished downloading='%s' s-a terminat de descărcat %s%s%d downloading, %d seeding%s%s, %s=%s%s%d descarc, %d donez%s%s, %s &All=&All &Close=&ÃŽnchide &Help=&Ajutor &Ignore=&Ignoră &No=&Nu &OK=&OK &Open=&Deschide &Retry=&Reîncearcă &Save=&Salvează &Unlock=&Deblochează &Yes=&Da /s=/s Abort=Anulează About=Despre Active=Active Add new torrent=Adaugă un nou torent &Add torrent=Adaugă torent Added on=Adăugat în Are you sure to remove torrent '%s' and all associated DATA?=Sigur ÅŸtergeÅ£i '%s' ÅŸi toate fiÅŸierele asociate? Are you sure to remove torrent '%s'?=Siguri È™tergeÅ£i torentul '%s'? Authentication=Autentificare b=b Cancel=Anulare Client=Client Close to tray=ÃŽnchidere la sertar (tray) Comment=Comentariu Completed on=Terminat pe Completed=Terminate Confirmation=Confirmare connected=conectat Connecting to daemon=Se conectează la proces Connection error occurred=A apărut eroare de conexiune Copy=Copiere Country=Å¢ara Created on=Creat pe D: %s/s=D: %s/s Destination folder=Director destinaÅ£ie Disconnected=Deconectat Donate to support further development=DonaÅ£i pentru susÅ£inerea dezvoltării Done=Progres Don't download=Nu descărca Down limit=Limita descărcare Down speed=Descărcare Down=Descărcare Download complete=Descărcare completă Downloaded=Descărcat Downloading=Se descarcă Enable DHT=Permite DHT Enable Peer Exchange=Permite schimb de gazde Enable port forwarding=Permite port forwarding Encryption disabled=Criptare anulată Encryption enabled=Criptare permisă Encryption required=Criptare cerută Encryption=Criptare Error=Eroare ETA=Estimare E&xit=IeÅŸire File name=Nume fiÅŸier Files=FiÅŸiere Finished=Terminat Flag images archive is needed to display country flags.~Download this archive now?=Arhiva imaginilor cu etichete este necesară pentru afiÅŸarea simbolurilor de Å£ară.~DescărcaÅ£i această arhivă acum? Flags=Etichete GB=GB General=General Geo IP database is needed to resolve country by IP address.~Download this database now?=Baza de date IP-uri geografice este necesară pentru afiÅŸarea IP-urilor pe Å£ara de provenienţă.~DescărcaÅ£i această bază de date acum? Global bandwidth settings=Setări lăţime de bandă generală Global peer limit=Limită generală de parteneri Hash=Hash Have=Solicitări Hide=Ascunde High priority=Prioritate maximă high=sus Host=Gazdă in swarm=in swarm Inactive=Inactiv Incoming port is closed. Check your firewall settings=Portul intrărilor este închis. VerificaÅ£i setările din firewall Incoming port tested successfully=Portul intrărilor testat cu succes Incoming port=Port intrări Information=InformaÅ£ie KB/s=KB/s KB=KB Language=Limba Last active=Ultima oară activ License=Licenţă Low priority=Prioritate scăzută low=jos Max peers=Nr maxim de parteneri Maximum download speed=Viteză maximă de descărcare Maximum upload speed=Viteză maximă de încărcare MB=MB Minimize to tray=Minimizare în sertar (tray) Name=Nume No host name specified=Nume gazdă nespecificat No proxy server specified=Server proxy nespecificat No to all=Nu tuturor Normal priority=Prioritate normală normal=normal of=din Open containing folder=Deschide directorul conÅ£inător Open=Deschide Password=Parolă Paths=Căi Peer limit=Limită parteneri Peers=Parteneri Pieces=PărÅ£i Port=Port Priority=Prioritate Properties=Proprietăţi Proxy password=Parolă proxy Proxy port=Port proxy Proxy server=Server proxy Proxy user name=Utilizator proxy Ratio=RaÅ£ie Reconnect in %d seconds=Reconectare în %d secunde Remaining=Au rămas Remote host=Gazdă la distanţă Remove torrent and Data=Åžterge torent ÅŸi date Remove torrent=ÃŽnlăturare torent Remove=Șterge Resolve country=DefineÈ™te Å£ara Resolve host name=DefineÈ™te numele gazdei seconds=secunde Seed ratio=RaÈ›ie partajare Seeding=Se donează Seeds=Donatori Select a .torrent to open=Selectează un .torrent pentru deschidere Select all=Selectează tot Select none=Deselectează tot Setup columns=Setare coloane Share ratio=RaÈ›ie de Show country flag=Arată steagul ţării Show=Arată Size=Mărime skip=sări Start all torrents=Start toate torentele Start torrent=Start torent Start=Start Status=Stare Stop all torrents=Stop toate torentele Stop all=Stop total Stop torrent=Stop torent Stop=Stop Stopped=Oprite TB=TB Test port=Port de test T&ools=Unelte Torrent contents=ConÅ£inut torent Torrent properties=Proprietăţi torent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Verificarea torentului poate dura un timp.~Sigur doriÅ£i verificarea torentului '%s'? &Torrent=Torent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=TorenÅ£i (*.torrent)|*.torrent|Toate fiÅŸierele (*.*)|*.* Total size=Mărime totală Tracker status=Statut tracker Tracker update on=Actualizare tracker pe Tracker=Tracker Trackers=Trackere Transfer=Transfer Transmission options=OpÅ£iuni Transmission Transmission%s at %s:%s=Transmission%s la %s:%s Tray icon always visible=IconiÅ£a din sertar (tray) vizibilă mereu Tray icon=IconiÅ£a din sertar (tray) U: %s/s=ÃŽ: %s/s Unable to extract flag image=Imposibil de extras imagine steag Unable to get files list=Imposibil de găsit lista de fiÅŸiere Unknown=Necunoscut Up limit=Limită încărcare Up speed=ÃŽncărcare Up=ÃŽncărcare Update complete=Actualizare completă Update GeoIP database=Actualizare baza de date GeoIP Update in=Actualizare în Updating=Se actualizează Upload speed=Viteză încărcare Uploaded=ÃŽncărcat User name=Nume utilizator Verify torrent=Verificare torent &Verify=Verifică Verifying=Se verifică Version %s=Versiune %s Waiting=AÅŸteptaÅ£i Warning=Avertizare Wasted=Pierdut Working=Se lucrează Yes to &All=Da &All No tracker=Fără tracker %s downloaded=%s descărcat %s of %s downloaded=%s din %s descărcat %d torrents=%d torente Add .part extension to incomplete files=Adaugă extensia .part pentru fiÅŸiere incomplete Add torrent link=Adaugă legătura către torent Are you sure to remove %d selected torrents and all their associated DATA?=Sigur doriÅ£i să ÅŸtergeÅ£i torentele selectate %d ÅŸi toate fiÅŸierele asociate? Are you sure to remove %d selected torrents?=Sigur doriÅ£i să ÅŸtergeÅ£i torentele selectate %d? Bandwidth=Lăţime de bandă Directory for incomplete files=Director pentru fiÅŸiere incomplete Download=Descarcă Enable blocklist=Activează lista neagră ID=ID Move torrent data from current location to new location=Mută torentul din locaÅ£ia curentă în noua locaÅ£ie New location for torrent data=Noua locaÅ£ie pentru datele torentului No link was specified=Nicio legătură specificată No torrent location was specified=Nicio locaÅ£ie a torentului specificată Path=Cale Reannounce (get more peers)=ReanunÈ›are (pentru mai mulÅ£i parteneri) Set data location=Setează destinaÅ£ia fiÅŸierelor Size to download=Mărime de descărcat The block list has been updated successfully.~The list entries count: %d=Lista neagră a fost actualizată cu succes.~Numărul de intrări în listă: %d The directory for incomplete files was not specified=Directorul pentru fiÅŸiere incomplete nu a fost specificat The downloads directory was not specified=Directorul pentru descărcări nu a fost specificat Torrent data location=DestinaÅ£ia fiÅŸierelor din torent Torrents=Torente Unable to execute "%s"=Imposibil de executat "%s" Update blocklist=Actualizează lista neagră URL of a .torrent file or a magnet link=URL al unui fiÅŸier .torrent sau legătură Columns setup=Setare coloane Add torrent=Adaugă torent Delete a .torrent file after a successful addition=Șterge fiÈ™ierul .torrent după adăugarea reuÈ™ită Torrent=Torent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Verificarea torentelor poate dura un timp îndelungat.~Sigur doriÈ›i verificarea %d torentelor? Unable to load OpenSSL library files: %s and %s=Nu se pot încărca fiÈ™ierele bibliotecilor OpenSSL: %s È™i %s Use SSL=FoloseÈ™te SSL Add tracker=Adaugă tracker Alternate bandwidth settings=Lățime de bandă alternativă Apply alternate bandwidth settings automatically=Aplică automat lățimea de bandă alternativă Are you sure to delete connection '%s'?=Sigur È™tergeÈ›i conexiunea '%s'? Are you sure to remove tracker '%s'?=Sigur È™tergeÈ›i tracker '%s'? Days=Zile Delete=Șterge Disk cache size=Mărime cache disc Download speeds (KB/s)=Viteze descărcare (KB/s) Edit tracker=Editează tracker Enable Local Peer Discovery=Permite descoperire partener local Free disk space=SpaÈ›iu liber pe disc Free: %s=Liber: %s From=De la minutes=minute Misc=Diverse New connection=Conexiune nouă New=Nou No tracker URL was specified=URL Tracker nespecificat Proxy=Proxy Remove tracker=Șterge tracker Rename=Redenumire Speed limit menu items=Meniu limitare de viteză Stop seeding when inactive for=OpreÈ™te partajarea după inactivitatea de The invalid time value was entered=A fost introdusă o valoare de timp invalidă to=către Tracker announce URL=Tracker anunță URL Tracker properties=Proprietăți Tracker Unlimited=Nelimitat Upload speeds (KB/s)=Viteze încărcare (KB/s) Use alternate bandwidth settings=FoloseÈ™te lățimea de bandă alternativă average=medie Browse=Navighează Enable µTP=Permite µTP Select a folder for download=Alege director pentru descărcare Select torrent location=Alege locaÈ›ie torent A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Donate via PayPal,WebMoney,Credit card=Donate via PayPal,WebMoney,Credit card Download speed=Download speed Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.uk0000644000000000000000000005020412261612465014334 0ustar rootrootTranslationLanguage=УкраїнÑька %ds=%dÑ %d x %s (have %d)=%d x %s (в наÑвноÑті %d) %s (%d hashfails)=%s (%d помилок хешу) %s (%s done)=%s (%s готово) '%s' has finished downloading=Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ '%s' завершено %s%s%d downloading, %d seeding%s%s, %s=%s%s%d завантажуєтьÑÑ, %d роздаєтьÑÑ%s%s, %s &All=Ð’Ñе &Close=Закрити &Help=Довідка &Ignore=Ігнорувати &No=ÐÑ– &OK=&OK &Open=&Відкрити &Retry=Повторити &Save=Зберегти &Unlock=&Розблокувати &Yes=Так Abort=Припинити About=Про програму Active=Ðктивні Add new torrent=Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ торренту &Add torrent=Додати торрент Added on=Додано Are you sure to remove torrent '%s' and all associated DATA?=Ви впевнені, що хочете видалити торрент~'%s'~Ñ– вÑÑ– данні, Ñкі пов'Ñзані з ним? Are you sure to remove torrent '%s'?=Ви впевнені, що хочете видалити торрент~'%s' ? Authentication=ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Cancel=Закрити Client=Клієнт Close to tray=Закривати в трей Comment=Коментар Completed on=Завершено Completed=Завершені Confirmation=ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ connected=підключено Connecting to daemon=ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ Transmission Connection error occurred=Помилка Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Copy=Копіювати Country=Країна Created on=Створений D: %s/s=Зав.: %s/Ñ Destination folder=Папка Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Disconnected=Відключено Donate to support further development=Підтримайте фінанÑово подальший розвиток Donate via PayPal,WebMoney,Credit card=Перерахувати кошти через PayPal,WebMoney,Credit card Done=Готово Don't download=Ðе завантажувати Down limit=Ліміт Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Down speed=Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Down=Вниз Download complete=Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾ Download speed=ШвидкіÑть Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Downloaded=Завантажено Downloading=ЗавантажуєтьÑÑ Enable DHT=Дозволити DHT Enable Peer Exchange=Дозволити обмін пірами Enable port forwarding=Дозволити Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ñ€Ñ‚Ñƒ Encryption=Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Error=Помилка ETA=ЗалишилоÑÑŒ E&xit=Вихід File name=Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ Files=Файли Finished=Завершено Flag images archive is needed to display country flags.~Download this archive now?=Ð”Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ð¿Ð¾Ñ€Ñ–Ð² країн необхідно архів з зображеннÑми прапорів.~Завантажити цей архів зараз? Flags=Прапори General=Загальні Geo IP database is needed to resolve country by IP address.~Download this database now?=Ð”Ð»Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÑ€Ð°Ñ—Ð½Ð¸ за IP-адреÑою необхідна база даних Geo IP.~Завантажити цю базу зараз? Global bandwidth settings=Глобальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті Global peer limit=Глобальний ліміт пірів Hash=Хеш Have=в наÑвноÑті Hide=Приховати High priority=ВиÑокий пріоритет high=виÑокий Host=ХоÑÑ‚ in swarm=в купі Inactive=Ðеактивні Incoming port=Вхідний порт Information=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ KB/s=КБ/Ñ Last active=ОÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть License=Ð›Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Low priority=Ðизький пріоритет low=низький Max peers=Ліміт пірів Maximum download speed=Ліміт швидкоÑті Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Maximum upload speed=Ліміт швидкоÑті Ð²Ñ–Ð´Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Minimize to tray=Згортати в трей Name=Ðазва No to all=ÐÑ– Ð´Ð»Ñ Ð²ÑÑ–Ñ… Normal priority=Ðормальний пріоритет normal=нормальний Open containing folder=Відкрити папку, що міÑтить об'єкт Open=Відкрити Password=Пароль Paths=ШлÑхи Peer limit=Ліміт пірів Peers=Піри Pieces=ЧаÑтин Port=Порт Priority=Пріоритет Properties=ВлаÑтивоÑті Proxy password=Пароль прокÑÑ– Proxy port=Порт прокÑÑ– Proxy server=ПрокÑÑ–-Ñервер Proxy user name=КориÑтувач прокÑÑ– Ratio=Коефіцієнт Reconnect in %d seconds=Повтор Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %d Ñекунд Remaining=ЗалишилоÑÑŒ Remote host=ХоÑÑ‚ "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="ВідповідніÑть локальних Ñ– віддалених шлÑхів.~~Ðаприклад:~/share=\\pch\share~/var/downloads/music=Z:\music" Remove torrent and Data=Видалити торрент Ñ– файли Remove torrent=Видалити торрент Remove=Видалити Resolve country=Визначити країну Resolve host name=Визначити ім'Ñ Ñ…Ð¾Ñту seconds=Ñекунд Seed ratio=Коефіцієнт роздачі Seeding=РоздаєтьÑÑ Seeds=Сіди Select a .torrent to open=Виберіть торрент-файл Select all=Вибрати вÑе Select none=СкаÑувати вибір Setup columns=Ðалаштувати Ñтовпчики Share ratio=Коефіцієнт Show country flag=Показувати прапор країни Show=Показати Size=Розмір skip=пропуÑтити Start all torrents=ЗапуÑтити вÑÑ– торренти Start torrent=ЗапуÑтити торрент Start=ЗапуÑтити Status=Стан Stop all torrents=Зупинити вÑÑ– торренти Stop all=Зупинити вÑе Stop torrent=Зупинити торрент Stop=Зупинити Stopped=Зупинено Test port=ТеÑÑ‚ порту T&ools=ІнÑтрументи Torrent contents=ВміÑÑ‚ торренту Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Перевірка торрента може тривати доÑить довго.~Ви впевнені, що бажаєте перевірити торрент '%s'? &Torrent=Торрент Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Торренти (*.torrent)|*.torrent|Ð’ÑÑ– файли (*.*)|*.* Total size=Загальний розмір Tracker status=Стан трекеру Tracker update on=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ñ€ÐµÐºÐµÑ€Ð° Tracker=Трекер Trackers=Трекери Transfer=Передача Transmission options=Параметри Transmission Tray icon always visible=Відображати піктограму у треї Tray icon=СиÑтемний трей U: %s/s=Розд: %s/Ñ Unable to extract flag image=Ðеможливо видобути Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ð¿Ð¾Ñ€Ñƒ Unable to get files list=Ðеможливо отримати ÑпиÑок файлів Unknown=Ðевідомо Up limit=Ліміт Ð²Ñ–Ð´Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Up speed=Ð’Ñ–Ð´Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Up=Вгору Update complete=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾ Update GeoIP database=Оновити базу даних GeoIP Updating=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Update in=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· Upload speed=ШвидкіÑть Ð²Ñ–Ð´Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Uploaded=Віддано User name=КориÑтувач Verify torrent=Перевірити торрент &Verify=Перевірити Verifying=Перевірити Version %s=ВерÑÑ–Ñ %s Waiting=ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Warning=ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Wasted=Завантажено даремно Working=Працює Yes to &All=Так, Ð´Ð»Ñ Ð²ÑÑ–Ñ… Transmission%s at %s:%s=Transmission%s на %s:%s Torrent properties=ВлаÑтивоÑті торрента /s=/Ñ Encryption disabled=Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¾ Encryption enabled=Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¾ Encryption required=Завжди вимагаєтьÑÑ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Incoming port is closed. Check your firewall settings=Вхідний порт закрито. Перевірте Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ брандмауера. Incoming port tested successfully=Вхідний порт уÑпішно протеÑтований. of=з b=б GB=ГБ KB=КБ MB=МБ TB=ТБ No host name specified=Ðе вказаний хоÑÑ‚. No proxy server specified=Ðе вказано прокÑÑ–-Ñервер. Language=Мова No tracker=Ðемає трекеру %s downloaded=%s завантажено %s of %s downloaded=%s з %s завантажено %d torrents=%d торрентів Add .part extension to incomplete files=Додавати Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ .part до неповніÑтю завантажених файлів Add torrent link=Додати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° торрент Are you sure to remove %d selected torrents and all their associated DATA?=Ви впевнені, що бажаєте видалити %d відмічених торрентів Ñ– вÑÑ– дані, Ñкі пов'Ñзані з ними? Are you sure to remove %d selected torrents?=Ви впевнені, що бажаєте видалити %d відмічених торрентів? Bandwidth=ШвидкіÑть Directory for incomplete files=Папка Ð´Ð»Ñ Ð½Ðµ повніÑтю завантажених файлів Download=Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Enable blocklist=Дозволити ÑпиÑок блокувань ID=ID No link was specified=Ðе вказано поÑÐ¸Ð»Ð°Ð½Ð½Ñ No torrent location was specified=Ðе вказано Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾Ñ€Ñ€ÐµÐ½Ñ‚Ñƒ Path=ШлÑÑ… Reannounce (get more peers)=ПереоголоÑити (Отримати більше пірів) Set data location=Задати Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Size to download=Розмір Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ The block list has been updated successfully.~The list entries count: %d=Перелік блокувань уÑпішно оновлено.~КількіÑть елементів переліку: %d The directory for incomplete files was not specified=Папку Ð´Ð»Ñ Ð½ÐµÐ¿Ð¾Ð²Ð½Ñ–Ñтю завантажених файлів не визначено The downloads directory was not specified=Папку Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ визначено Torrents=Торренти Unable to execute "%s"=Ðеможливо виконати "%s" Update blocklist=Оновити перелік блокувань URL of a .torrent file or a magnet link=URL Ð´Ð»Ñ .torrent файлу або magnet link Move torrent data from current location to new location=ПереміÑтити дані торренту з поточного Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð² нове New location for torrent data=Ðове Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… торренту Torrent data location=Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… торренту Columns setup=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñтовбців Add torrent=Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ‚Ð¾Ñ€Ñ€ÐµÐ½Ñ‚Ñƒ Torrent=Торрент Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Перевірка торрентів може тривати доÑить довго.~Ви впевнені, що бажаєте перевірити %d торрентів? Unable to load OpenSSL library files: %s and %s=Ðеможливо завантажити файли бібліотеки OpenSSL: %s Ñ– %s Delete a .torrent file after a successful addition=ВидалÑти .torrent файл піÑÐ»Ñ Ð²Ð´Ð°Ð»Ð¾Ð³Ð¾ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Use SSL=ВикориÑтовувати SSL Add tracker=Додати трекер Alternate bandwidth settings=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð»ÑŒÑ‚ÐµÑ€Ð½Ð°Ñ‚Ð¸Ð²Ð½Ð¾Ñ— швидкоÑті Apply alternate bandwidth settings automatically=Ðвтоматично заÑтоÑовувати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð»ÑŒÑ‚ÐµÑ€Ð½Ð°Ñ‚Ð¸Ð²Ð½Ð¾Ñ— швидкоÑті Are you sure to delete connection '%s'?=Ви впевнені що бажаєте видалити з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ '%s'? Are you sure to remove tracker '%s'?=Ви впевнені що бажаєте видалити трекер '%s'? Days=Днів Delete=Видалити Disk cache size=Розмір диÑкового кешу Download speeds (KB/s)=ШвидкіÑть Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ (КБ/Ñ) Edit tracker=Редагувати трекер Enable Local Peer Discovery=Дозволити доÑÐ»Ñ–Ð´Ð¶ÐµÐ½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¸Ñ… пірів Free disk space=Вільно на диÑку Free: %s=Вільно: %s From=З minutes=хвилин Misc=Різне New connection=Ðове з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ New=Ðове No tracker URL was specified=Ðе вказано адреÑу трекера Proxy=ПрокÑÑ– Remove tracker=Видалити трекер Rename=Перейменувати Speed limit menu items=Елементи меню швидкоÑті Stop seeding when inactive for=Припинити роздачу Ñкщо вона неактивна The invalid time value was entered=Ðекоректно задано Ñ‡Ð°Ñ to=по Tracker announce URL=ÐдреÑа Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ñ‚Ñ€ÐµÐºÐµÑ€Ð° Tracker properties=ВлаÑтивоÑті трекера Unlimited=Ðеобмежено Upload speeds (KB/s)=ШвидкіÑть роздачі (КБ/Ñ) Use alternate bandwidth settings=ВикориÑтовувати альтернативні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті average=ÑÐµÑ€ÐµÐ´Ð½Ñ Browse=ПроглÑнути Enable µTP=Включити µTP Select a folder for download=Вибрати папку Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Select torrent location=Вибрати Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾Ñ€Ñ€ÐµÐ½Ñ‚Ñƒ A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=ДоÑтупна нова верÑÑ–Ñ %s.~Ваша поточна верÑÑ–Ñ: %s~Ðова верÑÑ–Ñ: %s~~Ви хочете перейти на Ñторінку завантаженнÑ? Advanced=Додатково Check for new version every=ПеревірÑти наÑвніÑть оновлень кожні Check for updates=Перевірити наÑвніÑть оновлень Consider active torrents as stalled when idle for=Вважати активні торренти завиÑлими, Ñкщо немає активноÑті Do you wish to enable automatic checking for a new version of %s?=Ви хочете включити автоматичну перевірку оновлень %s? Donate!=Перерахувати кошти! Download queue size=Розмір черги Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Error checking for new version=Помилка перевірки оновлень Folder grouping=Ð“Ñ€ÑƒÐ¿ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ папках Force start=ЗапуÑтити примуÑово Home page=Ð”Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñторінка Modify trackers=Змінити трекери Move bottom=ПереміÑтити в кінець Move down queue=Вниз в черзі Move down=ПереміÑтити вниз Move top=ПереміÑтити в початок Move up queue=Вгору в черзі Move up=ПереміÑтити вгору No updates have been found.~You are running the latest version of %s=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ знайдені.~У Ð²Ð°Ñ Ð²Ñтановлена ​​найновіша верÑÑ–Ñ %s Queue position=ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð² черзі Queue=Черга Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Торренти, Ð´Ð»Ñ Ñких не було активноÑті N хвилин, не враховуютьÑÑ Ð² черзі Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ роздачі Tracker grouping=Ð“Ñ€ÑƒÐ¿ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ трекерам Upload queue size=Розмір черги роздачі View=ВиглÑд Visit home page=Відвідати домашню Ñторінку days=днів Active time=Ð§Ð°Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñті Automatically add torrent links from the clipboard=Ðвтоматично додавати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° торенти з буферу обміну Copy file path to clipboard=Копіювати шлÑÑ… до файлу у буфер обміну Cumulative=Сукупний Current=Поточний Files added=Файли додано Filter pane=Вікно фільтрів Global statistics=Загальна ÑтатиÑтика Info pane=Інформаційна панель Statistics=СтатиÑтика Status bar=РÑдок Ñтану %dd=%dд %dh=%dг %dm=%dхв All torrents=Ð’ÑÑ– торенти Application options=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ Ask for password=Запит Ð¿Ð°Ñ€Ð¾Ð»Ñ Authentication required=Ðеобхідна Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Average out transfer speeds to eliminate fluctuations=Ð¡ÐµÑ€ÐµÐ´Ð½Ñ Ð²Ð¸Ñ…Ñ–Ð´Ð½Ð° швидкіÑть Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ ÐºÐ¾Ð»Ð¸Ð²Ð°Ð½ÑŒ Connect to %s=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· %s Connect to Transmission using proxy server=З'Ñ”Ð´Ð½Ð²Ð½Ð½Ñ Ð· Transmission через прокÑÑ– Ñервер Connect to Transmission=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Transmission Connection name=Ім'Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Could not connect to tracker==Ðе можу з'єднатиÑÑŒ з трекером Data display=Показати дані Data refresh interval when minimized=Інтервал Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… згорнутої програми Data refresh interval=Інтервал Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… Default download folder on remote host=Папка завантажень по замовчуванню на віддаленому вузлі Disconnect from Transmission=Роз'єднатиÑÑŒ з Transmission Downloading torrent file=Завантажити файл торенту Font size=Розмір шрифту Handle .torrent files by %s=Опрацьовувати .torrent файли за допомогою %s Handle magnet links by %s=Опрацьовувати magnet поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð·Ð° допомогою %s Invalid name specified=Вказано неправильне ім'Ñ Manage connections to Transmission=Керувати з'єднаннÑми з Transmission Manage connections=Керувати з'єднаннÑми Network (WAN)=Мережа (WAN) New connection to Transmission=Ðове з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Transmission Pick random port on Transmission launch=Обирати випадковий порт при запуÑку Transmission Please enter a password to connect to %s=Будь-лаÑка, введіть пароль Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Будь-лаÑка, визначте Ñк %s буде з'єднуватиÑÑŒ з віддаленим вузлом на Ñкому запущено Ñлужбу Transmission Prompt for download options when adding a new torrent=Запитувати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸ добавлені нового торенту RPC path=RPC шлÑÑ… Save as=Зберегти Ñк Seeding time=Ð§Ð°Ñ Ñ€Ð¾Ð·Ð´Ð°Ñ‡Ñ– Show advanced options=Показати розширені опції Show notifications in tray icon=Показувати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² треї System integration=Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð² ÑиÑтему Torrent already exists in the list=Торент вже в ÑпиÑку Torrent not registered with this tracker==Торент не зареєÑтровано на цьому трекері Unable to find path mapping.~Use the application's options to setup path mappings=Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ шлÑÑ… відображеннÑ.~ВикориÑтовуйте Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ ÑˆÐ»Ñхів. Update trackers for the existing torrent?=Оновити трекери Ð´Ð»Ñ Ñ–Ñнуючого торенту? You need to restart the application to apply changes=Треба перезавантажити програму Ð´Ð»Ñ Ð·Ð°ÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ TransGUI/lang/transgui.lv0000644000000000000000000003301312261612465014335 0ustar rootrootTranslationLanguage=LatvieÅ¡u %ds=%ds %d x %s (have %d)=%d x %s (novilkts %d) %s (%d hashfails)=%s (%d hashfaili) %s (%s done)=%s (%s pabeigti) '%s' has finished downloading='%s' vilkÅ¡ana pabeigta %s%s%d downloading, %d seeding%s%s, %s=%s%s%d velk, %d atdod%s%s, %s &All=Visi &Close=AizvÄ“rt &Help=PalÄ«dzÄ«ba &Ignore=IgnorÄ“t &No=NÄ“ &OK=&OK &Open=&AtvÄ“rt &Retry=AtkÄrtot &Save=SaglabÄt &Unlock=&AtbloÄ·Ä“t &Yes=JÄ Abort=Abort About=Par programmu Active=AktÄ«vi Add new torrent=Pievienot jaunu torrentu &Add torrent=Pievienot jaunu torrentu Added on=Pievienots Are you sure to remove torrent '%s' and all associated DATA?=Vai tieÅ¡Äm vÄ“laties dzÄ“st torrentu '%s', un visus ar to saistÄ«tos datus? Are you sure to remove torrent '%s'?=Vai tieÅ¡Äm vÄ“laties dzÄ“st torrentu '%s'? Authentication=AutentifikÄcija Cancel=Atcelt Client=Klients Close to tray=AizvÄ“rt minimizÄ“jot Comment=KomentÄrs Completed on=Pabeigts Completed=Pabeigti Confirmation=ApstiprinÄjums connected=savienoti Connecting to daemon=Savienojas ar Transmission Connection error occurred=SavienoÅ¡anÄs kļūda Copy=KopÄ“t Country=Valsts Created on=Izveidots D: %s/s=Velk: %s/s Destination folder=Mape saglabÄÅ¡anai Disconnected=AtslÄ“gts Donate to support further development=Atbalstiet finansiÄli tÄlÄko attÄ«stÄ«bu Donate via PayPal,WebMoney,Credit card=PÄrskaitÄ«t naudu caur PayPal,WebMoney,Credit card Done=Pabeigts Don't download=Nevilkt Down limit=Vilk. limits Down speed=Vilk. Ätrums Down=Uz leju Download complete=VilkÅ¡ana pabeigta Download speed=VilkÅ¡anas Ätrums Downloaded=Novilkts Downloading=Velk Enable DHT=Atļaut DHT Enable Peer Exchange=IeslÄ“gt iesaistÄ«to apmaiņu Enable port forwarding=IeslÄ“gt portu pÄradresÄciju Encryption=Å ifrēšana Error=Kļūda ETA=Atlicis E&xit=Iziet File name=Faila vÄrds Files=Faili Finished=Pabeigts Flag images archive is needed to display country flags.~Download this archive now?=Lai attÄ“lotu valsts karogus nepiecieÅ¡ams karogu attÄ“lu arhÄ«vs.~LejuplÄdÄ“t arhÄ«vu? Flags=Karogi General=VispÄrÄ«gi Geo IP database is needed to resolve country by IP address.~Download this database now?=Lai noteiktu valsti pÄ“c IP adreses nepiecieÅ¡ama Geo IP datubÄze.~LejuplÄdÄ“t Å¡o datubÄzi? Global bandwidth settings=KopÄ“jie Ätruma uzstÄdÄ«jumi Global peer limit=KopÄ“jais iesaistÄ«to skaits Hash=Hash Have=Novilkts Hide=NoslÄ“pt High priority=Augsta prioritÄte high=augsts Host=IP in swarm=gaida Inactive=NeaktÄ«vi Incoming port=Ieejas ports Information=InformÄcija KB/s=KB/s Last active=PÄ“dÄ“jÄ aktivitÄte License=Licence Low priority=Zema prioritÄte low=zems Max peers=IesaistÄ«to skaits Maximum download speed=MaksimÄlais vilkÅ¡anas Ätrums Maximum upload speed=MaksimÄlais atdoÅ¡anas Ätrums Minimize to tray=MinimizÄ“t sistÄ“mjoslÄ Name=Nosaukums No to all=NÄ“ visiem Normal priority=NormÄla prioritÄte normal=normÄls Open containing folder=AtvÄ“rt objektu saturoÅ¡o mapi Open=AtvÄ“rt Password=Parole Paths=Ceļi Peer limit=IesaistÄ«to skaits Peers=IesastÄ«tie Pieces=Daļiņas Port=Ports Priority=PrioritÄte Properties=Īpašības Proxy password=Proksi parole Proxy port=Proksi ports Proxy server=Proksi serveris Proxy user name=Proksi lietotÄjs Ratio=AttiecÄ«ba Reconnect in %d seconds=AtkÄrtoti savienoties pÄ“c %d sekundÄ“m Remaining=Atlicis Remote host=IP "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="LokÄlo ceļu piemÄ“roÅ¡anas attÄlinÄtiem.~~PiemÄ“ram:~/share=\\pch\share~/var/downloads/music=Z:\music" Remove torrent and Data=DzÄ“st torrentu un datus Remove torrent=DzÄ“st torrentu Remove=DzÄ“st Resolve country=Noteikt valsti Resolve host name=Noteikt IP vÄrdu seconds=sekundes Seed ratio=AttiecÄ«ba Seeding=Atdod Seeds=DevÄ“ji Select a .torrent to open=IzvÄ“lieties torrentu lai atvÄ“rtu Select all="=IzvÄ“lieties visu" Select none=Atcelt izvÄ“li Setup columns=UzstÄdÄ«t kolonnas Share ratio=AttiecÄ«ba Show country flag=RÄdÄ«t valsts karogu Show=RÄdÄ«t Size=IzmÄ“rs skip=izlaist Start all torrents=Palaist visus torrentus Start torrent=Palaist torrentu Start=Palaist Status=Statuss Stop all torrents=ApturÄ“t visus torrentus Stop all=ApturÄ“t visu Stop torrent=ApturÄ“t torrentu Stop=ApturÄ“t Stopped=ApturÄ“ts Test port=Porta tests T&ools=IestatÄ«jumi Torrent contents=Torrenta saturs Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrenta pÄrbaude var aizņemt daudz laika.~Vai esat pÄrliecinÄts, ka vÄ“laties pÄrbaudÄ«t torrentu '%s'? &Torrent=Torrents Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenti (*.torrent)|*.torrent|Visi faili (*.*)|*.* Total size=KopÄ“jais izmÄ“rs Tracker status=Trakera statuss Tracker update on=Trakera atjaunoÅ¡ana Tracker=Trakeris Trackers=Trakeri Transfer=PÄrsÅ«tÄ«ti Transmission options=Transmission konfigurÄcija Tray icon always visible=SistÄ“mjoslas ikona vienmÄ“r redzama Tray icon=SistÄ“mjoslas ikona U: %s/s=Atd.: %s/s Unable to extract flag image=Nav iespÄ“jams atpakot karoga attÄ“lu Unable to get files list=Nav iespÄ“jams iegÅ«t failu sarakstu Unknown=NezinÄms Up limit=Atd. limits Up speed=Atd. Ätrums Up=Uz augÅ¡u Update complete=AtjaunoÅ¡ana pabeigta Update GeoIP database=Atjaunot Geo IP datubÄzi Updating=Atjauno Update in=AtjaunoÅ¡ana pÄ“c Upload speed=AtdoÅ¡anas Ätrums Uploaded=Atdots User name=LietotÄjs Verify torrent=PÄrbaudÄ«t torrentu &Verify=PÄrbaudÄ«t Verifying=PÄrbauda Version %s=Versija %s Waiting=Gaida Warning=BrÄ«dinÄjums Wasted=Izniekoti Working=StrÄdÄ Yes to &All=JÄ visiem Transmission%s at %s:%s=Transmission%s atrodas %s:%s Torrent properties=Torrenta Ä«pašības /s=/s Encryption disabled=Å ifrēšana atslÄ“gta Encryption enabled=Å ifrēšana ieslÄ“gta Encryption required=VienmÄ“r lietot Å¡ifrēšanu Incoming port is closed. Check your firewall settings=IeejoÅ¡ais ports slÄ“gts. PÄrbaudiet jÅ«su ugunsmÅ«ra uzstÄdÄ«jumus Incoming port tested successfully=IeejoÅ¡ais ports darbojas of=no b=g GB=GB KBKB MB=MB TB=TB No host name specified=IP nav norÄdÄ«ta No proxy server specified=Proksi serveris nav norÄdÄ«ts Language=Valoda No tracker=Nav trakera %s downloaded=%s novilkts %s of %s downloaded=%s no %s novilkts %d torrents=%d torrenti Add .part extension to incomplete files=Pievienot paplaÅ¡inÄjumu .part pilnÄ«bÄ nenovilktiem failiem Add torrent link=Pievienot saiti uz torrentu Are you sure to remove %d selected torrents and all their associated DATA?=Vai tieÅ¡Äm vÄ“laties dzÄ“st %d atzÄ«mÄ“tos torrentus, un visus ar tiem saistÄ«tos datus? Are you sure to remove %d selected torrents?=Vai tieÅ¡Äm vÄ“laties dzÄ“st %d atzÄ«mÄ“tos torrentus? Bandwidth=Ä€trums Directory for incomplete files=Mape pilnÄ«bÄ nenovilktiem failiem Download=VilkÅ¡ana Enable blocklist=Atļaut bloÄ·Ä“to sarakstu ID=ID No link was specified=Nav norÄdÄ«ta saite No torrent location was specified=Nav norÄdÄ«ta torrenta atraÅ¡anÄs vieta Path=Ceļš Reannounce (get more peers)=ReanunsÄ“t (iegÅ«t vairÄk iesastÄ«tos) Set data location=NorÄdÄ«t atraÅ¡anÄs vietu Size to download=LejupielÄdes izmÄ“rs The block list has been updated successfully.~The list entries count: %d=Bloķēšanas saraksts atjaunots veigsmÄ«gi.~Ierakstu sakits: %d The directory for incomplete files was not specified=Nav norÄdÄ«ta mape pilnÄ«bÄ nenovilktiem failiem The downloads directory was not specified=Nav norÄdÄ«ta mape failu lejupielÄdei Torrents=Torrenti Unable to execute "%s"=Nav iespÄ“jams izpildÄ«t "%s" Update blocklist=Atjaunot bloÄ·Ä“to sarakstu URL of a .torrent file or a magnet link=URL .torrent failam vai magnet saite Move torrent data from current location to new location=PÄrvietot torrenta datus no dotÄs vietas uz jaunu New location for torrent data=JaunÄ torrenta datu atraÅ¡anÄs vieta Torrent data location=Torrenta datu atraÅ¡anÄs vieta Columns setup=Kolonu uzstÄdīšana KB=KB Add torrent=Add torrent Delete a .torrent file after a successful addition=Delete a .torrent file after a successful addition Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrents verification may take a long time.~Are you sure to start verification of %d torrents? Unable to load OpenSSL library files: %s and %s=Unable to load OpenSSL library files: %s and %s Use SSL=Use SSL Add tracker=Add tracker Alternate bandwidth settings=Alternate bandwidth settings Apply alternate bandwidth settings automatically=Apply alternate bandwidth settings automatically Are you sure to delete connection '%s'?=Are you sure to delete connection '%s'? Are you sure to remove tracker '%s'?=Are you sure to remove tracker '%s'? Days=Days Delete=Delete Disk cache size=Disk cache size Download speeds (KB/s)=Download speeds (KB/s) Edit tracker=Edit tracker Enable Local Peer Discovery=Enable Local Peer Discovery Free disk space=Free disk space Free: %s=Free: %s From=From minutes=minutes Misc=Misc New connection=New connection New=New No tracker URL was specified=No tracker URL was specified Proxy=Proxy Remove tracker=Remove tracker Rename=Rename Speed limit menu items=Speed limit menu items Stop seeding when inactive for=Stop seeding when inactive for The invalid time value was entered=The invalid time value was entered to=to Tracker announce URL=Tracker announce URL Tracker properties=Tracker properties Unlimited=Unlimited Upload speeds (KB/s)=Upload speeds (KB/s) Use alternate bandwidth settings=Use alternate bandwidth settings average=average Browse=Browse Enable µTP=Enable µTP Select a folder for download=Select a folder for download Select torrent location=Select torrent location A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.pt_br0000644000000000000000000003270112261612465015025 0ustar rootrootTranslationLanguage=Portugues do Brasil "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Mapeamentos de caminho remoto para local. Examplo:~/share=\\pch\share~/var/downloads/musicas=Z:\musicas" %d x %s (have %d)=%d x %s (have %d) %ds=%ds %s (%d hashfails)=%s (%d falhas) %s (%s done)=%s (%s concluido) '%s' has finished downloading='%s' terminou de baixar %s%s%d downloading, %d seeding%s%s, %s=%s%s%d baixando, %d semeando%s%s, %s &All=&Todos &Close=&Fechar &Help=&Ajuda &Ignore=&Ignorar &No=&Não &OK=&OK &Open=&Abrir &Retry=&Repetir &Save=&Salvar &Unlock=&Desbloquear &Yes=&Sim /s=/s Abort=Abortar About=Sobre Active=Ativos Add .part extension to incomplete files=Adicionar extenção .part aos arquivos incompletos Add new torrent=Adicionar novo torrent &Add torrent=Adicionar torrent Add torrent link=Adicionar link do torrent Added on=Adicionado em Are you sure to remove torrent '%s' and all associated DATA?=Tem certeza que deseja remover o torrent '%s' e todos os arquivos associados? Are you sure to remove torrent '%s'?=Tem certeza que deseja remover o torrent '%s'? Authentication=Autenticação b=b Cancel=Cancelar Client=Cliente Close to tray=Minimizar para a bandeja Comment=Comentario Completed on=Completo em Completed=Completo Confirmation=Confirmação connected=conectado Connecting to daemon=Conectando ao daemon Connection error occurred=Ocorreu um erro de conexão Copy=Copiar Country=Pais Created on=Criado em D: %s/s=D: %s/s Destination folder=Pasta de destino Directory for incomplete files=Pasta dos arquivos incompletos Disconnected=Desconectado Donate to support further development=Doar para suporte e desenvolvimento Donate via PayPal,WebMoney,Credit card=Doação via PayPal,WebMoney,Credit card Done=Pronto Don't download=Não baixar Down limit=Limite de download Down speed=Velocidade de download Down=Download Download complete=Download completo Download speed=Velocidade de download Downloaded=Recebidos Downloading=Baixando Enable blocklist=Habilitar lista de bloqueio Enable DHT=Habilitar DHT Enable Peer Exchange=Habilitar troca de peer Enable port forwarding=Habilitar redirecionamento de portas Encryption disabled=Encriptação desabilitada Encryption enabled=Encriptação habilitada Encryption required=Exigir encriptação Encryption=Encriptação Error=Erro ETA=ETA E&xit=Sair File name=Nome do arquivo Files=Arquivo Finished=Finalizado Flag images archive is needed to display country flags.~Download this archive now?=O arquivo de bandeiras é necessário para exibir bandeiras de países. Baixar agora? Flags=Bandeiras GB=GB General=Geral Geo IP database is needed to resolve country by IP address.~Download this database now?=Banco de dados Geo IP é necessária para descobrir o país pelo endereço IP. Baixar este banco de dados agora? Global bandwidth settings=Configurações globais de banda Global peer limit=Limite global de peer Hash=Hash Have=Tem(Have) Hide=Ocultar High priority=Alta prioridade high=Alto Host=Host in swarm=na multidão Inactive=Inativo Incoming port is closed. Check your firewall settings=Porta de entrada está fechada, verifique a configuração do firewall Incoming port=Porta de Entrada Information=Informação KB/s=KB/s KB=KB Language=Linguagem Last active=Ultimo ativo License=Licença Low priority=Baixa prioridade low=low Max peers=Maximo de peers Maximum download speed=Velocidade maxima de download Maximum upload speed=Velocidade maxima de upload MB=MB Minimize to tray=Minimizar para a bandeja Name=Nome No host name specified=Hostname não especificado No proxy server specified=Servidor de proxy não especificado No to all=Não para todos Normal priority=Prioridade normal normal=normal of=de Open containing folder=Abrir pasta de destino Open=Abrir Password=Senha Paths=Caminho Peer limit=Limite de peers Peers=Peers Pieces=Peças Port=Porta Priority=Prioridade Properties=Propriedade Proxy password=Senha do proxy Proxy port=Porta do proxy Proxy server=Servidor proxy Proxy user name=Usuario proxy Ratio=Relação Reannounce (get more peers)=Reanunciar (Obter mais peers) Reconnect in %d seconds=Reconectar em %d segundos Remaining=Restando Remote host=Host remoto Remove torrent and Data=Remover torrent e arquivos Remove torrent=Remover torrent Remove=Remover Resolve country=Resolver pais Resolve host name=Resolver hostname seconds=segundos Seed ratio=Relação de seeds Seeding=Semeando Seeds=Sementes Select a .torrent to open=Selecionar o .torrent para abrir Select all=Selecionar todos Select none=Deselecionar Set data location=Configurar localização dos arquivos Setup columns=Configurar colunas Share ratio=Relação de compartilhamento Show country flag=Mostrar a bandeira do país Show=Exibir Size=Tamanho skip=Pular Start all torrents=Iniciar todos os torrents Start torrent=Iniciar torrent Start=Iniciar Status=Status Stop all torrents=Parar todos os torrents Stop all=Parar tudo Stop torrent=Parar torrent Stop=Parar Stopped=Parado TB=TB Test port=Testar porta T&ools=Ferramentas Torrent contents=Conteudo do torrent Torrent properties=Propriedade do torrent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=A verificação pode levar um longo tempo. Você tem certeza que deseja verificar o torrent '% s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrents (*.torrent)|*.torrent|Todos os arquivos (*.*)|*.* Total size=Tamanho total Tracker status=Estado do tracker Tracker update on=Tracker atualizado em Tracker=Tracker Trackers=Trackers Transfer=Transferir Transmission options=Opções do Transmission Transmission%s at %s:%s=Transmission%s at %s:%s Tray icon always visible=Exibir icone na bandeja Tray icon=Icone na bandeja U: %s/s=U: %s/s Unable to extract flag image=Não foi possível extrair imagem da bandeira Unable to get files list=Não foi possível obter a lista de arquivos Unknown=Desconhecido Up limit=limitede upload Up speed=Velocidade de upload Up=Upload Update blocklist=Atualizar lista de bloqueados Update complete=Atualização completa Update GeoIP database=Atualizar dados do GeoIP Update in=Atualizado em Updating=Atualizando Upload speed=Velocidade de upload Uploaded=Enviados User name=Login Verify torrent=Verificar torrent &Verify=Verificar Verifying=Verificando Version %s=Versão %s Waiting=Aguardando Warning=Aviso Wasted=Descartado Working=Trabalhando Yes to &All=Sim para &Todos No tracker=Sem tracker %d torrents=%d torrents %s downloaded=%s downloaded %s of %s downloaded=%s of %s downloaded Are you sure to remove %d selected torrents and all their associated DATA?=Are you sure to remove %d selected torrents and all their associated DATA? Are you sure to remove %d selected torrents?=Are you sure to remove %d selected torrents? Bandwidth=Bandwidth Columns setup=Columns setup Download=Download ID=ID Incoming port tested successfully=Incoming port tested successfully Move torrent data from current location to new location=Move torrent data from current location to new location New location for torrent data=New location for torrent data No link was specified=No link was specified No torrent location was specified=No torrent location was specified Path=Path Size to download=Size to download The block list has been updated successfully.~The list entries count: %d=The block list has been updated successfully.~The list entries count: %d The directory for incomplete files was not specified=The directory for incomplete files was not specified The downloads directory was not specified=The downloads directory was not specified Torrent data location=Torrent data location Torrents=Torrents Unable to execute "%s"=Unable to execute "%s" URL of a .torrent file or a magnet link=URL of a .torrent file or a magnet link Add torrent=Add torrent Delete a .torrent file after a successful addition=Delete a .torrent file after a successful addition Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torrents verification may take a long time.~Are you sure to start verification of %d torrents? Unable to load OpenSSL library files: %s and %s=Unable to load OpenSSL library files: %s and %s Use SSL=Use SSL Add tracker=Add tracker Alternate bandwidth settings=Alternate bandwidth settings Apply alternate bandwidth settings automatically=Apply alternate bandwidth settings automatically Are you sure to delete connection '%s'?=Are you sure to delete connection '%s'? Are you sure to remove tracker '%s'?=Are you sure to remove tracker '%s'? Days=Days Delete=Delete Disk cache size=Disk cache size Download speeds (KB/s)=Download speeds (KB/s) Edit tracker=Edit tracker Enable Local Peer Discovery=Enable Local Peer Discovery Free disk space=Free disk space Free: %s=Free: %s From=From minutes=minutes Misc=Misc New connection=New connection New=New No tracker URL was specified=No tracker URL was specified Proxy=Proxy Remove tracker=Remove tracker Rename=Rename Speed limit menu items=Speed limit menu items Stop seeding when inactive for=Stop seeding when inactive for The invalid time value was entered=The invalid time value was entered to=to Tracker announce URL=Tracker announce URL Tracker properties=Tracker properties Unlimited=Unlimited Upload speeds (KB/s)=Upload speeds (KB/s) Use alternate bandwidth settings=Use alternate bandwidth settings average=average Browse=Browse Enable µTP=Enable µTP Select a folder for download=Select a folder for download Select torrent location=Select torrent location A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Advanced Check for new version every=Check for new version every Check for updates=Check for updates Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Do you wish to enable automatic checking for a new version of %s? Donate!=Donate! Download queue size=Download queue size Error checking for new version=Error checking for new version Folder grouping=Folder grouping Force start=Force start Home page=Home page Modify trackers=Modify trackers Move bottom=Move bottom Move down queue=Move down queue Move down=Move down Move top=Move top Move up queue=Move up queue Move up=Move up No updates have been found.~You are running the latest version of %s=No updates have been found.~You are running the latest version of %s Queue position=Queue position Queue=Queue Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue Tracker grouping=Tracker grouping Upload queue size=Upload queue size View=View Visit home page=Visit home page days=days Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.be0000644000000000000000000005020512261612465014304 0ustar rootrootTranslationLanguage=БеларуÑÐºÐ°Ñ %ds=%dÑ %d x %s (have %d)=%d x %s (у наÑўнаÑьці %d) %s (%d hashfails)=%s (%d памылак Ñ…Ñшу) %s (%s done)=%s (%s гатова) '%s' has finished downloading=Запампоўка '%s' завершана %s%s%d downloading, %d seeding%s%s, %s=%s%s%d запампоўваецца, %d раздаецца%s%s, %s &All=УÑÑ‘ &Close=Зачыніць &Help=Дапамога &Ignore=Ігнараваць &No=Ðе &OK=&OK &Open=&Ðдчыніць &Retry=Паўтор &Save=Захаваць &Unlock=&Ðдамкнуць &Yes=Так Abort=Ðдмена About=Ðб праграме Active=ÐÐºÑ‚Ñ‹ÑžÐ½Ñ‹Ñ Add new torrent=Даданьне новага торÑнту &Add torrent=Дадаць торÑнт Added on=Даданы Are you sure to remove torrent '%s' and all associated DATA?=Ð’Ñ‹ Ñапраўды жадаеце выдаліць торÑнт~'%s'~Ñ– ÑžÑе дадзеныÑ, зьвÑÐ·Ð°Ð½Ñ‹Ñ Ð·ÑŒ ім? Are you sure to remove torrent '%s'?=Ð’Ñ‹ Ñапраўды жадаеце выдаліць торÑнт~'%s' ? Authentication=ÐўтÑÐ½Ñ‚Ñ‹Ñ„Ñ–ÐºÐ°Ñ†Ñ‹Ñ Cancel=Ðдмена Client=Кліент Close to tray=ЗачынÑць у трÑй Comment=КамÑнтар Completed on=Завершаны Completed=Завершаны Confirmation=Пацьцвержаньне connected=падлучаны Connecting to daemon=ПадлучÑньне да дÑману Transmission Connection error occurred=Памылка падлучÑÐ½ÑŒÐ½Ñ Copy=КапіÑваць Country=Краіна Created on=Створаны D: %s/s=Загр: %s/Ñ Destination folder=ТÑчка Ð´Ð»Ñ Ð·Ð°Ð¿Ð°Ð¼Ð¿Ð¾ÑžÐºÑ– Disconnected=Ðдлучаны Donate to support further development=Калі-лаÑка, падтрымайце далейшае разьвіцьцё праекту Donate via PayPal,WebMoney,Credit card=Пералічыць Ñродкі праз PayPal,WebMoney,Credit card Done=Гатова Don't download=Ðе пампаваць Down limit=Ліміт запампоўкі Down speed=ХуткаÑьць запампоўкі Down=Уніз Download complete=Запампоўка завершана Download speed=ХуткаÑьць запампоўкі Downloaded=Запампавана Downloading=Пампуецца Enable DHT=Дазволіць DHT Enable Peer Exchange=Дазволіць абмен пірамі Enable port forwarding=Дазволіць пракід порту Encryption=Шыфраваньне Error=Памылка ETA=ЗаÑталоÑÑ Ñ‡Ð°Ñу E&xit=ВыйÑьце File name=Ð†Ð¼Ñ Ñ„Ð°Ð¹Ð»Ñƒ Files=Файлы Finished=Завершана Flag images archive is needed to display country flags.~Download this archive now?=Ð”Ð·ÐµÐ»Ñ Ð°Ð´Ð»ÑŽÑÑ‚Ñ€Ð°Ð²Ð°Ð½ÑŒÐ½Ñ ÑьцÑгаў краін неабходны архіў зь выÑвамі ÑьцÑгаў.~Запампаваць гÑты архіў зараз? Flags=СьцÑгі General=Ðгульнае Geo IP database is needed to resolve country by IP address.~Download this database now?=Ð”Ð»Ñ Ð²Ñ‹Ð·Ð½Ð°Ñ‡ÑÐ½ÑŒÐ½Ñ ÐºÑ€Ð°Ñ–Ð½Ñ‹ згодна з IP-адраÑам, неабходна база дадзеных Geo IP.~Запампаваць базу дадзеных зараз? Global bandwidth settings=ÐÐ³ÑƒÐ»ÑŒÐ½Ñ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹ хуткаÑьці Global peer limit=Ðгульны ліміт піраў Hash=Ð¥Ñш Have=у наÑўнаÑьці Hide=Схаваць High priority=ВышÑйшы прыÑрытÑÑ‚ high=вышÑйшы Host=Вузел in swarm=у кучы Inactive=ÐÐµÐ°ÐºÑ‚Ñ‹ÑžÐ½Ñ‹Ñ Incoming port=УваходзÑчы порт Information=Ð†Ð½Ñ„Ð°Ñ€Ð¼Ð°Ñ†Ñ‹Ñ KB/s=КБ/Ñ Last active=ÐпошнÑÑ Ð°ÐºÑ‚Ñ‹ÑžÐ½Ð°Ñьць License=ЛіцÑÐ½Ð·Ñ–Ñ Low priority=ÐіжÑйшы прыÑрытÑÑ‚ low=ніжÑÐ¹ÑˆÐ°Ñ Max peers=Ліміт колькаÑьці піраў Maximum download speed=Ліміт хуткаÑьці запампоўкі Maximum upload speed=Ліміт хуткаÑьці ÑÑ–Ð´Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Minimize to tray=Згортваць у трÑй Name=Ðазва No to all=ÐЕ Ð´Ð»Ñ ÑžÑÑ–Ñ… Normal priority=Ðармалёвы прыÑрытÑÑ‚ normal=нармалёвы Open containing folder=Ðдчыніць Ñ‚Ñчку, што утрымоўвае файлы Open=Ðдчыніць Password=Пароль Paths=ШлÑÑ…Ñ– Peer limit=Ліміт піраў Peers=Піры Pieces=ЧаÑтак Port=Порт Priority=ПрыÑрытÑÑ‚ Properties=УлаÑьціваÑьці Proxy password=Пароль прокÑÑ– Proxy port=Порт прокÑÑ– Proxy server=ПрокÑÑ–-ÑÑрвÑÑ€ Proxy user name=КарыÑтальнік прокÑÑ– Ratio=СуадноÑіны Reconnect in %d seconds=Спроба падлучÑÐ½ÑŒÐ½Ñ Ð¿Ñ€Ð°Ð·ÑŒ %d ÑÑкундаў Remaining=ЗаÑталоÑÑ Remote host=Ðддалены хоÑÑ‚ "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="СупаÑтаўленьне лакальных шлÑхоў аддаленым.~~Ðапрыклад:~/share=\\pch\share~/var/downloads/music=Z:\music" Remove torrent and Data=Выдаліць торÑнт Ñ– файлы Remove torrent=Выдаліць торÑнт Remove=Выдаліць Resolve country=Вызначаць краіну Resolve host name=Вызначаць Ñ–Ð¼Ñ Ñ…Ð¾Ñту seconds=ÑÑкундаў Seed ratio=КаÑфіцыент аддачы Seeding=Раздаецца Seeds=Сіды Select a .torrent to open=ÐбÑрыце торÑнт-файл Select all=Ðбраць уÑÑ‘ Select none=ÐдмÑніць выбар Setup columns=Ðаладзіць Ñлупкі Share ratio=РÑйтынг Show country flag=ÐдлюÑтраваць ÑьцÑг краіны Show=Паказаць Size=Памер skip=прапуÑьціць Start all torrents=ЗапуÑьціць уÑе торÑнты Start torrent=ЗапуÑьціць торÑнт Start=ЗапуÑьціць Status=Стан Stop all torrents=Спыніць уÑе торÑнты Stop all=Спыніць уÑÑ‘ Stop torrent=Спыніць торÑнт Stop=Спыніць Stopped=Спынена Test port=Праверка порту T&ools=Прылады Torrent contents=ЗьмеÑÑ‚ торÑнту Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Ð”Ð»Ñ Ð¿Ñ€Ð°Ð²ÐµÑ€ÐºÑ– торÑнту можа ÑпатрÑбіцца шмат чаÑу.~Ð’Ñ‹ ўпÑўнены, што жадаеце праверыць торÑнт '%s'? &Torrent=ТорÑнт Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=ТорÑнты (*.torrent)|*.torrent|УÑе файлы (*.*)|*.* Total size=Ðгульны памер Tracker status=Стан трÑкеру Tracker update on=Ðбнаўленьне трÑкеру Tracker=ТрÑкер Trackers=ТрÑкеры Transfer=Перадача Transmission options=ПарамÑтры Transmission Tray icon always visible=ÐдлюÑтроўваць значку Ñž трÑÑ– Tray icon=СіÑÑ‚Ñмны трÑй U: %s/s=Разд: %s/Ñ Unable to extract flag image=Ðемагчыма вынÑць выÑву ÑьцÑгу Unable to get files list=Ðемагчыма атрымаць ÑÑŒÐ¿Ñ–Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž Unknown=ÐевÑдома Up limit=Ліміт аддачы Up speed=Ðддача Up=Угору Update complete=Ðбнаўленьне завершана Update GeoIP database=Ðбнавіць базу дадзеных GeoIP Updating=ÐбнаўлÑецца Update in=Ðбнаўленьне праз Upload speed=ХуткаÑьць аддачы Uploaded=Ðддадзена User name=КарыÑтальнік Verify torrent=Праверыць торÑнт &Verify=Праверыць Verifying=Праверка Version %s=Ð’ÑÑ€ÑÑ–Ñ %s Waiting=Чаканьне Warning=ПапÑÑ€Ñджаньне Wasted=Запампавана дарÑмна Working=Працуе Yes to &All=Так, Ð´Ð»Ñ ÑžÑÑ–Ñ… Transmission%s at %s:%s=Transmission%s на %s:%s Torrent properties=УлаÑьціваÑьці торÑнту /s=/Ñ Encryption disabled=Шыфраваньне адключана Encryption enabled=Шыфраваньне ўключана Encryption required=ЗаўÑёды патрабуецца шыфраваньне Incoming port is closed. Check your firewall settings=УваходзÑчы порт зачынены. Праверце налады вашае ÑетказаÑлоны. Incoming port tested successfully=УваходзÑчы порт правераны паÑьпÑхова. of=з b=б GB=ГБ KB=КБ MB=МБ TB=ТБ No host name specified=Ðе вызначаны вузел. No proxy server specified=Ðе вызначаны прокÑÑ–-ÑÑрвÑÑ€. Language=мова No tracker=ÐÑма трÑкеру %s downloaded=%s запампавана %s of %s downloaded=%s з %s запампавана %d torrents=%d торÑнтаў Add .part extension to incomplete files=Дадаваць пашырÑньне .part да чаÑткова запампованых файлаў Add torrent link=Дадаць ÑпаÑылку на торÑнт Are you sure to remove %d selected torrents and all their associated DATA?=Ð’Ñ‹ Ñапраўды жадаеце выдаліць %d адзначаных торÑнтаў Ñ– ÑžÑе дадзеныÑ, зьвÑÐ·Ð°Ð½Ñ‹Ñ Ð·ÑŒ імі? Are you sure to remove %d selected torrents?=Ð’Ñ‹ Ñапраўды жадаеце выдаліць %d адзначаных торÑнтаў? Bandwidth=ХуткаÑьць Directory for incomplete files=ТÑчка Ð´Ð»Ñ Ñ‡Ð°Ñткова запампаваных файлаў Download=Запампоўка Enable blocklist=Задзейнічаць ÑÑŒÐ¿Ñ–Ñ Ð±Ð»Ð°ÐºÑ–Ñ€Ð¾Ð²Ð°Ðº ID=ID No link was specified=Ðе вызначана ÑпаÑылка No torrent location was specified=Ðе вызначана меÑца Ð·Ð°Ñ…Ð¾ÑžÐ²Ð°Ð½ÑŒÐ½Ñ Ñ‚Ð¾Ñ€Ñнту Path=ШлÑÑ… Reannounce (get more peers)=ÐнанÑаваць прымуÑова (атрымаць болей піраў) Set data location=Задаць новае меÑцазнаходжаньне файлаў Size to download=Памер запампоўкі The block list has been updated successfully.~The list entries count: %d=Ð¡ÑŒÐ¿Ñ–Ñ Ð±Ð»Ð°ÐºÑ–Ñ€Ð¾Ð²Ð°Ðº паÑьпÑхова абноўлены.~КолькаÑьць Ñлементаў у ÑьпіÑе: %d The directory for incomplete files was not specified=ÐÑ Ð²Ñ‹Ð·Ð½Ð°Ñ‡Ð°Ð½Ð° Ñ‚Ñчка Ð´Ð»Ñ Ð½ÐµÐ´Ð°Ð¿Ð°Ð¼Ð¿Ð°Ð²Ð°Ð½Ñ‹Ñ… файлаў The downloads directory was not specified=ÐÑ Ð²Ñ‹Ð·Ð½Ð°Ñ‡Ð°Ð½Ð° Ñ‚Ñчка Ð´Ð»Ñ Ð·Ð°Ð¿Ð°Ð¼Ð¿Ð¾ÑžÐºÑ– файлаў Torrents=ТорÑнты Unable to execute "%s"=Ðемагчыма выканаць "%s" Update blocklist=Ðбнавіць ÑÑŒÐ¿Ñ–Ñ Ð±Ð»Ð°ÐºÑ–Ñ€Ð¾Ð²Ð°Ðº URL of a .torrent file or a magnet link=URL Ð´Ð»Ñ .torrent файлу ці magnet link Move torrent data from current location to new location=ПераÑунуць Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ Ð·ÑŒ бÑгучага меÑца Ñž новае New location for torrent data=Ðовае размÑшчÑньне дадзеных торÑнту Torrent data location=РазмÑшчÑньне дадзеных торÑнту Columns setup=Ðалады Ñлупкоў Add torrent=Дадаць торÑнт Torrent=ТорÑнт Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Праверка торÑнтаў можа занÑць шмат чаÑу.~Ð’Ñ‹ ўпÑўнены, што жадаеце праверыць %d торÑнтаў? Unable to load OpenSSL library files: %s and %s=Ðемагчыма задзейнічаць файлы бібліÑÑ‚Ñкі OpenSSL: %s Ñ– %s Delete a .torrent file after a successful addition=Выдаліць .torrent файл паÑÑŒÐ»Ñ Ð¿Ð°ÑьпÑховага Ð´Ð°Ð´Ð°Ð½ÑŒÐ½Ñ Use SSL=ВыкарыÑтоўваць SSL Add tracker=Дадаць трÑкер Alternate bandwidth settings=ÐльтÑÑ€Ð½Ð°Ñ‚Ñ‹ÑžÐ½Ñ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹ хуткаÑьці Apply alternate bandwidth settings automatically=УÑталёўваць альтÑÑ€Ð½Ð°Ñ‚Ñ‹ÑžÐ½Ñ‹Ñ Ñ…ÑƒÑ‚ÐºÐ°Ñьці аўтаматычна Are you sure to delete connection '%s'?=Ð’Ñ‹ Ñапраўды жадаеце выдаліць злучÑньне '%s'? Are you sure to remove tracker '%s'?=Ð’Ñ‹ Ñапраўды жадаеце выдаліць трÑкер '%s'? Days=Дзён Delete=Выдаліць Disk cache size=Памер дыÑкавага кÑшу Download speeds (KB/s)=ХуткаÑьць запампоўкі (Kб/Ñ) Edit tracker=РÑдагаваць трÑкер Enable Local Peer Discovery=Дазволіць мÑÑÑ†Ð¾Ð²Ñ‹Ñ Ð¿Ñ–Ñ€Ñ‹ Free disk space=Ð’Ð¾Ð»ÑŒÐ½Ð°Ñ Ð´Ñ‹ÑÐºÐ°Ð²Ð°Ñ Ð¿Ñ€Ð°Ñтора Free: %s=Вольна: %s From=з minutes=хвілін Misc=Рознае New connection=Ðовае злучÑньне New=Стварыць No tracker URL was specified=ÐÑ Ð²Ñ‹Ð·Ð½Ð°Ñ‡Ð°Ð½Ñ‹ URL трÑкеру Proxy=ПрокÑÑ–-ÑÑрвÑÑ€ Remove tracker=Выдаліць трÑкер Rename=Перайменаваць Speed limit menu items=Элементы мÑню Ð°Ð±Ð¼ÐµÐ¶Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Ñ…ÑƒÑ‚ÐºÐ°Ñьці Stop seeding when inactive for=Спыніць Ñідаваньне калі неактыўна The invalid time value was entered=Уведзена нÑÑлушнае значÑньне чаÑу to=па Tracker announce URL=Ðбвешчаньне URL трÑкеру Tracker properties=УлаÑьціваÑьці трÑкеру Unlimited=Ðеабмежавана Upload speeds (KB/s)=ХуткаÑьць аддачы (Kб/Ñ) Use alternate bandwidth settings=ВыкарыÑтоўваць альтÑÑ€Ð½Ð°Ñ‚Ñ‹ÑžÐ½Ñ‹Ñ Ñ…ÑƒÑ‚ÐºÐ°Ñьці average=ÑÑÑ€ÑднÑÑ Browse=ÐглÑд Enable µTP=Дазволіць µTP Select a folder for download=ÐбÑрыце шлÑÑ… Ð´Ð»Ñ Ð·Ð°Ð¿Ð°Ð¼Ð¿Ð¾ÑžÐºÑ– Select torrent location=ÐбÑрыце меÑцазнаходжаньне торÑнту A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=ДаÑÑжна Ð½Ð¾Ð²Ð°Ñ Ð²ÑÑ€ÑÑ–Ñ %s.~Ð’Ð°ÑˆÐ°Ñ Ð±ÑÐ³ÑƒÑ‡Ð°Ñ Ð²ÑÑ€ÑÑ–Ñ: %s~ÐÐ¾Ð²Ð°Ñ Ð²ÑÑ€ÑÑ–Ñ: %s~~ПерайÑьці на Ñтаронку запампоўкі? Advanced=Дадаткова Check for new version every=ПравÑраць наÑўнаÑьць абнаўленьнÑÑž ÐºÐ¾Ð¶Ð½Ñ‹Ñ Check for updates=Праверыць наÑўнаÑьць Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Consider active torrents as stalled when idle for=Лічыць торÑнты завіÑлымі, калі Ñны Ð½ÐµÐ°ÐºÑ‚Ñ‹ÑžÐ½Ñ‹Ñ Do you wish to enable automatic checking for a new version of %s?=Ці жадаеце ўключыць аўтаматычную праверку абнаўленьнÑÑž %s? Donate!=ÐхвÑраваць Ñродкі! Download queue size=Памер чаргі запамповак Error checking for new version=Памылка праверкі Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Folder grouping=Гуртаваньне па Ñ‚Ñчках Force start=ПрымуÑовы Ñтарт Home page=ХатнÑÑ Ñтаронка Modify trackers=ЗьмÑніць трÑкеры Move bottom=ПераÑунуць у канец Move down queue=ПераÑунуць у канец чаргі Move down=ПераÑунуць уніз Move top=ПераÑунуць у пачатак Move up queue=ПераÑунуць у пачатак чаргі Move up=ПераÑунуць угору No updates have been found.~You are running the latest version of %s=Ðбнаўленьні Ð½Ñ Ð·Ð½Ð¾Ð¹Ð´Ð·ÐµÐ½Ñ‹Ñ.~Ð’Ñ‹ маеце найноўшую вÑÑ€ÑÑ–ÑŽ %s Queue position=МеÑца Ñž чарзе Queue=Чарга Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=ТорÑнты, што былі Ð½ÐµÐ°ÐºÑ‚Ñ‹ÑžÐ½Ñ‹Ñ N хвілін Ð½Ñ ÑžÐ»Ñ–Ñ‡Ð²Ð°ÑŽÑ†Ñ†Ð° Ñž чарзе запампоўкі ці аддачы Tracker grouping=Гуртаваньне па трÑкерах Upload queue size=Памер чÑргі аддачы View=ВыглÑд Visit home page=Ðаведаць хатнюю Ñтаронку days=дзён Active time=Ð§Ð°Ñ Ð°ÐºÑ‚Ñ‹ÑžÐ½Ð°Ñьці Automatically add torrent links from the clipboard=Ðўтаматычна дадаць ÑпаÑылкі на торÑнт зь буфÑру абмену Copy file path to clipboard=Капіраваць шлÑÑ… да файлу Ñž буфÑÑ€ абмену Cumulative=За ўвеÑÑŒ Ñ‡Ð°Ñ Current=БÑÐ³ÑƒÑ‡Ð°Ñ ÑÑÑÑ–Ñ Files added=Дададзена файлаў Filter pane=ПанÑль фільтрацыі Global statistics=ÐÐ³ÑƒÐ»ÑŒÐ½Ð°Ñ ÑтатыÑтыка Info pane=ПанÑль інфармацыі Statistics=СтатыÑтыка Status bar=Радок Ñтану %dd=%dd %dh=%dh %dm=%dm All torrents=УÑе торÑнты Application options=Опцыі праграмы Ask for password=Запытваць пароль Authentication required=Запыт аўтарызацыі Average out transfer speeds to eliminate fluctuations=СÑÑ€ÑднÑÑ Ñ…ÑƒÑ‚ÐºÐ°Ñьць перадачы дадзеных з ліквідацыÑй ваганьнÑÑž Connect to %s=ПадлучÑньне да %s Connect to Transmission using proxy server=Падлучыцца да Transmission праз прокÑÑ–-ÑÑрвÑÑ€ Connect to Transmission=Падлучыцца да Transmission Connection name=Ðазва злучÑÐ½ÑŒÐ½Ñ Could not connect to tracker==Ðемагчыма падлучыцца да трÑкеру Data display=ÐдлюÑтраваньне дадзеных Data refresh interval when minimized=ІнтÑрвал Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ… у згорнутым Ñтане Data refresh interval=ІнтÑрвал Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ… Default download folder on remote host=Ð—Ð²Ñ‹Ñ‡Ð°Ð¹Ð½Ð°Ñ Ñ‚Ñчка Ð´Ð»Ñ Ð·Ð°Ð¿Ð°Ð¼Ð¿Ð¾ÑžÐºÑ– на адлеглым вузле Disconnect from Transmission=Ðдлучыцца ад Transmission Downloading torrent file=Запампоўка торÑнт файлу Font size=Памер шрыфту Handle .torrent files by %s=Ðпрацаваць .torrent файлы з дапамогай %s Handle magnet links by %s=Ðпрацаваць magnet ÑпаÑылкі з дапамогай %s Invalid name specified=Вызначана неправільнае Ñ–Ð¼Ñ Manage connections to Transmission=Кіраваньне падлучÑньнÑмі да Transmission Manage connections=Кіраваньне падлучÑньнÑмі Network (WAN)=Сеціва (WAN) New connection to Transmission=Ðовае падлучÑньне да Transmission Pick random port on Transmission launch=ВыкарыÑтоўваць выпадковы порт Ð¿Ð°Ð´Ñ‡Ð°Ñ Ð·Ð°Ð¿ÑƒÑку Transmission Please enter a password to connect to %s=Калі-лаÑка, увÑдзіце пароль Ð´Ð»Ñ Ð¿Ð°Ð´Ð»ÑƒÑ‡ÑÐ½ÑŒÐ½Ñ Ð´Ð° %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Калі-лаÑка, вызначце Ñкім чынам %s будзе падлучацца да адлеглага хоÑту, дзе працуе дÑман Transmission Prompt for download options when adding a new torrent=Запытваць парамÑтры запампоўкі Ð¿Ð°Ð´Ñ‡Ð°Ñ Ð´Ð°Ð´Ð°Ð½ÑŒÐ½Ñ Ð½Ð¾Ð²Ð°Ð³Ð° торÑнту RPC path=ШлÑÑ… RPC Save as=Захаваць Ñк Seeding time=Ð§Ð°Ñ ÑÑ–Ð´Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Show advanced options=Паказаць Ð¿Ð°ÑˆÑ‹Ñ€Ð°Ð½Ñ‹Ñ Ð¿Ð°Ñ€Ð°Ð¼Ñтры Show notifications in tray icon=ÐдлюÑтроўваць паведамленьні з абразку Ñž трÑÑ– System integration=ІнтÑÐ³Ñ€Ð°Ñ†Ñ‹Ñ Ð·ÑŒ ÑÑ–ÑÑ‚Ñмай Torrent already exists in the list=ТорÑнт ужо Ñ‘Ñьць у ÑьпіÑе Torrent not registered with this tracker==ТорÑнт не зарÑгіÑтраваны на гÑтым трÑкеры Unable to find path mapping.~Use the application's options to setup path mappings=Ðемагчыма знайÑьці адпаведнаÑьць шлÑхоў.~СкарыÑтайцеÑÑ Ð¿Ð°Ñ€Ð°Ð¼Ñтрамі Ð¿Ñ€Ñ‹ÐºÐ»Ð°Ð´Ð°Ð½ÑŒÐ½Ñ Ð´Ð»Ñ Ð½Ð°Ð»Ð°Ð´Ð¶Ð²Ð°Ð½ÑŒÐ½Ñ Ð°Ð´Ð¿Ð°Ð²ÐµÐ´Ð½Ð°Ñьці шлÑхоў Update trackers for the existing torrent?=Ðбнавіць трÑкеры Ð´Ð»Ñ Ñ–Ñнуючага торÑнту? You need to restart the application to apply changes=Ðеабходны перазапуÑк прыкладаньнÑ, каб ужыць зьмены TransGUI/lang/transgui.lt0000644000000000000000000003355612261612465014347 0ustar rootrootTranslationLanguage=Lietuvių '%s' has finished downloading=Baigtas „%s“ atsiuntimas /s=/s &Add torrent=&PridÄ—ti torentÄ… &All=&Visi &Close=&Uždaryti &Help=&Pagalba &Yes=&Taip &Ignore=&Ignoruoti &No=&Ne &OK=&Gerai &Open=&Atverti &Retry=&Pakartoti &Save=&IÅ¡saugoti &Torrent=&Torentas &Unlock=&Atrakinti &Verify=&Patikrinti %d torrents=%d torentai %d x %s (have %d)=%d x %s (turi %d) %ds=%dsek. %s (%d hashfails)=%s (%d heÅ¡o klaidos) %s (%s done)=%s (%s baigta) %s downloaded=%s atsiųsta %s of %s downloaded=%s iÅ¡ %s atsiųsta %s%s%d downloading, %d seeding%s%s, %s=%s%s%d atsiunÄiama, %d iÅ¡siunÄiama%s%s, %s A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=Pasiekiama nauja versija %s.~JÅ«sų dabartinÄ— versija: %s~Nauja versija: %s~~Ar norite pereiti į atsiuntimo puslapį? Abort=AtÅ¡aukti About=Apie Active time=Aktyvumo laikas Active=AktyvÅ«s Add .part extension to incomplete files=PridÄ—ti plÄ—tinį .part nebaigtiems failams Add new torrent=PridÄ—ti naujÄ… torentÄ… Add torrent link=PridÄ—ti torento nuorodÄ… Add torrent=PridÄ—ti torentÄ… Add tracker=PridÄ—ti seklį Added on=PridÄ—jimo data Advanced=IÅ¡samiau Alternate bandwidth settings=AlternatyvÅ«s greiÄio nustatymai Apply alternate bandwidth settings automatically=Pritaikyti alternatyvius greiÄio nustatymus automatiÅ¡kai Are you sure to delete connection '%s'?=Ar tikrai norite iÅ¡trinti ryšį „%s“? Are you sure to remove %d selected torrents and all their associated DATA?=Ar tikrai norite paÅ¡alinti %d pažymÄ—tus torentus ir visus susijusius duomenis? Are you sure to remove %d selected torrents?=Ar tikrai norite paÅ¡alinti %d pažymÄ—tus torentus? Are you sure to remove torrent '%s' and all associated DATA?=Ar tikrai norite paÅ¡alinti torentÄ… „%s“ ir visus su juo susietus duomenis? Are you sure to remove torrent '%s'?=Ar tikrai norite paÅ¡alinti torentÄ… „%s“? Are you sure to remove tracker '%s'?=Ar tikrai norite paÅ¡alinti seklį „%s“? Authentication=TapatybÄ—s nustatymas Automatically add torrent links from the clipboard=AutomatiÅ¡kai pridÄ—ti torentų nuorodas iÅ¡ mainų srities average=vidurkis b=b Bandwidth=Greitis Browse=NarÅ¡yti Cancel=AtÅ¡aukti Check for new version every=Tikrinti, ar yra nauja versija kas Check for updates=Tikrinti, ar yra atnaujinimų Client=Klientas Close to tray=Užverti į sistemos dÄ—klÄ… Columns setup=Stulpelių nustatymai Comment=Komentaras Completed on=Baigimo laikas Completed=Baigti Confirmation=Patvirtinimas connected=prisijungÄ™ Connecting to daemon=Jungiamasi prie tarnybos Connection error occurred=RyÅ¡io klaida Consider active torrents as stalled when idle for=Laikyti aktyvius torentus sulaikytais kai jie neveiksnÅ«s Copy file path to clipboard=Kopijuoti failo keliÄ… į mainų sritį Copy=Kopijuoti Country=Å alis Created on=Sukurtas Cumulative=Per visÄ… laikÄ… Current=Å i sesija D: %s/s=Ats.: %s/s Days=Dienos days=dienų Delete a .torrent file after a successful addition=PaÅ¡alinti .torrent failÄ… sÄ—kmingai pridÄ—jus Delete=IÅ¡trinti Destination folder=Atsiuntimų aplankas Directory for incomplete files=Aplankas nebaigtiems failams Disconnected=AtsijungÄ™s Disk cache size=Disko podÄ—lio dydis Do you wish to enable automatic checking for a new version of %s?=Ar norite įjungti automatinį naujų versijų tikrinimÄ… %s? Don't download=Nesiųsti Donate to support further development=Paremkite finansiÅ¡kai tolesnį vystymÄ… Donate via PayPal,WebMoney,Credit card=Paremkite per PayPal,WebMoney,Кreditine kortele Donate!=Paremti! Done=Baigta Down limit=Ats. apribojimas Down speed=Ats. greitis Down=Žemyn Download complete=Atsiuntimas baigtas Download queue size=Atsiuntimo eilÄ—s dydis Download speed=Atsiuntimo greitis Download speeds (KB/s)=Atsiuntimo greitis (КB/s) Download=Atsiuntimas Downloaded=Atsiųsta Downloading=AtsiunÄiami E&xit=IÅ¡eiti Edit tracker=Taisyti seklį Enable blocklist=Ä®jungti blokavomo sÄ…rašą Enable DHT=Ä®jungti DHT Enable Local Peer Discovery=Ä®jungti vietinių mazgų paieÅ¡kÄ… Enable Peer Exchange=Ä®jungti pasikeitimÄ… tarp mazgų Enable port forwarding=Ä®jungti prievadų persiuntimÄ… Enable µTP=Ä®jungti µTP Encryption disabled=Å ifravimas iÅ¡jungtas Encryption enabled=Å ifravimas įjungtas Encryption required=Å ifravimas privalomas Encryption=Kodavimas Error checking for new version=Klaida tikrinant naujos versijos Error=Klaida ETA=Liko File name=Failo pavadinimas Files added=PridÄ—ta failų Files=Failai Filter pane=Filtro panÄ—lÄ— Finished=Baigti Flag images archive is needed to display country flags.~Download this archive now?=Reikalingas vÄ—liavų archyvas, kad parodyti Å¡alių vÄ—liavas.~Ar atsiųsti šį archyvÄ… dabar? Flags=VÄ—liavos Folder grouping=Aplankų grupavimas Force start=Priverstinai paleisti Free disk space=Laisva disko vieta Free: %s=Laisva: %s From=Nuo GB=GB General=Bendri Geo IP database is needed to resolve country by IP address.~Download this database now?=Reikalinga Geo IP duomenų bazÄ—, kad nustatyti Å¡alį pagal IP adresÄ….~Ar atsiųsti duomenų bazÄ™ dabar? Global bandwidth settings=Bendri greiÄio nustatymai Global peer limit=Bandras mazgų limitas Global statistics=Bendroji statistika Hash=HeÅ¡as Have=Turi Hide=SlÄ—pti High priority=AukÅ¡tas prioritetas high=aukÅ¡tas Home page=Namų puslapis Host=Mazgas ID=ID Yes to &All=Visiems taip in swarm=debesyje Inactive=NeaktyvÅ«s Incoming port is closed. Check your firewall settings=Įėjimo prievadas uždarytas. Patikrinkite savo ugniasienÄ—s nustatymus. Incoming port tested successfully=Įėjimo prievadai sÄ—kmingai iÅ¡bandyti Incoming port=Ä®einantis prievadas Info pane=Informacijos panÄ—lÄ— Information=Informacija KB/s=KB/s KB=KB Language=Kalba Last active=Paskiausia veikla License=Licensija Low priority=Žemas prioritetas low=žemas Max peers=Daugiausiai mazgų Maximum download speed=Didžiausias atsiuntimo greitis Maximum upload speed=Didžiausias iÅ¡siuntimo greitis MB=MB Minimize to tray=Sumažinti į sistemos dÄ—klÄ… minutes=min. Misc=Ä®vairÅ«s Modify trackers=Keisti seklius Move bottom=Perkelti į apaÄiÄ… Move down queue=Perkelti į eilÄ—s apaÄiÄ… Move down=Perkelti žemyn Move top=Perkelti į viršų Move torrent data from current location to new location=Perkelti torento duomenis iÅ¡ esamos vietos į naujÄ… Move up queue=Perkelti į eilÄ—s viršų Move up=Perkelti aukÅ¡tyn Name=Pavadinimas New connection=Naujas ryÅ¡ys New location for torrent data=Nauja torento duomenų buvimo vieta New=Naujas No host name specified=Nenurodytas mazgo pavadinimas. No link was specified=Nenurodyta nuoroda No proxy server specified=Nenurodytas proxy serverio pavadinimas. No to all=Visiems ne No torrent location was specified=Nenurodyta torento buvimo vieta No tracker URL was specified=Nenurodytas joks seklio URL No tracker=NÄ—ra seklio No updates have been found.~You are running the latest version of %s=Atnaujinimų nerasta.~Naudojama naujausia „%s“ versija Normal priority=Normalus prioritetas normal=normalus of=iÅ¡ Open containing folder=Atidaryti objektÄ… turintį aplankÄ… Open=Atidaryti Password=Slaptažodis Path=Kelias Paths=Keliai Peer limit=Mazgų limitas Peers=Mazgai Pieces=Dalių Port=Prievadas Priority=Prioritetas Properties=SavybÄ—s Proxy password=Proxy slaptažodis Proxy port=Proxy prievadas Proxy server=Proxy srveris Proxy user name=Proxy naudotojo vardas Proxy=Proxy Queue position=Pozicija eilÄ—je Queue=EilÄ— Ratio=Santykis Reannounce (get more peers)=Anonsuoti (gauti daugiau mazgų) Reconnect in %d seconds=Prisijungimo kartojimas po %d sek. Remaining=Liko Remote host=NutolÄ™s mazgas Remove torrent and Data=PaÅ¡alinti torentÄ… ir failus Remove torrent=PaÅ¡alinti torentÄ… Remove tracker=PaÅ¡alinti seklį Remove=PaÅ¡alinti Rename=Pervardinti Resolve country=Nustatyti Å¡alį Resolve host name=Nustatyti mazgo vardÄ… seconds=sek. Seed ratio=SÄ—jimo koficientas Seeding=SÄ—jama Seeds=SÄ—klos Select a .torrent to open=Parinkite .torrent failÄ… Select a folder for download=Pasirinkite aplankÄ… atsiuntimui Select all=PažymÄ—ti visus Select none=Nieko nežymÄ—ti Select torrent location=Pasirinkite torento vietÄ… Set data location=Pakeisti duomenų buvimo vietÄ… Setup columns=Nustatyti stulpelius Share ratio=Dalinimosi santykis Show country flag=Rodyti Å¡alies vÄ—liavÄ… Show=Rodyti Size to download=Atsiuntimo dydis Size=Dydis skip=praleisti Speed limit menu items=GreiÄio ribojimo meniu elementai Start all torrents=Paleisti visus torentus Start torrent=Paleisti torentÄ… Start=Paleisti Statistics=Statistika Status bar=BÅ«senos juosta Status=BÅ«sena Stop all torrents=Sustabdyti visus torentus Stop all=Sustabdyti visus Stop seeding when inactive for=Baigti sÄ—ti kai neaktyvus Stop torrent=Sustabdyti torentÄ… Stop=Sustabdyti Stopped=Sustabdyti T&ools=Ä®rankiai TB=TB Test port=Bandyti prievadÄ… The block list has been updated successfully.~The list entries count: %d=Blokuojamų sÄ…raÅ¡as buvo sÄ—kmingai atnaujintas.~SÄ…raÅ¡e įrašų: %d The directory for incomplete files was not specified=Nenurodytas aplankas nebaigtiems failams The downloads directory was not specified=Nenurodytas atsiuntimų aplankas The invalid time value was entered=Ä®vesta netinkama laiko reikÅ¡mÄ— to=iki Torrent contents=Torento turinys Torrent data location=Torento duomenų buvimo vieta Torrent properties=Torento nustatymai Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torento patikrinimas gali užtrukti.~Ar tikrai norite patikrinti torentÄ… „%s“? Torrent=Тоrentas Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Тorentai (*.torrent)|*.torrent|Visi failai (*.*)|*.* Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torentai neaktyvÅ«s N minuÄių, neįtraukiami į atsiuntimo ar iÅ¡siuntimo eiles Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Torento patikrinimas gali užtrukti.~Ar tikrai norite patikrinti '%d' torentų? Torrents=Torentai Total size=Bendras dydis Tracker announce URL=Seklio informavimo URL Tracker grouping=Seklių grupavimas Tracker properties=Seklio savybÄ—s Tracker status=Seklio bÅ«sena Tracker update on=Seklio atnaujinimas Tracker=Seklys Trackers=Sekliai Tray icon always visible=Sistemo dÄ—klo piktograma visada matoma Tray icon=Sistemo dÄ—klo piktograma Transfer=Perdavimas Transmission options=Transmission nustatymai Transmission%s at %s:%s=Transmission%s adresu %s:%s U: %s/s=IÅ¡s.: %s/s Unable to execute "%s"=Nepavyksta įvykdyti „%s“ Unable to extract flag image= iÅ¡skleisti vÄ—liavos paveikslÄ—lio Unable to get files list=Nepavyko gauti failų sÄ…raÅ¡o Unable to load OpenSSL library files: %s and %s=Nepavyksta įkelti OpenSSL bibliotekos failų: %s ir %s Unknown=Nežinomas Unlimited=Neribotai Up limit=IÅ¡s. riba Up speed=IÅ¡s. greitis Up=AukÅ¡tyn Update blocklist=Atnaujinti blokuojamų sÄ…rašą Update complete=Atnaujinimas baigtas Update GeoIP database=Atnaujinti GeoIP duomenų bazÄ™ Update in=Atnaujinimas po Updating=Atnaujinama Upload queue size=IÅ¡siuntimo eilÄ—s dydis Upload speed=IÅ¡siuntimo greitis Upload speeds (KB/s)=IÅ¡siuntimo greitis (КB/s) Uploaded=IÅ¡siųsta URL of a .torrent file or a magnet link=.torrent failo URL arba magnet nuoroda Use alternate bandwidth settings=Naudoti alternatyvius greiÄius Use SSL=Naudoti SSL User name=Naudotojo vardas Verify torrent=Patikrinti torentÄ… Verifying=Tikrinama Version %s=Versija %s View=Rodymas Visit home page=Aplankyti namų puslapį Waiting=Laukiama Warning=PerspÄ—jimas Wasted=Panaudota Working=Dirba "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music" %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/lang/transgui.sv0000644000000000000000000003307712261612465014356 0ustar rootrootTranslationLanguage=Svenska "Remote to local path mappings.~~Examples:~/share=\\pch\share~/var/downloads/music=Z:\music"="Mappning remote til lokala sökvägar.~~Till exempel:~/share=\\pch\share~/var/downloads/musik=Z:\musik" %d x %s (have %d)=%d x %s (har %d) %ds=%ds %s (%d hashfails)=%s (%d checksummefel) %s (%s done)=%s (%s klar) '%s' has finished downloading='%s' är klar med nedladdningen %s%s%d downloading, %d seeding%s%s, %s=%s%s%d laddar ned, %d delar%s%s, %s &All=&Alla &Close=&Stäng &Help=&Hjälp &Ignore=&Ignorera &No=&Nej &OK=&OK &Open=&Öppna &Retry=&Försök igen &Save=&Spara &Unlock=LÃ¥s upp &Yes=&Ja /s=/s Abort=Avbryt About=Om Active=Aktiv Add new torrent=Addera ny torrent &Add torrent=Addera torrent Added on=Adderades till Are you sure to remove torrent '%s' and all associated DATA?=Är du säker pÃ¥ att du vill ta bort '%s' och alla tillhörande data? Are you sure to remove torrent '%s'?=Är du säker pÃ¥ att du vill ta bort torrenten '%s'? Authentication=Autentisering b=b Cancel=Avbryt Client=Klient Close to tray=Stäng till aktivitetsfältet Comment=Kommentar Completed on=Avslutad Completed=Avslutad Confirmation=Bekräftelse connected=Ansluten Connecting to daemon=Ansluter till server Connection error occurred=Anslutningsfel Copy=Kopiera Country=Land Created on=Skapad D: %s/s=D: %s/s Destination folder=Destinationsmapp Disconnected=FrÃ¥nkopplad Donate to support further development=Donera för att stödja fortsatt utveckling Donate via PayPal,WebMoney,Credit card=Donera via PayPal,WebMoney,Credit card Done=Färdig Don't download=Ladda inte ned Down limit=Ner gräns Down speed=Nedladdningshastighet Down=Ner Download complete=Nedladdning klar Download speed=Nedladdingshastighet Downloaded=Nerladdad Downloading=Laddar ner Enable DHT=Aktivera DHT Enable Peer Exchange=Aktivera Peer Exchange Enable port forwarding=Aktivera portvidaresänding Encryption disabled=Kryptering avstängd Encryption enabled=Kryptering aktiverad Encryption required=Kryptering krävs Encryption=Kryptering Error=Fel ETA=ETA E&xit=Avsluta File name=Filnamn Files=Filer Finished=Avslutad Flag images archive is needed to display country flags.~Download this archive now?=Ett arkiv med flaggbilder är nödvändigt för att visa landsflaggor.~Önskar du att ladda ned denna nu? Flags=Flaggor GB=GB General=Allmänt Geo IP database is needed to resolve country by IP address.~Download this database now?=En geo-IP databas är nödvändig för att visa vilket land en IP-adress tillhör.~Önskar du att ladda ned denna databasen nu?? Global bandwidth settings=Global bredbands inställningar Global peer limit=Max antal globala kamrater Hash=Checksum Have=Ha Hide=Dölj High priority=Hög prioritet high=hög Host=värd in swarm=i svärm Inactive=Inaktiv Incoming port is closed. Check your firewall settings=Inkommande port är stängd. Kontrollera brandväggs inställningarna Incoming port tested successfully=Inkommande port är öppen Incoming port=Inkommande port Information=Information KB/s=KB/s KB=KB Language=SprÃ¥k Last active=Senast aktiv License=Licens Low priority=LÃ¥g prioritet low=lÃ¥g Max peers=Max antal peers Maximum download speed=Max nedladdningshastighet Maximum upload speed=Max uppladdningshastighet MB=MB Minimize to tray=Minimera till aktivitetsfältet Name=Namn No host name specified=Inget värdnamn specifierat No proxy server specified=Ingen proxyserver specifierad No to all=Nej till alla Normal priority=Normal prioritet normal=normal of=av Open containing folder=Öppna mÃ¥lmapp Open=Öppna Password=Lösenord Paths=Sökvägar Peer limit=Peer begränsning Peers=Peers Pieces=Delar Port=Port Priority=Prioritet Properties=Egenskaper Proxy password=Lösenord Proxy Proxy port=Proxy port Proxy server=Proxy server Proxy user name=Användarnamn pÃ¥ proxy server Ratio=FörhÃ¥llande Reconnect in %d seconds=Anslut pÃ¥ nytt om %d sekunder Remaining=Ã…terstÃ¥ende Remote host=Fjärrserver Remove torrent and Data=Ta bort torrent och data Remove torrent=Ta bort torrent Remove=Ta bort Resolve country=Check land Resolve host name=Check värdnamn seconds=sekunder Seed ratio=DelningsförhÃ¥llande Seeding=Delar Seeds=Delare Select a .torrent to open=Välj en .torrent att öppna Select all=Välj alla Select none=Välj ingen Setup columns=Ställ in kolumner Share ratio=DelningsförhÃ¥llande Show country flag=Visa flagga Show=Visa Size=Storlek skip=hoppa över Start all torrents=Starta alla torrenter Start torrent=Starta torrent Start=Starta Status=Status Stop all torrents=Stoppa alla torrenter Stop all=Stoppa alla Stop torrent=Stopp torrent Stop=Stopp Stopped=Stoppad TB=TB Test port=Testa port T&ools=Verktyg Torrent contents=InnehÃ¥ll i torrent Torrent properties=Egenskaper för torrent Torrent verification may take a long time.~Are you sure to start verification of torrent '%s'?=Torrentverifikation kan ta lÃ¥ng tid.~Är du säker pÃ¥ att du vill starta torrentverifikation '%s'? &Torrent=Torrent Torrents (*.torrent)|*.torrent|All files (*.*)|*.*=Torrenter (*.torrent)|*.torrent|Alla filar (*.*)|*.* Total size=Total storlek Tracker status=Trackerstatus Tracker update on=Uppdaterar tracker Tracker=Tracker Trackers=Trackers Transfer=Överföring Transmission options=Alternativ för Transmission Transmission%s at %s:%s=Transmission%s pÃ¥ %s:%s Tray icon always visible=Ikon i aktivitetsfältet alltid synlig Tray icon=Ikon i aktivitetsfältet U: %s/s=U: %s/s Unable to extract flag image=Kunde inte hämta flaggbild Unable to get files list=Kunde inte finna fillista Unknown=Okänt Up limit=Uppladdnings gräns Up speed=Uppladdnings hastighet Up=Upp Update complete=Uppdatering genomförd Update GeoIP database=Uppdatera GeoIP databas Update in=Uppdaterar om Updating=Uppdaterar Upload speed=Uppladdnings hastighet Uploaded=Uppladdat User name=Användarnamn Verify torrent=Verifiera torrents &Verify=Verifiera Verifying=Verifierar Version %s=Version %s Waiting=Väntar Warning=Varning Wasted=Bortkastad Working=Arbetar Yes to &All=Ja till &Alla No tracker=Ingen tracker %s downloaded=%s nedladdat %s of %s downloaded=%s av %s nedladdad %d torrents=%d torrents Add .part extension to incomplete files=Lägg till .part pÃ¥ ofullständiga filer Add torrent link=Lägg till en torrentlänk Are you sure to remove %d selected torrents and all their associated DATA?=är du säker pÃ¥ att du önskar att ta bort %d valda torrenter och alla tilhörande data? Are you sure to remove %d selected torrents?=är du säker pÃ¥ att du önskar att ta bort %d valda torrenter? Bandwidth=Bandbredd Directory for incomplete files=Mapp för ofullständiga filer Download=Ladda ner Enable blocklist=Aktivera blockeringslistan ID=ID Move torrent data from current location to new location=Flytta data frÃ¥n nuvarande till ny plats New location for torrent data=Ny plats för torrent data No link was specified=Ingen länk specifierad No torrent location was specified=Ingen torrentplats var specifierad Path=Sökväg Reannounce (get more peers)=Tillkännage (skaffa flera peers) Set data location=Sätt dataplats Size to download=Storlek pÃ¥ nedladdning The block list has been updated successfully.~The list entries count: %d=Blockeringslistan blev uppdaterad.~Det är %d registrerade i listan The directory for incomplete files was not specified=Mappen för ofullständiga filer var inte specifierad The downloads directory was not specified=Det blev inte specifierat en neddladdnings map Torrent data location=Dataplats for torrent Torrents=Torrents Unable to execute "%s"=Kunde inte köra "%s" Update blocklist=Uppdatera blockeringslista URL of a .torrent file or a magnet link=Adressen till en .torrent-fil eller en magnet-länk Columns setup=Kolumn inställning Add torrent=Lägg till torrent Delete a .torrent file after a successful addition=Ta bort .torrent-filen efter att den är uppladdad till servern Torrent=Torrent Torrents verification may take a long time.~Are you sure to start verification of %d torrents?=Dataverifiering kan ta lÃ¥ng tid.~Är du säker pÃ¥ att du önskar att verifiera %d torrenter? Unable to load OpenSSL library files: %s and %s=Klarade inte att ladda OpenSSL biblioteks filerna: %s och %s frÃ¥n OpenSSL Use SSL=Använd SSL Add tracker=Lägg till tracker Alternate bandwidth settings=Alternativ bandbredds inställingar Apply alternate bandwidth settings automatically=Använd alternativ bandbreds inställningar automatiskt Are you sure to delete connection '%s'?=Är du säker pÃ¥ att du vill ta bort anslutningen '%s'? Are you sure to remove tracker '%s'?=Är du säker pÃ¥ att du vill ta bort trackern '%s'? Days=Dagar Delete=Ta bort Disk cache size=Storlek pÃ¥ disk cachen Download speeds (KB/s)=Nedladdingshastighet (KB/s) Edit tracker=Redigera tracker Enable Local Peer Discovery=Aktivera Lokal Peer upptäckt Free disk space=Ledigt diskutrymme Free: %s=Ledigt: %s From=FrÃ¥n minutes=minuter Misc=Diverse New connection=Ny anslutning New=Ny No tracker URL was specified=Ingen Trackeradress var specifierad Proxy=Proxy Remove tracker=Radera tracker Rename=Byt namn Speed limit menu items=Hastighetsbegränsningar Stop seeding when inactive for=Avsluta delning när inaktiv i The invalid time value was entered=Ett ogiltig tidsvärde angavs to=till Tracker announce URL=Trackerns annonseringsadress Tracker properties=Trackerinställningar Unlimited=Obegränsat Upload speeds (KB/s)=Uppladdnings hastighet (KB/s) Use alternate bandwidth settings=Använd alternativ bandbredds inställning average=genomsnitt Browse=Bläddra Enable µTP=Aktivera µTP Select a folder for download=Välj nedladdningsmapp Select torrent location=Välj torrentplats A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page?=A new version of %s is available.~Your current version: %s~The new version: %s~~Do you wish to open the Downloads web page? Advanced=Avancerad Check for new version every=Sök efter nya versioner varje Check for updates=Sök efter uppdateringar Consider active torrents as stalled when idle for=Consider active torrents as stalled when idle for Do you wish to enable automatic checking for a new version of %s?=Vill du aktivera automatisk kontroll av nya versioner av %s? Donate!=Donera! Download queue size=Storlek pÃ¥ download kön Error checking for new version=Fel kontroll av ny version Folder grouping=Gruppering av mappar Force start=Tvinga start Home page=Hemsida Modify trackers=Modifiera trackers Move bottom=Flytta till botten av kön Move down queue=Flytta ner i kön Move down=Flytta ner Move top=Flytta längst upp Move up queue=Flytta till toppen av kön Move up=Flytta upp No updates have been found.~You are running the latest version of %s=Inga uppdateringar hittades.~Du kör senaste versionen av %s Queue position=Kö position Queue=Kö Torrents that are idle for N minuets aren't counted toward the Download queue or Upload queue=Torrents som är inaktiva under N minuter räknas inte mot Download kön eller Upload kön Tracker grouping=Tracker gruppering Upload queue size=Upload kö storlek View=Visa Visit home page=Besök hemsidan days=dagar Active time=Active time Automatically add torrent links from the clipboard=Automatically add torrent links from the clipboard Copy file path to clipboard=Copy file path to clipboard Cumulative=Cumulative Current=Current Files added=Files added Filter pane=Filter pane Global statistics=Global statistics Info pane=Info pane Statistics=Statistics Status bar=Status bar %dd=%dd %dh=%dh %dm=%dm All torrents=All torrents Application options=Application options Ask for password=Ask for password Authentication required=Authentication required Average out transfer speeds to eliminate fluctuations=Average out transfer speeds to eliminate fluctuations Connect to %s=Connect to %s Connect to Transmission using proxy server=Connect to Transmission using proxy server Connect to Transmission=Connect to Transmission Connection name=Connection name Could not connect to tracker==Could not connect to tracker Data display=Data display Data refresh interval when minimized=Data refresh interval when minimized Data refresh interval=Data refresh interval Default download folder on remote host=Default download folder on remote host Disconnect from Transmission=Disconnect from Transmission Downloading torrent file=Downloading torrent file Font size=Font size Handle .torrent files by %s=Handle .torrent files by %s Handle magnet links by %s=Handle magnet links by %s Invalid name specified=Invalid name specified Manage connections to Transmission=Manage connections to Transmission Manage connections=Manage connections Network (WAN)=Network (WAN) New connection to Transmission=New connection to Transmission Pick random port on Transmission launch=Pick random port on Transmission launch Please enter a password to connect to %s=Please enter a password to connect to %s Please specify how %s will connect to a remote host running Transmission daemon (service)=Please specify how %s will connect to a remote host running Transmission daemon (service) Prompt for download options when adding a new torrent=Prompt for download options when adding a new torrent RPC path=RPC path Save as=Save as Seeding time=Seeding time Show advanced options=Show advanced options Show notifications in tray icon=Show notifications in tray icon System integration=System integration Torrent already exists in the list=Torrent already exists in the list Torrent not registered with this tracker==Torrent not registered with this tracker Unable to find path mapping.~Use the application's options to setup path mappings=Unable to find path mapping.~Use the application's options to setup path mappings Update trackers for the existing torrent?=Update trackers for the existing torrent? You need to restart the application to apply changes=You need to restart the application to apply changes TransGUI/main.lfm0000644000000000000000000072643412260021101012635 0ustar rootrootinherited MainForm: TMainForm Left = 222 Height = 506 Top = 157 Width = 780 HorzScrollBar.Page = 740 VertScrollBar.Page = 420 VertScrollBar.Range = 24 ActiveControl = panTop AllowDropFiles = True Caption = ' ' ClientHeight = 487 ClientWidth = 780 Constraints.MinHeight = 300 Constraints.MinWidth = 300 Menu = MainMenu OnActivate = FormActivate OnClose = FormClose OnCreate = FormCreate OnDestroy = FormDestroy OnDropFiles = FormDropFiles OnResize = FormResize OnShow = FormShow OnWindowStateChange = FormWindowStateChange object panTop: TPanel[0] Left = 0 Height = 127 Top = 26 Width = 780 Align = alClient BevelOuter = bvNone ClientHeight = 127 ClientWidth = 780 Constraints.MinHeight = 80 TabOrder = 0 object HSplitter: TSplitter Left = 130 Height = 127 Top = 0 Width = 5 AutoSnap = False MinSize = 50 OnChangeBounds = HSplitterChangeBounds end object panFilter: TPanel Left = 0 Height = 127 Top = 0 Width = 130 Align = alLeft BevelOuter = bvNone ClientHeight = 127 ClientWidth = 130 TabOrder = 2 object panSearch: TPanel Left = 0 Height = 21 Top = 104 Width = 130 Align = alBottom AutoSize = True BorderSpacing.Top = 2 BorderSpacing.Bottom = 2 BevelOuter = bvNone ClientHeight = 21 ClientWidth = 130 TabOrder = 0 object imgSearch: TImage Left = 0 Height = 21 Top = 0 Width = 24 Align = alLeft Center = True Picture.Data = { 055449636F6E7E05000000000100010010100000010008006805000016000000 2800000010000000200000000100080000000000000000000000000000000000 0000000000000000FFFFFC00FFFFF700FFFEEA00FFFFE600FFF7EA00F5EFE000 FFFFDA00FFFED400FFF2D700FFFCCD00FFF9C700FFF5CA00FFF5C300F3EDDA00 F1E5DD00EFDAC200E2CECE00DCC5C500C9D0D400FFF1BE00FFEEBD00FFE5B900 FFE4B100FFDEAB00F3D2A800F1CFA900F5CFA400E0C0B600EFC9A600E0C7AE00 E0BDB300E5BEA500E0B89E00D8BCBA00DCB3A000D2B4A400D2B19E00CCB29900 CEB09600CCAC9300CEA88E00BDE3FF00A5D8FF008DCDFF0089B0CA00ABABAB00 A1A1A100BC9A8600B9938A00B79E8C00B7948100AD8A8D00AA878E0096969600 9F8581008C8C8C0081818100987D8200907676008B75740053B8FF00588CCF00 4088DF004385D9004483D6007979A7007376A80073758900777777007B616100 6C6C6C00626262005F5B5B005757570000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000FFFFFF004A3749444A4A4A4A4A4A4A4A4A4A4A4A2D433448444A4A4A 4A4A4A4A4A4A4A4A2C3E423348444A4A4A4A4A4A4A4A4A4A293C3F413348444A 4A4A4A4A4A4A4A4A4A2A3C3F413348354A4A4A4A4A4A4A4A4A4A2B3C40413938 2E46494947374A4A4A4A4A2B3C3D373B2F261D233B49374A4A4A4A4A2A123319 0C070606053649374A4A4A4A4A101C0C0C060200000D45464A4A4A4A4A1E0C16 0A060200010631494A4A4A4A4A1F0B170C070302030625494A4A4A4A4A1F0B15 14090706060727464A4A4A4A4A1B0B0815130C0A0C0C32374A4A4A4A4A110F00 0414171613183A4A4A4A4A4A4A4A210E0209140C1A304A4A4A4A4A4A4A4A4A11 24282022374A4A4A8FFF000007FF000003FF000001FF000080FF0000C0030000 E0010000F0000000F8000000F8000000F8000000F8000000F8000000F8010000 FC030000FE070000 } end object edSearch: TEdit Left = 24 Height = 21 Top = 0 Width = 106 Align = alClient OnChange = edSearchChange TabOrder = 0 end end object lvFilter: TVarGrid Left = 0 Height = 102 Top = 0 Width = 130 Align = alClient Columns = < item Title.Caption = ' ' Width = 10 end> Constraints.MinWidth = 50 FixedCols = 0 FixedRows = 0 Options = [goColSizing, goColMoving, goRowSelect, goThumbTracking, goDblClickAutoSize] PopupMenu = pmFilter RowCount = 1 ScrollBars = ssAutoVertical TabOrder = 1 OnClick = lvFilterClick OnResize = lvFilterResize Images = ImageList16 OnCellAttributes = lvFilterCellAttributes OnDrawCell = lvFilterDrawCell end end object gTorrents: TVarGrid Left = 135 Height = 127 Top = 0 Width = 645 Align = alClient Columns = < item Title.Caption = 'Name' Width = 200 end item Alignment = taRightJustify Title.Caption = 'Size' Width = 60 end item Alignment = taCenter Title.Caption = 'Done' end item Title.Caption = 'Status' Width = 70 end item Alignment = taRightJustify Title.Caption = 'Seeds' Width = 55 end item Alignment = taRightJustify Title.Caption = 'Peers' Width = 55 end item Alignment = taRightJustify Title.Caption = 'Down speed' Width = 75 end item Alignment = taRightJustify Title.Caption = 'Up speed' Width = 75 end item Alignment = taRightJustify Title.Caption = 'ETA' Width = 80 end item Alignment = taRightJustify Title.Caption = 'Ratio' end item Alignment = taRightJustify Title.Caption = 'Downloaded' Width = 0 Visible = False end item Alignment = taRightJustify Title.Caption = 'Uploaded' Width = 0 Visible = False end item Title.Caption = 'Tracker' Width = 0 Visible = False end item Title.Caption = 'Tracker status' Width = 0 Visible = False end item Title.Caption = 'Added on' Width = 0 Visible = False end item Title.Caption = 'Completed on' Width = 0 Visible = False end item Title.Caption = 'Last active' Width = 0 Visible = False end item Title.Caption = 'Path' Width = 0 Visible = False end item Title.Caption = 'Priority' Width = 0 Visible = False end item Alignment = taRightJustify Title.Caption = 'Size to download' Width = 0 Visible = False end item Alignment = taRightJustify Title.Caption = 'ID' Width = 0 Visible = False end item Alignment = taRightJustify Title.Caption = 'Queue position' Width = 0 Visible = False end item Alignment = taRightJustify SizePriority = 0 Title.Caption = 'Seeding time' Width = 0 Visible = False end> FixedCols = 0 Options = [goFixedVertLine, goFixedHorzLine, goColSizing, goColMoving, goRowSelect, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook] PopupMenu = pmTorrents RowCount = 1 TabOrder = 0 OnClick = gTorrentsClick OnDblClick = gTorrentsDblClick OnResize = gTorrentsResize OnSetEditText = gTorrentsSetEditText Images = ImageList16 MultiSelect = True SortColumn = 0 OnCellAttributes = gTorrentsCellAttributes OnDrawCell = gTorrentsDrawCell OnSortColumn = gTorrentsSortColumn OnQuickSearch = gTorrentsQuickSearch OnEditorShow = gTorrentsEditorShow OnEditorHide = gTorrentsEditorHide end end object StatusBar: TStatusBar[1] Left = 0 Height = 23 Top = 464 Width = 780 Panels = < item Width = 300 end item Width = 120 end item Width = 120 end item Width = 120 end item Width = 50 end> SimplePanel = False OnMouseDown = StatusBarMouseDown end object PageInfo: TPageControl[2] Left = 0 Height = 306 Top = 158 Width = 780 ActivePage = tabGeneral Align = alBottom Constraints.MinHeight = 50 Images = ImageList16 TabIndex = 0 TabOrder = 1 OnChange = PageInfoChange OnResize = PageInfoResize object tabGeneral: TTabSheet Caption = 'General' ClientHeight = 279 ClientWidth = 772 ImageIndex = 4 object sbGenInfo: TScrollBox Left = 0 Height = 253 Top = 26 Width = 772 HorzScrollBar.Page = 768 VertScrollBar.Page = 249 Align = alClient ClientHeight = 249 ClientWidth = 768 TabOrder = 0 object panGeneralInfo: TPanel Left = 0 Height = 78 Top = 153 Width = 768 Align = alTop BevelOuter = bvNone ChildSizing.LeftRightSpacing = 4 ChildSizing.TopBottomSpacing = 4 ChildSizing.HorizontalSpacing = 16 ChildSizing.VerticalSpacing = 4 ChildSizing.EnlargeHorizontal = crsHomogenousChildResize ChildSizing.ControlsPerLine = 4 ClientHeight = 78 ClientWidth = 768 TabOrder = 0 object txTorrentNameLabel: TLabel Left = 4 Height = 14 Top = 4 Width = 32 Caption = 'Name:' ParentColor = False end object txTorrentName: TLabel Left = 181 Height = 14 Top = 4 Width = 74 Caption = 'txTorrentName' ParentColor = False ShowAccelChar = False end object txCreatedLabel: TLabel Left = 381 Height = 14 Top = 4 Width = 59 Caption = 'Created on:' ParentColor = False end object txCreated: TLabel Left = 578 Height = 14 Top = 4 Width = 50 Caption = 'txCreated' ParentColor = False end object txTotalSizeLabel: TLabel Left = 4 Height = 14 Top = 22 Width = 50 Caption = 'Total size:' ParentColor = False end object txTotalSize: TLabel Left = 181 Height = 14 Top = 22 Width = 54 Caption = 'txTotalSize' ParentColor = False end object txPiecesLabel: TLabel Left = 381 Height = 14 Top = 22 Width = 35 Caption = 'Pieces:' ParentColor = False end object txPieces: TLabel Left = 578 Height = 14 Top = 22 Width = 41 Caption = 'txPieces' ParentColor = False end object txHashLabel: TLabel Left = 4 Height = 14 Top = 40 Width = 29 Caption = 'Hash:' ParentColor = False end object txHash: TLabel Left = 181 Height = 14 Top = 40 Width = 35 Caption = 'txHash' ParentColor = False ShowAccelChar = False end object txCommentLabel: TLabel Left = 381 Height = 14 Top = 40 Width = 181 AutoSize = False Caption = 'Comment:' ParentColor = False end object txComment: TLabel Left = 578 Height = 14 Top = 40 Width = 56 Caption = 'txComment' ParentColor = False ShowAccelChar = False end object txAddedOnLabel: TLabel Left = 4 Height = 14 Top = 58 Width = 51 Caption = 'Added on:' ParentColor = False end object txAddedOn: TLabel Left = 181 Height = 14 Top = 58 Width = 56 Caption = 'txAddedOn' ParentColor = False end object txCompletedOnLabel: TLabel Left = 381 Height = 14 Top = 58 Width = 71 Caption = 'Completed on:' ParentColor = False end object txCompletedOn: TLabel Left = 578 Height = 14 Top = 58 Width = 76 Caption = 'txCompletedOn' ParentColor = False end end object panTransfer: TPanel Left = 0 Height = 111 Top = 21 Width = 768 Align = alTop BevelOuter = bvNone ChildSizing.LeftRightSpacing = 4 ChildSizing.TopBottomSpacing = 4 ChildSizing.HorizontalSpacing = 16 ChildSizing.VerticalSpacing = 4 ChildSizing.EnlargeHorizontal = crsHomogenousChildResize ChildSizing.ControlsPerLine = 6 ClientHeight = 111 ClientWidth = 768 TabOrder = 1 object txStatusLabel: TLabel Left = 4 Height = 14 Top = 4 Width = 36 Caption = 'Status:' ParentColor = False end object txStatus: TLabel Left = 140 Height = 14 Top = 4 Width = 42 Caption = 'txStatus' ParentColor = False end object txErrorLabel: TLabel Left = 262 Height = 14 Top = 4 Width = 29 Caption = 'Error:' ParentColor = False end object txError: TLabel Left = 407 Height = 14 Top = 4 Width = 35 Caption = 'txError' ParentColor = False end object txRemainingLabel: TLabel Left = 541 Height = 14 Top = 4 Width = 54 Caption = 'Remaining:' ParentColor = False end object txRemaining: TLabel Left = 651 Height = 14 Top = 4 Width = 60 Caption = 'txRemaining' ParentColor = False end object txDownloadedLabel: TLabel Left = 4 Height = 14 Top = 22 Width = 64 Caption = 'Downloaded:' ParentColor = False end object txDownloaded: TLabel Left = 140 Height = 14 Top = 22 Width = 70 Caption = 'txDownloaded' ParentColor = False end object txUploadedLabel: TLabel Left = 262 Height = 14 Top = 22 Width = 50 Caption = 'Uploaded:' ParentColor = False end object txUploaded: TLabel Left = 407 Height = 14 Top = 22 Width = 56 Caption = 'txUploaded' ParentColor = False end object txWastedLabel: TLabel Left = 541 Height = 14 Top = 22 Width = 42 Caption = 'Wasted:' ParentColor = False end object txWasted: TLabel Left = 651 Height = 14 Top = 22 Width = 48 Caption = 'txWasted' ParentColor = False end object txDownSpeedLabel: TLabel Left = 4 Height = 14 Top = 40 Width = 84 Caption = 'Download speed:' ParentColor = False end object txDownSpeed: TLabel Left = 140 Height = 14 Top = 40 Width = 68 Caption = 'txDownSpeed' ParentColor = False end object txUpSpeedLabel: TLabel Left = 262 Height = 14 Top = 40 Width = 70 Caption = 'Upload speed:' ParentColor = False end object txUpSpeed: TLabel Left = 407 Height = 14 Top = 40 Width = 54 Caption = 'txUpSpeed' ParentColor = False end object txRatioLabel: TLabel Left = 541 Height = 14 Top = 40 Width = 58 Caption = 'Share ratio:' ParentColor = False end object txRatio: TLabel Left = 651 Height = 14 Top = 40 Width = 36 Caption = 'txRatio' ParentColor = False end object txDownLimitLabel: TLabel Left = 4 Height = 14 Top = 58 Width = 53 Caption = 'Down limit:' ParentColor = False end object txDownLimit: TLabel Left = 140 Height = 14 Top = 58 Width = 59 Caption = 'txDownLimit' ParentColor = False end object txUpLimitLabel: TLabel Left = 262 Height = 14 Top = 58 Width = 39 Caption = 'Up limit:' ParentColor = False end object txUpLimit: TLabel Left = 407 Height = 14 Top = 58 Width = 45 Caption = 'txUpLimit' ParentColor = False end object txDummy1: TLabel Left = 541 Height = 1 Top = 58 Width = 1 ParentColor = False end object txDummy2: TLabel Left = 651 Height = 1 Top = 58 Width = 1 ParentColor = False end object txSeedsLabel: TLabel Left = 4 Height = 14 Top = 76 Width = 34 Caption = 'Seeds:' ParentColor = False end object txSeeds: TLabel Left = 140 Height = 14 Top = 76 Width = 40 Caption = 'txSeeds' ParentColor = False end object txPeersLabel: TLabel Left = 262 Height = 14 Top = 76 Width = 32 Caption = 'Peers:' ParentColor = False end object txPeers: TLabel Left = 407 Height = 14 Top = 76 Width = 38 Caption = 'txPeers' ParentColor = False end object txMaxPeersLabel: TLabel Left = 541 Height = 14 Top = 76 Width = 55 Caption = 'Max peers:' ParentColor = False end object txMaxPeers: TLabel Left = 651 Height = 14 Top = 76 Width = 58 Caption = 'txMaxPeers' ParentColor = False end object txTrackerLabel: TLabel Left = 4 Height = 14 Top = 94 Width = 41 Caption = 'Tracker:' ParentColor = False end object txTracker: TLabel Left = 140 Height = 14 Top = 94 Width = 47 Caption = 'txTracker' ParentColor = False end object txTrackerUpdateLabel: TLabel Left = 262 Height = 14 Top = 94 Width = 93 Caption = 'Tracker update on:' ParentColor = False end object txTrackerUpdate: TLabel Left = 407 Height = 14 Top = 94 Width = 82 Caption = 'txTrackerUpdate' ParentColor = False end object txLastActiveLabel: TLabel Left = 541 Height = 14 Top = 94 Width = 57 Caption = 'Last active:' ParentColor = False end object txLastActive: TLabel Left = 651 Height = 14 Top = 94 Width = 61 Caption = 'txLastActive' ParentColor = False end end object txTransferHeader: TPanel Left = 2 Height = 17 Top = 2 Width = 764 Align = alTop Alignment = taLeftJustify BorderSpacing.Around = 2 BevelOuter = bvNone Caption = 'Transfer' Font.Height = -13 Font.Style = [fsBold] ParentColor = False ParentFont = False TabOrder = 2 end object txTorrentHeader: TPanel Left = 2 Height = 17 Top = 134 Width = 764 Align = alTop Alignment = taLeftJustify BorderSpacing.Around = 2 BevelOuter = bvNone Caption = 'Torrent' Font.Height = -13 Font.Style = [fsBold] ParentColor = False ParentFont = False TabOrder = 3 end end object panProgress: TPanel Left = 0 Height = 26 Top = 0 Width = 772 Align = alTop BevelOuter = bvNone ClientHeight = 26 ClientWidth = 772 TabOrder = 1 object txDownProgressLabel: TLabel Left = 8 Height = 26 Top = 0 Width = 64 Align = alLeft BorderSpacing.Left = 8 Caption = 'Downloaded:' Layout = tlCenter ParentColor = False end object txDownProgress: TLabel Left = 724 Height = 26 Top = 0 Width = 40 Align = alRight Alignment = taRightJustify Anchors = [akTop, akRight] BorderSpacing.Right = 8 Caption = '100.0%' Layout = tlCenter ParentColor = False end object pbDownloaded: TPaintBox Left = 78 Height = 14 Top = 6 Width = 640 Align = alClient Anchors = [akTop, akLeft, akRight] BorderSpacing.Around = 6 OnPaint = pbDownloadedPaint end end end object tabTrackers: TTabSheet Caption = 'Trackers' ClientHeight = 279 ClientWidth = 772 ImageIndex = 5 object lvTrackers: TVarGrid Left = 0 Height = 279 Top = 0 Width = 772 Align = alClient Columns = < item Title.Caption = 'Name' Width = 200 end item Title.Caption = 'Status' Width = 200 end item Title.Caption = 'Update in' Width = 80 end item Title.Caption = 'Seeds' end> FixedCols = 0 Options = [goFixedVertLine, goFixedHorzLine, goColSizing, goColMoving, goRowSelect, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook] PopupMenu = pmTrackers RowCount = 1 TabOrder = 0 OnDblClick = lvTrackersDblClick OnKeyDown = lvTrackersKeyDown SortColumn = 0 HideSelection = True OnCellAttributes = lvTrackersCellAttributes end end object tabPeers: TTabSheet Caption = 'Peers' ClientHeight = 279 ClientWidth = 772 ImageIndex = 6 object lvPeers: TVarGrid Left = 0 Height = 279 Top = 0 Width = 772 Align = alClient Columns = < item Title.Caption = 'Host' Width = 150 end item Alignment = taRightJustify Title.Caption = 'Port' Width = 0 Visible = False end item Title.Caption = 'Country' Width = 100 end item Title.Caption = 'Client' Width = 150 end item Title.Caption = 'Flags' Width = 70 end item Alignment = taRightJustify Title.Caption = 'Have' end item Alignment = taRightJustify Title.Caption = 'Up speed' Width = 80 end item Alignment = taRightJustify Title.Caption = 'Down speed' Width = 80 end> FixedCols = 0 Options = [goFixedVertLine, goFixedHorzLine, goColSizing, goColMoving, goRowSelect, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook] PopupMenu = pmPeers RowCount = 1 TabOrder = 0 Images = imgFlags SortColumn = 0 HideSelection = True OnCellAttributes = lvPeersCellAttributes end end object tabFiles: TTabSheet Caption = 'Files' ClientHeight = 279 ClientWidth = 772 ImageIndex = 7 object lvFiles: TVarGrid Left = 0 Height = 279 Top = 0 Width = 772 Align = alClient Columns = < item Title.Caption = 'File name' Width = 350 end item Alignment = taRightJustify Title.Caption = 'Size' Width = 80 end item Alignment = taCenter Title.Caption = 'Done' Width = 70 end item Alignment = taCenter Title.Alignment = taCenter Title.Caption = '%' Width = 70 end item Title.Caption = 'Priority' Width = 100 end> FixedCols = 0 Options = [goFixedVertLine, goFixedHorzLine, goColSizing, goColMoving, goRowSelect, goThumbTracking, goDblClickAutoSize, goHeaderHotTracking, goHeaderPushedLook] PopupMenu = pmFiles RowCount = 1 TabOrder = 0 OnDblClick = lvFilesDblClick OnSetEditText = lvFilesSetEditText Images = ImageList16 MultiSelect = True SortColumn = 0 HideSelection = True OnEditorShow = lvFilesEditorShow OnEditorHide = lvFilesEditorHide end end object tabStats: TTabSheet Caption = 'Statistics' ClientHeight = 279 ClientWidth = 772 ImageIndex = 42 object gStats: TVarGrid Left = 4 Height = 248 Top = 24 Width = 760 Anchors = [akTop, akLeft, akRight, akBottom] Columns = < item Title.Caption = ' ' Width = 200 end item Alignment = taRightJustify Title.Alignment = taCenter Title.Caption = 'Current' Width = 120 end item Alignment = taRightJustify Title.Alignment = taCenter Title.Caption = 'Cumulative' Width = 120 end> FixedCols = 0 Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goColSizing, goRowSelect, goThumbTracking, goDblClickAutoSize] RowCount = 1 TabOrder = 0 HideSelection = True end object txGlobalStats: TLabel Left = 4 Height = 14 Top = 4 Width = 79 Caption = 'Global statistics:' ParentColor = False end end end object VSplitter: TSplitter[3] Cursor = crVSplit Left = 0 Height = 5 Top = 153 Width = 780 Align = alBottom AutoSnap = False MinSize = 80 OnChangeBounds = VSplitterChangeBounds ResizeAnchor = akBottom end object MainToolBar: TToolBar[4] Left = 0 Height = 24 Top = 0 Width = 780 AutoSize = True BorderSpacing.Bottom = 2 Caption = 'MainToolBar' Images = ImageList16 Indent = 4 ParentShowHint = False ShowHint = True TabOrder = 2 object ToolButton1: TToolButton Left = 49 Top = 2 Action = acAddTorrent end object ToolButton2: TToolButton Left = 95 Top = 2 Width = 10 Caption = 'ToolButton2' Style = tbsSeparator end object ToolButton3: TToolButton Left = 105 Top = 2 Action = acStartTorrent end object ToolButton4: TToolButton Left = 128 Top = 2 Action = acStopTorrent end object tbStopTorrent: TToolButton Left = 151 Top = 2 Action = acRemoveTorrent end object ToolButton6: TToolButton Left = 39 Top = 2 Width = 10 Caption = 'ToolButton6' Style = tbsSeparator end object ToolButton7: TToolButton Left = 263 Top = 2 Width = 10 Caption = 'ToolButton7' Style = tbsSeparator end object ToolButton8: TToolButton Left = 273 Top = 2 Action = acDaemonOptions end object ToolButton9: TToolButton Left = 72 Top = 2 Action = acAddLink end object tbConnect: TToolButton Left = 4 Top = 2 Action = acConnect DropdownMenu = pmConnections Style = tbsDropDown end object tbtAltSpeed: TToolButton Left = 240 Top = 2 Action = acAltSpeed end object sepAltSpeed: TToolButton Left = 230 Top = 2 Width = 10 Caption = 'sepAltSpeed' Style = tbsSeparator end object sepQueue: TToolButton Left = 174 Top = 2 Width = 10 Caption = 'sepQueue' Style = tbsSeparator end object tbQMoveUp: TToolButton Left = 184 Top = 2 Action = acQMoveUp end object tbQMoveDown: TToolButton Left = 207 Top = 2 Action = acQMoveDown end end object panReconnect: TPanel[5] Left = 532 Height = 44 Top = 56 Width = 161 BevelOuter = bvNone ClientHeight = 44 ClientWidth = 161 Color = clInfoBk Font.Color = clInfoText ParentColor = False ParentFont = False TabOrder = 5 Visible = False OnResize = panReconnectResize object txConnErrorLabel: TLabel Left = 16 Height = 14 Top = 8 Width = 129 Align = alTop Alignment = taCenter BorderSpacing.Left = 16 BorderSpacing.Top = 8 BorderSpacing.Right = 16 BorderSpacing.Bottom = 4 Caption = 'Connection error occurred:' ParentColor = False end object txConnError: TLabel Left = 16 Height = 1 Top = 26 Width = 129 Align = alTop Alignment = taCenter BorderSpacing.Left = 16 BorderSpacing.Right = 16 Font.Color = clInfoText Font.Style = [fsBold] ParentColor = False ParentFont = False end object txReconnectSecs: TLabel Left = 16 Height = 1 Top = 35 Width = 129 Align = alTop Alignment = taCenter BorderSpacing.Left = 16 BorderSpacing.Top = 8 BorderSpacing.Right = 16 BorderSpacing.Bottom = 8 ParentColor = False end object panReconnectFrame: TShape Left = 4 Height = 19 Top = 24 Width = 24 Brush.Style = bsClear Pen.Color = clBtnShadow end end object panDetailsWait: TPanel[6] Left = 495 Height = 33 Top = 56 Width = 27 BevelOuter = bvNone TabOrder = 3 Visible = False end object MainMenu: TMainMenu[7] Images = ImageList16 left = 8 top = 28 object miTorrent: TMenuItem Caption = '&Torrent' object miConnect: TMenuItem Caption = 'Connect to Transmission' Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00FF000000FF0000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000FF6666 00FF666600FF000000FF000000FF000000000000000000000000000000000000 0000000000000000000000000000000000FF000000FF000000FF666600FF8080 00FF808000FF808000FF808000FF000000FF000000FF00000000000000000000 00000000000000000000000000FF868686FF868686FF000000FF006666FF0080 80FF008080FFC0C0C0FFC0C0C0FF808000FF808000FF000000FF000000000000 000000000000000000FF868686FFC0C0C0FFF8F8F8FF000000FF000000FF00FF FFFF00FFFFFF008080FFD7D7D7FFF8F8F8FFF8F8F8FF808000FF000000000000 0000000000FF868686FFC0C0C0FFF8F8F8FFC0C0C0FFB2B2B2FF868686FF0000 00FF000000FFC0C0C0FFD7D7D7FFF8F8F8FF808000FF00000000000000000000 00FF868686FFC0C0C0FFF8F8F8FFC0C0C0FFC0C0C0FFC0C0C0FFB2B2B2FFA4A0 A0FF868686FF000000FF000000FFC0C0C0FF000000FF00000000000000FF8686 86FFC0C0C0FFF8F8F8FFDDDDDDFFDDDDDDFFC0C0C0FFB2B2B2FFC0C0C0FFB2B2 B2FFB2B2B2FF868686FF868686FF000000FF000000FF00000000868686FFC0C0 C0FFF8F8F8FF868686FF868686FFC0C0C0FFC0C0C0FFC0C0C0FFB2B2B2FFB2B2 B2FFB2B2B2FFA4A0A0FFA4A0A0FF868686FF868686FF00000000868686FFF8F8 F8FFDDDDDDFFDDDDDDFFC0C0C0FF868686FF868686FFB2B2B2FFC0C0C0FFB2B2 B2FFB2B2B2FFB2B2B2FFA4A0A0FFA4A0A0FF868686FF00000000000000008686 86FF868686FFDDDDDDFFDDDDDDFFCCCCCCFFCCCCCCFF868686FF868686FFB2B2 B2FFB2B2B2FFA4A0A0FFA4A0A0FF868686FF0000000000000000000000000000 000000000000868686FF868686FFDDDDDDFFCCCCCCFFCCCCCCFFC0C0C0FF8686 86FF868686FFB2B2B2FF868686FF000000000000000000000000000000000000 0000000000FF868686FF00000000868686FF868686FFCCCCCCFFCCCCCCFFC0C0 C0FFC0C0C0FF868686FF00000000000000000000000000000000000000000000 00FF868686FF00000000000000000000000000000000868686FF868686FFCCCC CCFF868686FF0000000000000000000000000000000000000000000000FF8686 86FF000000000000000000000000000000000000000000000000000000008686 86FF000000000000000000000000000000000000000000000000 } ImageIndex = 18 object sepCon1: TMenuItem Tag = 1 Caption = '-' end object MenuItem71: TMenuItem Tag = 1 Action = acNewConnection OnClick = acNewConnectionExecute end object MenuItem75: TMenuItem Tag = 1 Action = acConnOptions OnClick = acConnOptionsExecute end end object MenuItem73: TMenuItem Action = acDisconnect Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000268D0FF0268D0FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF000066 CEFF0268D0FF0268D0FF0268D0FF2A90EEFF3FA5FAFF0268D0FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF000268D0FF1177 DEFF2A90EEFF5DC3FFFF58BDFFFF278CECFF0268D0FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF000268D0FF3FA5 FAFF2288E0FF3BA0EEFF45ABFFFF379CFFFF0268D0FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF000268 D0FF58BDFFFF187EE0FF2186EEFF379CFFFF0268D0FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000268D0FF379CFFFF1177DEFF1C82E9FF0268D0FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00898A8CFF535558FFFF00FF00FF00 FF00FF00FF000268D0FF379CFFFF0D73DBFF0268D0FFFF00FF00FF00FF00FF00 FF00FF00FF00118413FF006D00FF898A8CFF535558FFFF00FF00898A8CFF898A 8CFFFF00FF00FF00FF000268D0FF0268D0FFFF00FF00FF00FF00FF00FF00FF00 FF0006720AFF128921FF4BE47EFF006D00FFFF00FF00898A8CFF535558FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF0009780EFF2EC65CFF249D38FF44DD65FF006D00FF535558FFFF00FF00898A 8CFF535558FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF0009780EFF2EC65CFF1A9E35FF0C8513FF059F07FF006D00FF898A8CFF5355 58FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF0009780EFF27B84CFF18A32EFF07800CFF037A05FF009700FF006D00FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00128921FF0D8819FF088C0DFF038403FF037A05FF006D00FF006D00FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF001D93 2EFF33C34EFF047607FF006500FF006500FF006D00FF006500FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00108019FF36CF 54FF088C0DFF006500FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00046E08FF0476 07FF006500FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acDisconnectExecute end object MenuItem3: TMenuItem Caption = '-' end object MenuItem2: TMenuItem Action = acAddTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000029AD D6FF31B5DEFF21ADD6FF00000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000029AD D6FF9CDEEFFF84EFFFFF4AC6E7FF21ADD6FF18A5C6FF18A5C6FF18A5C6FF0000 00000000000000000000000000000000000000000000000000000000000029AD D6FF52BDE7FF9CFFFFFF94FFFFFF73DEF7FF73DEF7FF73DEF7FF73DEF7FF4AC6 E7FF21ADD6FF18A5C6FF000000000000000000000000000000000000000029AD D6FF52BDE7FFADFFFFFF8CF7FFFF8CEFFFFF8CEFFFFF8CEFFFFF73DEF7FF73DE F7FF73DEF7FF4AC6EFFF21ADD6FF0000000000000000000000000000000029AD D6FF29ADD6FFADDEEFFF94F7FFFF94F7FFFF8CEFFFFF8CEFFFFF8CEFFFFF8CEF FFFF73DEF7FF73DEF7FF4AC6EFFF0000000000000000000000000000000029AD D6FF73DEF7FF29ADD6FF9CFFFFFF8CF7FFFF8CF7FFFF8CF7FFFF8CEFFFFF8CEF FFFF8CEFFFFF73DEF7FF73DEF7FF18A5C6FF00000000000000000000000029AD D6FF94F7FFFF29ADD6FFADDEEFFFA5EFF7FFA5EFF7FFA5F7FFFF8CEFFFFF8CEF FFFF8CEFFFFF73DEF7FF007308FF18A5C6FF00000000000000000000000029AD D6FF9CFFFFFF73DEF7FF29ADD6FF18A5C6FF18A5C6FF18A5C6FFADDEEFFF8CF7 FFFF84EFFFFF007308FF5AE78CFF007308FF18A5C6FF000000000000000029AD D6FF9CFFFFFF94F7FFFF73DEF7FF73DEF7FF73DEF7FF6BDEF7FF29ADD6FFADDE EFFF007308FF52D67BFF42D66BFF31C64AFF007308FF000000000000000029AD D6FF9CFFFFFF94F7FFFF94F7FFFF94F7FFFF94F7FFFF73DEF7FF73DEF7FF29AD D6FF18A5C6FF108C21FF31C64AFF109C21FF18A5C6FF000000000000000029AD D6FFC6FFFFFF94FFFFFF9CFFFFFFD6FFFFFFD6FFFFFF8CEFFFFF94EFFFFF73DE F7FF73DEF7FF088410FF18AD29FF088410FF00000000000000000000000021AD D6FF9CDEEFFFC6FFFFFFC6FFFFFF9CDEEFFF18ADD6FF18A5C6FF18A5C6FF18A5 C6FF18A5C6FF088C10FF08A518FF000000000000000000000000000000000000 000031B5DEFF29ADD6FF18A5C6FF18A5C6FF0000000000000000000000000000 0000088C10FF08A518FF088410FF000000000000000000000000000000000000 00000000000000000000000000000000000000000000007308FF087B08FF088C 10FF088C10FF087B08FF00000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000 } OnClick = acAddTorrentExecute end object MenuItem49: TMenuItem Action = acAddLink Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 20000000000000040000640000006400000000000000000000000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF00AE9B8BFF81654EFF745840FF968170FF0000FF000000FF000000 FF00AE9B8BFF81654EFF745840FF968170FF0000FF000000FF000000FF000000 FF00B79F8EFFCFB8A9FFEBD6C8FFE5CBBBFFB3957FFF80624CFFBAA99BFFB79F 8EFFCFB8A9FFEBD6C8FFE5CBBBFFB3957FFF80624CFF0000FF000000FF00C9B5 A6FFE6D6CBFFB4A091FFA08672FFC1A895FF4F2C12FF472409FF472409FF4724 09FF735642FFA08672FFC1A895FFD2BCAEFFB99C88FF8A7361FF0000FF00BBA0 8DFFFFFFFFFF60412AFF0000FF00DBC2B1FFFFFFFFFFFFFEF7FFFFF1E6FFFAE3 D4FFF3D7C7FF9D897AFF0000FF00C0A592FFEFD6C7FF6B4B32FF0000FF00CDBA ACFFF2E6DEFFB4A193FF6B4E39FF816450FFCAB19EFFD3B5A2FFCDB19CFFDAC0 AFFFCDB39FFF6F543EFF816450FFBFAB9CFFCBB1A1FF9D8674FF0000FF000000 FF00E6DBD2FFEDE1D7FFFFFFFDFFFFF7F1FFD8C4B7FFAB9180FFCBBAAEFFCFBA AAFFEDE1D7FFFFFFFDFFFFF7F1FFD8C4B7FFAB9180FF0000FF000000FF000000 FF0043854BFF407D47FF467A4AFFDDD0C6FFC7B4A8FF0000FF000000FF000000 FF00D2C0B4FFC0A795FFBAA18DFFC7B4A8FF0000FF000000FF000000FF000000 FF00488E50FF7BC88DFF3F7D47FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF006BB271FF76B3 7BFF4D9554FF8AD29AFF43854BFF66986CFF6C9871FF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF0059AE60FFA4E3 B2FF9FE0ADFF9ADCA8FF94D9A3FF8ED59FFF528A58FF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF005CB463FF59AE 60FF55A65CFFAAE7B7FF4C9653FF6AA270FF619867FF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF005BAF62FFB9F0C3FF509E58FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF0067B86DFF59AE60FF5BAA62FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF00 } OnClick = acAddLinkExecute end object MenuItem4: TMenuItem Action = acStartTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00663432FF6D372FFF814125FF814125FF6D37 2FFF673432FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0072392DFF804026FFAF580FFFC26105FFCA6602FFCB6602FFC462 07FFB1590FFF844224FF683431FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF007C3F28FFA85514FFD46B00FFD26B02FFCD6804FFCB6300FFCD6700FFCE67 00FFD06900FFD66C00FFA75415FF693531FFFF00FF00FF00FF00FF00FF008242 25FFAE5810FFD36A00FFCA6200FFCE6D18FFEAC197FFD5853BFFC85C00FFCB65 00FFCB6601FFCB6601FFD56B00FFA85513FF663332FFFF00FF00FF00FF00984C 19FFD26900FFCC6802FFCA6401FFCD6B11FFF5E4D2FFFBF2EAFFD98D4AFFC85B 00FFCB6500FFCB6601FFCB6601FFD66B00FF814125FFFF00FF00A95512FFBF63 0BFFD3710CFFD17314FFD17311FFD27619FFF5DFC9FFFFFFFFFFFCF6EFFFE2A8 71FFCA6001FFC96100FFCB6601FFD16900FFB35A0EFF693531FFB0590EFFD175 18FFD98024FFD9832CFFDA8329FFDA852FFFF6E2CFFFFFFFFFFFFFFFFFFFFFFF FFFFEAC098FFCB670FFFC96100FFCE6800FFC56406FF693531FFB35D11FFDC8B 36FFE19343FFE1903FFFE1913EFFE19343FFF8E6D3FFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFF2D9C0FFCF7219FFCB6400FFCD6602FF6B3630FFB96110FFE4A2 5EFFECB175FFE8A056FFE9A056FFE8A25AFFFAE9D8FFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFEDCCAAFFCE6E17FFCB6500FFCD6602FF733A2CFFB15A0EFFE6A7 69FFF5CEA5FFF0B171FFF1AD69FFF0AF6DFFFBEBDCFFFFFFFFFFFFFFFFFFFFFE FEFFE7B684FFC96005FFC96100FFCE6800FFC56307FF683532FFB45C0DFFDD95 4DFFFBDFC2FFF9D1A8FFF9BE81FFF6BB7FFFFCEEDEFFFFFFFFFFFCF3E8FFE6AB 6FFFCF6905FFCB6400FFCB6601FFD16900FFB1590FFF693531FFFF00FF00C36C 1AFFF8D1A9FFFEEFDFFFFED6ABFFF8C186FFFDF2E7FFFBEFE3FFE6A35DFFD87D 1EFFD27414FFCB6703FFCB6500FFD66B00FF7E4027FFFF00FF00FF00FF00B75B 08FFDC9148FFFDE4CAFFFEF2E5FFFAD8B7FFF8DBBCFFEBAB69FFDF8931FFD982 2AFFD17619FFCD6B08FFD56B00FFA35116FF71392EFFFF00FF00FF00FF00FF00 FF00B35A0DFFDA9149FFFAD9B7FFFEEFDFFFFBE3CAFFF3CCA5FFECBA86FFE7AE 72FFE19B51FFD87C1CFFA55313FF7D3F29FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00B65B09FFC26E22FFDE9F63FFEABF93FFF0CBA6FFECC297FFDEA5 6EFFC27938FF964E21FF7C3F28FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00AF5A12FFB3611BFFB2611FFFA95B1EFFAC59 16FFA35317FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acStartTorrentExecute end object MenuItem64: TMenuItem Action = acForceStartTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00004B98FF004D9CFF0052A6FF0052A6FF004D 9CFF004B98FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00004E9EFF0052A6FF005EBEFF0062C6FF0065CCFF0065CCFF0064 CAFF005FC0FF0053A8FF004B98FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000051A4FF005DBCFF0069D4FF0069D4FF0067D0FF0064CAFF0065CCFF0066 CEFF0067D0FF006AD6FF005DBCFF004C9AFFFF00FF00FF00FF00FF00FF000052 A6FF005EBEFF0068D2FF0064CAFF0072E6FF81BFFFFF1187FFFF0063C8FF0064 CAFF0065CCFF0065CCFF0069D4FF005CBAFF004B98FFFF00FF00FF00FF000057 B0FF0068D2FF0066CEFF0064CAFF006EDEFFC7E3FFFFE5F2FFFF2390FFFF0063 C8FF0064CAFF0065CCFF0065CCFF006AD6FF0052A6FFFF00FF001E7AD8FF0064 CAFF006EDEFF0071E4FF0070E2FF0074EAFFBFDFFFFFFFFFFFFFEBF5FFFF53A8 FFFF0064CAFF0063C8FF0065CCFF0067D0FF005FC0FF004C9AFF1E7CDCFF0073 E8FF007DFCFF0581FFFF0380FFFF0983FFFFC5E2FFFFFFFFFFFFFFFFFFFFFFFF FFFF83C0FFFF006CDAFF0063C8FF0066CEFF0064CAFF004C9AFF1E80E4FF1388 FFFF2591FFFF218FFFFF1F8EFFFF2591FFFFCBE5FFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFB3D9FFFF0073E8FF0064CAFF0066CEFF004C9AFF1E82E8FF43A0 FFFF61AFFFFF3F9EFFFF3F9EFFFF43A0FFFFD3E9FFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFF97CAFFFF0071E4FF0064CAFF0066CEFF004E9EFF1E7CDCFF4FA6 FFFF9BCCFFFF61AFFFFF5BACFFFF5DADFFFFD7EBFFFFFFFFFFFFFFFFFFFFFDFE FFFF6BB4FFFF0066CEFF0063C8FF0066CEFF0065CCFF004C9AFF1E7DDEFF2B94 FFFFBDDEFFFFA1CFFFFF7BBCFFFF75B9FFFFDBEDFFFFFFFFFFFFE5F2FFFF55A9 FFFF0069D4FF0064CAFF0065CCFF0067D0FF005FC0FF004C9AFFFF00FF00006D DCFFA1CFFFFFDDEEFFFFA9D3FFFF7FBEFFFFE5F2FFFFDFEFFFFF43A0FFFF007A F6FF0072E6FF0066CEFF0064CAFF006AD6FF0051A4FFFF00FF00FF00FF001E7C DCFF2591FFFFC7E3FFFFE3F1FFFFB1D8FFFFB5DAFFFF55A9FFFF1187FFFF0380 FFFF0074EAFF0069D4FF0069D4FF005BB8FF004E9EFFFF00FF00FF00FF00FF00 FF00207EDFFF2390FFFFB1D8FFFFDDEEFFFFC5E2FFFF99CBFFFF73B8FFFF59AB FFFF3398FFFF0079F4FF005BB8FF0052A6FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00207DDDFF0071E4FF419FFFFF7DBDFFFF97CAFFFF83C0FFFF4DA5 FFFF007CFAFF005AB6FF0051A4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF001F7DDEFF1F84ECFF1F86F0FF1F81E6FF1F7E E0FF1F7AD8FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem22: TMenuItem Action = acStartAllTorrents OnClick = acStartAllTorrentsExecute end object MenuItem5: TMenuItem Action = acStopTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00663432FF6D372FFF824125FF824125FF6D37 2FFF673432FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0072392DFF814125FFB0580FFFC26105FFCA6602FFCB6602FFC462 07FFB2590FFF854323FF683431FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF007C3F28FFA95514FFD36B00FFD16A02FFCD6803FFCB6400FFCC6700FFCD67 00FFCF6900FFD56C00FFA85415FF693531FFFF00FF00FF00FF00FF00FF008242 25FFAF5810FFD26A00FFCA6300FFCD6A0FFFCD6A0FFFCB6400FFCA6201FFCB66 01FFCB6601FFCB6601FFD46B00FFA95513FF663332FFFF00FF00FF00FF00994D 19FFD26A02FFCD6A05FFCB6602FFCC6806FFCD6C0AFFCC6A07FFCD6B0AFFCA64 01FFCB6601FFCB6601FFCB6601FFD56B00FF824125FFFF00FF00A95512FFC065 0DFFD57513FFD3781CFFD27414FFF3DBC2FFFFFFFFFFD98F45FFD98F45FFFFFF FFFFF2D9C0FFCA6401FFCB6601FFD06900FFB45A0EFF693531FFB0590EFFD278 1CFFDC872FFFDD8A37FFDA842CFFF5DEC7FFFFFFFFFFD88D41FFD88D41FFFFFF FFFFF3DAC2FFCB6604FFCA6401FFCD6800FFC56406FF693531FFB45E12FFDE8E 3AFFE59B4EFFE69B4FFFE39446FFF6E2CCFFFFFFFFFFDC954CFFD98F43FFFFFF FFFFF3DAC1FFCC6A07FFCD6B0BFFCB6500FFCD6602FF6C3630FFB96211FFE5A4 61FFEEB478FFEDAB67FFEBA660FFF9E6D3FFFFFFFFFFE29F5CFFDD9750FFFFFF FFFFF3DAC1FFCC6A07FFCC690AFFCB6500FFCD6602FF743A2CFFB15A0EFFE7A8 6AFFF6CB9EFFF4B87BFFF3B474FFFBEBD9FFFFFFFFFFE8AD71FFE3A362FFFFFF FFFFF4DCC4FFCB6502FFCA6401FFCD6800FFC56307FF683532FFB45C0DFFDE96 4FFFFBDBB9FFF9CB9AFFF8C084FFFDEEDFFFFFFFFFFFEFBC86FFEBB278FFFFFF FFFFF5DEC7FFCE6C0BFFCB6702FFD06900FFB2590FFF693531FFFF00FF00C46D 1BFFF8D0A6FFFDE4CAFFFBCC9AFFF8C186FFF7C792FFF3BB81FFEAA760FFE395 44FFDA842DFFD17314FFCC6805FFD56B01FF7F4027FFFF00FF00FF00FF00B75B 08FFDD9249FFFDE1C4FFFDE7CFFFF9CFA3FFF8CD9FFFF2B779FFEBA45BFFE498 4BFFDA8631FFD17415FFD56D04FFA45216FF71392EFFFF00FF00FF00FF00FF00 FF00B35A0DFFDB924AFFFAD7B3FFFDE8D2FFFADCBBFFF4C99DFFEFBC86FFEAB1 74FFE39E55FFD97F21FFA65514FF7D3F29FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00B65B09FFC36F24FFDFA064FFEBBF92FFF1CAA4FFEDC296FFDFA6 6FFFC47B3AFF975022FF7C3F28FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00AF5A12FFB3611BFFB36220FFAA5C1FFFAC59 16FFA35317FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acStopTorrentExecute end object MenuItem23: TMenuItem Action = acStopAllTorrents OnClick = acStopAllTorrentsExecute end object MenuItem6: TMenuItem Action = acRemoveTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF0001079FFF0313A9FF0418AEFF0419AEFF0313 A9FF0108A0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0001049DFF041CB1FF0730C0FF0734C4FF0735C5FF0735C5FF0734 C3FF0731C1FF041FB3FF01069EFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000109A1FF052BC3FF0735C7FF0733C2FF0732C2FF0732C2FF0732C2FF0732 C2FF0733C3FF0735C4FF062DBEFF020CA4FFFF00FF00FF00FF00FF00FF000104 9BFF052BCAFF0636D8FF0431CDFF0027C4FF032EC1FF0732C2FF0732C2FF0430 C1FF0027BFFF042FC1FF0735C4FF072EBEFF01069EFFFF00FF00FF00FF00031A BAFF0537E7FF0331DDFF123DD8FF6480E0FF1840CBFF002CC1FF022DC0FF0F38 C4FF6580D9FF1B43C7FF052FC1FF0735C5FF051FB3FFFF00FF0001049EFF0430 E4FF0436F1FF002AE4FF5070E9FFFFFFFFFFB7C4F1FF0D36CAFF042DC3FFA2B2 E8FFFFFFFFFF6984DAFF0026BEFF0733C3FF0731C1FF0108A0FF020FAFFF0336 FAFF0335F8FF0232EEFF0A35E8FF8CA2F2FFFFFFFFFFB4C2F1FFA9B8EDFFFFFF FFFFA7B7E9FF133AC4FF052FC1FF0732C2FF0734C4FF0313AAFF0619BCFF1747 FEFF093AFCFF0435F8FF0131F0FF002BE8FF91A5F4FFFFFFFFFFFFFFFFFFABBA EFFF062FC5FF022DC0FF0732C2FF0732C2FF0736C5FF0419AEFF0B1DBEFF4168 FEFF1C49FCFF0335FBFF0031F9FF0531F2FFA4B5F7FFFFFFFFFFFFFFFFFFB9C6 F2FF0D36D0FF002CC6FF0732C2FF0732C2FF0736C5FF0418ADFF0613B4FF5B7C FCFF486CFDFF0133FBFF113CFBFFA1B4FEFFFFFFFFFFA4B6F8FF92A7F5FFFFFF FFFFB6C4F2FF1A41D3FF042FC8FF0732C4FF0734C3FF0212A9FF0003A0FF4A6A F3FF8FA6FFFF1F46FBFF4C6FFCFFFFFFFFFFA7B8FEFF0733F6FF002AEDFF8CA2 F6FFFFFFFFFF627FE7FF0028D0FF0734CCFF0730C3FF00069FFFFF00FF001A2F CBFF99AFFFFF8BA2FEFF214DFBFF4D71FCFF0E3DFBFF0030FBFF0031F7FF0636 F1FF4C6EF1FF103CE3FF0432DBFF0636D7FF041CB5FFFF00FF00FF00FF000004 A0FF415EECFFB8C7FFFF9CAFFDFF3A5CFCFF0A3AFBFF0335FBFF0335FBFF0133 F9FF052FF2FF0635EBFF0537E9FF052CCDFF00049CFFFF00FF00FF00FF00FF00 FF000309A5FF4260ECFFA9BBFFFFBDCAFFFF8EA5FEFF6483FDFF5073FCFF4A6E FDFF3961FDFF1444F9FF042CD7FF0109A2FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF000004A0FF1E32CDFF5876F6FF859EFEFF8BA3FFFF7994FEFF5376 FCFF234AF0FF051EC5FF01049CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000004A0FF0917B6FF1022C3FF0D1FC2FF0311 B4FF01059FFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acRemoveTorrentExecute end object MenuItem38: TMenuItem Action = acRemoveTorrentAndData OnClick = acRemoveTorrentAndDataExecute end object MenuItem59: TMenuItem Caption = '-' end object miPriority: TMenuItem Caption = 'Priority' object MenuItem60: TMenuItem Action = acSetHighPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 200000000000000400006400000064000000000000000000000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF0000404CD8FF162FD0FF162FD0FF404C D8FF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF0000173CE5FF6699FFFF99CCFFFF99CCFFFF6699 FFFF193EE5FF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF00000F38EBFF5084FFFF6699FFFF74A7FFFF74A7FFFF6699 FFFF5488FFFF0F38EBFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF00004B67F2FF2254FDFF3C6FFFFF4E81FFFF5A8DFFFF5A8DFFFF5084 FFFF3F73FFFF275AFFFF4B67F2FF00FF000000FF000000FF000000FF000000FF 000000FF00001A43F9FF184BFFFF275AFFFF3366FFFF3D71FFFF3D71FFFF3D71 FFFF275AFFFF184BFFFF1A43F9FF00FF000000FF000000FF000000FF000000FF 000000FF00001A43F9FF0033FFFF184BFFFF3366FFFF3F73FFFF3F73FFFF3366 FFFF184BFFFF0033FFFF1A43F9FF00FF000000FF000000FF000000FF000000FF 000000FF00005273FEFF103DF9FF486CFEFF5B7DFFFF6B8AFEFF6B8AFEFF5B7D FFFF4368FEFF0B39FAFF5273FEFF00FF000000FF000000FF000000FF000000FF 000000FF000000FF00000033FFFF4F6DF8FF7991FCFF899EFDFF899EFDFF758E FBFF4F6DF8FF0033FFFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF00000033FFFF4969F7FF91A2F7FF91A2F7FF4969 F7FF0033FFFF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF00005273FEFF193FFDFF193FFDFF5273 FEFF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF0000 } OnClick = acSetHighPriorityExecute end object MenuItem61: TMenuItem Action = acSetNormalPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 20000000000000040000640000006400000000000000000000000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF001D915CFF0D8145FF0D8145FF1D91 5CFF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000D8E46FF00E03FFF01FF1FFF01FF1FFF00E0 3FFF0F8E47FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF00089344FF00D142FF00E03FFF00E83AFF00E83AFF00E0 3FFF00D442FF089344FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000CB85AFF00B146FF00C546FF00D043FF00D842FF00D842FF00D1 42FF00C744FF00B845FF0CB85AFF0000FF000000FF000000FF000000FF000000 FF000000FF0004A74AFF00AF44FF00B845FF00BF46FF00C644FF00C644FF00C6 44FF00B845FF00AF44FF04A74AFF0000FF000000FF000000FF000000FF000000 FF000000FF0004A74AFF009F42FF00AF44FF00BF46FF00C744FF00C744FF00BF 46FF00AF44FF009F42FF04A74AFF0000FF000000FF000000FF000000FF000000 FF000000FF0000D159FF03A147FF00CA55FF00D858FF01DF5CFF01DF5CFF00D8 58FF00C854FF02A045FF00D159FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF00009F42FF07C45BFF05E466FF04F06BFF04F06BFF06DF 64FF07C45BFF009F42FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00009F42FF07BF57FF10E570FF10E570FF07BF 57FF009F42FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF0000D159FF00AC4EFF00AC4EFF00D1 59FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF00 } OnClick = acSetNormalPriorityExecute end object MenuItem62: TMenuItem Action = acSetLowPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 200000000000000400006400000064000000000000000000000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF00003F97D7FF168BCFFF168BCFFF3F97 D7FF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000017A2E4FF65E5FFFF98FFFFFF98FFFFFF65E5 FFFF19A3E4FF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF00000FA5EAFF4FDBFFFF65E5FFFF73ECFFFF73ECFFFF65E5 FFFF53DDFFFF0FA5EAFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF00004ABAF2FF21C1FDFF3BD0FFFF4DD9FFFF59DFFFFF59DFFFFF4FDB FFFF3ED2FFFF26C5FFFF4ABAF2FF00FF000000FF000000FF000000FF000000FF 000000FF000019B2F9FF17BEFFFF26C5FFFF32CBFFFF3CD1FFFF3CD1FFFF3CD1 FFFF26C5FFFF17BEFFFF19B2F9FF00FF000000FF000000FF000000FF000000FF 000000FF000019B2F9FF00B2FFFF17BEFFFF32CBFFFF3ED2FFFF3ED2FFFF32CB FFFF17BEFFFF00B2FFFF19B2F9FF00FF000000FF000000FF000000FF000000FF 000000FF000051C8FEFF0FB1F9FF47C6FEFF5ACEFFFF6AD3FEFF6AD3FEFF5ACE FFFF42C5FEFF0AB0FAFF51C8FEFF00FF000000FF000000FF000000FF000000FF 000000FF000000FF000000B2FFFF4EC1F8FF78D2FCFF88D7FDFF88D7FDFF74D0 FBFF4EC1F8FF00B2FFFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000B2FFFF48BFF7FF90D4F7FF90D4F7FF48BF F7FF00B2FFFF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000051C8FEFF18B0FDFF18B0FDFF51C8 FEFF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF0000 } OnClick = acSetLowPriorityExecute end end object miQueue: TMenuItem Caption = 'Queue' object MenuItem78: TMenuItem Action = acQMoveTop end object MenuItem79: TMenuItem Action = acQMoveUp Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00044906FF055B09FF066C0CFF066C0CFF055E 0AFF044C06FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00056009FF056009FF089113FF09B018FF09B31AFF09B319FF09B1 19FF079614FF05680CFF05680CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000A6A15FF0A7F15FF0BB61CFF09B91AFF08B418FF07B216FF09B319FF09B4 19FF09B81AFF09B91AFF078310FF044D06FFFF00FF00FF00FF00FF00FF000B6A 15FF0F8522FF16BD34FF11B727FF0BB21CFF07B116FFC2ECC6FFFFFFFFFF50C8 5CFF09B219FF09B419FF09BA1AFF078410FF06670CFFFF00FF00FF00FF000B6A 15FF20BE49FF1BBD40FF14B730FF0AB21FFF09B219FFC0EBC4FFFFFFFFFF51C9 5DFF09B219FF09B219FF09B319FF09BA1AFF06670CFFFF00FF00087210FF1B9A 3AFF2AC65BFF1DBB45FF0EB425FF0BB31BFF09B21AFFBFEBC3FFFFFFFFFF50C8 5CFF09B219FF09B219FF09B219FF09B81AFF089413FF045D09FF087210FF2AB6 5BFF2CC565FF22BD4DFF2DBD3EFF12B421FF0CB31CFFC0EBC3FFFFFFFFFF50C8 5CFF09B219FF2ABC38FF17B626FF09B51AFF08AB17FF045D09FF0F821CFF37C2 6CFF33C76CFF36C56AFFE4F7E9FFA5E4B5FF2AC053FFC5EED0FFFFFFFFFF41C3 4DFF4FC85AFFF5FCF6FFADE5B2FF08B419FF09B319FF05650BFF138D23FF58CC 83FF42C977FF37C56EFFF1FBF5FFFFFFFFFFA3E5BDFFCBF0DAFFFFFFFFFF85D9 8EFFE4F7E6FFFFFFFFFF97DE9EFF0AB41AFF09B319FF066D0DFF0F911DFF6FD2 93FF5FD38DFF31C369FF80DAA3FFF9FDFAFFFDFFFEFFF9FDFBFFFDFEFEFFFBFE FBFFFFFFFFFF95DEA4FF1DB93DFF11B82BFF08B119FF05610AFF0F911DFF67CC 83FF9BE5BAFF38C670FF30C369FF7AD89EFFF8FDFAFFFFFFFFFFFFFFFFFFFFFF FFFF9FE2B1FF20BD48FF1AB93EFF10BA29FF08A317FF05610AFFFF00FF0037B6 50FFBCEDD2FF82DBA4FF28C063FF2FC267FF78D89DFFF5FCF8FFFFFFFFFFB1E8 C3FF22BC4BFF1DBA41FF18B736FF14C030FF0A8517FFFF00FF00FF00FF0037B6 50FF71D28CFFD2F4E1FF80DAA3FF36C46DFF2DC267FF8ADDAAFFC9EFD8FF3DC7 6FFF24BE56FF23BC4DFF1FC146FF16AE34FF0A8517FFFF00FF00FF00FF00FF00 FF0025AE39FF84D89FFFDBF7EAFFAFE8C6FF6BD493FF52CC81FF44C978FF49CA 7BFF48CB78FF39CB6AFF21B649FF0F7C1FFFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0066CD81FF66CD81FFADE8C5FFCCF2DEFFBAEDD1FFA6E7C2FF91E2 B3FF64D492FF2FB157FF2FB157FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF0032B74EFF52C46FFF61CB80FF5DC87DFF43B9 64FF24A342FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem80: TMenuItem Action = acQMoveDown Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00044906FF055B09FF066C0CFF066C0CFF055E 0AFF044C06FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00056009FF056009FF089113FF09B018FF09B31AFF09B319FF09B1 19FF079614FF05680CFF05680CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000A6A15FF0A7F15FF0BB61CFF09B91AFF08B418FF07B216FF09B319FF09B4 19FF09B81AFF09B91AFF078310FF044D06FFFF00FF00FF00FF00FF00FF000B6A 15FF0F8522FF16BD34FF11B727FF0BB21CFF07B116FF6FD177FFBCEAC1FF20B9 2FFF09B219FF09B419FF09BA1AFF078410FF06670CFFFF00FF00FF00FF000B6A 15FF20BE49FF1BBD40FF14B730FF0AB21FFF58CB63FFF2FBF3FFFFFFFFFFA6E3 ACFF0BB31BFF09B219FF09B319FF09BA1AFF06670CFFFF00FF00087210FF1B9A 3AFF2AC65BFF1DBB45FF0EB425FF5BCC66FFF6FCF7FFFFFFFFFFFFFFFFFFFFFF FFFF96DE9DFF0BB31BFF09B219FF09B81AFF089413FF045D09FF087210FF2AB6 5BFF2CC565FF22BD4DFF67CF73FFF7FDF8FFFDFEFDFFF8FDF9FFFDFEFDFFFAFD FAFFFFFFFFFF8EDC95FF0EB41EFF09B51AFF08AB17FF045D09FF0F821CFF37C2 6CFF33C76CFF36C56AFFF0FAF3FFFFFFFFFF9CE2AFFFC7EED2FFFFFFFFFF83D8 8BFFE4F7E6FFFFFFFFFF97DE9EFF08B419FF09B319FF05650BFF138D23FF58CC 83FF42C977FF37C56EFFE6F8EDFFADE8C4FF38C670FFC9F0D9FFFFFFFFFF44C5 51FF4FC85CFFF5FCF6FFADE5B2FF0AB41AFF09B319FF066D0DFF0F911DFF6FD2 93FF5FD38DFF31C369FF50CC80FF3DC773FF38C56FFFCAF0D8FFFFFFFFFF5CCE 76FF1AB93EFF37C153FF25BC44FF11B82BFF08B119FF05610AFF0F911DFF67CC 83FF9BE5BAFF38C670FF30C369FF38C56FFF38C56FFFCBF0D9FFFFFFFFFF61D0 81FF1EBC49FF1EBC47FF1AB93EFF10BA29FF08A317FF05610AFFFF00FF0037B6 50FFBCEDD2FF82DBA4FF28C063FF2FC267FF38C56FFFCCF0DAFFFFFFFFFF67D2 89FF20BB4AFF1DBA41FF18B736FF14C030FF0A8517FFFF00FF00FF00FF0037B6 50FF71D28CFFD2F4E1FF80DAA3FF36C46DFF2DC267FFCDF1DBFFFFFFFFFF67D3 8EFF24BE56FF23BC4DFF1FC146FF16AE34FF0A8517FFFF00FF00FF00FF00FF00 FF0025AE39FF84D89FFFDBF7EAFFAFE8C6FF6BD493FF52CC81FF44C978FF49CA 7BFF48CB78FF39CB6AFF21B649FF0F7C1FFFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0066CD81FF66CD81FFADE8C5FFCCF2DEFFBAEDD1FFA6E7C2FF91E2 B3FF64D492FF2FB157FF2FB157FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF0032B74EFF52C46FFF61CB80FF5DC87DFF43B9 64FF24A342FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem81: TMenuItem Action = acQMoveBottom end end object MenuItem51: TMenuItem Action = acReannounceTorrent OnClick = acReannounceTorrentExecute end object MenuItem7: TMenuItem Action = acVerifyTorrent OnClick = acVerifyTorrentExecute end object MenuItem54: TMenuItem Action = acMoveTorrent OnClick = acMoveTorrentExecute end object MenuItem98: TMenuItem Action = acRename end object MenuItem21: TMenuItem Action = acTorrentProps OnClick = acTorrentPropsExecute end object MenuItem1: TMenuItem Caption = '-' end object miExit: TMenuItem Action = acExit OnClick = acExitExecute end end object miView: TMenuItem Caption = 'View' object MenuItem88: TMenuItem Action = acSelectAll end object MenuItem34: TMenuItem Action = acSetupColumns end object MenuItem35: TMenuItem Caption = '-' end object MenuItem41: TMenuItem Action = acFolderGrouping end object MenuItem89: TMenuItem Action = acTrackerGrouping end object MenuItem93: TMenuItem Caption = '-' end object MenuItem94: TMenuItem Action = acFilterPane end object MenuItem95: TMenuItem Action = acInfoPane end object MenuItem96: TMenuItem Action = acStatusBar end end object miTools: TMenuItem Caption = 'T&ools' object MenuItem18: TMenuItem Action = acDaemonOptions Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000292946FF7F5586FF7F5586FF0000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000292946FF7F5586FFBF9CC2FFBF9CC2FF1A47D9FF00000000000000000000 0000000000000000000000000000000000000000000000000000000000002929 46FF0323D2FF8C89E6FF001ACEFF8380CBFF1A47D9FF00000000000000000000 0000000000000000000000000000000000000000000000000000292946FF0323 D2FF93A9FFFF0436FFFF001ACEFFB7B4E4FF1A47D9FF00000000000000000000 00000000000000000000000000000000000000000000292946FF0323D2FF93A9 FFFF0033FFFF001ACEFF9BA7EAFF1A47D9FF0000000000000000000000000000 000000000000000000000000000000000000292946FF0323D2FF93A9FFFF0033 FFFF001ACEFF93A3EEFF1A47D9FF000000000000000000000000000000000000 0000000000000000000000000000292946FF0323D2FF93A9FFFF0033FFFF001A CEFF93A3EEFF1A47D9FF00000000000000000000000000000000000000000000 0000595959FF595959FF595959FF0323D2FF93A9FFFF0033FFFF001ACEFF93A3 EEFF1A47D9FF0000000000000000000000000000000000000000000000005959 59FF595959FFABABABFF999999FF7A83BDFF0031FCFF001ACEFF93A3EEFF1A47 D9FF000000000000000000000000000000000000000000000000595959FF5959 59FF9C9C9CFFB9B9B9FFC4C4C4FFA8A8A8FF5665AEFF7393EEFF1A47D9FF0000 0000000000000000000000000000000000000000000000000000595959FFABAB ABFFCDCDCDFFC7C7C7FFC3C3C3FFCFCFCFFFA7A7A7FF595959FF000000000000 0000000000000000000000000000000000000000000000000000595959FFDEDE DEFF595959FF595959FFB7B7B7FFD6D6D6FFBCBCBCFF595959FF000000000000 0000000000000000000000000000000000000000000000000000595959FF5959 59FF00000000999999FF595959FFDEDEDEFFC8C8C8FF595959FF000000000000 0000000000000000000000000000000000000000000000000000595959FF0000 0000999999FF595959FFD4D4D4FFE0E0E0FF595959FF999999FF000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000595959FFCBCBCBFFCBCBCBFF575757FF595959FF00000000000000000000 000000000000000000000000000000000000000000000000000000000000ABAB ABFF595959FF595959FF595959FF999999FF0000000000000000000000000000 0000000000000000000000000000000000000000000000000000 } OnClick = acDaemonOptionsExecute end object MenuItem17: TMenuItem Action = acOptions OnClick = acOptionsExecute end object MenuItem32: TMenuItem Caption = '-' end object MenuItem70: TMenuItem Action = acAltSpeed Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000001067B84F0C5EACCF0A5AA9F30958A5FD0754A0FF034E 63FE00493DFE004737FF004737FE00493BE50050415300000000000000000F69 C3111267BBBC146CC2DC1F73C5FE3B8AD6FF4491DBFF3A8AD6FF1469B4FF0067 51FF007A5FFF00896BFF009272FF006F56FF00503EFA00504040000000001D7D D9352484DFFF65ADF2FF6EB2F3FF5BA3E7FF357EA7FF16637EFF0E617FFF005E 49FF087E64FF138D72FF19A083FF029071FF006D54FF005545C5000000004095 EA0C56A6F4EF68B2F6FF7CBEFBFF0B5750FF00503EFF005442FF00755AFF0481 65FF0C9B7CFF159E7FFF16987BFF1AA284FF008768FF005B48F2000000002288 EE1E409AF0EA57A6F3BE3893ECE3025C4BE400614BFF02795EFF01A882FF0380 65FF0EA483FF168972FF157866FF179B7EFF007C60FF00624CF90C70CFEB1173 D2FC69B1F7791176D6A22287E88600614C9D006952FF048F70FF02C79BFF0083 65FF0CA281FF20AF90FF20AA8DFF0E9174FF00785DFF006651C1278AE9E72588 E8D7000000001379D9D72288EE1E006B5232007158FD017E62FF08CDA1FF01D2 A4FF007B5FFF037259FF047E63FF038669FF017D62FF006A5152000000000000 00000A6DD0311E83E3BD000000000000000000775C85007E62FF018E6FFF07CE A2FF06DEAFFF06D6A8FF08C198FF028A6CFF00765C8D00000000000000000000 00000C70D0ED0C6FD0EF00000000000000000000000000816553008B6CC00093 72F7009473FF008C6CDE008366A8007A5F430000000000000000000000000000 00002287E8C82187E8C600000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000 } OnClick = acAltSpeedExecute end object MenuItem52: TMenuItem Action = acUpdateBlocklist OnClick = acUpdateBlocklistExecute end object MenuItem33: TMenuItem Action = acUpdateGeoIP OnClick = acUpdateGeoIPExecute end end object miHelp: TMenuItem Caption = '&Help' object MenuItem86: TMenuItem Action = acCheckNewVersion end object miHomePage: TMenuItem Caption = 'Visit home page' OnClick = miHomePageClick end object miDonate: TMenuItem Caption = 'Donate!' OnClick = miDonateClick end object MenuItem87: TMenuItem Caption = '-' end object miAbout: TMenuItem Caption = 'About...' OnClick = miAboutClick end end end object ActionList: TActionList[8] left = 40 top = 28 object acConnect: TAction Caption = 'Connect to Transmission' Hint = 'Connect to Transmission' ImageIndex = 18 OnExecute = acConnectExecute end object acAddTorrent: TAction Category = 'Torrent' Caption = '&Add torrent...' Hint = 'Add torrent' ImageIndex = 0 OnExecute = acAddTorrentExecute ShortCut = 45 end object acStartTorrent: TAction Category = 'Torrent' Caption = 'Start' Hint = 'Start torrent' ImageIndex = 1 OnExecute = acStartTorrentExecute ShortCut = 114 end object acSetHighPriority: TAction Category = 'Files' Caption = 'High priority' ImageIndex = 26 OnExecute = acSetHighPriorityExecute ShortCut = 16456 end object acSetNormalPriority: TAction Category = 'Files' Caption = 'Normal priority' ImageIndex = 25 OnExecute = acSetNormalPriorityExecute ShortCut = 16462 end object acSetLowPriority: TAction Category = 'Files' Caption = 'Low priority' ImageIndex = 24 OnExecute = acSetLowPriorityExecute ShortCut = 16460 end object acSetNotDownload: TAction Category = 'Files' Caption = 'Don''t download' ImageIndex = 23 OnExecute = acSetNotDownloadExecute ShortCut = 16457 end object acOptions: TAction Category = 'Tools' Caption = 'Application options...' OnExecute = acOptionsExecute ShortCut = 16504 end object acDaemonOptions: TAction Category = 'Tools' Caption = 'Transmission options...' Hint = 'Transmission options' ImageIndex = 8 OnExecute = acDaemonOptionsExecute ShortCut = 120 end object acExit: TAction Caption = 'E&xit' OnExecute = acExitExecute ShortCut = 32856 end object acResolveHost: TAction Category = 'Peers' Caption = 'Resolve host name' OnExecute = acResolveHostExecute end object acResolveCountry: TAction Category = 'Peers' Caption = 'Resolve country' OnExecute = acResolveCountryExecute end object acShowCountryFlag: TAction Category = 'Peers' Caption = 'Show country flag' OnExecute = acShowCountryFlagExecute end object acUpdateGeoIP: TAction Category = 'Tools' Caption = 'Update GeoIP database' OnExecute = acUpdateGeoIPExecute end object acOpenFile: TAction Category = 'Files' Caption = 'Open' ImageIndex = 28 OnExecute = acOpenFileExecute end object acOpenContainingFolder: TAction Category = 'Files' Caption = 'Open containing folder' ImageIndex = 22 OnExecute = acOpenContainingFolderExecute ShortCut = 16397 end object acUpdateBlocklist: TAction Category = 'Tools' Caption = 'Update blocklist' OnExecute = acUpdateBlocklistExecute end object acSelectAll: TAction Category = 'View' Caption = 'Select all' OnExecute = acSelectAllExecute ShortCut = 16449 end object acShowApp: TAction Caption = 'Show' OnExecute = acShowAppExecute end object acHideApp: TAction Caption = 'Hide' OnExecute = acHideAppExecute end object acAddTracker: TAction Category = 'Trackers' Caption = 'Add tracker...' OnExecute = acAddTrackerExecute end object acEditTracker: TAction Category = 'Trackers' Caption = 'Edit tracker...' OnExecute = acEditTrackerExecute end object acDelTracker: TAction Category = 'Trackers' Caption = 'Remove tracker' OnExecute = acDelTrackerExecute end object acConnOptions: TAction Category = 'Tools' Caption = 'Manage connections...' OnExecute = acConnOptionsExecute end object acNewConnection: TAction Caption = 'New connection...' OnExecute = acNewConnectionExecute end object acDisconnect: TAction Caption = 'Disconnect from Transmission' ImageIndex = 27 OnExecute = acDisconnectExecute end object acAltSpeed: TAction Category = 'Tools' Caption = 'Use alternate bandwidth settings' Hint = 'Use alternate bandwidth settings' ImageIndex = 38 OnExecute = acAltSpeedExecute ShortCut = 119 end object acForceStartTorrent: TAction Category = 'Torrent' Caption = 'Force start' ImageIndex = 39 OnExecute = acForceStartTorrentExecute ShortCut = 8306 end object acStopTorrent: TAction Category = 'Torrent' Caption = 'Stop' Hint = 'Stop torrent' ImageIndex = 2 OnExecute = acStopTorrentExecute ShortCut = 115 end object acRemoveTorrent: TAction Category = 'Torrent' Caption = 'Remove' Hint = 'Remove torrent' ImageIndex = 3 OnExecute = acRemoveTorrentExecute ShortCut = 46 end object acRemoveTorrentAndData: TAction Category = 'Torrent' Caption = 'Remove torrent and Data' OnExecute = acRemoveTorrentAndDataExecute ShortCut = 8238 end object acVerifyTorrent: TAction Category = 'Torrent' Caption = '&Verify' Hint = 'Verify torrent' OnExecute = acVerifyTorrentExecute end object acTorrentProps: TAction Category = 'Torrent' Caption = 'Properties...' OnExecute = acTorrentPropsExecute ShortCut = 32781 end object acStartAllTorrents: TAction Category = 'Torrent' Caption = 'Start all torrents' Hint = 'Start all torrents' OnExecute = acStartAllTorrentsExecute ShortCut = 16498 end object acStopAllTorrents: TAction Category = 'Torrent' Caption = 'Stop all torrents' Hint = 'Stop all' OnExecute = acStopAllTorrentsExecute ShortCut = 16499 end object acSetupColumns: TAction Category = 'View' Caption = 'Setup columns...' OnExecute = acSetupColumnsExecute end object acAddLink: TAction Category = 'Torrent' Caption = 'Add torrent link...' Hint = 'Add torrent link' ImageIndex = 21 OnExecute = acAddLinkExecute ShortCut = 16429 end object acReannounceTorrent: TAction Category = 'Torrent' Caption = 'Reannounce (get more peers)' OnExecute = acReannounceTorrentExecute ShortCut = 16466 end object acMoveTorrent: TAction Category = 'Torrent' Caption = 'Set data location...' OnExecute = acMoveTorrentExecute ShortCut = 117 end object acQMoveTop: TAction Category = 'Torrent' Caption = 'Move top' OnExecute = acQMoveTopExecute ShortCut = 24625 end object acQMoveUp: TAction Category = 'Torrent' Caption = 'Move up' Hint = 'Move up queue' ImageIndex = 40 OnExecute = acQMoveUpExecute ShortCut = 16433 end object acQMoveDown: TAction Category = 'Torrent' Caption = 'Move down' Hint = 'Move down queue' ImageIndex = 41 OnExecute = acQMoveDownExecute ShortCut = 16434 end object acQMoveBottom: TAction Category = 'Torrent' Caption = 'Move bottom' OnExecute = acQMoveBottomExecute ShortCut = 24626 end object acCheckNewVersion: TAction Caption = 'Check for updates' OnExecute = acCheckNewVersionExecute end object acFolderGrouping: TAction Category = 'View' Caption = 'Folder grouping' OnExecute = acFolderGroupingExecute end object acTrackerGrouping: TAction Category = 'View' Caption = 'Tracker grouping' OnExecute = acTrackerGroupingExecute end object acAdvEditTrackers: TAction Category = 'Trackers' Caption = 'Modify trackers...' OnExecute = acAdvEditTrackersExecute end object acFilterPane: TAction Category = 'View' Caption = 'Filter pane' Checked = True OnExecute = acFilterPaneExecute end object acInfoPane: TAction Category = 'View' Caption = 'Info pane' Checked = True OnExecute = acInfoPaneExecute end object acStatusBar: TAction Category = 'View' Caption = 'Status bar' Checked = True OnExecute = acStatusBarExecute end object acCopyPath: TAction Category = 'Files' Caption = 'Copy file path to clipboard' OnExecute = acCopyPathExecute end object acRename: TAction Category = 'Torrent' Caption = 'Rename' OnExecute = acRenameExecute ShortCut = 113 Visible = False end end object TorrentsListTimer: TTimer[9] Enabled = False Interval = 100 OnTimer = TorrentsListTimerTimer left = 253 top = 55 end object OpenTorrentDlg: TOpenDialog[10] Title = 'Select a .torrent to open' Filter = 'Torrents (*.torrent)|*.torrent|All files (*.*)|*.*' Options = [ofAllowMultiSelect, ofEnableSizing, ofViewDetail] left = 213 top = 55 end object pmTorrents: TPopupMenu[11] Images = ImageList16 OnPopup = pmTorrentsPopup left = 149 top = 55 object MenuItem68: TMenuItem Action = acOpenFile Default = True Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF009933 00FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00993300FF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00993300FFAA5F1FFF993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00993300FFBA7D48FF993300FF993300FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00993300FFCDA27CFFD8B596FF993300FF9933 00FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFE1C6B0FFECDCCDFFEDDD D1FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFF4E9E2FFFDF9 F5FFFBF4ECFF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFE1C0ABFFF7E9 DAFFF4E0CCFFE1BA9CFF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFF6E6D6FFF3DEC8FFF0D5 BAFFE3B995FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFEDCAA8FFEAC1 99FFE7B98BFFDFA875FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFE3AE 79FFE0A56BFFDD9C5CFFDA944FFF993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF009933 00FF993300FF993300FF993300FF993300FF993300FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem42: TMenuItem Action = acOpenContainingFolder Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFF078DBEFFFF00FF00FF00FF00FF00FF00078DBEFF25A1 D1FF71C6E8FF84D7FAFF66CDF9FF65CDF9FF65CDF9FF65CDF9FF65CDF8FF65CD F9FF65CDF8FF66CEF9FF3AADD8FF1999C9FFFF00FF00FF00FF00078DBEFF4CBC E7FF39A8D1FFA0E2FBFF6FD4FAFF6FD4F9FF6ED4FAFF6FD4F9FF6FD4FAFF6FD4 FAFF6FD4FAFF6ED4F9FF3EB1D9FFC9F0F3FF078DBEFFFF00FF00078DBEFF72D6 FAFF078DBEFFAEE9FCFF79DCFBFF79DCFBFF79DCFBFF79DCFBFF79DCFBFF7ADC FBFF79DCFAFF79DCFAFF44B5D9FFC9F0F3FF078DBEFFFF00FF00078DBEFF79DD FBFF1899C7FF9ADFF3FF92E7FCFF84E4FBFF83E4FCFF83E4FCFF84E4FCFF83E4 FCFF83E4FBFF84E5FCFF48B9DAFFC9F0F3FF1496C4FFFF00FF00078DBEFF82E3 FCFF43B7DCFF65C2E0FFABF0FCFF8DEBFCFF8DEBFCFF8DEBFDFF8DEBFDFF8DEB FCFF8DEBFDFF8DEBFCFF4CBBDAFFC9F0F3FFC9F0F3FF078DBEFF078DBEFF8AEA FCFF77DCF3FF219CC7FFFEFFFFFFC8F7FDFFC9F7FDFFC9F7FDFFC9F7FEFFC8F7 FEFFC9F7FDFFC8F7FEFF9BD5E6FFEAFEFEFFD2F3F8FF078DBEFF078DBEFF93F0 FEFF93F0FDFF1697C5FF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF9BF5 FEFF9AF6FEFF9AF6FEFF9BF5FDFF9BF6FEFF9AF6FEFF9BF5FEFF9AF6FDFF9BF5 FEFF9AF6FEFF9AF6FEFF0989BAFFFF00FF00FF00FF00FF00FF00078DBEFFFEFE FEFFA0FBFFFFA0FBFEFFA0FBFEFFA1FAFEFFA1FBFEFFA0FAFEFFA1FBFEFFA1FB FFFFA0FBFFFFA1FBFFFF0989BAFFFF00FF00FF00FF00FF00FF00FF00FF00078D BEFFFEFEFEFFA5FEFFFFA5FEFFFFA5FEFFFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00078DBEFF078DBEFF078DBEFF078DBEFFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acOpenContainingFolderExecute end object pmSepOpen1: TMenuItem Caption = '-' end object MenuItem8: TMenuItem Action = acStartTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00663432FF6D372FFF814125FF814125FF6D37 2FFF673432FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0072392DFF804026FFAF580FFFC26105FFCA6602FFCB6602FFC462 07FFB1590FFF844224FF683431FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF007C3F28FFA85514FFD46B00FFD26B02FFCD6804FFCB6300FFCD6700FFCE67 00FFD06900FFD66C00FFA75415FF693531FFFF00FF00FF00FF00FF00FF008242 25FFAE5810FFD36A00FFCA6200FFCE6D18FFEAC197FFD5853BFFC85C00FFCB65 00FFCB6601FFCB6601FFD56B00FFA85513FF663332FFFF00FF00FF00FF00984C 19FFD26900FFCC6802FFCA6401FFCD6B11FFF5E4D2FFFBF2EAFFD98D4AFFC85B 00FFCB6500FFCB6601FFCB6601FFD66B00FF814125FFFF00FF00A95512FFBF63 0BFFD3710CFFD17314FFD17311FFD27619FFF5DFC9FFFFFFFFFFFCF6EFFFE2A8 71FFCA6001FFC96100FFCB6601FFD16900FFB35A0EFF693531FFB0590EFFD175 18FFD98024FFD9832CFFDA8329FFDA852FFFF6E2CFFFFFFFFFFFFFFFFFFFFFFF FFFFEAC098FFCB670FFFC96100FFCE6800FFC56406FF693531FFB35D11FFDC8B 36FFE19343FFE1903FFFE1913EFFE19343FFF8E6D3FFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFF2D9C0FFCF7219FFCB6400FFCD6602FF6B3630FFB96110FFE4A2 5EFFECB175FFE8A056FFE9A056FFE8A25AFFFAE9D8FFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFEDCCAAFFCE6E17FFCB6500FFCD6602FF733A2CFFB15A0EFFE6A7 69FFF5CEA5FFF0B171FFF1AD69FFF0AF6DFFFBEBDCFFFFFFFFFFFFFFFFFFFFFE FEFFE7B684FFC96005FFC96100FFCE6800FFC56307FF683532FFB45C0DFFDD95 4DFFFBDFC2FFF9D1A8FFF9BE81FFF6BB7FFFFCEEDEFFFFFFFFFFFCF3E8FFE6AB 6FFFCF6905FFCB6400FFCB6601FFD16900FFB1590FFF693531FFFF00FF00C36C 1AFFF8D1A9FFFEEFDFFFFED6ABFFF8C186FFFDF2E7FFFBEFE3FFE6A35DFFD87D 1EFFD27414FFCB6703FFCB6500FFD66B00FF7E4027FFFF00FF00FF00FF00B75B 08FFDC9148FFFDE4CAFFFEF2E5FFFAD8B7FFF8DBBCFFEBAB69FFDF8931FFD982 2AFFD17619FFCD6B08FFD56B00FFA35116FF71392EFFFF00FF00FF00FF00FF00 FF00B35A0DFFDA9149FFFAD9B7FFFEEFDFFFFBE3CAFFF3CCA5FFECBA86FFE7AE 72FFE19B51FFD87C1CFFA55313FF7D3F29FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00B65B09FFC26E22FFDE9F63FFEABF93FFF0CBA6FFECC297FFDEA5 6EFFC27938FF964E21FF7C3F28FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00AF5A12FFB3611BFFB2611FFFA95B1EFFAC59 16FFA35317FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acStartTorrentExecute end object MenuItem77: TMenuItem Action = acForceStartTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00004B98FF004D9CFF0052A6FF0052A6FF004D 9CFF004B98FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00004E9EFF0052A6FF005EBEFF0062C6FF0065CCFF0065CCFF0064 CAFF005FC0FF0053A8FF004B98FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000051A4FF005DBCFF0069D4FF0069D4FF0067D0FF0064CAFF0065CCFF0066 CEFF0067D0FF006AD6FF005DBCFF004C9AFFFF00FF00FF00FF00FF00FF000052 A6FF005EBEFF0068D2FF0064CAFF0072E6FF81BFFFFF1187FFFF0063C8FF0064 CAFF0065CCFF0065CCFF0069D4FF005CBAFF004B98FFFF00FF00FF00FF000057 B0FF0068D2FF0066CEFF0064CAFF006EDEFFC7E3FFFFE5F2FFFF2390FFFF0063 C8FF0064CAFF0065CCFF0065CCFF006AD6FF0052A6FFFF00FF001E7AD8FF0064 CAFF006EDEFF0071E4FF0070E2FF0074EAFFBFDFFFFFFFFFFFFFEBF5FFFF53A8 FFFF0064CAFF0063C8FF0065CCFF0067D0FF005FC0FF004C9AFF1E7CDCFF0073 E8FF007DFCFF0581FFFF0380FFFF0983FFFFC5E2FFFFFFFFFFFFFFFFFFFFFFFF FFFF83C0FFFF006CDAFF0063C8FF0066CEFF0064CAFF004C9AFF1E80E4FF1388 FFFF2591FFFF218FFFFF1F8EFFFF2591FFFFCBE5FFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFB3D9FFFF0073E8FF0064CAFF0066CEFF004C9AFF1E82E8FF43A0 FFFF61AFFFFF3F9EFFFF3F9EFFFF43A0FFFFD3E9FFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFF97CAFFFF0071E4FF0064CAFF0066CEFF004E9EFF1E7CDCFF4FA6 FFFF9BCCFFFF61AFFFFF5BACFFFF5DADFFFFD7EBFFFFFFFFFFFFFFFFFFFFFDFE FFFF6BB4FFFF0066CEFF0063C8FF0066CEFF0065CCFF004C9AFF1E7DDEFF2B94 FFFFBDDEFFFFA1CFFFFF7BBCFFFF75B9FFFFDBEDFFFFFFFFFFFFE5F2FFFF55A9 FFFF0069D4FF0064CAFF0065CCFF0067D0FF005FC0FF004C9AFFFF00FF00006D DCFFA1CFFFFFDDEEFFFFA9D3FFFF7FBEFFFFE5F2FFFFDFEFFFFF43A0FFFF007A F6FF0072E6FF0066CEFF0064CAFF006AD6FF0051A4FFFF00FF00FF00FF001E7C DCFF2591FFFFC7E3FFFFE3F1FFFFB1D8FFFFB5DAFFFF55A9FFFF1187FFFF0380 FFFF0074EAFF0069D4FF0069D4FF005BB8FF004E9EFFFF00FF00FF00FF00FF00 FF00207EDFFF2390FFFFB1D8FFFFDDEEFFFFC5E2FFFF99CBFFFF73B8FFFF59AB FFFF3398FFFF0079F4FF005BB8FF0052A6FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00207DDDFF0071E4FF419FFFFF7DBDFFFF97CAFFFF83C0FFFF4DA5 FFFF007CFAFF005AB6FF0051A4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF001F7DDEFF1F84ECFF1F86F0FF1F81E6FF1F7E E0FF1F7AD8FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem9: TMenuItem Action = acStopTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00663432FF6D372FFF824125FF824125FF6D37 2FFF673432FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0072392DFF814125FFB0580FFFC26105FFCA6602FFCB6602FFC462 07FFB2590FFF854323FF683431FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF007C3F28FFA95514FFD36B00FFD16A02FFCD6803FFCB6400FFCC6700FFCD67 00FFCF6900FFD56C00FFA85415FF693531FFFF00FF00FF00FF00FF00FF008242 25FFAF5810FFD26A00FFCA6300FFCD6A0FFFCD6A0FFFCB6400FFCA6201FFCB66 01FFCB6601FFCB6601FFD46B00FFA95513FF663332FFFF00FF00FF00FF00994D 19FFD26A02FFCD6A05FFCB6602FFCC6806FFCD6C0AFFCC6A07FFCD6B0AFFCA64 01FFCB6601FFCB6601FFCB6601FFD56B00FF824125FFFF00FF00A95512FFC065 0DFFD57513FFD3781CFFD27414FFF3DBC2FFFFFFFFFFD98F45FFD98F45FFFFFF FFFFF2D9C0FFCA6401FFCB6601FFD06900FFB45A0EFF693531FFB0590EFFD278 1CFFDC872FFFDD8A37FFDA842CFFF5DEC7FFFFFFFFFFD88D41FFD88D41FFFFFF FFFFF3DAC2FFCB6604FFCA6401FFCD6800FFC56406FF693531FFB45E12FFDE8E 3AFFE59B4EFFE69B4FFFE39446FFF6E2CCFFFFFFFFFFDC954CFFD98F43FFFFFF FFFFF3DAC1FFCC6A07FFCD6B0BFFCB6500FFCD6602FF6C3630FFB96211FFE5A4 61FFEEB478FFEDAB67FFEBA660FFF9E6D3FFFFFFFFFFE29F5CFFDD9750FFFFFF FFFFF3DAC1FFCC6A07FFCC690AFFCB6500FFCD6602FF743A2CFFB15A0EFFE7A8 6AFFF6CB9EFFF4B87BFFF3B474FFFBEBD9FFFFFFFFFFE8AD71FFE3A362FFFFFF FFFFF4DCC4FFCB6502FFCA6401FFCD6800FFC56307FF683532FFB45C0DFFDE96 4FFFFBDBB9FFF9CB9AFFF8C084FFFDEEDFFFFFFFFFFFEFBC86FFEBB278FFFFFF FFFFF5DEC7FFCE6C0BFFCB6702FFD06900FFB2590FFF693531FFFF00FF00C46D 1BFFF8D0A6FFFDE4CAFFFBCC9AFFF8C186FFF7C792FFF3BB81FFEAA760FFE395 44FFDA842DFFD17314FFCC6805FFD56B01FF7F4027FFFF00FF00FF00FF00B75B 08FFDD9249FFFDE1C4FFFDE7CFFFF9CFA3FFF8CD9FFFF2B779FFEBA45BFFE498 4BFFDA8631FFD17415FFD56D04FFA45216FF71392EFFFF00FF00FF00FF00FF00 FF00B35A0DFFDB924AFFFAD7B3FFFDE8D2FFFADCBBFFF4C99DFFEFBC86FFEAB1 74FFE39E55FFD97F21FFA65514FF7D3F29FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00B65B09FFC36F24FFDFA064FFEBBF92FFF1CAA4FFEDC296FFDFA6 6FFFC47B3AFF975022FF7C3F28FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00AF5A12FFB3611BFFB36220FFAA5C1FFFAC59 16FFA35317FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acStopTorrentExecute end object MenuItem10: TMenuItem Action = acRemoveTorrent Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF0001079FFF0313A9FF0418AEFF0419AEFF0313 A9FF0108A0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0001049DFF041CB1FF0730C0FF0734C4FF0735C5FF0735C5FF0734 C3FF0731C1FF041FB3FF01069EFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000109A1FF052BC3FF0735C7FF0733C2FF0732C2FF0732C2FF0732C2FF0732 C2FF0733C3FF0735C4FF062DBEFF020CA4FFFF00FF00FF00FF00FF00FF000104 9BFF052BCAFF0636D8FF0431CDFF0027C4FF032EC1FF0732C2FF0732C2FF0430 C1FF0027BFFF042FC1FF0735C4FF072EBEFF01069EFFFF00FF00FF00FF00031A BAFF0537E7FF0331DDFF123DD8FF6480E0FF1840CBFF002CC1FF022DC0FF0F38 C4FF6580D9FF1B43C7FF052FC1FF0735C5FF051FB3FFFF00FF0001049EFF0430 E4FF0436F1FF002AE4FF5070E9FFFFFFFFFFB7C4F1FF0D36CAFF042DC3FFA2B2 E8FFFFFFFFFF6984DAFF0026BEFF0733C3FF0731C1FF0108A0FF020FAFFF0336 FAFF0335F8FF0232EEFF0A35E8FF8CA2F2FFFFFFFFFFB4C2F1FFA9B8EDFFFFFF FFFFA7B7E9FF133AC4FF052FC1FF0732C2FF0734C4FF0313AAFF0619BCFF1747 FEFF093AFCFF0435F8FF0131F0FF002BE8FF91A5F4FFFFFFFFFFFFFFFFFFABBA EFFF062FC5FF022DC0FF0732C2FF0732C2FF0736C5FF0419AEFF0B1DBEFF4168 FEFF1C49FCFF0335FBFF0031F9FF0531F2FFA4B5F7FFFFFFFFFFFFFFFFFFB9C6 F2FF0D36D0FF002CC6FF0732C2FF0732C2FF0736C5FF0418ADFF0613B4FF5B7C FCFF486CFDFF0133FBFF113CFBFFA1B4FEFFFFFFFFFFA4B6F8FF92A7F5FFFFFF FFFFB6C4F2FF1A41D3FF042FC8FF0732C4FF0734C3FF0212A9FF0003A0FF4A6A F3FF8FA6FFFF1F46FBFF4C6FFCFFFFFFFFFFA7B8FEFF0733F6FF002AEDFF8CA2 F6FFFFFFFFFF627FE7FF0028D0FF0734CCFF0730C3FF00069FFFFF00FF001A2F CBFF99AFFFFF8BA2FEFF214DFBFF4D71FCFF0E3DFBFF0030FBFF0031F7FF0636 F1FF4C6EF1FF103CE3FF0432DBFF0636D7FF041CB5FFFF00FF00FF00FF000004 A0FF415EECFFB8C7FFFF9CAFFDFF3A5CFCFF0A3AFBFF0335FBFF0335FBFF0133 F9FF052FF2FF0635EBFF0537E9FF052CCDFF00049CFFFF00FF00FF00FF00FF00 FF000309A5FF4260ECFFA9BBFFFFBDCAFFFF8EA5FEFF6483FDFF5073FCFF4A6E FDFF3961FDFF1444F9FF042CD7FF0109A2FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF000004A0FF1E32CDFF5876F6FF859EFEFF8BA3FFFF7994FEFF5376 FCFF234AF0FF051EC5FF01049CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000004A0FF0917B6FF1022C3FF0D1FC2FF0311 B4FF01059FFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acRemoveTorrentExecute end object MenuItem39: TMenuItem Action = acRemoveTorrentAndData OnClick = acRemoveTorrentAndDataExecute end object MenuItem55: TMenuItem Caption = '-' end object pmiPriority: TMenuItem Caption = 'Priority' object MenuItem57: TMenuItem Action = acSetHighPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 200000000000000400006400000064000000000000000000000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF0000404CD8FF162FD0FF162FD0FF404C D8FF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF0000173CE5FF6699FFFF99CCFFFF99CCFFFF6699 FFFF193EE5FF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF00000F38EBFF5084FFFF6699FFFF74A7FFFF74A7FFFF6699 FFFF5488FFFF0F38EBFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF00004B67F2FF2254FDFF3C6FFFFF4E81FFFF5A8DFFFF5A8DFFFF5084 FFFF3F73FFFF275AFFFF4B67F2FF00FF000000FF000000FF000000FF000000FF 000000FF00001A43F9FF184BFFFF275AFFFF3366FFFF3D71FFFF3D71FFFF3D71 FFFF275AFFFF184BFFFF1A43F9FF00FF000000FF000000FF000000FF000000FF 000000FF00001A43F9FF0033FFFF184BFFFF3366FFFF3F73FFFF3F73FFFF3366 FFFF184BFFFF0033FFFF1A43F9FF00FF000000FF000000FF000000FF000000FF 000000FF00005273FEFF103DF9FF486CFEFF5B7DFFFF6B8AFEFF6B8AFEFF5B7D FFFF4368FEFF0B39FAFF5273FEFF00FF000000FF000000FF000000FF000000FF 000000FF000000FF00000033FFFF4F6DF8FF7991FCFF899EFDFF899EFDFF758E FBFF4F6DF8FF0033FFFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF00000033FFFF4969F7FF91A2F7FF91A2F7FF4969 F7FF0033FFFF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF00005273FEFF193FFDFF193FFDFF5273 FEFF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF0000 } OnClick = acSetHighPriorityExecute end object MenuItem56: TMenuItem Action = acSetNormalPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 20000000000000040000640000006400000000000000000000000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF001D915CFF0D8145FF0D8145FF1D91 5CFF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000D8E46FF00E03FFF01FF1FFF01FF1FFF00E0 3FFF0F8E47FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF00089344FF00D142FF00E03FFF00E83AFF00E83AFF00E0 3FFF00D442FF089344FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000CB85AFF00B146FF00C546FF00D043FF00D842FF00D842FF00D1 42FF00C744FF00B845FF0CB85AFF0000FF000000FF000000FF000000FF000000 FF000000FF0004A74AFF00AF44FF00B845FF00BF46FF00C644FF00C644FF00C6 44FF00B845FF00AF44FF04A74AFF0000FF000000FF000000FF000000FF000000 FF000000FF0004A74AFF009F42FF00AF44FF00BF46FF00C744FF00C744FF00BF 46FF00AF44FF009F42FF04A74AFF0000FF000000FF000000FF000000FF000000 FF000000FF0000D159FF03A147FF00CA55FF00D858FF01DF5CFF01DF5CFF00D8 58FF00C854FF02A045FF00D159FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF00009F42FF07C45BFF05E466FF04F06BFF04F06BFF06DF 64FF07C45BFF009F42FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00009F42FF07BF57FF10E570FF10E570FF07BF 57FF009F42FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF0000D159FF00AC4EFF00AC4EFF00D1 59FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF00 } OnClick = acSetNormalPriorityExecute end object MenuItem58: TMenuItem Action = acSetLowPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 200000000000000400006400000064000000000000000000000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF00003F97D7FF168BCFFF168BCFFF3F97 D7FF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000017A2E4FF65E5FFFF98FFFFFF98FFFFFF65E5 FFFF19A3E4FF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF00000FA5EAFF4FDBFFFF65E5FFFF73ECFFFF73ECFFFF65E5 FFFF53DDFFFF0FA5EAFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF00004ABAF2FF21C1FDFF3BD0FFFF4DD9FFFF59DFFFFF59DFFFFF4FDB FFFF3ED2FFFF26C5FFFF4ABAF2FF00FF000000FF000000FF000000FF000000FF 000000FF000019B2F9FF17BEFFFF26C5FFFF32CBFFFF3CD1FFFF3CD1FFFF3CD1 FFFF26C5FFFF17BEFFFF19B2F9FF00FF000000FF000000FF000000FF000000FF 000000FF000019B2F9FF00B2FFFF17BEFFFF32CBFFFF3ED2FFFF3ED2FFFF32CB FFFF17BEFFFF00B2FFFF19B2F9FF00FF000000FF000000FF000000FF000000FF 000000FF000051C8FEFF0FB1F9FF47C6FEFF5ACEFFFF6AD3FEFF6AD3FEFF5ACE FFFF42C5FEFF0AB0FAFF51C8FEFF00FF000000FF000000FF000000FF000000FF 000000FF000000FF000000B2FFFF4EC1F8FF78D2FCFF88D7FDFF88D7FDFF74D0 FBFF4EC1F8FF00B2FFFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000B2FFFF48BFF7FF90D4F7FF90D4F7FF48BF F7FF00B2FFFF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000051C8FEFF18B0FDFF18B0FDFF51C8 FEFF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF0000 } OnClick = acSetLowPriorityExecute end end object pmiQueue: TMenuItem Caption = 'Queue' object MenuItem82: TMenuItem Action = acQMoveTop end object MenuItem83: TMenuItem Action = acQMoveUp Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00044906FF055B09FF066C0CFF066C0CFF055E 0AFF044C06FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00056009FF056009FF089113FF09B018FF09B31AFF09B319FF09B1 19FF079614FF05680CFF05680CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000A6A15FF0A7F15FF0BB61CFF09B91AFF08B418FF07B216FF09B319FF09B4 19FF09B81AFF09B91AFF078310FF044D06FFFF00FF00FF00FF00FF00FF000B6A 15FF0F8522FF16BD34FF11B727FF0BB21CFF07B116FFC2ECC6FFFFFFFFFF50C8 5CFF09B219FF09B419FF09BA1AFF078410FF06670CFFFF00FF00FF00FF000B6A 15FF20BE49FF1BBD40FF14B730FF0AB21FFF09B219FFC0EBC4FFFFFFFFFF51C9 5DFF09B219FF09B219FF09B319FF09BA1AFF06670CFFFF00FF00087210FF1B9A 3AFF2AC65BFF1DBB45FF0EB425FF0BB31BFF09B21AFFBFEBC3FFFFFFFFFF50C8 5CFF09B219FF09B219FF09B219FF09B81AFF089413FF045D09FF087210FF2AB6 5BFF2CC565FF22BD4DFF2DBD3EFF12B421FF0CB31CFFC0EBC3FFFFFFFFFF50C8 5CFF09B219FF2ABC38FF17B626FF09B51AFF08AB17FF045D09FF0F821CFF37C2 6CFF33C76CFF36C56AFFE4F7E9FFA5E4B5FF2AC053FFC5EED0FFFFFFFFFF41C3 4DFF4FC85AFFF5FCF6FFADE5B2FF08B419FF09B319FF05650BFF138D23FF58CC 83FF42C977FF37C56EFFF1FBF5FFFFFFFFFFA3E5BDFFCBF0DAFFFFFFFFFF85D9 8EFFE4F7E6FFFFFFFFFF97DE9EFF0AB41AFF09B319FF066D0DFF0F911DFF6FD2 93FF5FD38DFF31C369FF80DAA3FFF9FDFAFFFDFFFEFFF9FDFBFFFDFEFEFFFBFE FBFFFFFFFFFF95DEA4FF1DB93DFF11B82BFF08B119FF05610AFF0F911DFF67CC 83FF9BE5BAFF38C670FF30C369FF7AD89EFFF8FDFAFFFFFFFFFFFFFFFFFFFFFF FFFF9FE2B1FF20BD48FF1AB93EFF10BA29FF08A317FF05610AFFFF00FF0037B6 50FFBCEDD2FF82DBA4FF28C063FF2FC267FF78D89DFFF5FCF8FFFFFFFFFFB1E8 C3FF22BC4BFF1DBA41FF18B736FF14C030FF0A8517FFFF00FF00FF00FF0037B6 50FF71D28CFFD2F4E1FF80DAA3FF36C46DFF2DC267FF8ADDAAFFC9EFD8FF3DC7 6FFF24BE56FF23BC4DFF1FC146FF16AE34FF0A8517FFFF00FF00FF00FF00FF00 FF0025AE39FF84D89FFFDBF7EAFFAFE8C6FF6BD493FF52CC81FF44C978FF49CA 7BFF48CB78FF39CB6AFF21B649FF0F7C1FFFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0066CD81FF66CD81FFADE8C5FFCCF2DEFFBAEDD1FFA6E7C2FF91E2 B3FF64D492FF2FB157FF2FB157FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF0032B74EFF52C46FFF61CB80FF5DC87DFF43B9 64FF24A342FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem85: TMenuItem Action = acQMoveDown Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00044906FF055B09FF066C0CFF066C0CFF055E 0AFF044C06FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00056009FF056009FF089113FF09B018FF09B31AFF09B319FF09B1 19FF079614FF05680CFF05680CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000A6A15FF0A7F15FF0BB61CFF09B91AFF08B418FF07B216FF09B319FF09B4 19FF09B81AFF09B91AFF078310FF044D06FFFF00FF00FF00FF00FF00FF000B6A 15FF0F8522FF16BD34FF11B727FF0BB21CFF07B116FF6FD177FFBCEAC1FF20B9 2FFF09B219FF09B419FF09BA1AFF078410FF06670CFFFF00FF00FF00FF000B6A 15FF20BE49FF1BBD40FF14B730FF0AB21FFF58CB63FFF2FBF3FFFFFFFFFFA6E3 ACFF0BB31BFF09B219FF09B319FF09BA1AFF06670CFFFF00FF00087210FF1B9A 3AFF2AC65BFF1DBB45FF0EB425FF5BCC66FFF6FCF7FFFFFFFFFFFFFFFFFFFFFF FFFF96DE9DFF0BB31BFF09B219FF09B81AFF089413FF045D09FF087210FF2AB6 5BFF2CC565FF22BD4DFF67CF73FFF7FDF8FFFDFEFDFFF8FDF9FFFDFEFDFFFAFD FAFFFFFFFFFF8EDC95FF0EB41EFF09B51AFF08AB17FF045D09FF0F821CFF37C2 6CFF33C76CFF36C56AFFF0FAF3FFFFFFFFFF9CE2AFFFC7EED2FFFFFFFFFF83D8 8BFFE4F7E6FFFFFFFFFF97DE9EFF08B419FF09B319FF05650BFF138D23FF58CC 83FF42C977FF37C56EFFE6F8EDFFADE8C4FF38C670FFC9F0D9FFFFFFFFFF44C5 51FF4FC85CFFF5FCF6FFADE5B2FF0AB41AFF09B319FF066D0DFF0F911DFF6FD2 93FF5FD38DFF31C369FF50CC80FF3DC773FF38C56FFFCAF0D8FFFFFFFFFF5CCE 76FF1AB93EFF37C153FF25BC44FF11B82BFF08B119FF05610AFF0F911DFF67CC 83FF9BE5BAFF38C670FF30C369FF38C56FFF38C56FFFCBF0D9FFFFFFFFFF61D0 81FF1EBC49FF1EBC47FF1AB93EFF10BA29FF08A317FF05610AFFFF00FF0037B6 50FFBCEDD2FF82DBA4FF28C063FF2FC267FF38C56FFFCCF0DAFFFFFFFFFF67D2 89FF20BB4AFF1DBA41FF18B736FF14C030FF0A8517FFFF00FF00FF00FF0037B6 50FF71D28CFFD2F4E1FF80DAA3FF36C46DFF2DC267FFCDF1DBFFFFFFFFFF67D3 8EFF24BE56FF23BC4DFF1FC146FF16AE34FF0A8517FFFF00FF00FF00FF00FF00 FF0025AE39FF84D89FFFDBF7EAFFAFE8C6FF6BD493FF52CC81FF44C978FF49CA 7BFF48CB78FF39CB6AFF21B649FF0F7C1FFFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF0066CD81FF66CD81FFADE8C5FFCCF2DEFFBAEDD1FFA6E7C2FF91E2 B3FF64D492FF2FB157FF2FB157FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF0032B74EFF52C46FFF61CB80FF5DC87DFF43B9 64FF24A342FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } end object MenuItem84: TMenuItem Action = acQMoveBottom end end object MenuItem50: TMenuItem Action = acReannounceTorrent OnClick = acReannounceTorrentExecute end object MenuItem11: TMenuItem Action = acVerifyTorrent OnClick = acVerifyTorrentExecute end object MenuItem53: TMenuItem Action = acMoveTorrent OnClick = acMoveTorrentExecute end object MenuItem99: TMenuItem Action = acRename end object MenuItem19: TMenuItem Caption = '-' end object MenuItem20: TMenuItem Action = acTorrentProps OnClick = acTorrentPropsExecute end object MenuItem36: TMenuItem Caption = '-' end object MenuItem37: TMenuItem Action = acSetupColumns OnClick = acSetupColumnsExecute end end object ApplicationProperties: TApplicationProperties[12] ShowButtonGlyphs = sbgSystem OnIdle = ApplicationPropertiesIdle OnEndSession = ApplicationPropertiesEndSession OnMinimize = ApplicationPropertiesMinimize OnRestore = ApplicationPropertiesRestore left = 348 top = 56 end object pmFiles: TPopupMenu[13] Images = ImageList16 OnPopup = pmFilesPopup left = 304 top = 92 object MenuItem40: TMenuItem Action = acOpenFile Default = True Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF009933 00FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00993300FF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00993300FFAA5F1FFF993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00993300FFBA7D48FF993300FF993300FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00993300FFCDA27CFFD8B596FF993300FF9933 00FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFE1C6B0FFECDCCDFFEDDD D1FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFF4E9E2FFFDF9 F5FFFBF4ECFF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFE1C0ABFFF7E9 DAFFF4E0CCFFE1BA9CFF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFF6E6D6FFF3DEC8FFF0D5 BAFFE3B995FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFEDCAA8FFEAC1 99FFE7B98BFFDFA875FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFE3AE 79FFE0A56BFFDD9C5CFFDA944FFF993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF009933 00FF993300FF993300FF993300FF993300FF993300FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acOpenFileExecute end object MenuItem44: TMenuItem Action = acOpenContainingFolder Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFF078DBEFFFF00FF00FF00FF00FF00FF00078DBEFF25A1 D1FF71C6E8FF84D7FAFF66CDF9FF65CDF9FF65CDF9FF65CDF9FF65CDF8FF65CD F9FF65CDF8FF66CEF9FF3AADD8FF1999C9FFFF00FF00FF00FF00078DBEFF4CBC E7FF39A8D1FFA0E2FBFF6FD4FAFF6FD4F9FF6ED4FAFF6FD4F9FF6FD4FAFF6FD4 FAFF6FD4FAFF6ED4F9FF3EB1D9FFC9F0F3FF078DBEFFFF00FF00078DBEFF72D6 FAFF078DBEFFAEE9FCFF79DCFBFF79DCFBFF79DCFBFF79DCFBFF79DCFBFF7ADC FBFF79DCFAFF79DCFAFF44B5D9FFC9F0F3FF078DBEFFFF00FF00078DBEFF79DD FBFF1899C7FF9ADFF3FF92E7FCFF84E4FBFF83E4FCFF83E4FCFF84E4FCFF83E4 FCFF83E4FBFF84E5FCFF48B9DAFFC9F0F3FF1496C4FFFF00FF00078DBEFF82E3 FCFF43B7DCFF65C2E0FFABF0FCFF8DEBFCFF8DEBFCFF8DEBFDFF8DEBFDFF8DEB FCFF8DEBFDFF8DEBFCFF4CBBDAFFC9F0F3FFC9F0F3FF078DBEFF078DBEFF8AEA FCFF77DCF3FF219CC7FFFEFFFFFFC8F7FDFFC9F7FDFFC9F7FDFFC9F7FEFFC8F7 FEFFC9F7FDFFC8F7FEFF9BD5E6FFEAFEFEFFD2F3F8FF078DBEFF078DBEFF93F0 FEFF93F0FDFF1697C5FF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF9BF5 FEFF9AF6FEFF9AF6FEFF9BF5FDFF9BF6FEFF9AF6FEFF9BF5FEFF9AF6FDFF9BF5 FEFF9AF6FEFF9AF6FEFF0989BAFFFF00FF00FF00FF00FF00FF00078DBEFFFEFE FEFFA0FBFFFFA0FBFEFFA0FBFEFFA1FAFEFFA1FBFEFFA0FAFEFFA1FBFEFFA1FB FFFFA0FBFFFFA1FBFFFF0989BAFFFF00FF00FF00FF00FF00FF00FF00FF00078D BEFFFEFEFEFFA5FEFFFFA5FEFFFFA5FEFFFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00078DBEFF078DBEFF078DBEFF078DBEFFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 } OnClick = acOpenContainingFolderExecute end object MenuItem100: TMenuItem Action = acRename end object MenuItem97: TMenuItem Action = acCopyPath end object pmSepOpen2: TMenuItem Caption = '-' end object MenuItem12: TMenuItem Action = acSetHighPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 200000000000000400006400000064000000000000000000000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF0000404CD8FF162FD0FF162FD0FF404C D8FF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF0000173CE5FF6699FFFF99CCFFFF99CCFFFF6699 FFFF193EE5FF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF00000F38EBFF5084FFFF6699FFFF74A7FFFF74A7FFFF6699 FFFF5488FFFF0F38EBFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF00004B67F2FF2254FDFF3C6FFFFF4E81FFFF5A8DFFFF5A8DFFFF5084 FFFF3F73FFFF275AFFFF4B67F2FF00FF000000FF000000FF000000FF000000FF 000000FF00001A43F9FF184BFFFF275AFFFF3366FFFF3D71FFFF3D71FFFF3D71 FFFF275AFFFF184BFFFF1A43F9FF00FF000000FF000000FF000000FF000000FF 000000FF00001A43F9FF0033FFFF184BFFFF3366FFFF3F73FFFF3F73FFFF3366 FFFF184BFFFF0033FFFF1A43F9FF00FF000000FF000000FF000000FF000000FF 000000FF00005273FEFF103DF9FF486CFEFF5B7DFFFF6B8AFEFF6B8AFEFF5B7D FFFF4368FEFF0B39FAFF5273FEFF00FF000000FF000000FF000000FF000000FF 000000FF000000FF00000033FFFF4F6DF8FF7991FCFF899EFDFF899EFDFF758E FBFF4F6DF8FF0033FFFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF00000033FFFF4969F7FF91A2F7FF91A2F7FF4969 F7FF0033FFFF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF00005273FEFF193FFDFF193FFDFF5273 FEFF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF0000 } OnClick = acSetHighPriorityExecute end object MenuItem13: TMenuItem Action = acSetNormalPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 20000000000000040000640000006400000000000000000000000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF001D915CFF0D8145FF0D8145FF1D91 5CFF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000D8E46FF00E03FFF01FF1FFF01FF1FFF00E0 3FFF0F8E47FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF00089344FF00D142FF00E03FFF00E83AFF00E83AFF00E0 3FFF00D442FF089344FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000CB85AFF00B146FF00C546FF00D043FF00D842FF00D842FF00D1 42FF00C744FF00B845FF0CB85AFF0000FF000000FF000000FF000000FF000000 FF000000FF0004A74AFF00AF44FF00B845FF00BF46FF00C644FF00C644FF00C6 44FF00B845FF00AF44FF04A74AFF0000FF000000FF000000FF000000FF000000 FF000000FF0004A74AFF009F42FF00AF44FF00BF46FF00C744FF00C744FF00BF 46FF00AF44FF009F42FF04A74AFF0000FF000000FF000000FF000000FF000000 FF000000FF0000D159FF03A147FF00CA55FF00D858FF01DF5CFF01DF5CFF00D8 58FF00C854FF02A045FF00D159FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF00009F42FF07C45BFF05E466FF04F06BFF04F06BFF06DF 64FF07C45BFF009F42FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00009F42FF07BF57FF10E570FF10E570FF07BF 57FF009F42FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF0000D159FF00AC4EFF00AC4EFF00D1 59FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF00 } OnClick = acSetNormalPriorityExecute end object MenuItem14: TMenuItem Action = acSetLowPriority Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 200000000000000400006400000064000000000000000000000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF00003F97D7FF168BCFFF168BCFFF3F97 D7FF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000017A2E4FF65E5FFFF98FFFFFF98FFFFFF65E5 FFFF19A3E4FF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF00000FA5EAFF4FDBFFFF65E5FFFF73ECFFFF73ECFFFF65E5 FFFF53DDFFFF0FA5EAFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF00004ABAF2FF21C1FDFF3BD0FFFF4DD9FFFF59DFFFFF59DFFFFF4FDB FFFF3ED2FFFF26C5FFFF4ABAF2FF00FF000000FF000000FF000000FF000000FF 000000FF000019B2F9FF17BEFFFF26C5FFFF32CBFFFF3CD1FFFF3CD1FFFF3CD1 FFFF26C5FFFF17BEFFFF19B2F9FF00FF000000FF000000FF000000FF000000FF 000000FF000019B2F9FF00B2FFFF17BEFFFF32CBFFFF3ED2FFFF3ED2FFFF32CB FFFF17BEFFFF00B2FFFF19B2F9FF00FF000000FF000000FF000000FF000000FF 000000FF000051C8FEFF0FB1F9FF47C6FEFF5ACEFFFF6AD3FEFF6AD3FEFF5ACE FFFF42C5FEFF0AB0FAFF51C8FEFF00FF000000FF000000FF000000FF000000FF 000000FF000000FF000000B2FFFF4EC1F8FF78D2FCFF88D7FDFF88D7FDFF74D0 FBFF4EC1F8FF00B2FFFF00FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000B2FFFF48BFF7FF90D4F7FF90D4F7FF48BF F7FF00B2FFFF00FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000051C8FEFF18B0FDFF18B0FDFF51C8 FEFF00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF0000 } OnClick = acSetLowPriorityExecute end object MenuItem15: TMenuItem Caption = '-' end object MenuItem16: TMenuItem Action = acSetNotDownload Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 20000000000000040000640000006400000000000000000000000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF00ADADADFFA3A3A3FFA3A3A3FFADAD ADFF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00A7A7A7FFBCBCBCFFC5C5C5FFC5C5C5FFBCBC BCFFA7A7A7FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF00A7A7A7FFB7B7B7FFBCBCBCFFBEBEBEFFBEBEBEFFBCBC BCFFB8B8B8FFA7A7A7FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF00B4B4B4FFAEAEAEFFB4B4B4FFB7B7B7FFB9B9B9FFB9B9B9FFB7B7 B7FFB4B4B4FFAFAFAFFFB4B4B4FF0000FF000000FF000000FF000000FF000000 FF000000FF00ACACACFFADADADFFAFAFAFFFB2B2B2FFB4B4B4FFB4B4B4FFB4B4 B4FFAFAFAFFFADADADFFACACACFF0000FF000000FF000000FF000000FF000000 FF000000FF00ACACACFFA8A8A8FFADADADFFB2B2B2FFB4B4B4FFB4B4B4FFB2B2 B2FFADADADFFA8A8A8FFACACACFF0000FF000000FF000000FF000000FF000000 FF000000FF00B8B8B8FFAAAAAAFFB6B6B6FFB9B9B9FFBCBCBCFFBCBCBCFFB9B9 B9FFB5B5B5FFA9A9A9FFB8B8B8FF0000FF000000FF000000FF000000FF000000 FF000000FF000000FF00A8A8A8FFB6B6B6FFBFBFBFFFC2C2C2FFC2C2C2FFBEBE BEFFB6B6B6FFA8A8A8FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00A8A8A8FFB4B4B4FFC2C2C2FFC2C2C2FFB4B4 B4FFA8A8A8FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF00B8B8B8FFACACACFFACACACFFB8B8 B8FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF00 } OnClick = acSetNotDownloadExecute end object MenuItem46: TMenuItem Caption = '-' end object MenuItem47: TMenuItem Action = acSetupColumns OnClick = acSetupColumnsExecute end end object TickTimer: TTimer[14] Enabled = False Interval = 200 OnTimer = TickTimerTimer left = 408 top = 55 end object ImageList16: TImageList[15] left = 412 top = 92 Bitmap = { 4C692B0000001000000010000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000007308FF087B08FF088C10FF088C10FF087B08FF0000 0000000000000000000000000000000000000000000031B5DEFF29ADD6FF18A5 C6FF18A5C6FF00000000000000000000000000000000088C10FF08A518FF0884 10FF0000000000000000000000000000000021ADD6FF9CDEEFFFC6FFFFFFC6FF FFFF9CDEEFFF18ADD6FF18A5C6FF18A5C6FF18A5C6FF18A5C6FF088C10FF08A5 18FF0000000000000000000000000000000029ADD6FFC6FFFFFF94FFFFFF9CFF FFFFD6FFFFFFD6FFFFFF8CEFFFFF94EFFFFF73DEF7FF73DEF7FF088410FF18AD 29FF088410FF00000000000000000000000029ADD6FF9CFFFFFF94F7FFFF94F7 FFFF94F7FFFF94F7FFFF73DEF7FF73DEF7FF29ADD6FF18A5C6FF108C21FF31C6 4AFF109C21FF18A5C6FF000000000000000029ADD6FF9CFFFFFF94F7FFFF73DE F7FF73DEF7FF73DEF7FF6BDEF7FF29ADD6FFADDEEFFF007308FF52D67BFF42D6 6BFF31C64AFF007308FF000000000000000029ADD6FF9CFFFFFF73DEF7FF29AD D6FF18A5C6FF18A5C6FF18A5C6FFADDEEFFF8CF7FFFF84EFFFFF007308FF5AE7 8CFF007308FF18A5C6FF000000000000000029ADD6FF94F7FFFF29ADD6FFADDE EFFFA5EFF7FFA5EFF7FFA5F7FFFF8CEFFFFF8CEFFFFF8CEFFFFF73DEF7FF0073 08FF18A5C6FF00000000000000000000000029ADD6FF73DEF7FF29ADD6FF9CFF FFFF8CF7FFFF8CF7FFFF8CF7FFFF8CEFFFFF8CEFFFFF8CEFFFFF73DEF7FF73DE F7FF18A5C6FF00000000000000000000000029ADD6FF29ADD6FFADDEEFFF94F7 FFFF94F7FFFF8CEFFFFF8CEFFFFF8CEFFFFF8CEFFFFF73DEF7FF73DEF7FF4AC6 EFFF0000000000000000000000000000000029ADD6FF52BDE7FFADFFFFFF8CF7 FFFF8CEFFFFF8CEFFFFF8CEFFFFF73DEF7FF73DEF7FF73DEF7FF4AC6EFFF21AD D6FF0000000000000000000000000000000029ADD6FF52BDE7FF9CFFFFFF94FF FFFF73DEF7FF73DEF7FF73DEF7FF73DEF7FF4AC6E7FF21ADD6FF18A5C6FF0000 00000000000000000000000000000000000029ADD6FF9CDEEFFF84EFFFFF4AC6 E7FF21ADD6FF18A5C6FF18A5C6FF18A5C6FF0000000000000000000000000000 00000000000000000000000000000000000029ADD6FF31B5DEFF21ADD6FF0000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00AF5A12FFB3611BFFB2611FFFA95B1EFFAC5916FFA35317FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00B65B09FFC26E 22FFDE9F63FFEABF93FFF0CBA6FFECC297FFDEA56EFFC27938FF964E21FF7C3F 28FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00B35A0DFFDA9149FFFAD9 B7FFFEEFDFFFFBE3CAFFF3CCA5FFECBA86FFE7AE72FFE19B51FFD87C1CFFA553 13FF7D3F29FFFF00FF00FF00FF00FF00FF00B75B08FFDC9148FFFDE4CAFFFEF2 E5FFFAD8B7FFF8DBBCFFEBAB69FFDF8931FFD9822AFFD17619FFCD6B08FFD56B 00FFA35116FF71392EFFFF00FF00FF00FF00C36C1AFFF8D1A9FFFEEFDFFFFED6 ABFFF8C186FFFDF2E7FFFBEFE3FFE6A35DFFD87D1EFFD27414FFCB6703FFCB65 00FFD66B00FF7E4027FFFF00FF00B45C0DFFDD954DFFFBDFC2FFF9D1A8FFF9BE 81FFF6BB7FFFFCEEDEFFFFFFFFFFFCF3E8FFE6AB6FFFCF6905FFCB6400FFCB66 01FFD16900FFB1590FFF693531FFB15A0EFFE6A769FFF5CEA5FFF0B171FFF1AD 69FFF0AF6DFFFBEBDCFFFFFFFFFFFFFFFFFFFFFEFEFFE7B684FFC96005FFC961 00FFCE6800FFC56307FF683532FFB96110FFE4A25EFFECB175FFE8A056FFE9A0 56FFE8A25AFFFAE9D8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDCCAAFFCE6E 17FFCB6500FFCD6602FF733A2CFFB35D11FFDC8B36FFE19343FFE1903FFFE191 3EFFE19343FFF8E6D3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D9C0FFCF72 19FFCB6400FFCD6602FF6B3630FFB0590EFFD17518FFD98024FFD9832CFFDA83 29FFDA852FFFF6E2CFFFFFFFFFFFFFFFFFFFFFFFFFFFEAC098FFCB670FFFC961 00FFCE6800FFC56406FF693531FFA95512FFBF630BFFD3710CFFD17314FFD173 11FFD27619FFF5DFC9FFFFFFFFFFFCF6EFFFE2A871FFCA6001FFC96100FFCB66 01FFD16900FFB35A0EFF693531FFFF00FF00984C19FFD26900FFCC6802FFCA64 01FFCD6B11FFF5E4D2FFFBF2EAFFD98D4AFFC85B00FFCB6500FFCB6601FFCB66 01FFD66B00FF814125FFFF00FF00FF00FF00824225FFAE5810FFD36A00FFCA62 00FFCE6D18FFEAC197FFD5853BFFC85C00FFCB6500FFCB6601FFCB6601FFD56B 00FFA85513FF663332FFFF00FF00FF00FF00FF00FF007C3F28FFA85514FFD46B 00FFD26B02FFCD6804FFCB6300FFCD6700FFCE6700FFD06900FFD66C00FFA754 15FF693531FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF0072392DFF8040 26FFAF580FFFC26105FFCA6602FFCB6602FFC46207FFB1590FFF844224FF6834 31FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00663432FF6D372FFF814125FF814125FF6D372FFF673432FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00AF5A12FFB3611BFFB36220FFAA5C1FFFAC5916FFA35317FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00B65B09FFC36F 24FFDFA064FFEBBF92FFF1CAA4FFEDC296FFDFA66FFFC47B3AFF975022FF7C3F 28FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00B35A0DFFDB924AFFFAD7 B3FFFDE8D2FFFADCBBFFF4C99DFFEFBC86FFEAB174FFE39E55FFD97F21FFA655 14FF7D3F29FFFF00FF00FF00FF00FF00FF00B75B08FFDD9249FFFDE1C4FFFDE7 CFFFF9CFA3FFF8CD9FFFF2B779FFEBA45BFFE4984BFFDA8631FFD17415FFD56D 04FFA45216FF71392EFFFF00FF00FF00FF00C46D1BFFF8D0A6FFFDE4CAFFFBCC 9AFFF8C186FFF7C792FFF3BB81FFEAA760FFE39544FFDA842DFFD17314FFCC68 05FFD56B01FF7F4027FFFF00FF00B45C0DFFDE964FFFFBDBB9FFF9CB9AFFF8C0 84FFFDEEDFFFFFFFFFFFEFBC86FFEBB278FFFFFFFFFFF5DEC7FFCE6C0BFFCB67 02FFD06900FFB2590FFF693531FFB15A0EFFE7A86AFFF6CB9EFFF4B87BFFF3B4 74FFFBEBD9FFFFFFFFFFE8AD71FFE3A362FFFFFFFFFFF4DCC4FFCB6502FFCA64 01FFCD6800FFC56307FF683532FFB96211FFE5A461FFEEB478FFEDAB67FFEBA6 60FFF9E6D3FFFFFFFFFFE29F5CFFDD9750FFFFFFFFFFF3DAC1FFCC6A07FFCC69 0AFFCB6500FFCD6602FF743A2CFFB45E12FFDE8E3AFFE59B4EFFE69B4FFFE394 46FFF6E2CCFFFFFFFFFFDC954CFFD98F43FFFFFFFFFFF3DAC1FFCC6A07FFCD6B 0BFFCB6500FFCD6602FF6C3630FFB0590EFFD2781CFFDC872FFFDD8A37FFDA84 2CFFF5DEC7FFFFFFFFFFD88D41FFD88D41FFFFFFFFFFF3DAC2FFCB6604FFCA64 01FFCD6800FFC56406FF693531FFA95512FFC0650DFFD57513FFD3781CFFD274 14FFF3DBC2FFFFFFFFFFD98F45FFD98F45FFFFFFFFFFF2D9C0FFCA6401FFCB66 01FFD06900FFB45A0EFF693531FFFF00FF00994D19FFD26A02FFCD6A05FFCB66 02FFCC6806FFCD6C0AFFCC6A07FFCD6B0AFFCA6401FFCB6601FFCB6601FFCB66 01FFD56B00FF824125FFFF00FF00FF00FF00824225FFAF5810FFD26A00FFCA63 00FFCD6A0FFFCD6A0FFFCB6400FFCA6201FFCB6601FFCB6601FFCB6601FFD46B 00FFA95513FF663332FFFF00FF00FF00FF00FF00FF007C3F28FFA95514FFD36B 00FFD16A02FFCD6803FFCB6400FFCC6700FFCD6700FFCF6900FFD56C00FFA854 15FF693531FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF0072392DFF8141 25FFB0580FFFC26105FFCA6602FFCB6602FFC46207FFB2590FFF854323FF6834 31FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00663432FF6D372FFF824125FF824125FF6D372FFF673432FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000004A0FF0917B6FF1022C3FF0D1FC2FF0311B4FF01059FFFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF000004A0FF1E32 CDFF5876F6FF859EFEFF8BA3FFFF7994FEFF5376FCFF234AF0FF051EC5FF0104 9CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF000309A5FF4260ECFFA9BB FFFFBDCAFFFF8EA5FEFF6483FDFF5073FCFF4A6EFDFF3961FDFF1444F9FF042C D7FF0109A2FFFF00FF00FF00FF00FF00FF000004A0FF415EECFFB8C7FFFF9CAF FDFF3A5CFCFF0A3AFBFF0335FBFF0335FBFF0133F9FF052FF2FF0635EBFF0537 E9FF052CCDFF00049CFFFF00FF00FF00FF001A2FCBFF99AFFFFF8BA2FEFF214D FBFF4D71FCFF0E3DFBFF0030FBFF0031F7FF0636F1FF4C6EF1FF103CE3FF0432 DBFF0636D7FF041CB5FFFF00FF000003A0FF4A6AF3FF8FA6FFFF1F46FBFF4C6F FCFFFFFFFFFFA7B8FEFF0733F6FF002AEDFF8CA2F6FFFFFFFFFF627FE7FF0028 D0FF0734CCFF0730C3FF00069FFF0613B4FF5B7CFCFF486CFDFF0133FBFF113C FBFFA1B4FEFFFFFFFFFFA4B6F8FF92A7F5FFFFFFFFFFB6C4F2FF1A41D3FF042F C8FF0732C4FF0734C3FF0212A9FF0B1DBEFF4168FEFF1C49FCFF0335FBFF0031 F9FF0531F2FFA4B5F7FFFFFFFFFFFFFFFFFFB9C6F2FF0D36D0FF002CC6FF0732 C2FF0732C2FF0736C5FF0418ADFF0619BCFF1747FEFF093AFCFF0435F8FF0131 F0FF002BE8FF91A5F4FFFFFFFFFFFFFFFFFFABBAEFFF062FC5FF022DC0FF0732 C2FF0732C2FF0736C5FF0419AEFF020FAFFF0336FAFF0335F8FF0232EEFF0A35 E8FF8CA2F2FFFFFFFFFFB4C2F1FFA9B8EDFFFFFFFFFFA7B7E9FF133AC4FF052F C1FF0732C2FF0734C4FF0313AAFF01049EFF0430E4FF0436F1FF002AE4FF5070 E9FFFFFFFFFFB7C4F1FF0D36CAFF042DC3FFA2B2E8FFFFFFFFFF6984DAFF0026 BEFF0733C3FF0731C1FF0108A0FFFF00FF00031ABAFF0537E7FF0331DDFF123D D8FF6480E0FF1840CBFF002CC1FF022DC0FF0F38C4FF6580D9FF1B43C7FF052F C1FF0735C5FF051FB3FFFF00FF00FF00FF0001049BFF052BCAFF0636D8FF0431 CDFF0027C4FF032EC1FF0732C2FF0732C2FF0430C1FF0027BFFF042FC1FF0735 C4FF072EBEFF01069EFFFF00FF00FF00FF00FF00FF000109A1FF052BC3FF0735 C7FF0733C2FF0732C2FF0732C2FF0732C2FF0732C2FF0733C3FF0735C4FF062D BEFF020CA4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF0001049DFF041C B1FF0730C0FF0734C4FF0735C5FF0735C5FF0734C3FF0731C1FF041FB3FF0106 9EFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF0001079FFF0313A9FF0418AEFF0419AEFF0313A9FF0108A0FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000000FF000000FF000000FF000000FF000000 FF000000FF00686868FF686868FF686868FF686868FF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF006C6C 6CFF848484FF979797FFA3A3A3FFA3A3A3FF979797FF848484FF6F6F6FFF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF007A7A7AFF9D9D 9DFFA1A1A1FFA1A1A1FFA1A1A1FFA1A1A1FFA1A1A1FFA1A1A1FF9B9B9BFF7A7A 7AFF0000FF000000FF000000FF000000FF000000FF00797979FF9A9A9AFF9B9B 9BFF9B9B9BFF9B9B9BFFA6A6A6FFF5F5F5FFD1D1D1FF9B9B9BFF9B9B9BFF9A9A 9AFF797979FF0000FF000000FF000000FF006B6B6BFF929292FF969696FF9696 96FF969696FF969696FFA2A2A2FFF5F5F5FFCECECEFF969696FF969696FF9696 96FF929292FF6B6B6BFF0000FF000000FF00797979FF909090FF909090FF9090 90FF909090FF909090FFAAAAAAFF9D9D9DFF909090FF909090FF909090FF9090 90FF909090FF797979FF0000FF00686868FF828282FF8B8B8BFF8B8B8BFF8B8B 8BFF929292FFE5E5E5FFFCFCFCFFF3F3F3FF858585FF7B7B7BFF757575FF7272 72FF6E6E6EFF6C6C6CFF686868FF686868FF848484FF858585FF858585FF8585 85FFBFBFBFFFB1B1B1FFFCFCFCFFF2F2F2FF6E6E6EFF6E6E6EFF6E6E6EFF6E6E 6EFF6E6E6EFF6E6E6EFF686868FF686868FF7E7E7EFF808080FF808080FF8080 80FF7A7A7AFFADADADFFFCFCFCFFC6C6C6FF6E6E6EFF6E6E6EFF6E6E6EFF6E6E 6EFF6E6E6EFF6E6E6EFF686868FF686868FF757575FF797979FF797979FF7070 70FF6E6E6EFFD8D8D8FFFCFCFCFF9A9A9AFF6E6E6EFF6E6E6EFF6E6E6EFF6E6E 6EFF6E6E6EFF6C6C6CFF686868FF0000FF006C6C6CFF727272FF6E6E6EFF6E6E 6EFF6E6E6EFFFCFCFCFFFCFCFCFF929292FFACACACFF6E6E6EFF6E6E6EFF6E6E 6EFF6E6E6EFF6B6B6BFF0000FF000000FF006A6A6AFF6E6E6EFF6E6E6EFF6E6E 6EFF777777FFF2F2F2FFFCFCFCFFD8D8D8FF777777FF6E6E6EFF6E6E6EFF6E6E 6EFF6C6C6CFF6A6A6AFF0000FF000000FF000000FF006A6A6AFF6E6E6EFF6E6E 6EFF6E6E6EFF808080FF929292FF6E6E6EFF6E6E6EFF6E6E6EFF6E6E6EFF6E6E 6EFF6A6A6AFF0000FF000000FF000000FF000000FF000000FF006A6A6AFF6E6E 6EFF6E6E6EFF6E6E6EFF6E6E6EFF6E6E6EFF6E6E6EFF6E6E6EFF6C6C6CFF6A6A 6AFF0000FF000000FF000000FF000000FF000000FF000000FF000000FF006A6A 6AFF6A6A6AFF6C6C6CFF6E6E6EFF6E6E6EFF6C6C6CFF6A6A6AFF6A6A6AFF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF00686868FF686868FF686868FF686868FF0000FF000000FF000000 FF000000FF000000FF000000FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF0051515140959595DF959595DF51515140FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FF009919FFFFFF00FFFFFF00FF009919FFFF FF00FFFFFF009F9F9FF0DCDCDCFFDCDCDCFF9F9F9FF0FFFFFF00FFFFFF00FF00 9919FFFFFF00FFFFFF00FF009919FF009933FFFFFF00FFFFFF00FF009933FFFF FF00FFFFFF00ABABABF3BBBBBBFFBBBBBBFFABABABF3FFFFFF00FFFFFF00FF00 9933FFFFFF00FFFFFF00FF009933FF00993EFF00990EFFFFFF00FF009934FF00 9918FFFFFF00B1B1B16CB7B7B7E7B7B7B7E7B1B1B16CFFFFFF00FF009918FF00 9934FFFFFF00FF00990EFF00993EFF009940FF009933FFFFFF00FF009920FF00 9959FF009906FFFFFF10FFFFFF38FFFFFF38FFFFFF10FF009906FF009959FF00 9920FFFFFF00FF009933FF009940FF00990FFF00996FFF009908FFFFFF00FF00 9947FF00992F3D3D3D308A8A8AFF8A8A8AFF3D3D3D30FF00992FFF009947FFFF FF00FF009908FF00996FFF00990FFFFFFF00FF009955FF00995FFFFFFF00FFFF FF00FFFFFF00777777A4909090FF909090FF777777A4FFFFFF00FFFFFF00FFFF FF00FF00995FFF009955FFFFFF00FFFFFF00FFFFFF00FF009990FF00996FFF00 990B181818107D7D7DF3666666F7666666F77D7D7DF318181810FF00990BFF00 996FFF009990FFFFFF00FFFFFF00FFFFFF00FFFFFF00FF00990CFF009972FF00 990C5B5B5B626A6A6AFF787878C3787878C36A6A6AFF5B5B5B62FF00990CFF00 9972FF00990CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00767676D4545454FF858585FF858585FF545454FF767676D4FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003434 34306A6A6AFF5F5F5FE5FFFFFF40FFFFFF405F5F5FE56A6A6AFF34343430FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006363 63A4585858FF6B6B6BFF717171FF717171FF6B6B6BFF585858FF636363A4FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00131313106868 68F2494949F7D5D5D54CFFFFFF40FFFFFF40D5D5D54C494949F7686868F21313 1310FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00484848625858 58FF555555FF676767FF636363FF636363FF676767FF555555FF585858FF4848 4862FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF005C5C5CD44545 45FF9090906EFFFFFF40FFFFFF40FFFFFF40FFFFFF409090906E454545FF5C5C 5CD4FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF34FFFF FF40FFFFFF0CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0CFFFFFF40FFFF FF34FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF004A4A4A164E4E4EBF4D4D4DFF4D4D4DFF4E4E4EBF4A4A4A16FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 00024A4A4ABE515151FF5E5E5EFF5E5E5EFF515151FF4A4A4ABE00000002FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0006454545FF575757FF585858FF585858FF575757FF454545FF00000006FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0009404040FF505050FF505050FF505050FF505050FF404040FF00000009FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 00093C3C3CFF494949FF494949FF494949FF494949FF3C3C3CFF00000009FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0009373737FF424242FF424242FF424242FF424242FF373737FF00000009FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0009313131D9373737FF3A3A3AFF3A3A3AFF373737FF313131D900000009FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 0007282828752E2E2EFF333333FF333333FF2E2E2EFF2828287500000007FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000 000400000010252525B02A2A2AFF2A2A2AFF252525B00000001000000004FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF002020 20252020207B212121D7232323FF242424FF212121E42121219A20202035FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001A1A1A241A1A1A761B1B1BD41B1B 1BFF1C1C1CFF1D1D1DFF1D1D1DFF1D1D1DFF1D1D1DFF1C1C1CFF1B1B1BFF1B1B 1BE21B1B1B961A1A1A34FFFFFF0014141435151515FF151515FF161616FF1616 16FF161616FF161616FF161616FF161616FF161616FF161616FF161616FF1616 16FF151515FF151515FF141414350E0E0E960E0E0EFF0F0F0FFF0F0F0FFF0F0F 0FFF0F0F0FFF0F0F0FFF0F0F0FFF0F0F0FFF0F0F0FFF0F0F0FFF0F0F0FFF0F0F 0FFF0F0F0FFF0E0E0EFF0E0E0E96080808E2080808FF080808FF080808FF0808 08FF080808FF080808FF080808FF080808FF080808FF080808FF080808FF0808 08FF080808FF080808FF080808E2000000120000001B0000001C0000001C0000 001C0000001C0000001C0000001C0000001C0000001C0000001C0000001C0000 001C0000001C0000001B00000012000000060000000900000009000000090000 0009000000090000000900000009000000090000000900000009000000090000 0009000000090000000900000006000000000000000000000000000000000000 000000000000999999FF9A9A9AFF9A9A9AFF9A9A9AFF9A9A9AFF9A9A9AFF9A9A 9AFF000000000000000000000000000000000000000000000000000000000000 000000000000999999FFFAFAFAFFFAFAFAFFFAFAFAFFD5D5D5FFFFFFFFFFC8C8 C8FF999999FF0000000000000000000000000000000000000000000000000000 000000000000999999FFF5F5F5FFF5F5F5FFF5F5F5FFD0D0D0FFFFFFFFFFFFFF FFFF999999FF0000000000000000000000000000000000000000000000000000 000000000000999999FFF0F0F0FFF0F0F0FFF0F0F0FFCCCCCCFFCCCCCCFFCCCC CCFF999999FF0000000000000000000000000000000000000000000000000000 0000000000008A8A8AFFD4D4D4FFD4D4D4FFDCDCDCFFEBEBEBFFEBEBEBFFEBEB EBFF999999FF00000000000000000000000000000000999999FF9A9A9AFF9A9A 9AFF9A9A9AFF9A9A9AFF9A9A9AFFB9B9B9FFCFCFCFFFD8D8D8FFE6E6E6FFE6E6 E6FF999999FF00000000000000000000000000000000999999FFFAFAFAFFFAFA FAFFFAFAFAFFD5D5D5FFFFFFFFFFC8C8C8FFB7B7B7FFCACACAFFE1E1E1FFE1E1 E1FF999999FF00000000000000000000000000000000999999FFF5F5F5FFF5F5 F5FFF5F5F5FFD0D0D0FFFFFFFFFFFFFFFFFF999999FFC5C5C5FFDBDBDBFFDBDB DBFF999999FF00000000000000000000000000000000999999FFF0F0F0FFF0F0 F0FFF0F0F0FFCCCCCCFFCCCCCCFFCCCCCCFF999999FFC0C0C0FFD6D6D6FFD6D6 D6FF999999FF00000000000000000000000000000000999999FFEBEBEBFFEBEB EBFFEBEBEBFFEBEBEBFFEBEBEBFFEBEBEBFF999999FF898989FF999999FF9999 99FF999999FF00000000000000000000000000000000999999FFE6E6E6FFE6E6 E6FFE6E6E6FFE6E6E6FFE6E6E6FFE6E6E6FF999999FF00000000000000000000 00000000000000000000000000000000000000000000999999FFE1E1E1FFE1E1 E1FFE1E1E1FFE1E1E1FFE1E1E1FFE1E1E1FF999999FF00000000000000000000 00000000000000000000000000000000000000000000999999FFDBDBDBFFDBDB DBFFDBDBDBFFDBDBDBFFDBDBDBFFDBDBDBFF999999FF00000000000000000000 00000000000000000000000000000000000000000000999999FFD6D6D6FFD6D6 D6FFD6D6D6FFD6D6D6FFD6D6D6FFD6D6D6FF999999FF00000000000000000000 00000000000000000000000000000000000000000000999999FF999999FF9999 99FF999999FF999999FF999999FF999999FF999999FF00000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000ABABABFF595959FF595959FF5959 59FF999999FF0000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000595959FFCBCBCBFFCBCB CBFF575757FF595959FF00000000000000000000000000000000000000000000 0000000000000000000000000000595959FF00000000999999FF595959FFD4D4 D4FFE0E0E0FF595959FF999999FF000000000000000000000000000000000000 0000000000000000000000000000595959FF595959FF00000000999999FF5959 59FFDEDEDEFFC8C8C8FF595959FF000000000000000000000000000000000000 0000000000000000000000000000595959FFDEDEDEFF595959FF595959FFB7B7 B7FFD6D6D6FFBCBCBCFF595959FF000000000000000000000000000000000000 0000000000000000000000000000595959FFABABABFFCDCDCDFFC7C7C7FFC3C3 C3FFCFCFCFFFA7A7A7FF595959FF000000000000000000000000000000000000 0000000000000000000000000000595959FF595959FF9C9C9CFFB9B9B9FFC4C4 C4FFA8A8A8FF5665AEFF7393EEFF1A47D9FF0000000000000000000000000000 000000000000000000000000000000000000595959FF595959FFABABABFF9999 99FF7A83BDFF0031FCFF001ACEFF93A3EEFF1A47D9FF00000000000000000000 00000000000000000000000000000000000000000000595959FF595959FF5959 59FF0323D2FF93A9FFFF0033FFFF001ACEFF93A3EEFF1A47D9FF000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000292946FF0323D2FF93A9FFFF0033FFFF001ACEFF93A3EEFF1A47D9FF0000 0000000000000000000000000000000000000000000000000000000000000000 000000000000292946FF0323D2FF93A9FFFF0033FFFF001ACEFF93A3EEFF1A47 D9FF000000000000000000000000000000000000000000000000000000000000 00000000000000000000292946FF0323D2FF93A9FFFF0033FFFF001ACEFF9BA7 EAFF1A47D9FF0000000000000000000000000000000000000000000000000000 0000000000000000000000000000292946FF0323D2FF93A9FFFF0436FFFF001A CEFFB7B4E4FF1A47D9FF00000000000000000000000000000000000000000000 000000000000000000000000000000000000292946FF0323D2FF8C89E6FF001A CEFF8380CBFF1A47D9FF00000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000292946FF7F5586FFBF9C C2FFBF9CC2FF1A47D9FF00000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000292946FF7F55 86FF7F5586FF0000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDBD56FFFDBD 56FFFDBD56FFFCBD55FFFDBD56FFFCBD55FFFCBD55FFFCBD55FFFCBC55FFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDBD56FFFD8B1DFFFD8B 1DFFF57D03FFF47C03FFF57D03FFF47C03FFCF6801FFCF6801FFCF6801FFFBBC 54FFFDFDFD00FDFDFD0000000000FDFDFD00FDBD56FFFD9633FFFD9633FFFD8B 1DFFFD8B1DFFF47C03FFF57D03FFF47C03FFCF6801FFCF6801FFCF6801FFCE67 01FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFD9633FFFD9633FFFD8B 1DFFFD8B1DFFFCFCFCFFFDFDFDFFFCFCFCFFF47C03FFCF6801FFCF6801FFCE67 01FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDA149FFFD9633FFFD96 33FFFD8B1DFFFCFCFCFFFDFDFDFFFCFCFCFF945312FFF47C03FFCF6801FFCE67 01FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDA149FFFDA149FFFD96 33FFFD9633FFFCFCFCFFFDFDFDFFFCFCFCFF945312FFF47C03FFF47C02FFCE67 01FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDA149FFFDA149FFFDFD FDFFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFFFCFCFCFFF47C02FFF37B 02FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDA959FFFDA149FFFDA1 49FFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFF945312FF935311FFF37B 02FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDA959FFFDA959FFFDA1 49FFFDA149FFFCFCFCFFFDFDFDFFFCFCFCFF945312FF945312FFFC8A1CFFF37B 02FFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDAF65FFFDA959FFFDA9 59FFFDA149FFFCA149FFFDFDFDFF945312FF945312FFFC8B1DFFFC8A1CFFFB8A 1CFFA8402BFFFDFDFD0000000000FDFDFD00FDBD56FFFDAF65FFFDAF65FFFDA9 59FFFDA959FFFCA149FFFDA149FF945312FFFC9633FFFC9633FFFC8A1CFFFB8A 1CFFA8402BFFFDFDFD0000000000FDFDFD00FDFDFD00A8402BFFFDAF65FFFDAF 65FFFDA959FFFCA958FFFDA149FFFCA149FFFC9633FFFC9633FFFC9532FFA73F 2AFFFDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00A8402BFFA840 2BFFA8402BFFA8402BFFA8402BFFA8402BFFA8402BFFA8402BFFA73F2AFFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD0041D241FF41D2 41FF41D241FF41D141FF41D241FF41D141FF41D141FF41D141FF40D140FFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD0041D241FF14B014FF14B0 14FF0AA60AFF0AA60AFF019701FF019701FF018001FF018001FF018001FF40D0 40FFFDFDFD00FDFDFD0000000000FDFDFD0041D241FF1FB81FFF1FB81FFF14B0 14FF14B014FF0AA60AFF0AA60AFF019701FF019701FF018001FF018001FF0180 01FF018001FFFDFDFD0000000000FDFDFD0041D241FF28C428FF1FB81FFF1FB8 1FFF14B014FF14B014FFFDFDFDFF0AA60AFF019701FF019701FF018001FF0180 01FF018001FFFDFDFD0000000000FDFDFD0041D241FF28C428FF28C428FF1FB8 1FFF1FB81FFFFCFCFCFFFDFDFDFFFCFCFCFF018001FF019701FF019601FF0180 01FF018001FFFDFDFD0000000000FDFDFD0041D241FF3AC83AFF28C428FF28C4 28FFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFF018001FF019601FF0196 01FF018001FFFDFDFD0000000000FDFDFD0041D241FF3AC83AFF3AC83AFFFDFD FDFFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFFFCFCFCFF018001FF0196 01FF018001FFFDFDFD0000000000FDFDFD0041D241FF41D241FF3AC83AFF3AC8 3AFF1FB81FFFFCFCFCFFFDFDFDFFFCFCFCFF0B680BFF0B680BFF018001FF0196 01FF018001FFFDFDFD0000000000FDFDFD0041D241FF58C858FF41D241FF3AC8 3AFF3AC83AFFFCFCFCFFFDFDFDFFFCFCFCFF136F13FF119011FF13AF13FF09A5 09FF018001FFFDFDFD0000000000FDFDFD0041D241FF58C858FF58C858FF41D2 41FF3AC83AFFFCFCFCFFFDFDFDFFFCFCFCFF136F13FF1FB81FFF13AF13FF09A5 09FF018001FFFDFDFD0000000000FDFDFD0041D241FF58C858FF58C858FF58C8 58FF41D241FF3AC83AFF1B771BFF1B761BFF136F13FF1FB81FFF13AF13FF13AF 13FF018001FFFDFDFD0000000000FDFDFD00FDFDFD00018001FF58C858FF58C8 58FF58C858FF41D141FF3AC83AFF28C428FF28C428FF28C428FF1EB71EFF0180 01FFFDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00018001FF0180 01FF018001FF018001FF018001FF018001FF018001FF018001FF018001FFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD006867FBFF6867 FBFF6867FBFF6766FAFF6867FBFF6766FAFF6766FAFF6766FAFF6766FAFFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD006867FBFF2E2DFBFF2322 FBFF2322FBFF1615FAFF0D0CFBFF0201F5FF0201E1FF0C0BCEFF0201BCFF6665 F9FFFDFDFD00FDFDFD0000000000FDFDFD006867FBFF4645FBFF3938FBFF2E2D FBFF2322FBFF2322FAFF1615FBFF0D0CFAFF0201F5FF0201E1FF0B0ACDFF0101 BCFF0201A6FFFDFDFD0000000000FDFDFD006867FBFF4645FBFF4645FBFF3938 FBFF2E2DFBFFFCFCFCFFFDFDFDFFFCFCFCFF0D0CFAFF0201F5FF0201E1FF0B0A CCFF0201A6FFFDFDFD0000000000FDFDFD006867FBFF5251FBFF4645FBFF4645 FBFF3938FBFFFCFCFCFFFDFDFDFFFCFCFCFF171692FF0605FAFF0201F5FF0101 E0FF0201A6FFFDFDFD0000000000FDFDFD006867FBFF5A59FBFF5251FBFF4645 FBFF4645FBFFFCFCFCFFFDFDFDFFFCFCFCFF171692FF0D0CFAFF0504FAFF0101 F4FF0201A6FFFDFDFD0000000000FDFDFD006867FBFF6261FBFF5A59FBFFFDFD FDFFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFFFCFCFCFF0C0BFAFF0504 F9FF0201A6FFFDFDFD0000000000FDFDFD006867FBFF6867FBFF6261FBFF5A59 FBFFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFF171692FF0B0ACDFF0C0B F9FF0201A6FFFDFDFD0000000000FDFDFD006867FBFF7170FBFF6867FBFF6261 FBFF4847CDFFFCFCFCFFFDFDFDFFFCFCFCFF171692FF171692FF0B0BCDFF1514 F9FF0201A6FFFDFDFD0000000000FDFDFD006867FBFF7574FBFF7170FBFF6867 FBFF6261FBFF4847CDFFFDFDFDFF2E2E92FF2E2E92FF3534CDFF2D2CFAFF2221 F9FF0201A6FFFDFDFD0000000000FDFDFD006867FBFF7574FBFF7574FBFF7170 FBFF6867FBFF6160FAFF4847CDFF2E2E92FF3534CDFF3938FAFF3837FAFF2D2C F9FF0201A6FFFDFDFD0000000000FDFDFD00FDFDFD000201A6FF7574FBFF7574 FBFF7170FBFF6665FAFF6261FBFF5958FAFF5151FAFF4645FAFF3837FAFF0101 A5FFFDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD000201A6FF0201 A6FF0201A6FF0201A6FF0201A6FF0201A6FF0201A6FF0201A6FF0201A5FFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD006767FDFF6767 FDFF6767FDFF6666FCFF6767FDFF6666FCFF6666FCFF6666FCFF6666FCFFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD006767FDFF2D2DFDFF2222 FDFF2222FDFF1515FCFF0C0CFDFF0101F7FF0101E3FF0B0BD0FF0101BEFF6565 FBFFFDFDFD00FDFDFD0000000000FDFDFD006767FDFF4545FDFF3838FDFF2D2D FDFF2222FDFF2222FCFF1515FDFF0C0CFCFF0101F7FF0101E3FF0A0ACFFF0101 BEFF0101A7FFFDFDFD0000000000FDFDFD006767FDFF4545FDFF4545FDFF3838 FDFF2D2DFDFF2222FCFFFDFDFDFF1515FCFF0C0CFCFF0101F7FF0101E3FF0A0A CEFF0101A7FFFDFDFD0000000000FDFDFD006767FDFF5151FDFF4545FDFF4545 FDFF3838FDFFFCFCFCFFFDFDFDFFFCFCFCFF0B0BD0FF0505FCFF0101F7FF0101 E2FF0101A7FFFDFDFD0000000000FDFDFD006767FDFF5959FDFF5151FDFF4545 FDFFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFF0B0BD0FF0404FCFF0101 F6FF0101A7FFFDFDFD0000000000FDFDFD006767FDFF6161FDFF5959FDFFFDFD FDFFFDFDFDFFFCFCFCFFFDFDFDFFFCFCFCFFFCFCFCFFFCFCFCFF0A0ACFFF0404 FBFF0101A7FFFDFDFD0000000000FDFDFD006767FDFF6767FDFF6161FDFF5959 FDFF4747CFFFFCFCFCFFFDFDFDFFFCFCFCFF161694FF161694FF0A0ACFFF0B0B FBFF0101A7FFFDFDFD0000000000FDFDFD006767FDFF7070FDFF6767FDFF6161 FDFF5959FDFFFCFCFCFFFDFDFDFFFCFCFCFF161694FF3434CFFF2121FCFF1414 FBFF0101A7FFFDFDFD0000000000FDFDFD006767FDFF7474FDFF7070FDFF6767 FDFF6161FDFFFCFCFCFFFDFDFDFFFCFCFCFF2E2E94FF3838FCFF2C2CFCFF2121 FBFF0101A7FFFDFDFD0000000000FDFDFD006767FDFF7474FDFF7474FDFF7070 FDFF6767FDFF6060FCFF2E2E94FF2E2E94FF2E2E94FF3838FCFF3737FCFF2C2C FBFF0101A7FFFDFDFD0000000000FDFDFD00FDFDFD000101A7FF7474FDFF7474 FDFF7070FDFF6565FCFF6161FDFF5858FCFF5051FCFF4545FCFF3737FCFF0101 A6FFFDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD000101A7FF0101 A7FF0101A7FF0101A7FF0101A7FF0101A7FF0101A7FF0101A7FF0101A6FFFBFB FB00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FCFCFC00FDFDFD00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FBFB FB00FDFDFD00FDFDFD0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FCFCFB00FCFCFB00FBFCFB00FBFCFB00FBFBFB00FBFBFB00FBFBFB00FBFB FB00FBFBFB00FBFBFB0000000000FDFDFD00FDFDFD00FDFDFD006767FCFF6666 FBFF6666FBFF6666FBFF6666FBFF6666FBFF6666FBFF6566FBFF6566FBFFFBFB FB00FBFBFB00FBFBFB0000000000FDFDFD00FDFDFD006767FCFF2D2DFCFF2222 FBFF2121FBFF1415FBFF0B0CFBFF0101F6FF0101E2FF0A0ACEFF0101BDFF6566 FBFFFDFDFD00FDFDFD0000000000FCFCFC006666FCFF4545FCFF3838FCFF2D2D FCFF2222FCFF2222FCFF1515FCFF0C0CFCFF0101F7FF0101E3FF0B0BD0FF0101 BFFF0101A7FFFCFCFC0000000000FDFDFD006767FCFF4545FCFF4545FCFFFCFC FCFFFCFCFCFF2222FCFF1515FCFF1515FCFFFCFCFCFFFDFDFDFF0101E4FF0B0B D0FF0101A7FFFDFDFD0000000000FCFCFC006666FCFF5050FCFF4545FCFFFCFC FCFFFCFCFCFFFCFCFCFF0B0BD0FFFCFCFCFFFCFCFCFFFCFCFCFF0101A7FF0101 E3FF0101A7FFFCFCFC0000000000FCFCFC006666FCFF5858FCFF5051FCFF4545 FCFFFCFCFCFFFCFCFCFFFCFCFCFFFCFCFCFFFCFCFCFF161694FF0101A7FF0101 F7FF0101A7FFFCFCFC0000000000FCFCFC006666FCFF6060FCFF5858FCFF5051 FCFF3434CFFFFCFCFCFFFCFCFCFFFCFCFCFF161694FF161694FF0B0BD0FF0505 FCFF0101A7FFFCFCFC0000000000FCFCFC006666FCFF6666FCFF6060FCFF5858 FCFFFCFCFCFFFCFCFCFFFCFCFCFFFCFCFCFFFCFCFCFF0B0BD0FF1515FCFF0C0C FCFF0101A7FFFCFCFC0000000000FCFCFC006666FCFF6F6FFCFF6666FCFFFCFC FCFFFCFCFCFFFCFCFCFF2E2E94FFFCFCFCFFFCFCFCFFFCFCFCFF0B0BD0FF1515 FCFF0101A7FFFCFCFC0000000000FDFDFD006767FCFF7474FCFF7070FCFFFCFC FCFFFCFCFCFF2E2E94FF2E2E94FF3434CFFFFCFCFCFFFCFCFCFF161694FF2222 FCFF0101A7FFFDFDFD0000000000FCFCFC006666FCFF7373FCFF7373FCFF6F6F FCFF2E2E94FF2E2E94FF4747CFFF5050FCFF3434CFFF2E2E94FF161694FF2D2D FCFF0101A7FFFCFCFC0000000000FCFCFC00FCFCFC000101A7FF7373FCFF7373 FCFF6F6FFCFF6666FCFF6060FCFF5858FCFF5050FCFF4545FCFF3838FCFF0101 A7FFFCFCFC00FCFCFC0000000000FCFCFC00FCFCFC00FCFCFC000101A7FF0101 A7FF0101A7FF0101A7FF0101A7FF0101A7FF0101A7FF0101A7FF0101A7FFFCFC FC00FCFCFC00FCFCFC0000000000FCFCFC00FCFCFC00FCFCFC00FCFCFC00FCFC FC00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FCFCFC00FCFC FC00FCFCFC00FCFCFC0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00A5C64DFFA5C6 4DFFA5C64DFFA5C64DFFA5C64DFFA5C64DFFA5C64DFFA5C64DFFA5C64DFFFDFD FD00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00A5C64DFF7F9242FF7789 3BFF6F8133FF667B2AFF667B2AFF5C7024FF556B18FF556B18FF4A5C16FFA5C6 4DFFFDFDFD00FDFDFD0000000000FDFDFD00A5C64DFF849A4AFF7F9242FF7789 3BFF77893BFF6F8133FF667B2AFF667B2AFF5C7024FF556B18FF556B18FF4A5C 16FF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF849A4AFF849A4AFF7F92 42FF77893BFF6F8133FF6F8133FF667B2AFF667B2AFFFDFDFDFFFDFDFDFF4A5C 16FF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF91A757FF849A4AFF849A 4AFF7F9242FF77893BFF6F8133FF6F8133FFFDFDFDFFFDFDFDFFFDFDFDFF3340 0FFF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF91A757FFFDFDFDFFFDFD FDFF849A4AFF7F9242FF77893BFFFDFDFDFFFDFDFDFFFDFDFDFF3C4918FF4A5C 16FF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF91A757FFFDFDFDFFFDFD FDFFFDFDFDFF7F9242FFFDFDFDFFFDFDFDFFFDFDFDFF3C4918FF5C7024FF5C70 24FF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF99AF5FFF91A757FFFDFD FDFFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFF4D5A29FF5C7024FF6F8133FF667B 2AFF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF99AF5FFF99AF5FFF91A7 57FFFDFDFDFFFDFDFDFFFDFDFDFF4D5A29FF667B2AFF77893BFF6F8133FF6F81 33FF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF9FB36DFF99AF5FFF99AF 5FFF91A757FFFDFDFDFF4D5A29FF6F8133FF7F9242FF7F9242FF77893BFF6F81 33FF3C4918FFFDFDFD0000000000FDFDFD00A5C64DFF9FB36DFF9FB36DFF99AF 5FFF99AF5FFF91A757FF77893BFF849A4AFF849A4AFF7F9242FF7F9242FF7789 3BFF3C4918FFFDFDFD0000000000FDFDFD00FDFDFD003C4918FF9FB36DFF9FB3 6DFF99AF5FFF99AF5FFF91A757FF91A757FF849A4AFF849A4AFF7F9242FF3C49 18FFFDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD003C4918FF3C49 18FF3C4918FF3C4918FF3C4918FF3C4918FF3C4918FF3C4918FF3C4918FFFDFD FD00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD007B7B7BFF7B7B 7BFF7B7B7BFF7B7B7BFF7B7B7BFF7B7B7BFF7B7B7BFF7B7B7BFF7B7B7BFFFDFD FD00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD007B7B7BFF735D73FF735D 73FF6B556BFF644E64FF5C465CFF5C465CFF533D53FF4B374BFF4B374BFF7B7B 7BFFFDFDFD00FDFDFD0000000000FDFDFD007B7B7BFF806D80FF7B657BFF735D 73FF735D73FF6B556BFF644E64FF5C465CFF5C465CFF533D53FF4B374BFF4B37 4BFF010101FFFDFDFD0000000000FDFDFD007B7B7BFF806D80FF806D80FF7B65 7BFF735D73FF6B556BFF6B556BFF644E64FF5C465CFF5C465CFF533D53FF4B37 4BFF010101FFFDFDFD0000000000FDFDFD007B7B7BFF867386FF806D80FF7B65 7BFF7B657BFFFDFDFDFFFDFDFDFFFDFDFDFF644E64FF5C465CFF5C465CFF533D 53FF010101FFFDFDFD0000000000FDFDFD007B7B7BFF8A778AFF867386FF806D 80FFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFF644E64FF5C465CFF5C46 5CFF010101FFFDFDFD0000000000FDFDFD007B7B7BFF917E91FF8A778AFF8673 86FFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFF413441FF644E64FF5C46 5CFF010101FFFDFDFD0000000000FDFDFD007B7B7BFF917E91FF917E91FF8A77 8AFFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFF413441FF6B556BFF644E 64FF010101FFFDFDFD0000000000FDFDFD007B7B7BFF998399FF917E91FF917E 91FF8A778AFFFDFDFDFFFDFDFDFFFDFDFDFF4B374BFF413441FF6B556BFF6B55 6BFF010101FFFDFDFD0000000000FDFDFD007B7B7BFF998399FF998399FF917E 91FF917E91FF8A778AFF533D53FF4C484CFF4B374BFF7B657BFF735D73FF6B55 6BFF010101FFFDFDFD0000000000FDFDFD007B7B7BFF9E889EFF998399FF9983 99FF917E91FF917E91FF8A778AFF867386FF806D80FF7B657BFF7B657BFF735D 73FF010101FFFDFDFD0000000000FDFDFD00FDFDFD00010101FF9E889EFF9983 99FF998399FF917E91FF917E91FF8A778AFF867386FF806D80FF7B657BFF0101 01FFFDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00010101FF0101 01FF010101FF010101FF010101FF010101FF010101FF010101FF010101FFFDFD FD00FDFDFD00FDFDFD0000000000FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFD FD00FDFDFD00FDFDFD0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00BDBDBDFFBDBD BDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFFFFF FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00BDBDBDFF858585FF8080 80FF7B7B7BFF757575FF707070FF6B6B6BFF646464FF5F5F5FFF575757FFBDBD BDFFFFFFFF00FFFFFF0000000000FFFFFF00BDBDBDFF939393FF8C8C8CFF8585 85FF808080FF7B7B7BFF757575FF707070FF6B6B6BFF646464FF5F5F5FFF5757 57FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFF939393FF939393FF8C8C 8CFF858585FFFFFFFFFFFFFFFFFFFFFFFFFF707070FF6B6B6BFF646464FF5F5F 5FFF566056FFFFFFFF0000000000FFFFFF00BDBDBDFF9D9D9DFF939393FF9393 93FF8C8C8CFFFFFFFFFFFFFFFFFFFFFFFFFF4C484CFF707070FF6B6B6BFF6464 64FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFA2A2A2FF9D9D9DFF9393 93FF939393FFFFFFFFFFFFFFFFFFFFFFFFFF4C484CFF757575FF707070FF6B6B 6BFF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFA2A2A2FF9D9D9DFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF757575FF7070 70FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFA7A7A7FFA2A2A2FF9D9D 9DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C484CFF4C484CFF7575 75FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFACACACFFA7A7A7FFA2A2 A2FF9D9D9DFFFFFFFFFFFFFFFFFFFFFFFFFF575757FF4C484CFF808080FF7A7A 7AFF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFACACACFFACACACFFA7A7 A7FFA2A2A2FF9D9D9DFFFFFFFFFF575757FF575757FF8C8C8CFF858585FF8080 80FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFB3B3B3FFACACACFFACAC ACFFA7A7A7FFA2A2A2FF9D9D9DFF5F5F5FFF939393FF8C8C8CFF8C8C8CFF8585 85FF566056FFFFFFFF0000000000FFFFFF00FFFFFF00566056FFB3B3B3FFACAC ACFFACACACFFA7A7A7FFA2A2A2FF9D9D9DFF9D9D9DFF939393FF8C8C8CFF5660 56FFFFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00566056FF5660 56FF566056FF566056FF566056FF566056FF566056FF566056FF566056FFFFFF FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00BDBDBDFFBDBD BDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFBDBDBDFFFFFF FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00BDBDBDFF858585FF8080 80FF7B7B7BFF757575FF707070FF6B6B6BFF646464FF5F5F5FFF575757FFBDBD BDFFFFFFFF00FFFFFF0000000000FFFFFF00BDBDBDFF939393FF8C8C8CFF8585 85FF808080FF7B7B7BFF757575FF707070FF6B6B6BFF646464FF5F5F5FFF5757 57FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFF939393FF939393FF8C8C 8CFF858585FF808080FFFFFFFFFF757575FF707070FF6B6B6BFF646464FF5F5F 5FFF566056FFFFFFFF0000000000FFFFFF00BDBDBDFF9D9D9DFF939393FF9393 93FF8C8C8CFFFFFFFFFFFFFFFFFFFFFFFFFF5F5F5FFF707070FF6B6B6BFF6464 64FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFA2A2A2FF9D9D9DFF9393 93FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F5F5FFF707070FF6B6B 6BFF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFA2A2A2FF9D9D9DFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F5F5FFF7070 70FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFA7A7A7FFA2A2A2FF9D9D 9DFF808080FFFFFFFFFFFFFFFFFFFFFFFFFF4C484CFF4C484CFF646464FF7575 75FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFACACACFFA7A7A7FFA2A2 A2FF9D9D9DFFFFFFFFFFFFFFFFFFFFFFFFFF575757FF6B6B6BFF808080FF7A7A 7AFF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFACACACFFACACACFFA7A7 A7FFA2A2A2FFFFFFFFFFFFFFFFFFFFFFFFFF575757FF8C8C8CFF858585FF8080 80FF566056FFFFFFFF0000000000FFFFFF00BDBDBDFFB3B3B3FFACACACFFACAC ACFFA7A7A7FFA2A2A2FF5F5F5FFF5F5F5FFF575757FF8C8C8CFF8C8C8CFF8585 85FF566056FFFFFFFF0000000000FFFFFF00FFFFFF00566056FFB3B3B3FFACAC ACFFACACACFFA7A7A7FFA2A2A2FF9D9D9DFF9D9D9DFF939393FF8C8C8CFF5660 56FFFFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00566056FF5660 56FF566056FF566056FF566056FF566056FF566056FF566056FF566056FFFFFF FF00FFFFFF00FFFFFF0000000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF0000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000148E28FF006E00FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00006E00FF046E08FF047607FF006500FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00108019FF36CF54FF088C0DFF0065 00FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF001D932EFF33C34EFF0476 07FF006500FF006500FF006E00FF006500FFFF00FF00FF00FF00FF00FF0011EE EDFF00CCFFFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00128921FF0D88 19FF088C0DFF018401FF037A05FF006E00FF006E00FFFF00FF0011EEEDFF00CC FFFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0009780EFF27B8 4CFF18A32EFF07800CFF037A05FF059F07FF037A05FF026C5BFFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0009780EFF2EC6 5CFF1A9E35FF0C8513FF059F07FF018401FF006053FF1277E6FF076DD4FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0009780EFF2EC6 5CFF249D38FF44DD65FF129611FF006053FF197EEAFF379CFFFF0A70D8FF076D D4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0006720AFF1289 21FF4BE47EFF36BA51FF00635FFF2186EEFF379CFFFF1177DEFF1C82E9FF076D D4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF001184 13FF1B9A2DFF006375FF288EEEFF58BDFFFF187EE0FF2186EEFF379CFFFF0268 D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00005F78FF1277E6FF3FA5FAFF2288E0FF3BA0EEFF45ABFFFF379CFFFF0268 D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF000B6DD7FF0B6DD7FF278CECFF58BDFFFF58BDFFFF278CECFF076D D4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0011EEEDFF00CC FFFFFF00FF00FF00FF00076DD4FF076DD4FF076DD4FF076DD4FF278CECFF3FA5 FAFF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF0011EEEDFF00CCFFFFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00076DD4FF2F95 EDFF4AAFFFFF076DD4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00076D D4FF197FDEFF076DD4FF076DD4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF000A70D8FFFF00FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E5E5E5FFE5E5 E5FFE5E5E5FFE5E5E5FFE5E5E5FFE5E5E5FFE5E5E5FFE5E5E5FFE5E5E5FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E5E5E5FFCCCCCCFFC5C5 C5FFC5C5C5FFC1C1C1FFBDBDBDFFBABABAFFBABABAFFB6B6B6FFB3B3B3FFE5E5 E5FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00E5E5E5FFCCCCCCFFCCCCCCFFCCCC CCFFC5C5C5FFC5C5C5FFC1C1C1FFBDBDBDFFBABABAFFBABABAFFB6B6B6FFB3B3 B3FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFD4D4D4FFCCCCCCFFCCCC CCFFCCCCCCFFC5C5C5FF000000FFC1C1C1FFBDBDBDFFBABABAFFBABABAFFB6B6 B6FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFD4D4D4FFD4D4D4FFCCCC CCFF000000FFCCCCCCFF000000FF636363FF000000FFBDBDBDFFBABABAFFB6B6 B6FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFD4D4D4FFD4D4D4FFD4D4 D4FF000000FF000000FF000000FF000000FF000000FF5E5E5EFFBDBDBDFFBABA BAFFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFDBDBDBFFD4D4D4FFD4D4 D4FFD4D4D4FF000000FF000000FF000000FF636363FF636363FFC1C1C1FFBDBD BDFFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFDBDBDBFFD4D4D4FFD4D4 D4FF000000FF000000FF000000FF000000FF000000FFC5C5C5FFC5C5C5FFC1C1 C1FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFDBDBDBFFDBDBDBFFD4D4 D4FF000000FF6A6A6AFF000000FF636363FF000000FF636363FFC5C5C5FFC1C1 C1FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFDBDBDBFFDBDBDBFFDBDB DBFFD4D4D4FF6A6A6AFF000000FF6A6A6AFFCCCCCCFF636363FFC5C5C5FFC5C5 C5FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00E5E5E5FFDBDBDBFFDBDBDBFFDBDB DBFFDBDBDBFFD4D4D4FFD4D4D4FF6A6A6AFFCCCCCCFFCCCCCCFFCCCCCCFFC5C5 C5FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00E5E5E5FFDBDBDBFFDBDB DBFFDBDBDBFFDBDBDBFFD4D4D4FFD4D4D4FFD4D4D4FFCCCCCCFFCCCCCCFFB3B3 B3FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B3B3B3FFB3B3 B3FFB3B3B3FFB3B3B3FFB3B3B3FFB3B3B3FFB3B3B3FFB3B3B3FFB3B3B3FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF0000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF00005790D4FF5790 D4FF5790D4FF5790D4FF5790D4FF5790D4FF5790D4FF5790D4FF5790D4FF00FF 000000FF000000FF000000FF000000FF000000FF00005790D4FF0E70E1FF0060 CCFF0060CCFF0060CCFF0056B7FF0056B7FF004BA0FF004BA0FF004BA0FF5790 D4FF00FF000000FF000000FF000000FF00005790D4FF0E70E1FF0E70E1FF0E70 E1FFFFFFFFFF0060CCFF0060CCFF0056B7FF0056B7FF004BA0FF004BA0FF004B A0FF0035AAFF00FF000000FF000000FF00005790D4FF2C83E7FF0E70E1FFFFFF FFFFFFFFFFFFFFFFFFFF0060CCFF0060CCFFFFFFFFFF0056B7FF004BA0FF004B A0FF0035AAFF00FF000000FF000000FF00005790D4FF2C83E7FFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFF0060CCFFFFFFFFFF09386DFF0056B7FF004B A0FF0035AAFF00FF000000FF000000FF00005790D4FF2C83E7FF2C83E7FF2C83 E7FFFFFFFFFF09386DFF09386DFF09386DFFFFFFFFFF09386DFF0056B7FF0056 B7FF0035AAFF00FF000000FF000000FF00005790D4FF4591E7FF2C83E7FF2C83 E7FFFFFFFFFF09386DFF27588FFF0E70E1FFFFFFFFFF09386DFF0060CCFF0056 B7FF0035AAFF00FF000000FF000000FF00005790D4FF4591E7FF4591E7FF2C83 E7FFFFFFFFFF09386DFF0E70E1FF0E70E1FFFFFFFFFF09386DFF0060CCFF0060 CCFF0035AAFF00FF000000FF000000FF00005790D4FF599CE7FF4591E7FF4591 E7FFFFFFFFFF09386DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0060 CCFF0035AAFF00FF000000FF000000FF00005790D4FF599CE7FF599CE7FF4591 E7FFFFFFFFFF09386DFF2C83E7FFFFFFFFFFFFFFFFFFFFFFFFFF09386DFF2758 8FFF0035AAFF00FF000000FF000000FF00005790D4FF599CE7FF599CE7FF599C E7FF4591E7FF09386DFF2C83E7FF2C83E7FFFFFFFFFF09386DFF27588FFF0E70 E1FF0035AAFF00FF000000FF000000FF000000FF00000035AAFF599CE7FF599C E7FF599CE7FF4591E7FF4591E7FF2C83E7FF2C83E7FF27588FFF0E70E1FF0035 AAFF00FF000000FF000000FF000000FF000000FF000000FF00000035AAFF0035 AAFF0035AAFF0035AAFF0035AAFF0035AAFF0035AAFF0035AAFF0035AAFF00FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF00000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF0067B86DFF59AE60FF5BAA 62FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF005BAF62FFB9F0C3FF509E 58FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF005CB463FF59AE60FF55A65CFFAAE7B7FF4C96 53FF6AA270FF619867FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF0059AE60FFA4E3B2FF9FE0ADFF9ADCA8FF94D9 A3FF8ED59FFF528A58FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF006BB271FF76B37BFF4D9554FF8AD29AFF4385 4BFF66986CFF6C9871FF0000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF00488E50FF7BC88DFF3F7D 47FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF0043854BFF407D47FF467A 4AFFDDD0C6FFC7B4A8FF0000FF000000FF000000FF00D2C0B4FFC0A795FFBAA1 8DFFC7B4A8FF0000FF000000FF000000FF000000FF00E6DBD2FFEDE1D7FFFFFF FDFFFFF7F1FFD8C4B7FFAB9180FFCBBAAEFFCFBAAAFFEDE1D7FFFFFFFDFFFFF7 F1FFD8C4B7FFAB9180FF0000FF000000FF00CDBAACFFF2E6DEFFB4A193FF6B4E 39FF816450FFCAB19EFFD3B5A2FFCDB19CFFDAC0AFFFCDB39FFF6F543EFF8164 50FFBFAB9CFFCBB1A1FF9D8674FF0000FF00BBA08DFFFFFFFFFF60412AFF0000 FF00DBC2B1FFFFFFFFFFFFFEF7FFFFF1E6FFFAE3D4FFF3D7C7FF9D897AFF0000 FF00C0A592FFEFD6C7FF6B4B32FF0000FF00C9B5A6FFE6D6CBFFB4A091FFA086 72FFC1A895FF4F2C12FF472409FF472409FF472409FF735642FFA08672FFC1A8 95FFD2BCAEFFB99C88FF8A7361FF0000FF000000FF00B79F8EFFCFB8A9FFEBD6 C8FFE5CBBBFFB3957FFF80624CFFBAA99BFFB79F8EFFCFB8A9FFEBD6C8FFE5CB BBFFB3957FFF80624CFF0000FF000000FF000000FF000000FF00AE9B8BFF8165 4EFF745840FF968170FF0000FF000000FF000000FF00AE9B8BFF81654EFF7458 40FF968170FF0000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00078DBEFF078DBEFF078D BEFF078DBEFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00078DBEFFFEFEFEFFA5FEFFFFA5FE FFFFA5FEFFFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFFFF00 FF00FF00FF00FF00FF00FF00FF00078DBEFFFEFEFEFFA0FBFFFFA0FBFEFFA0FB FEFFA1FAFEFFA1FBFEFFA0FAFEFFA1FBFEFFA1FBFFFFA0FBFFFFA1FBFFFF0989 BAFFFF00FF00FF00FF00FF00FF00078DBEFF9BF5FEFF9AF6FEFF9AF6FEFF9BF5 FDFF9BF6FEFF9AF6FEFF9BF5FEFF9AF6FDFF9BF5FEFF9AF6FEFF9AF6FEFF0989 BAFFFF00FF00FF00FF00FF00FF00078DBEFF93F0FEFF93F0FDFF1697C5FF078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF8AEAFCFF77DCF3FF219CC7FFFEFF FFFFC8F7FDFFC9F7FDFFC9F7FDFFC9F7FEFFC8F7FEFFC9F7FDFFC8F7FEFF9BD5 E6FFEAFEFEFFD2F3F8FF078DBEFF078DBEFF82E3FCFF43B7DCFF65C2E0FFABF0 FCFF8DEBFCFF8DEBFCFF8DEBFDFF8DEBFDFF8DEBFCFF8DEBFDFF8DEBFCFF4CBB DAFFC9F0F3FFC9F0F3FF078DBEFF078DBEFF79DDFBFF1899C7FF9ADFF3FF92E7 FCFF84E4FBFF83E4FCFF83E4FCFF84E4FCFF83E4FCFF83E4FBFF84E5FCFF48B9 DAFFC9F0F3FF1496C4FFFF00FF00078DBEFF72D6FAFF078DBEFFAEE9FCFF79DC FBFF79DCFBFF79DCFBFF79DCFBFF79DCFBFF7ADCFBFF79DCFAFF79DCFAFF44B5 D9FFC9F0F3FF078DBEFFFF00FF00078DBEFF4CBCE7FF39A8D1FFA0E2FBFF6FD4 FAFF6FD4F9FF6ED4FAFF6FD4F9FF6FD4FAFF6FD4FAFF6FD4FAFF6ED4F9FF3EB1 D9FFC9F0F3FF078DBEFFFF00FF00078DBEFF25A1D1FF71C6E8FF84D7FAFF66CD F9FF65CDF9FF65CDF9FF65CDF9FF65CDF8FF65CDF9FF65CDF8FF66CEF9FF3AAD D8FF1999C9FFFF00FF00FF00FF00FF00FF00078DBEFF078DBEFF078DBEFF078D BEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078DBEFF078D BEFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF00B8B8B8FFACACACFFACACACFFB8B8B8FF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF00A8A8A8FFB4B4B4FFC2C2C2FFC2C2C2FFB4B4B4FFA8A8A8FF0000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF00A8A8 A8FFB6B6B6FFBFBFBFFFC2C2C2FFC2C2C2FFBEBEBEFFB6B6B6FFA8A8A8FF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF00B8B8B8FFAAAA AAFFB6B6B6FFB9B9B9FFBCBCBCFFBCBCBCFFB9B9B9FFB5B5B5FFA9A9A9FFB8B8 B8FF0000FF000000FF000000FF000000FF000000FF000000FF00ACACACFFA8A8 A8FFADADADFFB2B2B2FFB4B4B4FFB4B4B4FFB2B2B2FFADADADFFA8A8A8FFACAC ACFF0000FF000000FF000000FF000000FF000000FF000000FF00ACACACFFADAD ADFFAFAFAFFFB2B2B2FFB4B4B4FFB4B4B4FFB4B4B4FFAFAFAFFFADADADFFACAC ACFF0000FF000000FF000000FF000000FF000000FF000000FF00B4B4B4FFAEAE AEFFB4B4B4FFB7B7B7FFB9B9B9FFB9B9B9FFB7B7B7FFB4B4B4FFAFAFAFFFB4B4 B4FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF00A7A7 A7FFB7B7B7FFBCBCBCFFBEBEBEFFBEBEBEFFBCBCBCFFB8B8B8FFA7A7A7FF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF00A7A7A7FFBCBCBCFFC5C5C5FFC5C5C5FFBCBCBCFFA7A7A7FF0000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF00ADADADFFA3A3A3FFA3A3A3FFADADADFF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF0000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000051C8FEFF18B0FDFF18B0FDFF51C8FEFF00FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000B2FFFF48BFF7FF90D4F7FF90D4F7FF48BFF7FF00B2FFFF00FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000B2 FFFF4EC1F8FF78D2FCFF88D7FDFF88D7FDFF74D0FBFF4EC1F8FF00B2FFFF00FF 000000FF000000FF000000FF000000FF000000FF000000FF000051C8FEFF0FB1 F9FF47C6FEFF5ACEFFFF6AD3FEFF6AD3FEFF5ACEFFFF42C5FEFF0AB0FAFF51C8 FEFF00FF000000FF000000FF000000FF000000FF000000FF000019B2F9FF00B2 FFFF17BEFFFF32CBFFFF3ED2FFFF3ED2FFFF32CBFFFF17BEFFFF00B2FFFF19B2 F9FF00FF000000FF000000FF000000FF000000FF000000FF000019B2F9FF17BE FFFF26C5FFFF32CBFFFF3CD1FFFF3CD1FFFF3CD1FFFF26C5FFFF17BEFFFF19B2 F9FF00FF000000FF000000FF000000FF000000FF000000FF00004ABAF2FF21C1 FDFF3BD0FFFF4DD9FFFF59DFFFFF59DFFFFF4FDBFFFF3ED2FFFF26C5FFFF4ABA F2FF00FF000000FF000000FF000000FF000000FF000000FF000000FF00000FA5 EAFF4FDBFFFF65E5FFFF73ECFFFF73ECFFFF65E5FFFF53DDFFFF0FA5EAFF00FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000017A2E4FF65E5FFFF98FFFFFF98FFFFFF65E5FFFF19A3E4FF00FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF00003F97D7FF168BCFFF168BCFFF3F97D7FF00FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF00000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF0000D159FF00AC4EFF00AC4EFF00D159FF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF00009F42FF07BF57FF10E570FF10E570FF07BF57FF009F42FF0000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF00009F 42FF07C45BFF05E466FF04F06BFF04F06BFF06DF64FF07C45BFF009F42FF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF0000D159FF03A1 47FF00CA55FF00D858FF01DF5CFF01DF5CFF00D858FF00C854FF02A045FF00D1 59FF0000FF000000FF000000FF000000FF000000FF000000FF0004A74AFF009F 42FF00AF44FF00BF46FF00C744FF00C744FF00BF46FF00AF44FF009F42FF04A7 4AFF0000FF000000FF000000FF000000FF000000FF000000FF0004A74AFF00AF 44FF00B845FF00BF46FF00C644FF00C644FF00C644FF00B845FF00AF44FF04A7 4AFF0000FF000000FF000000FF000000FF000000FF000000FF000CB85AFF00B1 46FF00C546FF00D043FF00D842FF00D842FF00D142FF00C744FF00B845FF0CB8 5AFF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000893 44FF00D142FF00E03FFF00E83AFF00E83AFF00E03FFF00D442FF089344FF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000D8E46FF00E03FFF01FF1FFF01FF1FFF00E03FFF0F8E47FF0000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF001D915CFF0D8145FF0D8145FF1D915CFF0000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF0000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF00005273FEFF193FFDFF193FFDFF5273FEFF00FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 00000033FFFF4969F7FF91A2F7FF91A2F7FF4969F7FF0033FFFF00FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF00000033 FFFF4F6DF8FF7991FCFF899EFDFF899EFDFF758EFBFF4F6DF8FF0033FFFF00FF 000000FF000000FF000000FF000000FF000000FF000000FF00005273FEFF103D F9FF486CFEFF5B7DFFFF6B8AFEFF6B8AFEFF5B7DFFFF4368FEFF0B39FAFF5273 FEFF00FF000000FF000000FF000000FF000000FF000000FF00001A43F9FF0033 FFFF184BFFFF3366FFFF3F73FFFF3F73FFFF3366FFFF184BFFFF0033FFFF1A43 F9FF00FF000000FF000000FF000000FF000000FF000000FF00001A43F9FF184B FFFF275AFFFF3366FFFF3D71FFFF3D71FFFF3D71FFFF275AFFFF184BFFFF1A43 F9FF00FF000000FF000000FF000000FF000000FF000000FF00004B67F2FF2254 FDFF3C6FFFFF4E81FFFF5A8DFFFF5A8DFFFF5084FFFF3F73FFFF275AFFFF4B67 F2FF00FF000000FF000000FF000000FF000000FF000000FF000000FF00000F38 EBFF5084FFFF6699FFFF74A7FFFF74A7FFFF6699FFFF5488FFFF0F38EBFF00FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 0000173CE5FF6699FFFF99CCFFFF99CCFFFF6699FFFF193EE5FF00FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF0000404CD8FF162FD0FF162FD0FF404CD8FF00FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF 000000FF000000FF000000FF0000046E08FF047607FF006500FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00108019FF36CF54FF088C0DFF006500FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF001D932EFF33C34EFF047607FF0065 00FF006500FF006D00FF006500FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00128921FF0D8819FF088C 0DFF038403FF037A05FF006D00FF006D00FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0009780EFF27B84CFF18A3 2EFF07800CFF037A05FF009700FF006D00FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0009780EFF2EC65CFF1A9E 35FF0C8513FF059F07FF006D00FF898A8CFF535558FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0009780EFF2EC65CFF249D 38FF44DD65FF006D00FF535558FFFF00FF00898A8CFF535558FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0006720AFF128921FF4BE4 7EFF006D00FFFF00FF00898A8CFF535558FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00118413FF006D 00FF898A8CFF535558FFFF00FF00898A8CFF898A8CFFFF00FF00FF00FF000268 D0FF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00898A8CFF535558FFFF00FF00FF00FF00FF00FF000268D0FF379C FFFF0D73DBFF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF000268D0FF379CFFFF1177 DEFF1C82E9FF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF000268D0FF58BDFFFF187EE0FF2186 EEFF379CFFFF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000268D0FF3FA5FAFF2288E0FF3BA0EEFF45AB FFFF379CFFFF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000268D0FF1177DEFF2A90EEFF5DC3FFFF58BD FFFF278CECFF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF000066CEFF0268D0FF0268D0FF0268 D0FF2A90EEFF3FA5FAFF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF000268D0FF0268D0FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FF993300FF993300FF9933 00FF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00993300FFE3AE79FFE0A56BFFDD9C5CFFDA94 4FFF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00993300FFEDCAA8FFEAC199FFE7B98BFFDFA875FF9933 00FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00993300FFF6E6D6FFF3DEC8FFF0D5BAFFE3B995FF993300FFFF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00993300FFE1C0ABFFF7E9DAFFF4E0CCFFE1BA9CFF9933 00FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00993300FFF4E9E2FFFDF9F5FFFBF4ECFF993300FF9933 00FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00993300FFE1C6B0FFECDCCDFFEDDDD1FF993300FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00993300FFCDA27CFFD8B596FF993300FF993300FFFF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF009933 00FFBA7D48FF993300FF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FFAA5F 1FFF993300FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FF993300FF9933 00FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00993300FF993300FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00993300FFFF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF0056C9D6FF56C9 D6FF56C9D6FF56C9D6FF56C9D6FF56C9D6FF56C9D6FF56C9D6FF56C9D6FF0000 FF000000FF000000FF000000FF000000FF000000FF0056C9D6FF00B4C8FF02A6 B7FF02A6B7FF0098A9FF0098A9FF0098A9FF008998FF008998FF017885FF56C9 D6FF0000FF000000FF000000FF000000FF0056C9D6FF00B4C8FF00B4C8FF00B4 C8FF02A6B7FF02A6B7FF0098A9FF0098A9FF008998FF008998FF008998FF0178 85FF007FAAFF0000FF000000FF000000FF0056C9D6FF04C3D8FF00B4C8FF00B4 C8FF00B4C8FF02A6B7FF02A6B7FF0098A9FF0098A9FF008998FF008998FF0089 98FF007FAAFF0000FF000000FF000000FF0056C9D6FF04C3D8FF04C3D8FF00B4 C8FFFFFFFFFFFFFFFFFF008998FFFFFFFFFFFFFFFFFF017885FF008998FF0089 98FF007FAAFF0000FF000000FF000000FF0056C9D6FF04C3D8FF04C3D8FF04C3 D8FFFFFFFFFFFFFFFFFF006671FFFFFFFFFFFFFFFFFF006671FF017885FF0089 98FF007FAAFF0000FF000000FF000000FF0056C9D6FF12CFE4FF04C3D8FF04C3 D8FFFFFFFFFFFFFFFFFF006671FFFFFFFFFFFFFFFFFF006671FF017885FF0098 A9FF007FAAFF0000FF000000FF000000FF0056C9D6FF12CFE4FF12CFE4FF04C3 D8FFFFFFFFFFFFFFFFFF006671FFFFFFFFFFFFFFFFFF006671FF008998FF0098 A9FF007FAAFF0000FF000000FF000000FF0056C9D6FF2ED5E7FF12CFE4FF12CF E4FFFFFFFFFFFFFFFFFF017885FFFFFFFFFFFFFFFFFF006671FF008998FF02A6 B7FF007FAAFF0000FF000000FF000000FF0056C9D6FF2ED5E7FF2ED5E7FF12CF E4FF02A6B7FF017885FF017885FF0098A9FF006671FF006671FF008998FF02A6 B7FF007FAAFF0000FF000000FF000000FF0056C9D6FF56C9D6FF2ED5E7FF12CF E4FF12CFE4FF02A6B7FF02A6B7FF04C3D8FF0098A9FF0098A9FF00B4C8FF02A6 B7FF007FAAFF0000FF000000FF000000FF000000FF00007FAAFF56C9D6FF2ED5 E7FF12CFE4FF12CFE4FF12CFE4FF04C3D8FF04C3D8FF04C3D8FF00B4C8FF007F AAFF0000FF000000FF000000FF000000FF000000FF000000FF00007FAAFF007F AAFF007FAAFF007FAAFF007FAAFF007FAAFF007FAAFF007FAAFF007FAAFF0000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00C2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00C2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF929292FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00929292FF929292FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFC2C2C2FF626262FF828282FFC2C2C2FF929292FF828282FF8282 82FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FF626262FF424242FF424242FF626262FFFFFFFF00929292FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00828282FF424242FF424242FF626262FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FF626262FF828282FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF424242FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF000000FF424242FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF000000FF424242FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF424242FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF626262FF6262 62FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00626262FF424242FF4242 42FF828282FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00828282FF424242FF4242 42FF828282FFC2C2C2FF929292FF929292FFC2C2C2FFC2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF626262FF8282 82FFC2C2C2FF929292FF929292FF929292FFC2C2C2FFFFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00929292FF828282FF828282FF929292FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FFC2C2C2FF929292FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF626262FF626262FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00626262FF424242FF424242FF828282FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00828282FF424242FF424242FF626262FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF626262FF828282FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF929292FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00929292FF929292FF9292 92FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00929292FF828282FF8282 82FF929292FFFFFFFF00C2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FF9292 92FFC2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FF424242FF424242FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00424242FF000000FF000000FF424242FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF828282FF6262 62FFC2C2C2FF424242FF000000FF000000FF424242FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00626262FF424242FF4242 42FF828282FF929292FF424242FF424242FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00828282FF424242FF4242 42FF626262FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF626262FF8282 82FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF929292FF929292FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00929292FF929292FF929292FF929292FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00929292FF828282FF828282FF929292FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FF929292FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FF828282FF626262FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00626262FF424242FF424242FF828282FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF929292FF9292 92FFC2C2C2FF828282FF424242FF424242FF626262FFC2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00929292FF929292FF9292 92FF929292FFC2C2C2FF626262FF828282FFC2C2C2FF424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00929292FF828282FF8282 82FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFFFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFFFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FF929292FF929292FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00929292FF929292FF929292FF929292FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00929292FF828282FF828282FF929292FFC2C2C2FF828282FF6262 62FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFC2C2C2FFC2C2C2FF929292FFC2C2C2FF828282FF424242FF4242 42FF828282FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00626262FF424242FF4242 42FF626262FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF828282FF6262 62FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FF424242FF424242FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004242 42FF000000FF000000FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004242 42FF000000FF000000FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FF424242FF424242FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FFC2C2C2FF929292FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFFFFFFF00C2C2C2FF828282FF9292 92FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00929292FF828282FF9292 92FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF929292FF9292 92FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FF828282FF626262FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF008282 82FF424242FF424242FF828282FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006262 62FF424242FF424242FF626262FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FF828282FF626262FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00424242FF000000FF0000 00FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF424242FF4242 42FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2C2FFC2C2 C2FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FFC2C2 C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FF929292FF929292FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FF828282FF929292FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF009292 92FF828282FF828282FF929292FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2 C2FFC2C2C2FF929292FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C2FF626262FF8282 82FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00626262FF424242FF4242 42FF626262FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FF424242FF424242FFC2C2C2FF828282FF424242FF4242 42FF828282FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00424242FF000000FF000000FF424242FFC2C2C2FF626262FF8282 82FFC2C2C2FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00424242FF000000FF000000FF424242FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00C2C2C2FF424242FF424242FFC2C2C2FFFFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000002287E8C82187E8C60000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000C70D0ED0C6FD0EF0000 0000000000000000000000816553008B6CC0009372F7009473FF008C6CDE0083 66A8007A5F43000000000000000000000000000000000A6DD0311E83E3BD0000 00000000000000775C85007E62FF018E6FFF07CEA2FF06DEAFFF06D6A8FF08C1 98FF028A6CFF00765C8D00000000278AE9E72588E8D7000000001379D9D72288 EE1E006B5232007158FD017E62FF08CDA1FF01D2A4FF007B5FFF037259FF047E 63FF038669FF017D62FF006A51520C70CFEB1173D2FC69B1F7791176D6A22287 E88600614C9D006952FF048F70FF02C79BFF008365FF0CA281FF20AF90FF20AA 8DFF0E9174FF00785DFF006651C1000000002288EE1E409AF0EA57A6F3BE3893 ECE3025C4BE400614BFF02795EFF01A882FF038065FF0EA483FF168972FF1578 66FF179B7EFF007C60FF00624CF9000000004095EA0C56A6F4EF68B2F6FF7CBE FBFF0B5750FF00503EFF005442FF00755AFF048165FF0C9B7CFF159E7FFF1698 7BFF1AA284FF008768FF005B48F2000000001D7DD9352484DFFF65ADF2FF6EB2 F3FF5BA3E7FF357EA7FF16637EFF0E617FFF005E49FF087E64FF138D72FF19A0 83FF029071FF006D54FF005545C5000000000F69C3111267BBBC146CC2DC1F73 C5FE3B8AD6FF4491DBFF3A8AD6FF1469B4FF006751FF007A5FFF00896BFF0092 72FF006F56FF00503EFA00504040000000000000000000000000000000001067 B84F0C5EACCF0A5AA9F30958A5FD0754A0FF034E63FE00493DFE004737FF0047 37FE00493BE50050415300000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF001F7DDEFF1F84ECFF1F86F0FF1F81E6FF1F7EE0FF1F7AD8FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00207DDDFF0071 E4FF419FFFFF7DBDFFFF97CAFFFF83C0FFFF4DA5FFFF007CFAFF005AB6FF0051 A4FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00207EDFFF2390FFFFB1D8 FFFFDDEEFFFFC5E2FFFF99CBFFFF73B8FFFF59ABFFFF3398FFFF0079F4FF005B B8FF0052A6FFFF00FF00FF00FF00FF00FF001E7CDCFF2591FFFFC7E3FFFFE3F1 FFFFB1D8FFFFB5DAFFFF55A9FFFF1187FFFF0380FFFF0074EAFF0069D4FF0069 D4FF005BB8FF004E9EFFFF00FF00FF00FF00006DDCFFA1CFFFFFDDEEFFFFA9D3 FFFF7FBEFFFFE5F2FFFFDFEFFFFF43A0FFFF007AF6FF0072E6FF0066CEFF0064 CAFF006AD6FF0051A4FFFF00FF001E7DDEFF2B94FFFFBDDEFFFFA1CFFFFF7BBC FFFF75B9FFFFDBEDFFFFFFFFFFFFE5F2FFFF55A9FFFF0069D4FF0064CAFF0065 CCFF0067D0FF005FC0FF004C9AFF1E7CDCFF4FA6FFFF9BCCFFFF61AFFFFF5BAC FFFF5DADFFFFD7EBFFFFFFFFFFFFFFFFFFFFFDFEFFFF6BB4FFFF0066CEFF0063 C8FF0066CEFF0065CCFF004C9AFF1E82E8FF43A0FFFF61AFFFFF3F9EFFFF3F9E FFFF43A0FFFFD3E9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97CAFFFF0071 E4FF0064CAFF0066CEFF004E9EFF1E80E4FF1388FFFF2591FFFF218FFFFF1F8E FFFF2591FFFFCBE5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3D9FFFF0073 E8FF0064CAFF0066CEFF004C9AFF1E7CDCFF0073E8FF007DFCFF0581FFFF0380 FFFF0983FFFFC5E2FFFFFFFFFFFFFFFFFFFFFFFFFFFF83C0FFFF006CDAFF0063 C8FF0066CEFF0064CAFF004C9AFF1E7AD8FF0064CAFF006EDEFF0071E4FF0070 E2FF0074EAFFBFDFFFFFFFFFFFFFEBF5FFFF53A8FFFF0064CAFF0063C8FF0065 CCFF0067D0FF005FC0FF004C9AFFFF00FF000057B0FF0068D2FF0066CEFF0064 CAFF006EDEFFC7E3FFFFE5F2FFFF2390FFFF0063C8FF0064CAFF0065CCFF0065 CCFF006AD6FF0052A6FFFF00FF00FF00FF000052A6FF005EBEFF0068D2FF0064 CAFF0072E6FF81BFFFFF1187FFFF0063C8FF0064CAFF0065CCFF0065CCFF0069 D4FF005CBAFF004B98FFFF00FF00FF00FF00FF00FF000051A4FF005DBCFF0069 D4FF0069D4FF0067D0FF0064CAFF0065CCFF0066CEFF0067D0FF006AD6FF005D BCFF004C9AFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00004E9EFF0052 A6FF005EBEFF0062C6FF0065CCFF0065CCFF0064CAFF005FC0FF0053A8FF004B 98FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00004B98FF004D9CFF0052A6FF0052A6FF004D9CFF004B98FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF0032B74EFF52C46FFF61CB80FF5DC87DFF43B964FF24A342FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0066CD81FF66CD 81FFADE8C5FFCCF2DEFFBAEDD1FFA6E7C2FF91E2B3FF64D492FF2FB157FF2FB1 57FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF0025AE39FF84D89FFFDBF7 EAFFAFE8C6FF6BD493FF52CC81FF44C978FF49CA7BFF48CB78FF39CB6AFF21B6 49FF0F7C1FFFFF00FF00FF00FF00FF00FF0037B650FF71D28CFFD2F4E1FF80DA A3FF36C46DFF2DC267FF8ADDAAFFC9EFD8FF3DC76FFF24BE56FF23BC4DFF1FC1 46FF16AE34FF0A8517FFFF00FF00FF00FF0037B650FFBCEDD2FF82DBA4FF28C0 63FF2FC267FF78D89DFFF5FCF8FFFFFFFFFFB1E8C3FF22BC4BFF1DBA41FF18B7 36FF14C030FF0A8517FFFF00FF000F911DFF67CC83FF9BE5BAFF38C670FF30C3 69FF7AD89EFFF8FDFAFFFFFFFFFFFFFFFFFFFFFFFFFF9FE2B1FF20BD48FF1AB9 3EFF10BA29FF08A317FF05610AFF0F911DFF6FD293FF5FD38DFF31C369FF80DA A3FFF9FDFAFFFDFFFEFFF9FDFBFFFDFEFEFFFBFEFBFFFFFFFFFF95DEA4FF1DB9 3DFF11B82BFF08B119FF05610AFF138D23FF58CC83FF42C977FF37C56EFFF1FB F5FFFFFFFFFFA3E5BDFFCBF0DAFFFFFFFFFF85D98EFFE4F7E6FFFFFFFFFF97DE 9EFF0AB41AFF09B319FF066D0DFF0F821CFF37C26CFF33C76CFF36C56AFFE4F7 E9FFA5E4B5FF2AC053FFC5EED0FFFFFFFFFF41C34DFF4FC85AFFF5FCF6FFADE5 B2FF08B419FF09B319FF05650BFF087210FF2AB65BFF2CC565FF22BD4DFF2DBD 3EFF12B421FF0CB31CFFC0EBC3FFFFFFFFFF50C85CFF09B219FF2ABC38FF17B6 26FF09B51AFF08AB17FF045D09FF087210FF1B9A3AFF2AC65BFF1DBB45FF0EB4 25FF0BB31BFF09B21AFFBFEBC3FFFFFFFFFF50C85CFF09B219FF09B219FF09B2 19FF09B81AFF089413FF045D09FFFF00FF000B6A15FF20BE49FF1BBD40FF14B7 30FF0AB21FFF09B219FFC0EBC4FFFFFFFFFF51C95DFF09B219FF09B219FF09B3 19FF09BA1AFF06670CFFFF00FF00FF00FF000B6A15FF0F8522FF16BD34FF11B7 27FF0BB21CFF07B116FFC2ECC6FFFFFFFFFF50C85CFF09B219FF09B419FF09BA 1AFF078410FF06670CFFFF00FF00FF00FF00FF00FF000A6A15FF0A7F15FF0BB6 1CFF09B91AFF08B418FF07B216FF09B319FF09B419FF09B81AFF09B91AFF0783 10FF044D06FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00056009FF0560 09FF089113FF09B018FF09B31AFF09B319FF09B119FF079614FF05680CFF0568 0CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00044906FF055B09FF066C0CFF066C0CFF055E0AFF044C06FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF0032B74EFF52C46FFF61CB80FF5DC87DFF43B964FF24A342FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF0066CD81FF66CD 81FFADE8C5FFCCF2DEFFBAEDD1FFA6E7C2FF91E2B3FF64D492FF2FB157FF2FB1 57FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF0025AE39FF84D89FFFDBF7 EAFFAFE8C6FF6BD493FF52CC81FF44C978FF49CA7BFF48CB78FF39CB6AFF21B6 49FF0F7C1FFFFF00FF00FF00FF00FF00FF0037B650FF71D28CFFD2F4E1FF80DA A3FF36C46DFF2DC267FFCDF1DBFFFFFFFFFF67D38EFF24BE56FF23BC4DFF1FC1 46FF16AE34FF0A8517FFFF00FF00FF00FF0037B650FFBCEDD2FF82DBA4FF28C0 63FF2FC267FF38C56FFFCCF0DAFFFFFFFFFF67D289FF20BB4AFF1DBA41FF18B7 36FF14C030FF0A8517FFFF00FF000F911DFF67CC83FF9BE5BAFF38C670FF30C3 69FF38C56FFF38C56FFFCBF0D9FFFFFFFFFF61D081FF1EBC49FF1EBC47FF1AB9 3EFF10BA29FF08A317FF05610AFF0F911DFF6FD293FF5FD38DFF31C369FF50CC 80FF3DC773FF38C56FFFCAF0D8FFFFFFFFFF5CCE76FF1AB93EFF37C153FF25BC 44FF11B82BFF08B119FF05610AFF138D23FF58CC83FF42C977FF37C56EFFE6F8 EDFFADE8C4FF38C670FFC9F0D9FFFFFFFFFF44C551FF4FC85CFFF5FCF6FFADE5 B2FF0AB41AFF09B319FF066D0DFF0F821CFF37C26CFF33C76CFF36C56AFFF0FA F3FFFFFFFFFF9CE2AFFFC7EED2FFFFFFFFFF83D88BFFE4F7E6FFFFFFFFFF97DE 9EFF08B419FF09B319FF05650BFF087210FF2AB65BFF2CC565FF22BD4DFF67CF 73FFF7FDF8FFFDFEFDFFF8FDF9FFFDFEFDFFFAFDFAFFFFFFFFFF8EDC95FF0EB4 1EFF09B51AFF08AB17FF045D09FF087210FF1B9A3AFF2AC65BFF1DBB45FF0EB4 25FF5BCC66FFF6FCF7FFFFFFFFFFFFFFFFFFFFFFFFFF96DE9DFF0BB31BFF09B2 19FF09B81AFF089413FF045D09FFFF00FF000B6A15FF20BE49FF1BBD40FF14B7 30FF0AB21FFF58CB63FFF2FBF3FFFFFFFFFFA6E3ACFF0BB31BFF09B219FF09B3 19FF09BA1AFF06670CFFFF00FF00FF00FF000B6A15FF0F8522FF16BD34FF11B7 27FF0BB21CFF07B116FF6FD177FFBCEAC1FF20B92FFF09B219FF09B419FF09BA 1AFF078410FF06670CFFFF00FF00FF00FF00FF00FF000A6A15FF0A7F15FF0BB6 1CFF09B91AFF08B418FF07B216FF09B319FF09B419FF09B81AFF09B91AFF0783 10FF044D06FFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00056009FF0560 09FF089113FF09B018FF09B31AFF09B319FF09B119FF079614FF05680CFF0568 0CFFFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 FF00044906FF055B09FF066C0CFF066C0CFF055E0AFF044C06FFFF00FF00FF00 FF00FF00FF00FF00FF00FF00FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF009595 95FF959595FF959595FF959595FF959595FF959595FF959595FF959595FF9595 95FF0000FF000000FF000000FF000000FF000000FF000000FF00959595FF7777 77FF656565FF656565FF656565FF5B5B5BFF5B5B5BFF4F4F4FFF4F4F4FFF4F4F 4FFF959595FF0000FF000000FF000000FF000000FF00959595FF777777FF7777 77FF777777FFFFFFFFFF656565FF656565FF5B5B5BFF5B5B5BFF4F4F4FFF4F4F 4FFF4F4F4FFF545454FF0000FF000000FF000000FF00959595FF898989FF7777 77FFFFFFFFFFFFFFFFFFFFFFFFFF656565FF656565FFFFFFFFFF5B5B5BFF4F4F 4FFF4F4F4FFF545454FF0000FF000000FF000000FF00959595FF898989FFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF656565FFFFFFFFFF3A3A3AFF5B5B 5BFF4F4F4FFF545454FF0000FF000000FF000000FF00959595FF898989FF8989 89FF898989FFFFFFFFFF3A3A3AFF3A3A3AFF3A3A3AFFFFFFFFFF3A3A3AFF5B5B 5BFF5B5B5BFF545454FF0000FF000000FF000000FF00959595FF959595FF8989 89FF898989FFFFFFFFFF3A3A3AFF5A5A5AFF777777FFFFFFFFFF3A3A3AFF6565 65FF5B5B5BFF545454FF0000FF000000FF000000FF00959595FF959595FF9595 95FF898989FFFFFFFFFF3A3A3AFF777777FF777777FFFFFFFFFF3A3A3AFF6565 65FF656565FF545454FF0000FF000000FF000000FF00959595FF9F9F9FFF9595 95FF959595FFFFFFFFFF3A3A3AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFF656565FF545454FF0000FF000000FF000000FF00959595FF9F9F9FFF9F9F 9FFF959595FFFFFFFFFF3A3A3AFF898989FFFFFFFFFFFFFFFFFFFFFFFFFF3A3A 3AFF5A5A5AFF545454FF0000FF000000FF000000FF00959595FF9F9F9FFF9F9F 9FFF9F9F9FFF959595FF3A3A3AFF898989FF898989FFFFFFFFFF3A3A3AFF5A5A 5AFF777777FF545454FF0000FF000000FF000000FF000000FF00545454FF9F9F 9FFF9F9F9FFF9F9F9FFF959595FF959595FF898989FF898989FF5A5A5AFF7777 77FF545454FF0000FF000000FF000000FF000000FF000000FF000000FF005454 54FF545454FF545454FF545454FF545454FF545454FF545454FF545454FF5454 54FF0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 FF000000FF000000FF000000FF00 } end object pmLabels: TPopupMenu[16] left = 268 top = 92 object miCopyLabel: TMenuItem Caption = 'Copy' OnClick = miCopyLabelClick end end object TrayIcon: TTrayIcon[17] BalloonFlags = bfInfo BalloonTimeout = 10000 PopUpMenu = pmTray OnClick = TrayIconDblClick OnDblClick = TrayIconDblClick left = 456 top = 56 end object pmTray: TPopupMenu[18] Images = ImageList16 left = 460 top = 88 object MenuItem43: TMenuItem Action = acShowApp Default = True OnClick = acShowAppExecute end object MenuItem63: TMenuItem Action = acHideApp OnClick = acHideAppExecute end object miTSep1: TMenuItem Caption = '-' end object MenuItem26: TMenuItem Action = acStartAllTorrents OnClick = acStartAllTorrentsExecute end object MenuItem27: TMenuItem Action = acStopAllTorrents OnClick = acStopAllTorrentsExecute end object MenuItem28: TMenuItem Caption = '-' end object pmiDownSpeedLimit: TMenuItem Caption = 'Maximum download speed' end object pmiUpSpeedLimit: TMenuItem Caption = 'Maximum upload speed' end object MenuItem76: TMenuItem Action = acAltSpeed Bitmap.Data = { 36040000424D3604000000000000360000002800000010000000100000000100 2000000000000004000064000000640000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 000000000000000000001067B84F0C5EACCF0A5AA9F30958A5FD0754A0FF034E 63FE00493DFE004737FF004737FE00493BE50050415300000000000000000F69 C3111267BBBC146CC2DC1F73C5FE3B8AD6FF4491DBFF3A8AD6FF1469B4FF0067 51FF007A5FFF00896BFF009272FF006F56FF00503EFA00504040000000001D7D D9352484DFFF65ADF2FF6EB2F3FF5BA3E7FF357EA7FF16637EFF0E617FFF005E 49FF087E64FF138D72FF19A083FF029071FF006D54FF005545C5000000004095 EA0C56A6F4EF68B2F6FF7CBEFBFF0B5750FF00503EFF005442FF00755AFF0481 65FF0C9B7CFF159E7FFF16987BFF1AA284FF008768FF005B48F2000000002288 EE1E409AF0EA57A6F3BE3893ECE3025C4BE400614BFF02795EFF01A882FF0380 65FF0EA483FF168972FF157866FF179B7EFF007C60FF00624CF90C70CFEB1173 D2FC69B1F7791176D6A22287E88600614C9D006952FF048F70FF02C79BFF0083 65FF0CA281FF20AF90FF20AA8DFF0E9174FF00785DFF006651C1278AE9E72588 E8D7000000001379D9D72288EE1E006B5232007158FD017E62FF08CDA1FF01D2 A4FF007B5FFF037259FF047E63FF038669FF017D62FF006A5152000000000000 00000A6DD0311E83E3BD000000000000000000775C85007E62FF018E6FFF07CE A2FF06DEAFFF06D6A8FF08C198FF028A6CFF00765C8D00000000000000000000 00000C70D0ED0C6FD0EF00000000000000000000000000816553008B6CC00093 72F7009473FF008C6CDE008366A8007A5F430000000000000000000000000000 00002287E8C82187E8C600000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000 } OnClick = acAltSpeedExecute end object MenuItem72: TMenuItem Caption = '-' end object MenuItem24: TMenuItem Action = acExit OnClick = acExitExecute end end object imgFlags: TImageList[19] Height = 12 Width = 18 left = 144 top = 92 Bitmap = { 4C6901000000120000000C000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00 } end object pmPeers: TPopupMenu[20] left = 340 top = 92 object MenuItem29: TMenuItem Action = acResolveHost OnClick = acResolveHostExecute end object MenuItem30: TMenuItem Action = acResolveCountry OnClick = acResolveCountryExecute end object MenuItem31: TMenuItem Action = acShowCountryFlag OnClick = acShowCountryFlagExecute end object MenuItem25: TMenuItem Caption = '-' end object MenuItem45: TMenuItem Action = acSetupColumns OnClick = acSetupColumnsExecute end end object FilterTimer: TTimer[21] Interval = 100 OnTimer = FilterTimerTimer left = 76 top = 28 end object pmTrackers: TPopupMenu[22] Images = ImageList16 left = 376 top = 92 object MenuItem65: TMenuItem Action = acAddTracker OnClick = acAddTrackerExecute end object MenuItem66: TMenuItem Action = acEditTracker OnClick = acEditTrackerExecute end object MenuItem92: TMenuItem Action = acAdvEditTrackers end object MenuItem67: TMenuItem Action = acDelTracker OnClick = acDelTrackerExecute end object sepTrackers: TMenuItem Caption = '-' end object MenuItem48: TMenuItem Action = acSetupColumns OnClick = acSetupColumnsExecute end end object pmConnections: TPopupMenu[23] left = 12 top = 72 object sepCon2: TMenuItem Tag = 1 Caption = '-' end object MenuItem69: TMenuItem Tag = 1 Action = acNewConnection OnClick = acNewConnectionExecute end object MenuItem74: TMenuItem Tag = 1 Action = acConnOptions OnClick = acConnOptionsExecute end end object pmDownSpeeds: TPopupMenu[24] left = 700 top = 96 end object pmUpSpeeds: TPopupMenu[25] left = 732 top = 96 end object pmFilter: TPopupMenu[26] left = 56 top = 72 object MenuItem90: TMenuItem Action = acFolderGrouping end object MenuItem91: TMenuItem Action = acTrackerGrouping end end end TransGUI/options.pas0000644000000000000000000002157312261763702013426 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit Options; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls, StdCtrls, Spin, Buttons, ButtonPanel, BaseForm, ConnOptions; type { TOptionsForm } TOptionsForm = class(TBaseForm) Buttons: TButtonPanel; cbDeleteTorrentFile: TCheckBox; cbLanguage: TComboBox; cbLinksFromClipboard: TCheckBox; cbShowAddTorrentWindow: TCheckBox; cbTrayClose: TCheckBox; cbTrayIconAlways: TCheckBox; cbTrayMinimize: TCheckBox; cbCheckNewVersion: TCheckBox; cbCalcAvg: TCheckBox; cbRegExt: TCheckBox; cbRegMagnet: TCheckBox; cbTrayNotify: TCheckBox; edIntfScale: TSpinEdit; edCheckVersionDays: TSpinEdit; edRefreshInterval: TSpinEdit; edRefreshIntervalMin: TSpinEdit; gbTray: TGroupBox; gbNewTorrent: TGroupBox; gbData: TGroupBox; gbSysInt: TGroupBox; txDays: TLabel; tabGeneral: TTabSheet; txPerc: TLabel; Page: TPageControl; tabAdvanced: TTabSheet; txLanguage: TLabel; txIntfScale: TLabel; txRefreshInterval: TLabel; txRefreshIntervalMin: TLabel; txSeconds: TLabel; txSeconds2: TLabel; procedure cbCheckNewVersionClick(Sender: TObject); procedure cbLanguageEnter(Sender: TObject); procedure cbLanguageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure cbLanguageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormShow(Sender: TObject); procedure OKButtonClick(Sender: TObject); private FLangList: TStringList; {$ifdef mswindows} FRegExt: boolean; FRegMagnet: boolean; {$endif mswindows} procedure FillLanguageItems; public ConnForm: TConnOptionsForm; end; implementation uses {$ifdef mswindows} registry, {$endif mswindows} main, utils, ResTranslator; { TOptionsForm } procedure TOptionsForm.FormCreate(Sender: TObject); var i: integer; pg: TTabSheet; {$ifdef mswindows} reg: TRegistry; s: string; {$endif mswindows} begin cbRegExt.Caption:=Format(cbRegExt.Caption, [AppName]); cbRegMagnet.Caption:=Format(cbRegMagnet.Caption, [AppName]); ConnForm:=TConnOptionsForm.Create(Self); while ConnForm.Page.ControlCount > 0 do begin pg:=ConnForm.Page.Pages[0]; pg.Parent:=Page; pg.TabVisible:=True; end; ConnForm.Page.Free; ConnForm.Page:=Page; Page.ActivePageIndex:=0; Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@OKButtonClick; cbLanguage.Items.Add(FTranslationLanguage); cbLanguage.ItemIndex:=0; i:=80*100 div (ScaleInt(100)*100 div IntfScale); i:=i - i mod 5; if i < 10 then i:=10; edIntfScale.MinValue:=i; {$ifdef LCLgtk2} cbLanguage.OnDropDown:=@cbLanguageEnter; cbLanguage.OnMouseMove:=@cbLanguageMouseMove; {$endif LCLgtk2} {$ifdef mswindows} gbSysInt.Visible:=True; reg:=TRegistry.Create; try if reg.OpenKeyReadOnly('Software\Classes\.torrent') then begin if reg.ReadString('') = AppName then begin reg.CloseKey; if reg.OpenKeyReadOnly(Format('Software\Classes\%s\shell\open\command', [AppName])) then begin s:=reg.ReadString(''); FRegExt:=CompareFilePath(s, Format('"%s" "%%1"', [ParamStr(0)])) = 0; end; end; end; reg.CloseKey; if reg.OpenKeyReadOnly('Software\Classes\Magnet\shell\open\command') then begin s:=reg.ReadString(''); FRegMagnet:=CompareFilePath(s, Format('"%s" "%%1"', [ParamStr(0)])) = 0; end; finally reg.Free; end; cbRegExt.Checked:=FRegExt; cbRegMagnet.Checked:=FRegMagnet; {$endif mswindows} end; procedure TOptionsForm.cbLanguageEnter(Sender: TObject); begin if not Assigned(FLangList) then FillLanguageItems; end; procedure TOptionsForm.cbCheckNewVersionClick(Sender: TObject); begin edCheckVersionDays.Enabled:=cbCheckNewVersion.Checked; end; procedure TOptionsForm.cbLanguageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin cbLanguageEnter(cbLanguage); end; procedure TOptionsForm.cbLanguageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin cbLanguageEnter(cbLanguage); end; procedure TOptionsForm.FillLanguageItems; var i: integer; begin AppBusy; cbLanguage.Items.BeginUpdate; try cbLanguage.Items.Clear; cbLanguage.Items.Add('English'); FLangList := GetAvailableTranslations; FLangList.Sort; with FLangList do for i := 0 to Count - 1 do cbLanguage.Items.Add(Names[i]); with cbLanguage do ItemIndex:= Items.IndexOf(FTranslationLanguage); finally cbLanguage.Items.EndUpdate; end; AppNormal; end; procedure TOptionsForm.FormDestroy(Sender: TObject); begin FLangList.Free; end; procedure TOptionsForm.FormShow(Sender: TObject); begin ConnForm.FormShow(nil); if ConnForm.edHost.Text = '' then begin tabGeneral.Hide; ActiveControl:=ConnForm.edHost; end; end; procedure TOptionsForm.OKButtonClick(Sender: TObject); var s: string; restart: boolean; {$ifdef mswindows} reg: TRegistry; {$endif mswindows} begin ConnForm.ModalResult:=mrNone; ConnForm.btOKClick(nil); if ConnForm.ModalResult = mrNone then exit; restart:=False; if cbLanguage.Text <> FTranslationLanguage then begin if cbLanguage.Text = 'English' then s:='-' else s:=FLangList.Values[cbLanguage.Text]; Ini.WriteString('Interface', 'TranslationFile', s); restart:=True; end; {$ifdef mswindows} reg:=TRegistry.Create; try if cbRegExt.Checked <> FRegExt then if cbRegExt.Checked then begin if reg.OpenKey('Software\Classes\.torrent', True) then begin reg.WriteString('', AppName); reg.CloseKey; if reg.OpenKey(Format('Software\Classes\%s\DefaultIcon', [AppName]), True) then begin reg.WriteString('', Format('"%s",0', [ParamStr(0)])); reg.CloseKey; if reg.OpenKey(Format('Software\Classes\%s\shell\open\command', [AppName]), True) then begin reg.WriteString('', Format('"%s" "%%1"', [ParamStr(0)])); reg.CloseKey; end; end; end; end else begin if reg.OpenKey('Software\Classes\.torrent', False) then begin reg.DeleteValue(''); reg.CloseKey; end; s:=Format('Software\Classes\%s', [AppName]); reg.DeleteKey(s + '\DefaultIcon'); reg.DeleteKey(s + '\shell\open\command'); reg.DeleteKey(s + '\shell\open'); reg.DeleteKey(s + '\shell'); reg.DeleteKey(s); end; if cbRegMagnet.Checked <> FRegMagnet then if cbRegMagnet.Checked then begin if reg.OpenKey('Software\Classes\Magnet', True) then begin if not reg.ValueExists('') then reg.WriteString('', 'Magnet URI'); reg.WriteString('Content Type', 'application/x-magnet'); reg.WriteString('URL Protocol', ''); reg.CloseKey; if reg.OpenKey('Software\Classes\Magnet\DefaultIcon', True) then begin reg.WriteString('', Format('"%s",0', [ParamStr(0)])); reg.CloseKey; if reg.OpenKey('Software\Classes\Magnet\shell', True) then begin reg.WriteString('', 'open'); reg.CloseKey; if reg.OpenKey('Software\Classes\Magnet\shell\open\command', True) then begin reg.WriteString('', Format('"%s" "%%1"', [ParamStr(0)])); reg.CloseKey; end; end; end; end; end else begin reg.DeleteKey('Software\Classes\Magnet\DefaultIcon'); reg.DeleteKey('Software\Classes\Magnet\shell\open\command'); end; finally reg.Free; end; {$endif mswindows} if edIntfScale.Value <> IntfScale then restart:=True; if restart then MessageDlg(sRestartRequired, mtInformation, [mbOk], 0); ModalResult:=mrOk; end; initialization {$I options.lrs} end. TransGUI/utils.pas0000644000000000000000000003603012261763702013065 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit utils; {$mode objfpc}{$H+} interface uses SysUtils, Classes, Controls, Forms, IniFiles, {$ifdef windows} Windows, win32int, InterfaceBase {$endif} {$ifdef unix} baseunix, unix, unixutil, process {$endif} ; type { TFileStreamUTF8 } TFileStreamUTF8 = class(THandleStream) private FFileName: utf8string; public constructor Create(const AFileName: utf8string; Mode: Word); constructor Create(const AFileName: utf8string; Mode: Word; Rights: Cardinal); destructor Destroy; override; property FileName: utf8string Read FFilename; end; { TIniFileUtf8 } TIniFileUtf8 = class(TIniFile) private FStream: TFileStreamUTF8; FFileName: string; public constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False); override; destructor Destroy; override; procedure UpdateFile; override; end; function FileOpenUTF8(Const FileName : string; Mode : Integer) : THandle; function FileCreateUTF8(Const FileName : string) : THandle; function FileCreateUTF8(Const FileName : string; Rights: Cardinal) : THandle; function GetTimeZoneDelta: TDateTime; procedure ShowTaskbarButton; procedure HideTaskbarButton; function IsTaskbarButtonVisible: boolean; procedure CenterOnParent(C: TControl); procedure EnableControls(AEnable: boolean; const AControls: array of TControl); function OpenURL(const URL: string; const Params: string = ''): boolean; function CompareFilePath(const p1, p2: string): integer; procedure AppBusy; procedure AppNormal; procedure ForceAppNormal; function ParamStrUTF8(Param: Integer): utf8string; function ParamCount: integer; function GetCmdSwitchValue(const Switch: string): string; function Base32Decode(const s: ansistring): ansistring; {$ifdef CALLSTACK} function GetLastExceptionCallStack: string; {$endif CALLSTACK} {$ifdef mswindows} procedure AllowSetForegroundWindow(dwProcessId: DWORD); {$endif mswindows} implementation uses {$ifdef CALLSTACK} lineinfo2, {$endif CALLSTACK} FileUtil, StdCtrls, Graphics; {$ifdef windows} function FileOpenUTF8(Const FileName : string; Mode : Integer) : THandle; const AccessMode: array[0..2] of Cardinal = ( GENERIC_READ, GENERIC_WRITE, GENERIC_READ or GENERIC_WRITE); ShareMode: array[0..4] of Integer = ( 0, 0, FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE); begin Result := CreateFileW(PWideChar(UTF8Decode(FileName)), dword(AccessMode[Mode and 3]), dword(ShareMode[(Mode and $F0) shr 4]), nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); //if fail api return feInvalidHandle (INVALIDE_HANDLE=feInvalidHandle=-1) end; function FileCreateUTF8(Const FileName : string) : THandle; begin Result := CreateFileW(PWideChar(UTF8Decode(FileName)), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); end; function FileCreateUTF8(Const FileName : string; Rights: Cardinal) : THandle; begin Result := CreateFileW(PWideChar(UTF8Decode(FileName)), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); end; var FParams: TStringList; function ParamStrUTF8(Param: Integer): utf8string; function SkipSpaces( P: PWideChar ): PWideChar; begin while True do begin while (P[0] <> #0) and (P[0] <= ' ') do Inc(P); if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break; end; Result := P; end; function SkipParam(P: PWideChar): PWideChar; begin P := SkipSpaces( P ); while P[0] > ' ' do if P[0] = '"' then begin Inc(P); while (P[0] <> #0) and (P[0] <> '"') do Inc(P); if P[0] <> #0 then Inc(P); end else Inc(P); Result := P; end; var P, P1: PWideChar; s: widestring; begin if Win32Platform <> VER_PLATFORM_WIN32_NT then begin Result:=FileUtil.ParamStrUTF8(Param); exit; end; if FParams = nil then begin FParams:=TStringList.Create; P := GetCommandLineW; while True do begin P := SkipSpaces( P ); P1 := P; P := SkipParam(P); if P = P1 then break; s := Copy( P1, 1, P - P1 ); if Length(s) >= 2 then if (s[1] = '"') and (s[Length(s)] = '"') then s:=Copy(s, 2, Length(s) - 2); FParams.Add(UTF8Encode(s)); end; // Getting real executable name SetLength(s, 1000); SetLength(s, GetModuleFileNameW(HINSTANCE, PWideChar(s), Length(s) + 1)); if s <> '' then if FParams.Count > 0 then FParams[0]:=UTF8Encode(s) else FParams.Add(UTF8Encode(s)); end; if Param >= FParams.Count then Result:='' else Result:=FParams[Param]; end; function ParamCount: integer; begin if Win32Platform <> VER_PLATFORM_WIN32_NT then Result:=System.ParamCount else begin if FParams = nil then ParamStrUTF8(0); Result:=FParams.Count - 1; end; end; {$else} // Non-Windows targets function FileOpenUTF8(Const FileName : string; Mode : Integer) : THandle; begin Result:=FileOpen(FileName, Mode); end; function FileCreateUTF8(Const FileName : string) : THandle; begin Result:=FileCreate(FileName); end; function FileCreateUTF8(Const FileName : string; Rights: Cardinal) : THandle; begin Result:=FileCreate(FileName, Rights); end; function ParamStrUTF8(Param: Integer): utf8string; begin Result:=FileUtil.ParamStrUTF8(Param); end; function ParamCount: integer; begin Result:=System.ParamCount; end; {$endif windows} { TFileStreamUTF8 } constructor TFileStreamUTF8.Create(const AFileName: utf8string; Mode: Word); var lHandle: THandle; begin FFileName:= AFileName; if Mode = fmcreate then lHandle:= FileCreateUTF8(AFileName) else lHandle:= FileOpenUTF8(AFileName, Mode); If (THandle(lHandle)=feInvalidHandle) then begin if Mode = fmCreate then raise EFCreateError.createfmt({SFCreateError}'Unable to create file "%s"', [AFileName]) else raise EFOpenError.Createfmt({SFOpenError}'Unable to open file "%s"', [AFilename]); end else inherited Create(lHandle); end; constructor TFileStreamUTF8.Create(const AFileName: utf8string; Mode: Word; Rights: Cardinal); var lHandle: THandle; begin FFileName:=AFileName; if Mode=fmcreate then lHandle:=FileCreateUTF8(AFileName,Rights) else lHandle:=FileOpenUTF8(AFileName,Mode); if (THandle(lHandle)=feInvalidHandle) then begin if Mode=fmcreate then raise EFCreateError.createfmt({SFCreateError}'Unable to create file "%s"',[AFileName]) else raise EFOpenError.Createfmt({SFOpenError}'Unable to open file "%s"',[AFilename]); end else inherited Create(lHandle); end; destructor TFileStreamUTF8.Destroy; begin FileClose(Handle); end; { TIniFileUtf8 } constructor TIniFileUtf8.Create(const AFileName: string; AEscapeLineFeeds: Boolean); var m: integer; begin FFileName:=AFileName; if FileExistsUTF8(FFileName) then m:=fmOpenRead or fmShareDenyNone else m:=fmCreate; FStream:=TFileStreamUTF8.Create(AFileName, m); inherited Create(FStream, AEscapeLineFeeds); FileClose(FStream.Handle); THandle(pointer(@FStream.Handle)^):=0; end; destructor TIniFileUtf8.Destroy; begin inherited Destroy; FStream.Free; end; procedure TIniFileUtf8.UpdateFile; var h: THANDLE; begin if FileExistsUTF8(FFileName) then h:=FileOpenUTF8(FFileName, fmOpenWrite or fmShareDenyWrite) else h:=FileCreateUTF8(FFileName); if h = THandle(-1) then raise Exception.Create('Unable to write to INI file.' + LineEnding + SysErrorMessageUTF8(GetLastOSError)); THandle(pointer(@FStream.Handle)^):=h; try inherited UpdateFile; FStream.Size:=FStream.Position; finally FileClose(FStream.Handle); THandle(pointer(@FStream.Handle)^):=0; end; end; // --------------------------------------------- function GetTimeZoneDelta: TDateTime; {$ifdef windows} var t: TIME_ZONE_INFORMATION; res: dword; {$endif} begin Result:=0; {$ifdef windows} res:=GetTimeZoneInformation(t); if res<> TIME_ZONE_ID_INVALID then begin case res of TIME_ZONE_ID_STANDARD: Result:=-t.StandardBias; TIME_ZONE_ID_DAYLIGHT: Result:=-t.DaylightBias; end; Result:=(-t.Bias + Result)/MinsPerDay; end; {$endif} {$ifdef unix} Result:=Tzseconds/SecsPerDay; {$endif} end; procedure ShowTaskbarButton; begin {$ifdef mswindows} ShowWindow(TWin32WidgetSet(WidgetSet).AppHandle, SW_SHOW); {$else} Application.MainForm.Visible:=True; {$endif mswindows} end; procedure HideTaskbarButton; begin {$ifdef mswindows} ShowWindow(TWin32WidgetSet(WidgetSet).AppHandle, SW_HIDE); {$else} Application.MainForm.Visible:=False; {$endif mswindows} end; function IsTaskbarButtonVisible: boolean; begin {$ifdef mswindows} Result:=IsWindowVisible(TWin32WidgetSet(WidgetSet).AppHandle); {$else} Result:=Application.MainForm.Visible; {$endif mswindows} end; {$ifdef unix} function UnixOpenURL(const FileName: String):Integer; var WrkProcess: TProcess; cmd, fn: String; begin Result:=-1; cmd:=FindDefaultExecutablePath('xdg-open'); if cmd = '' then begin cmd:=FindDefaultExecutablePath('gnome-open'); if cmd = '' then begin cmd:=FindDefaultExecutablePath('kioclient'); if cmd <> '' then cmd:=cmd + ' exec' else begin cmd:=FindDefaultExecutablePath('kfmclient'); if cmd = '' then exit; cmd:=cmd + ' exec'; end; end; end; fn:=FileName; if Pos('://', fn) > 0 then fn:=StringReplace(fn, '#', '%23', [rfReplaceAll]); WrkProcess:=TProcess.Create(nil); try WrkProcess.Options:=[poNoConsole]; WrkProcess.CommandLine:=cmd + ' "' + fn + '"'; WrkProcess.Execute; Result:=WrkProcess.ExitStatus; finally WrkProcess.Free; end; end; {$endif unix} function OpenURL(const URL, Params: string): boolean; {$ifdef mswindows} var s, p: widestring; {$endif mswindows} begin {$ifdef mswindows} s:=UTF8Decode(URL); p:=UTF8Decode(Params); if Win32Platform = VER_PLATFORM_WIN32_NT then Result:=ShellExecuteW(0, 'open', PWideChar(s), PWideChar(p), nil, SW_SHOWNORMAL) > 32 else Result:=ShellExecuteA(0, 'open', PChar(ansistring(s)), PChar(ansistring(p)), nil, SW_SHOWNORMAL) > 32; {$endif mswindows} {$ifdef darwin} Result:=fpSystem('Open "' + URL + '"') = 0; {$else darwin} {$ifdef unix} Result:=UnixOpenURL(URL) = 0; {$endif unix} {$endif darwin} end; var BusyCount: integer = 0; procedure AppBusy; begin Inc(BusyCount); Screen.Cursor:=crHourGlass; end; procedure AppNormal; begin Dec(BusyCount); if BusyCount <= 0 then begin BusyCount:=0; Screen.Cursor:=crDefault; end; end; procedure ForceAppNormal; begin BusyCount:=0; AppNormal; end; {$ifdef mswindows} procedure AllowSetForegroundWindow(dwProcessId: DWORD); type TAllowSetForegroundWindow = function(dwProcessId: DWORD): BOOL; stdcall; var _AllowSetForegroundWindow: TAllowSetForegroundWindow; begin _AllowSetForegroundWindow:=TAllowSetForegroundWindow(GetProcAddress(GetModuleHandle('user32.dll'), 'AllowSetForegroundWindow')); if Assigned(_AllowSetForegroundWindow) then _AllowSetForegroundWindow(dwProcessId); end; {$endif mswindows} function CompareFilePath(const p1, p2: string): integer; begin {$ifdef windows} Result:=AnsiCompareText(UTF8Decode(p1), UTF8Decode(p2)); {$else} Result:=AnsiCompareStr(UTF8Decode(p1), UTF8Decode(p2)); {$endif windows} end; function GetCmdSwitchValue(const Switch: string): string; var i, len: integer; s, ss: string; begin Result:=''; ss:='--' + Switch + '='; len:=Length(ss); for i:=1 to ParamCount do begin s:=ParamStrUTF8(i); if Copy(s, 1, len) = ss then begin Result:=Copy(s, len + 1, MaxInt); break; end; end; end; {$ifdef CALLSTACK} function GetLastExceptionCallStack: string; function GetAddrInfo(addr: pointer): string; var func, source : shortstring; line : longint; begin GetLineInfo(ptruint(addr), func, source, line); Result:='$' + HexStr(ptruint(addr), sizeof(ptruint) * 2); if func<>'' then Result:=Result + ' ' + func; if source<>'' then begin if func<>'' then Result:=Result + ', '; if line<>0 then Result:=Result + ' line ' + IntToStr(line); Result:=Result + ' of ' + source; end; end; var I: Integer; Frames: PPointer; begin Result := GetAddrInfo(ExceptAddr); Frames := ExceptFrames; for I := 0 to ExceptFrameCount - 1 do Result := Result + LineEnding + GetAddrInfo(Frames[I]); end; {$endif CALLSTACK} procedure CenterOnParent(C: TControl); var R: TRect; begin R:=C.BoundsRect; R.Left:=(C.Parent.ClientWidth - C.Width) div 2; R.Top:=(C.Parent.ClientHeight - C.Height) div 2; C.BoundsRect:=R; end; {$PUSH} {$RANGECHECKS OFF} function Base32Decode(const s: ansistring): ansistring; var optr, len, bcnt: integer; Output: PByteArray; Input: PAnsiChar; w: word; c: ansichar; b: byte; begin len:=Length(s); Input:=PAnsiChar(s); SetLength(Result, len); Output:=PByteArray(PAnsiChar(Result)); optr:=0; w:=0; bcnt:=0; while len > 0 do begin repeat c:=Input^; if c = '=' then begin len:=0; break; end; if c in ['A'..'Z'] then b:=Ord(c) - Ord('A') else if c in ['2'..'7'] then b:=Ord(c) - 24 else raise Exception.Create('Invalid base32 string.'); w:=(w shl 5) or b; Inc(bcnt, 5); Inc(Input); Dec(len); until (bcnt >= 8) or (len <= 0); if bcnt < 8 then Output^[optr]:=w shl (8 - bcnt) else begin Dec(bcnt, 8); Output^[optr]:=w shr bcnt; end; Inc(optr); end; SetLength(Result, optr); end; {$POP} procedure EnableControls(AEnable: boolean; const AControls: array of TControl); var i: integer; C: TControl; begin for i:=Low(AControls) to High(AControls) do begin C:=AControls[i]; C.Enabled:=AEnable; if (C is TCustomEdit) or (C is TCustomListBox) or (C is TCustomComboBox) then begin if AEnable then C.Color:=clDefault else C.Color:=clBtnFace; end; end; end; finalization {$ifdef windows} FreeAndNil(FParams); {$endif windows} end. TransGUI/addtracker.lfm0000644000000000000000000000264312256577645014044 0ustar rootrootinherited AddTrackerForm: TAddTrackerForm Left = 411 Height = 93 Top = 283 Width = 533 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Add tracker' ClientHeight = 93 ClientWidth = 533 OnCreate = FormCreate Position = poMainFormCenter object Buttons: TButtonPanel[0] Left = 8 Height = 26 Top = 59 Width = 517 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end object Panel1: TPanel[1] Left = 8 Height = 43 Top = 8 Width = 517 Align = alClient BorderSpacing.Around = 8 BevelOuter = bvNone ClientHeight = 43 ClientWidth = 517 TabOrder = 0 object txTrackerURL: TLabel Left = 0 Height = 14 Top = 0 Width = 113 Caption = 'Tracker announce URL:' ParentColor = False end object edTracker: TEdit Left = 0 Height = 21 Top = 20 Width = 516 Anchors = [akTop, akLeft, akRight] TabOrder = 0 end end end TransGUI/movetorrent.pas0000644000000000000000000000461212261763702014312 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit MoveTorrent; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ButtonPanel, ExtCtrls, BaseForm; resourcestring SNoTorrentDir = 'No torrent location was specified.'; SSelectFolder = 'Select torrent location'; type { TMoveTorrentForm } TMoveTorrentForm = class(TBaseForm) btBrowse: TButton; Buttons: TButtonPanel; cbMoveData: TCheckBox; edTorrentDir: TComboBox; Panel1: TPanel; txTorrentDir: TLabel; procedure btBrowseClick(Sender: TObject); procedure btOKClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } end; implementation uses main; { TMoveTorrentForm } procedure TMoveTorrentForm.btOKClick(Sender: TObject); begin edTorrentDir.Text:=Trim(edTorrentDir.Text); if edTorrentDir.Text = '' then begin edTorrentDir.SetFocus; MessageDlg(SNoTorrentDir, mtError, [mbOK], 0); exit; end; ModalResult:=mrOK; end; procedure TMoveTorrentForm.btBrowseClick(Sender: TObject); var s: string; begin s:=MainForm.SelectRemoteFolder(edTorrentDir.Text, SSelectFolder); if s <> '' then edTorrentDir.Text:=s; end; procedure TMoveTorrentForm.FormCreate(Sender: TObject); begin Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@btOKClick; end; initialization {$I movetorrent.lrs} end. TransGUI/transgui.lpi0000644000000000000000000001620512261763702013564 0ustar rootroot TransGUI/history.txt0000644000000000000000000005344112261770432013465 0ustar rootroot5.0.1 (Jan 04, 2014) [*] Include OpenSSL files in Windows setup. Issue #711. [-] Disable the "Save as" field when adding magnet links. Issue #725. [-] Incorrect color alternation when sorting the files list by any column except the "File name". 5.0 (Nov 05, 2013) [+] An option to change a torrent's file/folder name during addition (Transmission 2.80+ only). Issue #498. [+] Renaming of existing torrents, files and folders (Transmission 2.80+ only). Issue #498. [+] Display files list as a tree in the Files tab. Issue #572. [+] Show check boxes in the Files tree to allow more user friendly selection of files to be downloaded. [+] Import trackers from a duplicate torrent. Issue #522. [+] Option "Average out transfer speeds to eliminate fluctuations". ETA is much more reliable now. Issue #193. [+] When a completed torrent is double clicked, its file/folder is opened if possible. Issue #675. [+] Free disk space is updated when changing a destination folder in the Add torrent window (Transmission 2.80+ only). Issue #665. [+] Options to control handling of .torrent files and magnet links on Windows. Issue #645. [+] Added column "Seeding time". Also display seeding time in the "Share ratio" field in the torrent info pane. Issue #591. [+] Option "Show notifications in tray icon". Issue #590. [+] Display the number of files in the title of the Files tab. [+] Strike out torrents in the list during deletion while waiting for action to complete. [+] Option "RPC path". Issue #667. [+] Option "Ask for password". Issue #152. [+] Added many keyboard shortcuts. [*] Removed flickering of torrent details while changing current torrent. [*] Speed up rendering of torrent and file lists. [*] Update window contents while splitters are moving. [*] Use system default date/time format on Linux and OS X. Issue #624. [*] Restore display of a queue position for finished torrents. Issue #568. [*] Enabled the "Close to tray" option on Mac OS X and Ubuntu with Unity. It simply minimizes the application when the main window is closed. Issue #686. [*] Align the Ratio column to the right. Issue #668. [*] Increased drop down count for the folders combo box to 20. [*] Display ETA in more vague way. [-] The error state for stopped torrents was ignored. Issue #659. [-] Fixed garbage at the end of transgui.ini file. Issue #609. [-] Crash when the "Automatically add torrent links from the clipboard" is enabled in some cases. Issue #613. [-] Update statistics even if no torrents are selected. Issue #664. [-] Properly close the application on Windows log off. Issue #680. [-] Proper handling of the case when the previous application run has been terminated unexpectedly. Issue #680. [-] Incorrect current UI language detection. [-] The "Invalid argument" error or tracker URLs corruption when editing trackers in the torrent properties windows on Mac OS X. Issue #666. [-] Error "JSON element 'torrent-added' not found" when adding a duplicate torrent to Transmission 2.80+. Issue #683. 4.1 (Oct 06, 2012) [+] Option "Automatically add torrent links from the clipboard". Issue #571. [+] Statistics tab. Issue #157. [+] SOCKS 5 proxy support. Patch by Evgeny Rex. Issue #111. [+] Options to hide Filter pane, Info pane and status bar. Issue #98. [+] The "Copy file path to clipboard" command. Issue #326. [*] Greatly improved speed when handling large torrent and files lists. Issue #575. [*] Speedup adding torrents with many files (90K+). Issue #600. [*] Support for retina displays on OS X. Issue #594. [-] Reposition main window if the saved window position outside visible area. Issue #423. [-] Do not hide the Trackers tab when opening torrent properties if using old versions of Transmission. Issue #567. [-] Do not show tray icon if "Minimize to tray" and "Tray icon always visible" turned off. Issue #569. [-] Do not show queue position for finished (stopped) torrents. Issue #568. [-] Do not display tracker errors for stopped torrents. 4.0.3 (May 10, 2012) [*] Show a wait window when adding a new torrent while the main window is hidden. [*] Do not show torrent contents when adding magnet links. Issue #560. [*] Show "?" as a torrent size if the torrent meta data is not downloaded yet. Issue #560. [-] The "MainForm:TMainForm Can not focus" error when adding a new torrent while the application is closed to tray. Issue #553. [-] Crash when adding nonexisting torrent file. Issue #555. 4.0.2 (Apr 09, 2012) [*] Always search on torrent names when a letter is pressed. Issue #314. [-] A running instance of the application is not restored, when launching a new instance of the application. [-] Regression. Unable to add/edit trackers in v4.0. Issue #548. [-] Do not jump to the first column on key press. Issue #549. 4.0.1 (Apr 08, 2012) [-] Incorrect folder filtering when tracker grouping is disabled. Issue #545. 4.0 (Apr 07, 2012) [+] Added WebMoney and restored PayPal donation options. It is the right time to donate! :) [+] Support for torrent queues in Transmission 2.40+. Issue #445. [+] Support for adding several torrents at once. Issue #508. [+] Display a torrent name in the title of the Add torrent window. Issue #533. [+] Filer category "Error". Issue #420. [+] Trackers can be bulk edited in the torrent properties window. Issue #389. [+] Handle custom "rpc-url". Issue #523. [+] Option "User interface scaling". Issue #505. [+] Options to show/hide folder and tracker grouping. Issue #265. [+] Checking for a new version. Issue #361. [+] Searching in the torrent list by pressing letter keys. Issue #314. [+] Swedish translation by pesegolsson. [+] Finnish translation by thomazuu. [*] Unicode support for file operations on Windows. Issue #454. [*] Handle magnet links on Mac OS X. Issue #403. [*] Do not show the main application window when adding torrents, while the application is minimized to tray. Issue #455. [*] Allow specifying empty blocklist URL. Issue #543. [*] Caching of writes to the settings file. [-] The application hangs on start on Ubuntu 12.04 (with libglib 2.31+). Issue #489. [-] Do not change the last download dir when setting torrent's location. Issue #482. [-] Stall during auto reconnection on the "Connecting to daemon..." stage. [-] Unable to click again on a grid's header after the first click, unless you move the mouse cursor. [-] Scaling of the status bar on high DPI settings. Issue #495. [-] The "Access violation" error when connecting to a daemon using SSL in some cases. Issue #544. 3.2 (Nov 01, 2011) ------------------ [+] Support for Transmission 2.40+. [+] Support for Ubuntu 11.x. Issue #466, #470. [+] Support for high DPI display settings. [+] Save path history in the Set location window. Issue #422. [*] Use system defined font size. [*] Use system defined wheel scroll speed. Issue #306. [*] Use case insensitive reverse path mapping on Windows. [*] Accept HTTPS protocol when adding torrent via a web link. Issue #474. [*] When opening a folder/url on linux, use the following apps to do that: xdg-open, gnome-open, kioclient, kfmclient. Issue #355. [-] The "Connection refused" error, when creating a new connection on Mac OS X. Issue #426. [-] Tray icon menu items are not updated on Mac OS X. Issue #415. [-] The "Alternate speeds" flag is not displayed in menus on linux. [-] Overlapping of sizing grip on Mac OS X. [-] Tray icon menu transparency problem on linux. [-] Minimize to tray problems on linux. 3.1 (Apr 09, 2011) ------------------ [+] Support for Transmission 2.30+. [+] Implemented a folder browsing window for choosing torrent destination folder. [+] Option to Windows setup to handle magnet links by Transmission Remote GUI. [+] The "Enable µTP" daemon option. [+] Average download speed to torrent details. [+] Polish translation by piotr.tytus.dobrowolski. [+] Traditional Chinese (Taiwan) translation by thelordkao. [*] Hide popup menu items for tracker editing, if a transmission daemon does not support it. Issue #396. [*] Display "-" when disk free space can not be obtained. [-] Properly handle infinite ETA when sorting by the ETA column. Issue #397. [-] The "Unknown Run-Time error: 202" error when clicking on the filter separator, when there are no torrents in the list. Issue #401. 3.0 (Feb 27, 2011) ------------------ [+] Added the "Refresh interval when minimized" option. Issue #313. [+] Trackers can be added, edited and removed. Issue #275. [+] Added the following transmission options: Disk cache size, Blocklist URL, Enable Local Peer Discovery. [+] Added the "Stop seeding when inactive" option to the daemon and torrent options. [+] Implemented setting of alternate speed limits. Issue #166. [+] Added speed limit popup menu to the status bar. Issue #82. [+] Added speed limit menu items to the tray icon menu. Issue #339. [+] Added a toolbar button to enable/disable alternate bandwidth settings. Issue #325. [+] Show disk free space in the status bar. [+] Show torrent contents as a tree in the Add torrent window. Issue #81. [+] Show free disk space in the Add torrent window. Issue #354. [+] Show total size of selected files in the Add torrent window. [+] Remember size of the Add torrent window. [+] Added the "Connect" toolbar button with a drop-down list of available connections. [+] Added the "Disconnect" menu item. Issue #283. [+] Added Belarusian translation by rohierd. [+] Added Korean translation by kpsman. [+] Added Chinese translation by xtfllbl. [+] Added Ukrainian translation by PavelDudn. [+] Added Italian translation by ilnero71. [+] Added Greek translation by lamprakisa. [*] Separated connection settings from the program's options. [*] Improved handling of connection settings. [*] Use system button order for the OK/Cancel buttons. [*] Open the main window on single mouse click on the tray icon. Issue #372. [-] Increased time out for the "Update blocklist" command. Issue #353. [-] Fixed loading of OpenSSL files on some linux systems. Issue #369. [-] Prevent resources leak on Mac OS X. Issue #382. 2.2 (Nov 11, 2010) ------------------ [+] Added a wait animation to the right side of the toolbar. The animation is shown when a daemon response takes more than 1 second. [+] Added SSL support. Issue #227. [+] Verification of selected torrents. Issue #312. [+] Allow changing properties for multiple selected torrents. Issue #322. [+] Added option "Delete a .torrent file after after a successful addition". Patch by reardonia with some modifications. Issue #329. [+] Added a new command line option. --home= : Specifies a home directory for the program. All program's settings are stored in the home directory. You can run multiple instances of the program by specifying different home directories. [+] Added Norwegian translation by anoteng. Issue #293. [+] Added German translation by dboxtester. Issue #302. [+] Added Romanian translation by contactdanielro. Issue #340. [*] Speedup disconnection. [*] Better connection errors handling. [*] Added 2 separate items to the tray icon menu: Show and Hide. Double click on menu shows the application. [*] Use the Command key for multi-selection on MacOS. Issue #344. [*] Do not ask an Administrator password during installation on Mac OS X. [*] Resolve names using hosts file on unix. [-] Pressing the Delete key when editing the search/filter asks to delete the currently selected torrent. Issue #330. [-] Files with 0 bytes length always shown as 0% downloaded in the file status panel. Issue #321. [-] Changing torrent selection using keyboard did not work properly after changing sort order. Issue #338. [-] The dialog for downloading torrent is not brought up over GUI if GUI was minimized and restored. Issue #336. [-] Properly restore the running instance of application when adding a new torrent via file association on Windows. [-] Fixed opening files/folders with spaces on Linux. Issue #291. [-] Selection was not visible on some GTK themes. Issue #320. 2.1.1 (Aug 26, 2010) -------------------- [+] Added the -hidden command line parameter to start the program hidden. Only the program's tray icon will be visible. Issue #36. [*] "Minimize to tray" and "Close to tray" options are enabled and work properly on Linux. Issue #285. [*] Speed optimizations. The program can now easily handle thousands torrents. Issue #281. [*] Improved a hint on a partial text. [-] Fixed wrong data in columns in some cases. Issue #273. 2.1 (Aug 24, 2010) ------------------ [+] Ctrl+A selects all torrents/files. Issue #259. [+] Added the "Stopped" filter for torrents. Issue #268. [+] Support for adding magnet and URL links via the command line. Issue #261. [+] French, Spanish, Latvian, Portuguese Brazilian translations. [*] Use smaller font on Mac OS X. Issue #267. [*] Use proxy settings for GeoIP database download. Issue #278. [-] Improper update of the peer list. Issue #264. [-] Incorrect encoding of file names in the Add torrent window. [-] Problem to "Open containing folder" from the torrents list for some path mappings. Issue #260. [-] Improper filtering by path in some cases. Issue #262. 2.0 (Aug 11, 2010) ------------------ [+] Implemented the "Set location" command for torrents. Issue #110. [+] Support for adding a torrent via a URL or a magnet link. Issue #106, Issue #161. [+] Implemented filtering by download dir. It can be used as torrent labels. Issue #23. [+] Implemented changing of torrents priority. Issue #215. [+] Multiselect in the torrents list. [+] Added the "Reannounce (get more peers)" command for torrents. Issue #78. [+] Implemented the "Update blocklist" function. Issue #70. [+] Implemented per torrent seed ratio. Issue #201. [+] Added the following daemon options: "Enable blocklist", "Add .part extension to incomplete files", "Directory for incomplete files". [+] Added the "Priority", "Size to download" and "ID" columns to the torrents list. Issue #228, #245, #255. [+] Progress bars in the torrents and files lists are available on all platforms. [+] Sorting in the trackers, peers, files lists. [+] The columns setup window can be invoked for trackers, peers and files lists. [+] Icons for priorities. [+] Portable mode support. If the program finds the transgui.ini file in the same folder as the binary file, then it will store all configuration and data files in the program's folder, instead of the folder in a user profile. [+] Croatian translation by Tomislav Spoljaric. [+] Hungarian translation by zgyivi. [+] Czech translation by schunkac. [+] Dutch translation by Omkomarijke. [+] Danish translation by wilhjelm. [*] Disabled the "Close to tray" option on non-Windows platforms. [*] The "Open containing folder" command now highlights the active file in Windows Explorer window. Issue #145. [-] Fixed sorting by the Status column. Issue #253. [-] Fixed the "Invalid floating point operation" error when clicking on a torrent added via a magnet link. Issue #223. [-] Fixed the "Access violation" error when switching to the Trackers tab on old version of Transmission. Issue #231. [-] The "Open containing folder" command now properly handle torrents with sub-folders. Issue #222. [-] Lot of other bugs were fixed. 1.4 (May 06, 2010) ------------------ [+] Localization support by Alex Cherednichenko, aka Alex7Che. See http://code.google.com/p/transmisson-remote-gui/wiki/Localization page if you want to make a translation. [+] Russian translation by Alex Cherednichenko, aka Alex7Che. [+] Trackers page. [+] Torrent files can be dragged and dropped to the program window. [-] Lot of Mac OS X specific bugs were fixed. [-] Fixed the "Could not convert variant of type (Null) into type (Int64)" error when the "Seeds" column is hidden. Issues #172 and #187. [-] Fixed tracker errors handling for newer versions of the daemon. [-] Do not jump to the selected torrent when the list refreshes om linux. Issue #192. [-] Issue #177: Sorting by "Status" doesn't differentiate "Finished" and "Stopped". [-] Issue #212: Sorting by Seeds does not work properly. 1.3.2 (Jan 09, 2010) ------------------ [-] Fixed x10 progress percentage in some cases. Issue #171. [-] Fixed "List index (0) out of bounds" error in some cases. Issue #170. 1.3.1 (Dec 20, 2009) ------------------ [*] Support for Transmission 1.80. Issue #147. [-] Fixed 10x progress percentage in some rare cases. 1.3 (Oct 09, 2009) ------------------ [+] Alternate lines color in torrent,peer and file lists (Windows only). Issue #24. [-] Priority was set for wrong files. Issue #128. [-] Fixed error "JSON element 'downloadDir' not found." with Transmission 1.4x. Issue #132. [-] "Open" menu item was active if more than one file in the list was selected. Issue #127. [-] Crash when building pieces map for large torrents. Issue #130. 1.2 (Sep 07, 2009) ------------------ [+] Draw progress bar in torrents and files lists (works on Windows only). [+] Show torrent pieces map on General page when Transmission 1.60 or later is connected. Issue #99. [+] Added the following daemon options available in Transmission 1.60 and later: Peek random port on daemon launch, Enable DHT, Seed ratio, Port testing. [+] Support for connection profiles. [+] Ability to specify remote to local path mappings in program's options. [+] Ability to open file and open containing folder when path mappings are specified. [+] Added ini parameter MaxFoldersHistory to [Interface] section of .ini file. Issue #101. [-] Select items using right mouse click in torrent and files lists on GTK2. Issue #84. [-] Reconnect window was shown if torrent deletion took too much time. Issue #79. [-] Fixed connection error "Can't assign requested address" on Windows 2000 or older. Issue #90. [-] Restored Win9x support. [-] Fixed handling of international character in torrent path on General info page. Issue #96. [-] Double click is required to select/deselect files in Add torrent window. Issue #94 [-] Do not transform & character to underscore in labels. Issue #104. [-] More correct sorting on torrent name when there are several torrents with the same name. Issue #103. [-] Fixed changing of the following options for newer versions of transmission: Incoming port, Enable peer exchange, Global peer limit. [-] Fixed checking and un-checking files using Space key in Add torrent window. [-] Fixed red error images on successfully running torrents on Transmission 1.74+. Issue #121. 1.1 (May 21, 2009) ------------------ [+] Remove torrent and all associated data function. Issue #67. [+] Quick search for torrents list. Issue #21. [+] Automatically reconnect when connection was lost. Issue #9, Issue #41. [+] Torrent comment becomes clickable if it contains URL address. [*] Display full torrent path in detailed info pane. [*] Made torrent name column always visible. Issue #72. [*] Improved UI response time on Linux. [-] Wrong torrent name shown in dialog box when deleting/verifying torrent if filtering is active. Issue #69. [-] Window not maximized again after minimization on Windows. Issue #46. [-] Properly display date/time during daylight time. [-] Completed percentage of 99.99% are displayed as 100.0%. Noticed by leshekb. 1.0 (May 12, 2009) ------------------ [+] Added option 'Show parameters window when adding a new torrent'. [*] Support for the latest version of Transmission. Issues #45, #50, #60, #64. [*] Faster update of torrent details. [*] Transmission Remote GUI can be compiled as native Windows x64 application. [-] Several fixes to Unicode support. Issues #49, #56. [-] Strip only common part of path in case if a torrent contains multiple folders. Issue #22. [-] Wrong torrent name in delete confirmation window if name column is not the first in the list. Issue #34. [-] When opening several .torrent files via file association, multiple instances of the application are started. Issue #44. [-] Selected/focused mix-up in torrents list. Issue #62. [-] Error during updating of GeoIP database in some cases. [-] Several fixes for 64-bit targets. 0.95 beta (Jan 21, 2009) ------------------------ [+] Proxy support. [+] Show column's sort direction. [*] Resolve peer's country in real time since it is quick. [-] Fixed .torrent association if user home path contains non English letters. Bug #12. [-] Some images were displayed on list header after column moving on Vista. Bug #14. [-] Wrong sorting after columns were moved. Bug #15. [-] Splitter positions were reset after restoring from tray icon. Bug #16. [-] Enumeration of completed torrents. Bug #20. [-] Peers view for Transmission 1.40. 0.94 beta (Jan 18, 2009) ------------------------ [+] Columns setup for torrents list. [+] New columns to torrents list: Downloaded, Uploaded, Tracker, Tracker status, Added on, Completed on, Last active. [+] New entries on General info page: Tracker, Added on, Completed on, Last active. [+] Changing priority for multiple selected files. [*] Quicker shutdown of the application. [-] Buggy selection of files to download in add torrent window. [-] Wrong info was displayed in General info page for filtered torrents. [-] Allow @ character in password. [-] Do not require administrator privileges for Windows setup. 0.93 beta (Dec 30, 2008) ------------------------ [-] Unable to add new torrent. GeoIP database is downloading instead of this. 0.92 beta (Dec 30, 2008) ------------------------ [+] Torrent filtering by status and by tracker. [+] Resolving of peer's host by IP. [+] Resolving of peer's country by IP. [+] Displaying of peer's country flag. [*] Store parameters for add torrent window per each host. [*] Added "WARNING: Transmission daemon will crash if non-existent folder is specified." to add torrent window. [-] Some bug fixes. 0.91 beta (Dec 26, 2008) ------------------------ [+] Download folder and peer limit can be specified when adding new torrent. [+] Tray icon. [+] Balloon tooltip when torrent download is complete. [+] Added "Start all" and "Stop all" items into "Torrent" menu. [+] Torrent file name can be passed as command line parameter. [+] Peer limit can be changed in torrent properties. [+] Windows installer. [*] Only single instance of the application is allowed. [*] Improved startup speed on Linux. [-] Skipping of files to download was not possible during torrent addition. [-] "Access violation" error after removing a torrent. [-] Issues with non-english charsets. 0.9 beta -------- [+] Initial version. TransGUI/COPYING.txt0000644000000000000000000006347612231246163013101 0ustar rootroot GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! TransGUI/trcomp.lpk0000644000000000000000000000205311427210747013231 0ustar rootroot TransGUI/movetorrent.lfm0000644000000000000000000000377012256577645014326 0ustar rootrootinherited MoveTorrentForm: TMoveTorrentForm Left = 354 Height = 126 Top = 193 Width = 561 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Torrent data location' ClientHeight = 126 ClientWidth = 561 OnCreate = FormCreate Position = poMainFormCenter object Buttons: TButtonPanel[0] Left = 8 Height = 36 Top = 82 Width = 545 BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 1 Spacing = 8 ShowButtons = [pbOK, pbCancel] end object Panel1: TPanel[1] Left = 8 Height = 74 Top = 8 Width = 545 Align = alClient BorderSpacing.Left = 8 BorderSpacing.Top = 8 BorderSpacing.Right = 8 BevelOuter = bvNone ClientHeight = 74 ClientWidth = 545 Constraints.MinWidth = 545 TabOrder = 0 object txTorrentDir: TLabel Left = 0 Height = 14 Top = 0 Width = 145 Caption = 'New location for torrent data:' ParentColor = False end object edTorrentDir: TComboBox Left = 0 Height = 21 Top = 20 Width = 449 Anchors = [akTop, akLeft, akRight] DropDownCount = 20 ItemHeight = 13 TabOrder = 0 end object cbMoveData: TCheckBox Left = 0 Height = 17 Top = 47 Width = 285 Caption = 'Move torrent data from current location to new location' Checked = True State = cbChecked TabOrder = 2 end object btBrowse: TButton Left = 454 Height = 23 Top = 19 Width = 91 Anchors = [akTop, akRight] Caption = 'Browse...' OnClick = btBrowseClick TabOrder = 1 end end end TransGUI/Makefile0000644000000000000000000021051212026633637012661 0ustar rootroot# # Don't edit, this file is generated by FPCMake Version 2.0.0 [2011/12/25] # default: all MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-solaris x86_64-darwin x86_64-win64 x86_64-embedded arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian powerpc64-linux powerpc64-darwin powerpc64-embedded avr-embedded armeb-linux armeb-embedded mipsel-linux BSDs = freebsd netbsd openbsd darwin UNIXs = linux $(BSDs) solaris qnx haiku LIMIT83fs = go32v2 os2 emx watcom OSNeedsComspecToRunBatch = go32v2 watcom FORCE: .PHONY: FORCE override PATH:=$(patsubst %/,%,$(subst \,/,$(PATH))) ifneq ($(findstring darwin,$(OSTYPE)),) inUnix=1 #darwin SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH))) else ifeq ($(findstring ;,$(PATH)),) inUnix=1 SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH))) else SEARCHPATH:=$(subst ;, ,$(PATH)) endif endif SEARCHPATH+=$(patsubst %/,%,$(subst \,/,$(dir $(MAKE)))) PWD:=$(strip $(wildcard $(addsuffix /pwd.exe,$(SEARCHPATH)))) ifeq ($(PWD),) PWD:=$(strip $(wildcard $(addsuffix /pwd,$(SEARCHPATH)))) ifeq ($(PWD),) $(error You need the GNU utils package to use this Makefile) else PWD:=$(firstword $(PWD)) SRCEXEEXT= endif else PWD:=$(firstword $(PWD)) SRCEXEEXT=.exe endif ifndef inUnix ifeq ($(OS),Windows_NT) inWinNT=1 else ifdef OS2_SHELL inOS2=1 endif endif else ifneq ($(findstring cygdrive,$(PATH)),) inCygWin=1 endif endif ifdef inUnix SRCBATCHEXT=.sh else ifdef inOS2 SRCBATCHEXT=.cmd else SRCBATCHEXT=.bat endif endif ifdef COMSPEC ifneq ($(findstring $(OS_SOURCE),$(OSNeedsComspecToRunBatch)),) ifndef RUNBATCH RUNBATCH=$(COMSPEC) /C endif endif endif ifdef inUnix PATHSEP=/ else PATHSEP:=$(subst /,\,/) ifdef inCygWin PATHSEP=/ endif endif ifdef PWD BASEDIR:=$(subst \,/,$(shell $(PWD))) ifdef inCygWin ifneq ($(findstring /cygdrive/,$(BASEDIR)),) BASENODIR:=$(patsubst /cygdrive%,%,$(BASEDIR)) BASEDRIVE:=$(firstword $(subst /, ,$(BASENODIR))) BASEDIR:=$(subst /cygdrive/$(BASEDRIVE)/,$(BASEDRIVE):/,$(BASEDIR)) endif endif else BASEDIR=. endif ifdef inOS2 ifndef ECHO ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(ECHO),) ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(ECHO),) ECHO=echo else ECHO:=$(firstword $(ECHO)) endif else ECHO:=$(firstword $(ECHO)) endif endif export ECHO endif ifndef FPC ifdef PP FPC=$(PP) endif endif ifndef FPC FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH)))) ifneq ($(FPCPROG),) FPCPROG:=$(firstword $(FPCPROG)) ifneq ($(CPU_TARGET),) FPC:=$(shell $(FPCPROG) -P$(CPU_TARGET) -PB) else FPC:=$(shell $(FPCPROG) -PB) endif ifneq ($(findstring Error,$(FPC)),) override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH))))) else ifeq ($(strip $(wildcard $(FPC))),) FPC:=$(firstword $(FPCPROG)) endif endif else override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH))))) endif endif override FPC:=$(subst $(SRCEXEEXT),,$(FPC)) override FPC:=$(subst \,/,$(FPC))$(SRCEXEEXT) FOUNDFPC:=$(strip $(wildcard $(FPC))) ifeq ($(FOUNDFPC),) FOUNDFPC=$(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH)))) ifeq ($(FOUNDFPC),) $(error Compiler $(FPC) not found) endif endif ifndef FPC_COMPILERINFO FPC_COMPILERINFO:=$(shell $(FPC) -iVSPTPSOTO) endif ifndef FPC_VERSION FPC_VERSION:=$(word 1,$(FPC_COMPILERINFO)) endif export FPC FPC_VERSION FPC_COMPILERINFO unexport CHECKDEPEND ALLDEPENDENCIES ifndef CPU_TARGET ifdef CPU_TARGET_DEFAULT CPU_TARGET=$(CPU_TARGET_DEFAULT) endif endif ifndef OS_TARGET ifdef OS_TARGET_DEFAULT OS_TARGET=$(OS_TARGET_DEFAULT) endif endif ifndef CPU_SOURCE CPU_SOURCE:=$(word 2,$(FPC_COMPILERINFO)) endif ifndef CPU_TARGET CPU_TARGET:=$(word 3,$(FPC_COMPILERINFO)) endif ifndef OS_SOURCE OS_SOURCE:=$(word 4,$(FPC_COMPILERINFO)) endif ifndef OS_TARGET OS_TARGET:=$(word 5,$(FPC_COMPILERINFO)) endif FULL_TARGET=$(CPU_TARGET)-$(OS_TARGET) FULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE) ifeq ($(CPU_TARGET),armeb) ARCH=arm override FPCOPT+=-Cb else ifeq ($(CPU_TARGET),armel) ARCH=arm override FPCOPT+=-CaEABI else ARCH=$(CPU_TARGET) endif endif ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),) TARGETSUFFIX=$(OS_TARGET) SOURCESUFFIX=$(OS_SOURCE) else ifneq ($(findstring $(OS_TARGET),$(LIMIT83fs)),) TARGETSUFFIX=$(OS_TARGET) else TARGETSUFFIX=$(FULL_TARGET) endif SOURCESUFFIX=$(FULL_SOURCE) endif ifneq ($(FULL_TARGET),$(FULL_SOURCE)) CROSSCOMPILE=1 endif ifeq ($(findstring makefile,$(MAKECMDGOALS)),) ifeq ($(findstring $(FULL_TARGET),$(MAKEFILETARGETS)),) $(error The Makefile doesn't support target $(FULL_TARGET), please run fpcmake first) endif endif ifneq ($(findstring $(OS_TARGET),$(BSDs)),) BSDhier=1 endif ifeq ($(OS_TARGET),linux) linuxHier=1 endif export OS_TARGET OS_SOURCE ARCH CPU_TARGET CPU_SOURCE FULL_TARGET FULL_SOURCE TARGETSUFFIX SOURCESUFFIX CROSSCOMPILE ifdef FPCDIR override FPCDIR:=$(subst \,/,$(FPCDIR)) ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) override FPCDIR=wrong endif else override FPCDIR=wrong endif ifdef DEFAULT_FPCDIR ifeq ($(FPCDIR),wrong) override FPCDIR:=$(subst \,/,$(DEFAULT_FPCDIR)) ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) override FPCDIR=wrong endif endif endif ifeq ($(FPCDIR),wrong) ifdef inUnix override FPCDIR=/usr/local/lib/fpc/$(FPC_VERSION) ifeq ($(wildcard $(FPCDIR)/units),) override FPCDIR=/usr/lib/fpc/$(FPC_VERSION) endif else override FPCDIR:=$(subst /$(FPC),,$(firstword $(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH)))))) override FPCDIR:=$(FPCDIR)/.. ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) override FPCDIR:=$(FPCDIR)/.. ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) override FPCDIR:=$(BASEDIR) ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) override FPCDIR=c:/pp endif endif endif endif endif ifndef CROSSBINDIR CROSSBINDIR:=$(wildcard $(FPCDIR)/bin/$(TARGETSUFFIX)) endif ifneq ($(findstring $(OS_TARGET),darwin iphonesim),) ifeq ($(OS_SOURCE),darwin) DARWIN2DARWIN=1 endif endif ifndef BINUTILSPREFIX ifndef CROSSBINDIR ifdef CROSSCOMPILE ifndef DARWIN2DARWIN BINUTILSPREFIX=$(CPU_TARGET)-$(OS_TARGET)- endif endif endif endif UNITSDIR:=$(wildcard $(FPCDIR)/units/$(TARGETSUFFIX)) ifeq ($(UNITSDIR),) UNITSDIR:=$(wildcard $(FPCDIR)/units/$(OS_TARGET)) endif PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages $(FPCDIR)/packages/base $(FPCDIR)/packages/extra) ifndef FPCFPMAKE ifdef CROSSCOMPILE ifeq ($(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR)))),) FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH)))) ifneq ($(FPCPROG),) FPCPROG:=$(firstword $(FPCPROG)) FPCFPMAKE:=$(shell $(FPCPROG) -PB) ifeq ($(strip $(wildcard $(FPCFPMAKE))),) FPCFPMAKE:=$(firstword $(FPCPROG)) endif else override FPCFPMAKE=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH))))) endif else FPCFPMAKE=$(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR)))) FPMAKE_SKIP_CONFIG=-n export FPCFPMAKE export FPMAKE_SKIP_CONFIG endif else FPMAKE_SKIP_CONFIG=-n FPCFPMAKE=$(FPC) endif endif ifneq ($(findstring $(OS_TARGET),win32,win64),) PROG_VER=$(shell type VERSION.txt) else PROG_VER=$(shell cat VERSION.txt) endif ifeq ($(LAZARUS_DIR),) LAZARUS_DIR=$(strip $(dir $(realpath $(firstword $(strip $(wildcard $(addsuffix /lazbuild$(SRCEXEEXT),$(SEARCHPATH)))))))) lazarus_ok=$(strip $(wildcard $(LAZARUS_DIR)/lazbuild)) ifeq ($(lazarus_ok),) LAZARUS_DIR= lazarus_ok=$(strip $(wildcard $(HOME)/lazarus/lazbuild)) ifneq ($(lazarus_ok),) LAZARUS_DIR=$(HOME)/lazarus endif endif ifeq ($(LAZARUS_DIR),) $(error Lazarus directory was not found. Use LAZARUS_DIR= switch.) endif endif $(info Using Lazarus dir: $(LAZARUS_DIR)) LAZRES=$(LAZARUS_DIR)/tools/lazres$(SRCEXEEXT) LCL_WIDGETSET=gtk2 ifneq ($(findstring $(OS_TARGET),win32,win64),) LCL_WIDGETSET=win32 endif ifneq ($(findstring $(OS_TARGET),darwin),) LCL_WIDGETSET=carbon endif ifeq ($(DEBUG),) COMP_OPT=-O2 -g- -CX -XX -Xs else COMP_OPT=-O- -gs -dCALLSTACK endif ifeq ($(FULL_TARGET),i386-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-go32v2) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-win32) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-os2) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-freebsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-beos) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-haiku) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-netbsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-solaris) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-qnx) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-netware) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-openbsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-wdosx) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-darwin) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-emx) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-watcom) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-netwlibc) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-wince) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-symbian) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-nativent) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-iphonesim) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-freebsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-netbsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-amiga) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-atari) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-openbsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-palmos) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),m68k-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-netbsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-amiga) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-macos) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-darwin) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-morphos) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc-wii) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),sparc-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),sparc-netbsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),sparc-solaris) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),sparc-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),x86_64-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),x86_64-freebsd) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),x86_64-solaris) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),x86_64-darwin) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),x86_64-win64) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),x86_64-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-palmos) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-darwin) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-wince) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-gba) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-nds) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),arm-symbian) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc64-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc64-darwin) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),powerpc64-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),avr-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),armeb-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),armeb-embedded) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),mipsel-linux) override TARGET_PROGRAMS+=transgui endif ifeq ($(FULL_TARGET),i386-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-go32v2) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-win32) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-os2) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-freebsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-beos) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-haiku) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-netbsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-solaris) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-qnx) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-netware) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-openbsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-wdosx) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-darwin) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) -k-macosx_version_min -k10.5 -XR/Developer/SDKs/MacOSX10.5.sdk/ endif ifeq ($(FULL_TARGET),i386-emx) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-watcom) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-netwlibc) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-wince) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-symbian) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-nativent) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-iphonesim) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-freebsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-netbsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-amiga) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-atari) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-openbsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-palmos) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),m68k-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-netbsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-amiga) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-macos) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-darwin) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) -k-macosx_version_min -k10.5 -XR/Developer/SDKs/MacOSX10.5.sdk/ endif ifeq ($(FULL_TARGET),powerpc-morphos) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc-wii) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),sparc-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),sparc-netbsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),sparc-solaris) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),sparc-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),x86_64-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),x86_64-freebsd) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),x86_64-solaris) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),x86_64-darwin) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) -k-macosx_version_min -k10.5 -XR/Developer/SDKs/MacOSX10.5.sdk/ endif ifeq ($(FULL_TARGET),x86_64-win64) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),x86_64-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-palmos) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-darwin) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) -k-macosx_version_min -k10.5 -XR/Developer/SDKs/MacOSX10.5.sdk/ endif ifeq ($(FULL_TARGET),arm-wince) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-gba) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-nds) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),arm-symbian) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc64-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),powerpc64-darwin) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) -k-macosx_version_min -k10.5 -XR/Developer/SDKs/MacOSX10.5.sdk/ endif ifeq ($(FULL_TARGET),powerpc64-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),avr-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),armeb-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),armeb-embedded) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),mipsel-linux) override COMPILER_OPTIONS+=-MObjFPC -dLCL -dLCL$(LCL_WIDGETSET) $(COMP_OPT) endif ifeq ($(FULL_TARGET),i386-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-go32v2) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-win32) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-os2) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-freebsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-beos) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-haiku) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-netbsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-solaris) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-qnx) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-netware) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-openbsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-wdosx) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-darwin) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-emx) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-watcom) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-netwlibc) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-wince) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-symbian) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-nativent) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),i386-iphonesim) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-freebsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-netbsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-amiga) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-atari) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-openbsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-palmos) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),m68k-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-netbsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-amiga) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-macos) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-darwin) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-morphos) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc-wii) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),sparc-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),sparc-netbsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),sparc-solaris) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),sparc-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),x86_64-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),x86_64-freebsd) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),x86_64-solaris) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),x86_64-darwin) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),x86_64-win64) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),x86_64-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-palmos) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-darwin) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-wince) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-gba) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-nds) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),arm-symbian) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc64-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc64-darwin) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),powerpc64-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),avr-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),armeb-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),armeb-embedded) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifeq ($(FULL_TARGET),mipsel-linux) override COMPILER_UNITDIR+=synapse/source/lib json $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET) $(LAZARUS_DIR)/lcl/units/$(FULL_TARGET)/$(LCL_WIDGETSET) $(LAZARUS_DIR)/components/lazutils/lib/$(FULL_TARGET) endif ifdef REQUIRE_UNITSDIR override UNITSDIR+=$(REQUIRE_UNITSDIR) endif ifdef REQUIRE_PACKAGESDIR override PACKAGESDIR+=$(REQUIRE_PACKAGESDIR) endif ifdef ZIPINSTALL ifneq ($(findstring $(OS_TARGET),$(UNIXs)),) UNIXHier=1 endif else ifneq ($(findstring $(OS_SOURCE),$(UNIXs)),) UNIXHier=1 endif endif ifndef INSTALL_PREFIX ifdef PREFIX INSTALL_PREFIX=$(PREFIX) endif endif ifndef INSTALL_PREFIX ifdef UNIXHier INSTALL_PREFIX=/usr/local else ifdef INSTALL_FPCPACKAGE INSTALL_BASEDIR:=/pp else INSTALL_BASEDIR:=/$(PACKAGE_NAME) endif endif endif export INSTALL_PREFIX ifdef INSTALL_FPCSUBDIR export INSTALL_FPCSUBDIR endif ifndef DIST_DESTDIR DIST_DESTDIR:=$(BASEDIR) endif export DIST_DESTDIR ifndef COMPILER_UNITTARGETDIR ifdef PACKAGEDIR_MAIN COMPILER_UNITTARGETDIR=$(PACKAGEDIR_MAIN)/units/$(TARGETSUFFIX) else COMPILER_UNITTARGETDIR=units/$(TARGETSUFFIX) endif endif ifndef COMPILER_TARGETDIR COMPILER_TARGETDIR=. endif ifndef INSTALL_BASEDIR ifdef UNIXHier ifdef INSTALL_FPCPACKAGE INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/fpc/$(FPC_VERSION) else INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/$(PACKAGE_NAME) endif else INSTALL_BASEDIR:=$(INSTALL_PREFIX) endif endif ifndef INSTALL_BINDIR ifdef UNIXHier INSTALL_BINDIR:=$(INSTALL_PREFIX)/bin else INSTALL_BINDIR:=$(INSTALL_BASEDIR)/bin ifdef INSTALL_FPCPACKAGE ifdef CROSSCOMPILE ifdef CROSSINSTALL INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(SOURCESUFFIX) else INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX) endif else INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX) endif endif endif endif ifndef INSTALL_UNITDIR INSTALL_UNITDIR:=$(INSTALL_BASEDIR)/units/$(TARGETSUFFIX) ifdef INSTALL_FPCPACKAGE ifdef PACKAGE_NAME INSTALL_UNITDIR:=$(INSTALL_UNITDIR)/$(PACKAGE_NAME) endif endif endif ifndef INSTALL_LIBDIR ifdef UNIXHier INSTALL_LIBDIR:=$(INSTALL_PREFIX)/lib else INSTALL_LIBDIR:=$(INSTALL_UNITDIR) endif endif ifndef INSTALL_SOURCEDIR ifdef UNIXHier ifdef BSDhier SRCPREFIXDIR=share/src else ifdef linuxHier SRCPREFIXDIR=share/src else SRCPREFIXDIR=src endif endif ifdef INSTALL_FPCPACKAGE ifdef INSTALL_FPCSUBDIR INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME) else INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME) endif else INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION) endif else ifdef INSTALL_FPCPACKAGE ifdef INSTALL_FPCSUBDIR INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME) else INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(PACKAGE_NAME) endif else INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source endif endif endif ifndef INSTALL_DOCDIR ifdef UNIXHier ifdef BSDhier DOCPREFIXDIR=share/doc else ifdef linuxHier DOCPREFIXDIR=share/doc else DOCPREFIXDIR=doc endif endif ifdef INSTALL_FPCPACKAGE INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME) else INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION) endif else ifdef INSTALL_FPCPACKAGE INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc/$(PACKAGE_NAME) else INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc endif endif endif ifndef INSTALL_EXAMPLEDIR ifdef UNIXHier ifdef INSTALL_FPCPACKAGE ifdef BSDhier INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/fpc-$(FPC_VERSION)/$(PACKAGE_NAME) else ifdef linuxHier INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples else INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/fpc-$(FPC_VERSION)/examples/$(PACKAGE_NAME) endif endif else ifdef BSDhier INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION) else ifdef linuxHier INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION) else INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/$(PACKAGE_NAME)-$(PACKAGE_VERSION) endif endif endif else ifdef INSTALL_FPCPACKAGE INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples/$(PACKAGE_NAME) else INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples endif endif endif ifndef INSTALL_DATADIR INSTALL_DATADIR=$(INSTALL_BASEDIR) endif ifndef INSTALL_SHAREDDIR INSTALL_SHAREDDIR=$(INSTALL_PREFIX)/lib endif ifdef CROSSCOMPILE ifndef CROSSBINDIR CROSSBINDIR:=$(wildcard $(CROSSTARGETDIR)/bin/$(SOURCESUFFIX)) ifeq ($(CROSSBINDIR),) CROSSBINDIR:=$(wildcard $(INSTALL_BASEDIR)/cross/$(TARGETSUFFIX)/bin/$(FULL_SOURCE)) endif endif else CROSSBINDIR= endif BATCHEXT=.bat LOADEREXT=.as EXEEXT=.exe PPLEXT=.ppl PPUEXT=.ppu OEXT=.o ASMEXT=.s SMARTEXT=.sl STATICLIBEXT=.a SHAREDLIBEXT=.so SHAREDLIBPREFIX=libfp STATICLIBPREFIX=libp IMPORTLIBPREFIX=libimp RSTEXT=.rst EXEDBGEXT=.dbg ifeq ($(OS_TARGET),go32v1) STATICLIBPREFIX= SHORTSUFFIX=v1 endif ifeq ($(OS_TARGET),go32v2) STATICLIBPREFIX= SHORTSUFFIX=dos IMPORTLIBPREFIX= endif ifeq ($(OS_TARGET),watcom) STATICLIBPREFIX= OEXT=.obj ASMEXT=.asm SHAREDLIBEXT=.dll SHORTSUFFIX=wat IMPORTLIBPREFIX= endif ifeq ($(OS_TARGET),linux) BATCHEXT=.sh EXEEXT= HASSHAREDLIB=1 SHORTSUFFIX=lnx endif ifeq ($(OS_TARGET),freebsd) BATCHEXT=.sh EXEEXT= HASSHAREDLIB=1 SHORTSUFFIX=fbs endif ifeq ($(OS_TARGET),netbsd) BATCHEXT=.sh EXEEXT= HASSHAREDLIB=1 SHORTSUFFIX=nbs endif ifeq ($(OS_TARGET),openbsd) BATCHEXT=.sh EXEEXT= HASSHAREDLIB=1 SHORTSUFFIX=obs endif ifeq ($(OS_TARGET),win32) SHAREDLIBEXT=.dll SHORTSUFFIX=w32 endif ifeq ($(OS_TARGET),os2) BATCHEXT=.cmd AOUTEXT=.out STATICLIBPREFIX= SHAREDLIBEXT=.dll SHORTSUFFIX=os2 ECHO=echo IMPORTLIBPREFIX= endif ifeq ($(OS_TARGET),emx) BATCHEXT=.cmd AOUTEXT=.out STATICLIBPREFIX= SHAREDLIBEXT=.dll SHORTSUFFIX=emx ECHO=echo IMPORTLIBPREFIX= endif ifeq ($(OS_TARGET),amiga) EXEEXT= SHAREDLIBEXT=.library SHORTSUFFIX=amg endif ifeq ($(OS_TARGET),morphos) EXEEXT= SHAREDLIBEXT=.library SHORTSUFFIX=mos endif ifeq ($(OS_TARGET),atari) EXEEXT=.ttp SHORTSUFFIX=ata endif ifeq ($(OS_TARGET),beos) BATCHEXT=.sh EXEEXT= SHORTSUFFIX=be endif ifeq ($(OS_TARGET),haiku) BATCHEXT=.sh EXEEXT= SHORTSUFFIX=hai endif ifeq ($(OS_TARGET),solaris) BATCHEXT=.sh EXEEXT= SHORTSUFFIX=sun endif ifeq ($(OS_TARGET),qnx) BATCHEXT=.sh EXEEXT= SHORTSUFFIX=qnx endif ifeq ($(OS_TARGET),netware) EXEEXT=.nlm STATICLIBPREFIX= SHORTSUFFIX=nw IMPORTLIBPREFIX=imp endif ifeq ($(OS_TARGET),netwlibc) EXEEXT=.nlm STATICLIBPREFIX= SHORTSUFFIX=nwl IMPORTLIBPREFIX=imp endif ifeq ($(OS_TARGET),macos) BATCHEXT= EXEEXT= DEBUGSYMEXT=.xcoff SHORTSUFFIX=mac IMPORTLIBPREFIX=imp endif ifneq ($(findstring $(OS_TARGET),darwin iphonesim),) BATCHEXT=.sh EXEEXT= HASSHAREDLIB=1 SHORTSUFFIX=dwn EXEDBGEXT=.dSYM endif ifeq ($(OS_TARGET),gba) EXEEXT=.gba SHAREDLIBEXT=.so SHORTSUFFIX=gba endif ifeq ($(OS_TARGET),symbian) SHAREDLIBEXT=.dll SHORTSUFFIX=symbian endif ifeq ($(OS_TARGET),NativeNT) SHAREDLIBEXT=.dll SHORTSUFFIX=nativent endif ifeq ($(OS_TARGET),wii) EXEEXT=.dol SHAREDLIBEXT=.so SHORTSUFFIX=wii endif ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),) FPCMADE=fpcmade.$(SHORTSUFFIX) ZIPSUFFIX=$(SHORTSUFFIX) ZIPCROSSPREFIX= ZIPSOURCESUFFIX=src ZIPEXAMPLESUFFIX=exm else FPCMADE=fpcmade.$(TARGETSUFFIX) ZIPSOURCESUFFIX=.source ZIPEXAMPLESUFFIX=.examples ifdef CROSSCOMPILE ZIPSUFFIX=.$(SOURCESUFFIX) ZIPCROSSPREFIX=$(TARGETSUFFIX)- else ZIPSUFFIX=.$(TARGETSUFFIX) ZIPCROSSPREFIX= endif endif ifndef ECHO ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(ECHO),) ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(ECHO),) ECHO= __missing_command_ECHO else ECHO:=$(firstword $(ECHO)) endif else ECHO:=$(firstword $(ECHO)) endif endif export ECHO ifndef DATE DATE:=$(strip $(wildcard $(addsuffix /gdate$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(DATE),) DATE:=$(strip $(wildcard $(addsuffix /date$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(DATE),) DATE= __missing_command_DATE else DATE:=$(firstword $(DATE)) endif else DATE:=$(firstword $(DATE)) endif endif export DATE ifndef GINSTALL GINSTALL:=$(strip $(wildcard $(addsuffix /ginstall$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(GINSTALL),) GINSTALL:=$(strip $(wildcard $(addsuffix /install$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(GINSTALL),) GINSTALL= __missing_command_GINSTALL else GINSTALL:=$(firstword $(GINSTALL)) endif else GINSTALL:=$(firstword $(GINSTALL)) endif endif export GINSTALL ifndef CPPROG CPPROG:=$(strip $(wildcard $(addsuffix /cp$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(CPPROG),) CPPROG= __missing_command_CPPROG else CPPROG:=$(firstword $(CPPROG)) endif endif export CPPROG ifndef RMPROG RMPROG:=$(strip $(wildcard $(addsuffix /rm$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(RMPROG),) RMPROG= __missing_command_RMPROG else RMPROG:=$(firstword $(RMPROG)) endif endif export RMPROG ifndef MVPROG MVPROG:=$(strip $(wildcard $(addsuffix /mv$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(MVPROG),) MVPROG= __missing_command_MVPROG else MVPROG:=$(firstword $(MVPROG)) endif endif export MVPROG ifndef MKDIRPROG MKDIRPROG:=$(strip $(wildcard $(addsuffix /gmkdir$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(MKDIRPROG),) MKDIRPROG:=$(strip $(wildcard $(addsuffix /mkdir$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(MKDIRPROG),) MKDIRPROG= __missing_command_MKDIRPROG else MKDIRPROG:=$(firstword $(MKDIRPROG)) endif else MKDIRPROG:=$(firstword $(MKDIRPROG)) endif endif export MKDIRPROG ifndef ECHOREDIR ifndef inUnix ECHOREDIR=echo else ECHOREDIR=$(ECHO) endif endif ifndef COPY COPY:=$(CPPROG) -fp endif ifndef COPYTREE COPYTREE:=$(CPPROG) -Rfp endif ifndef MKDIRTREE MKDIRTREE:=$(MKDIRPROG) -p endif ifndef MOVE MOVE:=$(MVPROG) -f endif ifndef DEL DEL:=$(RMPROG) -f endif ifndef DELTREE DELTREE:=$(RMPROG) -rf endif ifndef INSTALL ifdef inUnix INSTALL:=$(GINSTALL) -c -m 644 else INSTALL:=$(COPY) endif endif ifndef INSTALLEXE ifdef inUnix INSTALLEXE:=$(GINSTALL) -c -m 755 else INSTALLEXE:=$(COPY) endif endif ifndef MKDIR MKDIR:=$(GINSTALL) -m 755 -d endif export ECHOREDIR COPY COPYTREE MOVE DEL DELTREE INSTALL INSTALLEXE MKDIR ifndef PPUMOVE PPUMOVE:=$(strip $(wildcard $(addsuffix /ppumove$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(PPUMOVE),) PPUMOVE= __missing_command_PPUMOVE else PPUMOVE:=$(firstword $(PPUMOVE)) endif endif export PPUMOVE ifndef FPCMAKE FPCMAKE:=$(strip $(wildcard $(addsuffix /fpcmake$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(FPCMAKE),) FPCMAKE= __missing_command_FPCMAKE else FPCMAKE:=$(firstword $(FPCMAKE)) endif endif export FPCMAKE ifndef ZIPPROG ZIPPROG:=$(strip $(wildcard $(addsuffix /zip$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(ZIPPROG),) ZIPPROG= __missing_command_ZIPPROG else ZIPPROG:=$(firstword $(ZIPPROG)) endif endif export ZIPPROG ifndef TARPROG TARPROG:=$(strip $(wildcard $(addsuffix /gtar$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(TARPROG),) TARPROG:=$(strip $(wildcard $(addsuffix /tar$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(TARPROG),) TARPROG= __missing_command_TARPROG else TARPROG:=$(firstword $(TARPROG)) endif else TARPROG:=$(firstword $(TARPROG)) endif endif export TARPROG ASNAME=$(BINUTILSPREFIX)as LDNAME=$(BINUTILSPREFIX)ld ARNAME=$(BINUTILSPREFIX)ar RCNAME=$(BINUTILSPREFIX)rc ifndef ASPROG ifdef CROSSBINDIR ASPROG=$(CROSSBINDIR)/$(ASNAME)$(SRCEXEEXT) else ASPROG=$(ASNAME) endif endif ifndef LDPROG ifdef CROSSBINDIR LDPROG=$(CROSSBINDIR)/$(LDNAME)$(SRCEXEEXT) else LDPROG=$(LDNAME) endif endif ifndef RCPROG ifdef CROSSBINDIR RCPROG=$(CROSSBINDIR)/$(RCNAME)$(SRCEXEEXT) else RCPROG=$(RCNAME) endif endif ifndef ARPROG ifdef CROSSBINDIR ARPROG=$(CROSSBINDIR)/$(ARNAME)$(SRCEXEEXT) else ARPROG=$(ARNAME) endif endif AS=$(ASPROG) LD=$(LDPROG) RC=$(RCPROG) AR=$(ARPROG) PPAS=ppas$(SRCBATCHEXT) ifdef inUnix LDCONFIG=ldconfig else LDCONFIG= endif ifdef DATE DATESTR:=$(shell $(DATE) +%Y%m%d) else DATESTR= endif ifndef UPXPROG ifeq ($(OS_TARGET),go32v2) UPXPROG:=1 endif ifeq ($(OS_TARGET),win32) UPXPROG:= endif ifdef UPXPROG UPXPROG:=$(strip $(wildcard $(addsuffix /upx$(SRCEXEEXT),$(SEARCHPATH)))) ifeq ($(UPXPROG),) UPXPROG= else UPXPROG:=$(firstword $(UPXPROG)) endif else UPXPROG= endif endif export UPXPROG ZIPOPT=-9 ZIPEXT=.zip ifeq ($(USETAR),bz2) TAROPT=vj TAREXT=.tar.bz2 else TAROPT=vz TAREXT=.tar.gz endif override REQUIRE_PACKAGES=rtl ifeq ($(FULL_TARGET),i386-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-go32v2) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-win32) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-os2) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-freebsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-beos) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-haiku) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-netbsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-solaris) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-qnx) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-netware) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-openbsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-wdosx) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-darwin) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-emx) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-watcom) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-netwlibc) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-wince) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-symbian) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-nativent) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),i386-iphonesim) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-freebsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-netbsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-amiga) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-atari) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-openbsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-palmos) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),m68k-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-netbsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-amiga) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-macos) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-darwin) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-morphos) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc-wii) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),sparc-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),sparc-netbsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),sparc-solaris) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),sparc-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),x86_64-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),x86_64-freebsd) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),x86_64-solaris) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),x86_64-darwin) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),x86_64-win64) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),x86_64-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-palmos) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-darwin) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-wince) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-gba) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-nds) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),arm-symbian) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc64-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc64-darwin) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),powerpc64-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),avr-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),armeb-linux) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),armeb-embedded) REQUIRE_PACKAGES_RTL=1 endif ifeq ($(FULL_TARGET),mipsel-linux) REQUIRE_PACKAGES_RTL=1 endif ifdef REQUIRE_PACKAGES_RTL PACKAGEDIR_RTL:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /rtl/Makefile.fpc,$(PACKAGESDIR)))))) ifneq ($(PACKAGEDIR_RTL),) ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)),) UNITDIR_RTL=$(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX) else UNITDIR_RTL=$(PACKAGEDIR_RTL) endif ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX)),) UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX) else ifneq ($(wildcard $(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX)),) UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX) else UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL) endif endif ifdef CHECKDEPEND $(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE): $(MAKE) -C $(PACKAGEDIR_RTL)/$(OS_TARGET) $(FPCMADE) override ALLDEPENDENCIES+=$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE) endif else PACKAGEDIR_RTL= UNITDIR_RTL:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /rtl/Package.fpc,$(UNITSDIR))))) ifneq ($(UNITDIR_RTL),) UNITDIR_RTL:=$(firstword $(UNITDIR_RTL)) else UNITDIR_RTL= endif endif ifdef UNITDIR_RTL override COMPILER_UNITDIR+=$(UNITDIR_RTL) endif ifdef UNITDIR_FPMAKE_RTL override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_RTL) endif endif ifndef NOCPUDEF override FPCOPTDEF=$(ARCH) endif ifneq ($(OS_TARGET),$(OS_SOURCE)) override FPCOPT+=-T$(OS_TARGET) endif ifneq ($(CPU_TARGET),$(CPU_SOURCE)) override FPCOPT+=-P$(ARCH) endif ifeq ($(OS_SOURCE),openbsd) override FPCOPT+=-FD$(NEW_BINUTILS_PATH) endif ifndef CROSSBOOTSTRAP ifneq ($(BINUTILSPREFIX),) override FPCOPT+=-XP$(BINUTILSPREFIX) endif ifneq ($(BINUTILSPREFIX),) override FPCOPT+=-Xr$(RLINKPATH) endif endif ifdef UNITDIR override FPCOPT+=$(addprefix -Fu,$(UNITDIR)) endif ifdef LIBDIR override FPCOPT+=$(addprefix -Fl,$(LIBDIR)) endif ifdef OBJDIR override FPCOPT+=$(addprefix -Fo,$(OBJDIR)) endif ifdef INCDIR override FPCOPT+=$(addprefix -Fi,$(INCDIR)) endif ifdef LINKSMART override FPCOPT+=-XX endif ifdef CREATESMART override FPCOPT+=-CX endif ifdef DEBUG override FPCOPT+=-gl override FPCOPTDEF+=DEBUG endif ifdef RELEASE ifneq ($(findstring 2.0.,$(FPC_VERSION)),) ifeq ($(CPU_TARGET),i386) FPCCPUOPT:=-OG2p3 endif ifeq ($(CPU_TARGET),powerpc) FPCCPUOPT:=-O1r endif else FPCCPUOPT:=-O2 endif override FPCOPT+=-Ur -Xs $(FPCCPUOPT) -n override FPCOPTDEF+=RELEASE endif ifdef STRIP override FPCOPT+=-Xs endif ifdef OPTIMIZE override FPCOPT+=-O2 endif ifdef VERBOSE override FPCOPT+=-vwni endif ifdef COMPILER_OPTIONS override FPCOPT+=$(COMPILER_OPTIONS) endif ifdef COMPILER_UNITDIR override FPCOPT+=$(addprefix -Fu,$(COMPILER_UNITDIR)) endif ifdef COMPILER_LIBRARYDIR override FPCOPT+=$(addprefix -Fl,$(COMPILER_LIBRARYDIR)) endif ifdef COMPILER_OBJECTDIR override FPCOPT+=$(addprefix -Fo,$(COMPILER_OBJECTDIR)) endif ifdef COMPILER_INCLUDEDIR override FPCOPT+=$(addprefix -Fi,$(COMPILER_INCLUDEDIR)) endif ifdef CROSSBINDIR override FPCOPT+=-FD$(CROSSBINDIR) endif ifdef COMPILER_TARGETDIR override FPCOPT+=-FE$(COMPILER_TARGETDIR) ifeq ($(COMPILER_TARGETDIR),.) override TARGETDIRPREFIX= else override TARGETDIRPREFIX=$(COMPILER_TARGETDIR)/ endif endif ifdef COMPILER_UNITTARGETDIR override FPCOPT+=-FU$(COMPILER_UNITTARGETDIR) ifeq ($(COMPILER_UNITTARGETDIR),.) override UNITTARGETDIRPREFIX= else override UNITTARGETDIRPREFIX=$(COMPILER_UNITTARGETDIR)/ endif else ifdef COMPILER_TARGETDIR override COMPILER_UNITTARGETDIR=$(COMPILER_TARGETDIR) override UNITTARGETDIRPREFIX=$(TARGETDIRPREFIX) endif endif ifdef CREATESHARED override FPCOPT+=-Cg ifeq ($(CPU_TARGET),i386) override FPCOPT+=-Aas endif endif ifeq ($(findstring 2.0.,$(FPC_VERSION)),) ifneq ($(findstring $(OS_TARGET),freebsd openbsd netbsd linux solaris),) ifeq ($(CPU_TARGET),x86_64) override FPCOPT+=-Cg endif endif endif ifdef LINKSHARED endif ifdef OPT override FPCOPT+=$(OPT) endif ifdef FPCOPTDEF override FPCOPT+=$(addprefix -d,$(FPCOPTDEF)) endif ifdef CFGFILE override FPCOPT+=@$(CFGFILE) endif ifdef USEENV override FPCEXTCMD:=$(FPCOPT) override FPCOPT:=!FPCEXTCMD export FPCEXTCMD endif override AFULL_TARGET=$(CPU_TARGET)-$(OS_TARGET) override AFULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE) ifneq ($(AFULL_TARGET),$(AFULL_SOURCE)) override ACROSSCOMPILE=1 endif ifdef ACROSSCOMPILE override FPCOPT+=$(CROSSOPT) endif override COMPILER:=$(FPC) $(FPCOPT) ifeq (,$(findstring -s ,$(COMPILER))) EXECPPAS= else ifeq ($(FULL_SOURCE),$(FULL_TARGET)) ifdef RUNBATCH EXECPPAS:=@$(RUNBATCH) $(PPAS) else EXECPPAS:=@$(PPAS) endif endif endif .PHONY: fpc_exes ifndef CROSSINSTALL ifneq ($(TARGET_PROGRAMS),) override EXEFILES=$(addsuffix $(EXEEXT),$(TARGET_PROGRAMS)) override EXEOFILES:=$(addsuffix $(OEXT),$(TARGET_PROGRAMS)) $(addprefix $(STATICLIBPREFIX),$(addsuffix $(STATICLIBEXT),$(TARGET_PROGRAMS))) $(addprefix $(IMPORTLIBPREFIX),$(addsuffix $(STATICLIBEXT),$(TARGET_PROGRAMS))) override EXEDBGFILES:=$(addsuffix $(EXEDBGEXT),$(TARGET_PROGRAMS)) override ALLTARGET+=fpc_exes override INSTALLEXEFILES+=$(EXEFILES) override CLEANEXEFILES+=$(EXEFILES) $(EXEOFILES) override CLEANEXEDBGFILES+=$(EXEDBGFILES) ifeq ($(OS_TARGET),os2) override CLEANEXEFILES+=$(addsuffix $(AOUTEXT),$(TARGET_PROGRAMS)) endif ifeq ($(OS_TARGET),emx) override CLEANEXEFILES+=$(addsuffix $(AOUTEXT),$(TARGET_PROGRAMS)) endif endif endif fpc_exes: $(COMPILER_TARGETDIR) $(COMPILER_UNITTARGETDIR) $(EXEFILES) ifdef TARGET_RSTS override RSTFILES=$(addsuffix $(RSTEXT),$(TARGET_RSTS)) override CLEANRSTFILES+=$(RSTFILES) endif .PHONY: fpc_all fpc_smart fpc_debug fpc_release fpc_shared $(FPCMADE): $(ALLDEPENDENCIES) $(ALLTARGET) @$(ECHOREDIR) Compiled > $(FPCMADE) fpc_all: $(FPCMADE) fpc_smart: $(MAKE) all LINKSMART=1 CREATESMART=1 fpc_debug: $(MAKE) all DEBUG=1 fpc_release: $(MAKE) all RELEASE=1 .SUFFIXES: $(EXEEXT) $(PPUEXT) $(OEXT) .pas .lpr .dpr .pp .rc .res $(COMPILER_UNITTARGETDIR): $(MKDIRTREE) $(COMPILER_UNITTARGETDIR) $(COMPILER_TARGETDIR): $(MKDIRTREE) $(COMPILER_TARGETDIR) %$(PPUEXT): %.pp $(COMPILER) $< $(EXECPPAS) %$(PPUEXT): %.pas $(COMPILER) $< $(EXECPPAS) %$(EXEEXT): %.pp $(COMPILER) $< $(EXECPPAS) %$(EXEEXT): %.pas $(COMPILER) $< $(EXECPPAS) %$(EXEEXT): %.lpr $(COMPILER) $< $(EXECPPAS) %$(EXEEXT): %.dpr $(COMPILER) $< $(EXECPPAS) %.res: %.rc windres -i $< -o $@ vpath %.pp $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR) vpath %.pas $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR) vpath %.lpr $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR) vpath %.dpr $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR) vpath %.inc $(COMPILER_INCLUDEDIR) vpath %$(OEXT) $(COMPILER_UNITTARGETDIR) vpath %$(PPUEXT) $(COMPILER_UNITTARGETDIR) .PHONY: fpc_shared override INSTALLTARGET+=fpc_shared_install ifndef SHARED_LIBVERSION SHARED_LIBVERSION=$(FPC_VERSION) endif ifndef SHARED_LIBNAME SHARED_LIBNAME=$(PACKAGE_NAME) endif ifndef SHARED_FULLNAME SHARED_FULLNAME=$(SHAREDLIBPREFIX)$(SHARED_LIBNAME)-$(SHARED_LIBVERSION)$(SHAREDLIBEXT) endif ifndef SHARED_LIBUNITS SHARED_LIBUNITS:=$(TARGET_UNITS) $(TARGET_IMPLICITUNITS) override SHARED_LIBUNITS:=$(filter-out $(INSTALL_BUILDUNIT),$(SHARED_LIBUNITS)) endif fpc_shared: ifdef HASSHAREDLIB $(MAKE) all CREATESHARED=1 LINKSHARED=1 CREATESMART=1 ifneq ($(SHARED_BUILD),n) $(PPUMOVE) -q $(SHARED_LIBUNITS) -i$(COMPILER_UNITTARGETDIR) -o$(SHARED_FULLNAME) -d$(COMPILER_UNITTARGETDIR) endif else @$(ECHO) Shared Libraries not supported endif fpc_shared_install: ifneq ($(SHARED_BUILD),n) ifneq ($(SHARED_LIBUNITS),) ifneq ($(wildcard $(COMPILER_UNITTARGETDIR)/$(SHARED_FULLNAME)),) $(INSTALL) $(COMPILER_UNITTARGETDIR)/$(SHARED_FULLNAME) $(INSTALL_SHAREDDIR) endif endif endif .PHONY: fpc_install fpc_sourceinstall fpc_exampleinstall ifdef INSTALL_UNITS override INSTALLPPUFILES+=$(addsuffix $(PPUEXT),$(INSTALL_UNITS)) endif ifdef INSTALL_BUILDUNIT override INSTALLPPUFILES:=$(filter-out $(INSTALL_BUILDUNIT)$(PPUEXT),$(INSTALLPPUFILES)) endif ifdef INSTALLPPUFILES override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) ifneq ($(UNITTARGETDIRPREFIX),) override INSTALLPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPUFILES))) override INSTALLPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPULINKFILES)))) endif override INSTALL_CREATEPACKAGEFPC=1 endif ifdef INSTALLEXEFILES ifneq ($(TARGETDIRPREFIX),) override INSTALLEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(notdir $(INSTALLEXEFILES))) endif endif fpc_install: all $(INSTALLTARGET) ifdef INSTALLEXEFILES $(MKDIR) $(INSTALL_BINDIR) ifdef UPXPROG -$(UPXPROG) $(INSTALLEXEFILES) endif $(INSTALLEXE) $(INSTALLEXEFILES) $(INSTALL_BINDIR) endif ifdef INSTALL_CREATEPACKAGEFPC ifdef FPCMAKE ifdef PACKAGE_VERSION ifneq ($(wildcard Makefile.fpc),) $(FPCMAKE) -p -T$(CPU_TARGET)-$(OS_TARGET) Makefile.fpc $(MKDIR) $(INSTALL_UNITDIR) $(INSTALL) Package.fpc $(INSTALL_UNITDIR) endif endif endif endif ifdef INSTALLPPUFILES $(MKDIR) $(INSTALL_UNITDIR) $(INSTALL) $(INSTALLPPUFILES) $(INSTALL_UNITDIR) ifneq ($(INSTALLPPULINKFILES),) $(INSTALL) $(INSTALLPPULINKFILES) $(INSTALL_UNITDIR) endif ifneq ($(wildcard $(LIB_FULLNAME)),) $(MKDIR) $(INSTALL_LIBDIR) $(INSTALL) $(LIB_FULLNAME) $(INSTALL_LIBDIR) ifdef inUnix ln -sf $(LIB_FULLNAME) $(INSTALL_LIBDIR)/$(LIB_NAME) endif endif endif ifdef INSTALL_FILES $(MKDIR) $(INSTALL_DATADIR) $(INSTALL) $(INSTALL_FILES) $(INSTALL_DATADIR) endif fpc_sourceinstall: distclean $(MKDIR) $(INSTALL_SOURCEDIR) $(COPYTREE) $(BASEDIR)/* $(INSTALL_SOURCEDIR) fpc_exampleinstall: $(addsuffix _distclean,$(TARGET_EXAMPLEDIRS)) ifdef HASEXAMPLES $(MKDIR) $(INSTALL_EXAMPLEDIR) endif ifdef EXAMPLESOURCEFILES $(COPY) $(EXAMPLESOURCEFILES) $(INSTALL_EXAMPLEDIR) endif ifdef TARGET_EXAMPLEDIRS $(COPYTREE) $(addsuffix /*,$(TARGET_EXAMPLEDIRS)) $(INSTALL_EXAMPLEDIR) endif .PHONY: fpc_clean fpc_cleanall fpc_distclean ifdef EXEFILES override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES)) override CLEANEXEDBGFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEDBGFILES)) endif ifdef CLEAN_PROGRAMS override CLEANEXEFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEEXT), $(CLEAN_PROGRAMS))) override CLEANEXEDBGFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEDBGEXT), $(CLEAN_PROGRAMS))) endif ifdef CLEAN_UNITS override CLEANPPUFILES+=$(addsuffix $(PPUEXT),$(CLEAN_UNITS)) endif ifdef CLEANPPUFILES override CLEANPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(CLEANPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) ifdef DEBUGSYMEXT override CLEANPPULINKFILES+=$(subst $(PPUEXT),$(DEBUGSYMEXT),$(CLEANPPUFILES)) endif override CLEANPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPUFILES)) override CLEANPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPULINKFILES))) endif fpc_clean: $(CLEANTARGET) ifdef CLEANEXEFILES -$(DEL) $(CLEANEXEFILES) endif ifdef CLEANEXEDBGFILES -$(DELTREE) $(CLEANEXEDBGFILES) endif ifdef CLEANPPUFILES -$(DEL) $(CLEANPPUFILES) endif ifneq ($(CLEANPPULINKFILES),) -$(DEL) $(CLEANPPULINKFILES) endif ifdef CLEANRSTFILES -$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES)) endif ifdef CLEAN_FILES -$(DEL) $(CLEAN_FILES) endif ifdef LIB_NAME -$(DEL) $(LIB_NAME) $(LIB_FULLNAME) endif -$(DEL) $(FPCMADE) Package.fpc $(PPAS) script.res link.res $(FPCEXTFILE) $(REDIRFILE) -$(DEL) *$(ASMEXT) *_ppas$(BATCHEXT) fpc_cleanall: $(CLEANTARGET) ifdef CLEANEXEFILES -$(DEL) $(CLEANEXEFILES) endif ifdef COMPILER_UNITTARGETDIR ifdef CLEANPPUFILES -$(DEL) $(CLEANPPUFILES) endif ifneq ($(CLEANPPULINKFILES),) -$(DEL) $(CLEANPPULINKFILES) endif ifdef CLEANRSTFILES -$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES)) endif endif ifdef CLEAN_FILES -$(DEL) $(CLEAN_FILES) endif -$(DELTREE) units -$(DEL) *$(OEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT) ifneq ($(PPUEXT),.ppu) -$(DEL) *.o *.ppu *.a endif -$(DELTREE) *$(SMARTEXT) -$(DEL) fpcmade.* Package.fpc $(PPAS) script.res link.res $(FPCEXTFILE) $(REDIRFILE) -$(DEL) *_ppas$(BATCHEXT) ifdef AOUTEXT -$(DEL) *$(AOUTEXT) endif ifdef DEBUGSYMEXT -$(DEL) *$(DEBUGSYMEXT) endif fpc_distclean: cleanall .PHONY: fpc_baseinfo override INFORULES+=fpc_baseinfo fpc_baseinfo: @$(ECHO) @$(ECHO) == Package info == @$(ECHO) Package Name..... $(PACKAGE_NAME) @$(ECHO) Package Version.. $(PACKAGE_VERSION) @$(ECHO) @$(ECHO) == Configuration info == @$(ECHO) @$(ECHO) FPC.......... $(FPC) @$(ECHO) FPC Version.. $(FPC_VERSION) @$(ECHO) Source CPU... $(CPU_SOURCE) @$(ECHO) Target CPU... $(CPU_TARGET) @$(ECHO) Source OS.... $(OS_SOURCE) @$(ECHO) Target OS.... $(OS_TARGET) @$(ECHO) Full Source.. $(FULL_SOURCE) @$(ECHO) Full Target.. $(FULL_TARGET) @$(ECHO) SourceSuffix. $(SOURCESUFFIX) @$(ECHO) TargetSuffix. $(TARGETSUFFIX) @$(ECHO) FPC fpmake... $(FPCFPMAKE) @$(ECHO) @$(ECHO) == Directory info == @$(ECHO) @$(ECHO) Required pkgs... $(REQUIRE_PACKAGES) @$(ECHO) @$(ECHO) Basedir......... $(BASEDIR) @$(ECHO) FPCDir.......... $(FPCDIR) @$(ECHO) CrossBinDir..... $(CROSSBINDIR) @$(ECHO) UnitsDir........ $(UNITSDIR) @$(ECHO) PackagesDir..... $(PACKAGESDIR) @$(ECHO) @$(ECHO) GCC library..... $(GCCLIBDIR) @$(ECHO) Other library... $(OTHERLIBDIR) @$(ECHO) @$(ECHO) == Tools info == @$(ECHO) @$(ECHO) As........ $(AS) @$(ECHO) Ld........ $(LD) @$(ECHO) Ar........ $(AR) @$(ECHO) Rc........ $(RC) @$(ECHO) @$(ECHO) Mv........ $(MVPROG) @$(ECHO) Cp........ $(CPPROG) @$(ECHO) Rm........ $(RMPROG) @$(ECHO) GInstall.. $(GINSTALL) @$(ECHO) Echo...... $(ECHO) @$(ECHO) Shell..... $(SHELL) @$(ECHO) Date...... $(DATE) @$(ECHO) FPCMake... $(FPCMAKE) @$(ECHO) PPUMove... $(PPUMOVE) @$(ECHO) Upx....... $(UPXPROG) @$(ECHO) Zip....... $(ZIPPROG) @$(ECHO) @$(ECHO) == Object info == @$(ECHO) @$(ECHO) Target Loaders........ $(TARGET_LOADERS) @$(ECHO) Target Units.......... $(TARGET_UNITS) @$(ECHO) Target Implicit Units. $(TARGET_IMPLICITUNITS) @$(ECHO) Target Programs....... $(TARGET_PROGRAMS) @$(ECHO) Target Dirs........... $(TARGET_DIRS) @$(ECHO) Target Examples....... $(TARGET_EXAMPLES) @$(ECHO) Target ExampleDirs.... $(TARGET_EXAMPLEDIRS) @$(ECHO) @$(ECHO) Clean Units......... $(CLEAN_UNITS) @$(ECHO) Clean Files......... $(CLEAN_FILES) @$(ECHO) @$(ECHO) Install Units....... $(INSTALL_UNITS) @$(ECHO) Install Files....... $(INSTALL_FILES) @$(ECHO) @$(ECHO) == Install info == @$(ECHO) @$(ECHO) DateStr.............. $(DATESTR) @$(ECHO) ZipName.............. $(ZIPNAME) @$(ECHO) ZipPrefix............ $(ZIPPREFIX) @$(ECHO) ZipCrossPrefix....... $(ZIPCROSSPREFIX) @$(ECHO) ZipSuffix............ $(ZIPSUFFIX) @$(ECHO) FullZipName.......... $(FULLZIPNAME) @$(ECHO) Install FPC Package.. $(INSTALL_FPCPACKAGE) @$(ECHO) @$(ECHO) Install base dir..... $(INSTALL_BASEDIR) @$(ECHO) Install binary dir... $(INSTALL_BINDIR) @$(ECHO) Install library dir.. $(INSTALL_LIBDIR) @$(ECHO) Install units dir.... $(INSTALL_UNITDIR) @$(ECHO) Install source dir... $(INSTALL_SOURCEDIR) @$(ECHO) Install doc dir...... $(INSTALL_DOCDIR) @$(ECHO) Install example dir.. $(INSTALL_EXAMPLEDIR) @$(ECHO) Install data dir..... $(INSTALL_DATADIR) @$(ECHO) @$(ECHO) Dist destination dir. $(DIST_DESTDIR) @$(ECHO) Dist zip name........ $(DIST_ZIPNAME) @$(ECHO) .PHONY: fpc_info fpc_info: $(INFORULES) .PHONY: fpc_makefile fpc_makefiles fpc_makefile_sub1 fpc_makefile_sub2 \ fpc_makefile_dirs fpc_makefile: $(FPCMAKE) -w -T$(OS_TARGET) Makefile.fpc fpc_makefile_sub1: ifdef TARGET_DIRS $(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_DIRS)) endif ifdef TARGET_EXAMPLEDIRS $(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_EXAMPLEDIRS)) endif fpc_makefile_sub2: $(addsuffix _makefile_dirs,$(TARGET_DIRS) $(TARGET_EXAMPLEDIRS)) fpc_makefile_dirs: fpc_makefile_sub1 fpc_makefile_sub2 fpc_makefiles: fpc_makefile fpc_makefile_dirs debug: fpc_debug smart: fpc_smart release: fpc_release units: fpc_units examples: shared: fpc_shared install: fpc_install sourceinstall: fpc_sourceinstall exampleinstall: fpc_exampleinstall distinstall: zipinstall: zipsourceinstall: zipexampleinstall: zipdistinstall: distclean: fpc_distclean cleanall: fpc_cleanall info: fpc_info makefiles: fpc_makefiles .PHONY: debug smart release units examples shared install sourceinstall exampleinstall distinstall zipinstall zipsourceinstall zipexampleinstall zipdistinstall distclean cleanall info makefiles ifneq ($(wildcard fpcmake.loc),) include fpcmake.loc endif transgui$(EXEEXT): $(patsubst %.lfm,%.lrs,$(wildcard *.lfm)) $(wildcard *.lfm) $(wildcard *.pas) %.lrs: %.lfm; $(LAZRES) $@ $< have_lazres=$(strip $(wildcard $(LAZRES))) ifeq ($(have_lazres),) check_lazres: $(MAKE) -C $(LAZARUS_DIR)/tools lazres$(SRCEXEEXT) else check_lazres: endif all: check_lazres fpc_all extraclean: -$(DEL) $(addprefix $(UNITTARGETDIRPREFIX), *$(OEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT) *.or *.res) clean: extraclean fpc_clean zipdist: all -$(DEL) -r ./Release/dist -$(MKDIRPROG) ./Release $(MKDIRPROG) ./Release/dist $(MKDIRPROG) ./Release/dist/lang -strip ./transgui$(EXEEXT) $(CPPROG) ./transgui$(EXEEXT) ./Release/dist $(CPPROG) ./readme.txt ./Release/dist $(CPPROG) ./history.txt ./Release/dist $(CPPROG) ./LICENSE.txt ./Release/dist $(CPPROG) ./transgui.png ./Release/dist $(CPPROG) ./lang/transgui.* ./Release/dist/lang -$(DEL) ./Release/transgui-$(PROG_VER)-$(FULL_TARGET).zip $(MAKE) -C ./Release/dist -f ../../Makefile int_zip ZIP_FILE=transgui-$(PROG_VER)-$(FULL_TARGET).zip -$(DEL) -r ./Release/dist int_zip: $(ZIPPROG) -9 -r ../$(ZIP_FILE) . TransGUI/connoptions.lfm0000644000000000000000000002317412256577645014313 0ustar rootrootinherited ConnOptionsForm: TConnOptionsForm Left = 401 Height = 379 Top = 186 Width = 529 HorzScrollBar.Page = 349 VertScrollBar.Page = 238 AutoSize = True BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Manage connections to Transmission' ClientHeight = 379 ClientWidth = 529 Constraints.MinHeight = 280 Constraints.MinWidth = 471 OnCreate = FormCreate OnShow = FormShow Position = poMainFormCenter object Page: TPageControl[0] Left = 8 Height = 268 Top = 69 Width = 513 ActivePage = tabConnection Align = alClient BorderSpacing.Left = 8 BorderSpacing.Right = 8 TabIndex = 0 TabOrder = 1 object tabConnection: TTabSheet Caption = 'Transmission' ClientHeight = 242 ClientWidth = 505 object txPassword: TLabel Left = 24 Height = 14 Top = 157 Width = 51 Caption = 'Password:' ParentColor = False end object txUserName: TLabel Left = 24 Height = 14 Top = 128 Width = 56 Caption = 'User name:' ParentColor = False end object txPort: TLabel Left = 8 Height = 14 Top = 76 Width = 25 Caption = 'Port:' ParentColor = False end object txHost: TLabel Left = 8 Height = 14 Top = 48 Width = 66 Caption = 'Remote host:' ParentColor = False end object edPassword: TEdit Left = 180 Height = 21 Top = 153 Width = 316 Anchors = [akTop, akLeft, akRight] EchoMode = emPassword PasswordChar = '*' TabOrder = 5 end object edUserName: TEdit Left = 180 Height = 21 Top = 125 Width = 316 Anchors = [akTop, akLeft, akRight] TabOrder = 4 end object edPort: TSpinEdit Left = 180 Height = 21 Top = 73 Width = 70 MaxValue = 65535 MinValue = 1 TabOrder = 1 Value = 65535 end object cbSSL: TCheckBox Left = 260 Height = 17 Top = 75 Width = 56 Caption = 'Use SSL' TabOrder = 2 end object edHost: TEdit Left = 180 Height = 21 Top = 45 Width = 316 Anchors = [akTop, akLeft, akRight] OnChange = edHostChange TabOrder = 0 end object txConnHelp: TLabel Left = 8 Height = 31 Top = 9 Width = 488 Anchors = [akTop, akLeft, akRight] AutoSize = False Caption = 'Please specify how %s will connect to a remote host running Transmission daemon (service).' ParentColor = False WordWrap = True end object cbAuth: TCheckBox Left = 8 Height = 17 Top = 101 Width = 131 Caption = 'Authentication required' OnClick = cbAuthClick TabOrder = 3 end object edRpcPath: TEdit Left = 180 Height = 21 Top = 209 Width = 316 TabOrder = 7 end object txRpcPath: TLabel Left = 8 Height = 14 Top = 212 Width = 50 Caption = 'RPC path:' ParentColor = False end object cbShowAdvanced: TCheckBox Left = 8 Height = 17 Top = 225 Width = 132 Caption = 'Show advanced options' OnClick = cbShowAdvancedClick TabOrder = 8 Visible = False end object cbAskPassword: TCheckBox Left = 180 Height = 17 Top = 181 Width = 101 Caption = 'Ask for password' OnClick = cbAskPasswordClick TabOrder = 6 end end object tabProxy: TTabSheet Caption = 'Proxy' ClientHeight = 227 ClientWidth = 505 object txProxy: TLabel Left = 8 Height = 14 Top = 37 Width = 67 Caption = 'Proxy server:' ParentColor = False end object txProxyPort: TLabel Left = 8 Height = 14 Top = 65 Width = 56 Caption = 'Proxy port:' ParentColor = False end object txProxyUserName: TLabel Left = 24 Height = 14 Top = 116 Width = 86 Caption = 'Proxy user name:' ParentColor = False end object txProxyPassword: TLabel Left = 24 Height = 14 Top = 144 Width = 82 Caption = 'Proxy password:' ParentColor = False end object cbUseProxy: TCheckBox Left = 8 Height = 17 Top = 10 Width = 228 Caption = 'Connect to Transmission using proxy server' OnClick = cbUseProxyClick TabOrder = 0 end object cbUseSocks5: TCheckBox Left = 260 Height = 17 Top = 64 Width = 60 Caption = 'SOCKS 5' OnClick = cbUseProxyClick TabOrder = 3 end object edProxy: TEdit Left = 180 Height = 21 Top = 34 Width = 316 Anchors = [akTop, akLeft, akRight] TabOrder = 1 end object edProxyPort: TSpinEdit Left = 180 Height = 21 Top = 62 Width = 70 MaxValue = 65535 MinValue = 1 TabOrder = 2 Value = 65535 end object edProxyUserName: TEdit Left = 180 Height = 21 Top = 113 Width = 316 Anchors = [akTop, akLeft, akRight] TabOrder = 4 end object edProxyPassword: TEdit Left = 180 Height = 21 Top = 141 Width = 316 Anchors = [akTop, akLeft, akRight] EchoMode = emPassword PasswordChar = '*' TabOrder = 5 end object cbProxyAuth: TCheckBox Left = 8 Height = 17 Top = 89 Width = 131 Caption = 'Authentication required' OnClick = cbProxyAuthClick TabOrder = 6 end end object tabPaths: TTabSheet Caption = 'Paths' ClientHeight = 227 ClientWidth = 505 OnShow = tabPathsShow object txPaths: TLabel Left = 8 Height = 66 Top = 8 Width = 488 Anchors = [akTop, akLeft, akRight] Caption = 'Remote to local path mappings.'#13#10#13#10'Examples:'#13#10'/share=\\pch\share'#13#10'/var/downloads/music=Z:\music' ParentColor = False WordWrap = True end object edPaths: TMemo Left = 8 Height = 133 Top = 82 Width = 488 Anchors = [akTop, akLeft, akRight, akBottom] ScrollBars = ssAutoVertical TabOrder = 0 WordWrap = False end end object tabMisc: TTabSheet Caption = 'Misc' ClientHeight = 227 ClientWidth = 505 object gbSpeed: TGroupBox Left = 8 Height = 125 Top = 6 Width = 488 Anchors = [akTop, akLeft, akRight] Caption = 'Speed limit menu items' ClientHeight = 107 ClientWidth = 484 TabOrder = 0 object txDownSpeeds: TLabel Left = 8 Height = 14 Top = 4 Width = 121 Caption = 'Download speeds (KB/s):' ParentColor = False end object txUpSpeeds: TLabel Left = 8 Height = 14 Top = 52 Width = 107 Caption = 'Upload speeds (KB/s):' ParentColor = False end object edDownSpeeds: TEdit Left = 8 Height = 21 Top = 24 Width = 468 Anchors = [akTop, akLeft, akRight] TabOrder = 0 end object edUpSpeeds: TEdit Left = 8 Height = 21 Top = 72 Width = 468 Anchors = [akTop, akLeft, akRight] TabOrder = 1 end end end end object Buttons: TButtonPanel[1] Left = 8 Height = 26 Top = 345 Width = 513 BorderSpacing.Left = 8 BorderSpacing.Top = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 BorderSpacing.Around = 0 OKButton.Name = 'OKButton' OKButton.DefaultCaption = True HelpButton.Name = 'HelpButton' HelpButton.DefaultCaption = True CloseButton.Name = 'CloseButton' CloseButton.DefaultCaption = True CancelButton.Name = 'CancelButton' CancelButton.DefaultCaption = True TabOrder = 2 Spacing = 8 ShowButtons = [pbOK, pbCancel] ShowBevel = False end object panTop: TPanel[2] Left = 8 Height = 53 Top = 8 Width = 513 Align = alTop BorderSpacing.Around = 8 BevelOuter = bvNone ClientHeight = 53 ClientWidth = 513 TabOrder = 0 object txConName: TLabel Left = 0 Height = 14 Top = 4 Width = 88 Caption = 'Connection name:' ParentColor = False end object cbConnection: TComboBox Left = 170 Height = 21 Top = 1 Width = 342 Anchors = [akTop, akLeft, akRight] ItemHeight = 13 OnSelect = cbConnectionSelect Style = csDropDownList TabOrder = 0 end object btNew: TButton Left = 170 Height = 23 Top = 28 Width = 111 Caption = 'New' OnClick = btNewClick TabOrder = 1 end object btDel: TButton Left = 402 Height = 23 Top = 28 Width = 111 Caption = 'Delete' OnClick = btDelClick TabOrder = 3 end object btRename: TButton Left = 286 Height = 23 Top = 28 Width = 111 Caption = 'Rename' OnClick = btRenameClick TabOrder = 2 end end end TransGUI/colsetup.pas0000644000000000000000000001203012261763702013555 0ustar rootroot{************************************************************************************* This file is part of Transmission Remote GUI. Copyright (c) 2008-2014 by Yury Sidorov. Transmission Remote GUI is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Transmission Remote GUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Transmission Remote GUI; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *************************************************************************************} unit ColSetup; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls, CheckLst, StdCtrls, ButtonPanel, ExtCtrls, VarGrid, BaseForm; type { TColSetupForm } TColSetupForm = class(TBaseForm) btDown: TButton; btUp: TButton; Buttons: TButtonPanel; lstColumns: TCheckListBox; Panel1: TPanel; procedure btDownClick(Sender: TObject); procedure btOkClick(Sender: TObject); procedure btUpClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure lstColumnsClick(Sender: TObject); procedure lstColumnsClickCheck(Sender: TObject); private FPersistentColumnId: integer; procedure UpdateUI; procedure MoveItem(Delta: integer); public { public declarations } end; function SetupColumns(LV: TVarGrid; PersistentColumnId: integer; const GridName: string): boolean; implementation uses main; function SetupColumns(LV: TVarGrid; PersistentColumnId: integer; const GridName: string): boolean; var i, j: integer; begin with TColSetupForm.Create(Application) do try if GridName <> '' then Caption:=Caption + ' - ' + GridName; FPersistentColumnId:=PersistentColumnId; for i:=0 to LV.Columns.Count - 1 do with LV.Columns[i] do begin j:=lstColumns.Items.Add(Title.Caption); lstColumns.Items.Objects[j]:=TObject(ptrint(ID)); if Width = 0 then Visible:=False; lstColumns.Checked[j]:=Visible; if ID = PersistentColumnId then lstColumns.Checked[j]:=True; end; UpdateUI; Result:=ShowModal = mrOk; if Result then begin LV.BeginUpdate; try for i:=0 to lstColumns.Items.Count - 1 do for j:=0 to LV.Columns.Count - 1 do with LV.Columns[j] do if ID = ptrint(lstColumns.Items.Objects[i]) then begin Index:=i; if ID - 1 = PersistentColumnId then lstColumns.Checked[i]:=True; if not Visible and (Visible <> lstColumns.Checked[i]) then begin Visible:=True; if Width < 32 then Width:=70; end else Visible:=lstColumns.Checked[i]; if not Visible and (LV.SortColumn = ID - 1) and (PersistentColumnId >= 0) then LV.SortColumn:=PersistentColumnId; break; end; finally LV.EndUpdate; end; end; finally Free; end; Application.ProcessMessages; end; { TColSetupForm } procedure TColSetupForm.btOkClick(Sender: TObject); var i: integer; begin for i:=0 to lstColumns.Items.Count - 1 do if lstColumns.Checked[i] then begin ModalResult:=mrOk; exit; end; MessageDlg('At least single column must be visible.', mtError, [mbOK], 0); end; procedure TColSetupForm.btDownClick(Sender: TObject); begin MoveItem(1); end; procedure TColSetupForm.btUpClick(Sender: TObject); begin MoveItem(-1); end; procedure TColSetupForm.FormCreate(Sender: TObject); begin Buttons.OKButton.ModalResult:=mrNone; Buttons.OKButton.OnClick:=@btOKClick; end; procedure TColSetupForm.lstColumnsClick(Sender: TObject); begin UpdateUI; end; procedure TColSetupForm.lstColumnsClickCheck(Sender: TObject); var i: integer; begin if FPersistentColumnId >= 0 then for i:=0 to lstColumns.Items.Count - 1 do if ptrint(lstColumns.Items.Objects[i]) = FPersistentColumnId then begin lstColumns.Checked[i]:=True; break; end; end; procedure TColSetupForm.UpdateUI; begin btUp.Enabled:=lstColumns.ItemIndex > 0; btDown.Enabled:=(lstColumns.ItemIndex >= 0) and (lstColumns.ItemIndex < lstColumns.Items.Count - 1); end; procedure TColSetupForm.MoveItem(Delta: integer); var c: boolean; OldIdx: integer; begin OldIdx:=lstColumns.ItemIndex; c:=lstColumns.Checked[OldIdx]; lstColumns.Items.Move(OldIdx, OldIdx+Delta); lstColumns.Checked[OldIdx+Delta]:=c; lstColumns.ItemIndex:=OldIdx+Delta; UpdateUI; end; initialization {$I colsetup.lrs} end.