Synapse/pl
Synapse ułatwia użycie portu szeregowego i dostarcza synchroniczną bibliotekę TCP/IP.
Przegląd
Synapse oferuje obsługę portu szeregowego oraz łączność TCP/IP. Różni się od innych bibliotek tym, że wymaga jedynie dodania do kodu kilku plików kodu źródłowego Synapse Pascal; nie ma potrzeby instalowania całych pakietów itp. Jedynym wyjątkiem jest to, że będziesz potrzebować zewnętrznej biblioteki kryptograficznej, jeśli chcesz używać szyfrowania, takiego jak SSL/TLS/SSH.
Zobacz dokumentację na oficjalnej stronie (link poniżej), aby uzyskać więcej informacji.
Instalacja
Instalacja może być tak prosta, jak po prostu skopiowanie wszystkich plików do katalogu aplikacji i dodanie odpowiednich modułów Synapse do klauzuli uses.
Bardziej eleganckim i zalecanym sposobem jest kompilacja pakietu laz_synapse.lpk, dzięki czemu możesz używać tych samych modułów we wszystkich swoich projektach.
Strona informacyjna pobierania/SVN Synapse: Strona pobierania Synapse
Wsparcie i zgłaszanie błędów
Projekt Synapse posiada listę mailingową, na której udzielane jest wsparcie i można przesyłać łatki.
Raporty o błędach można również przesyłać pocztą na listę dyskusyjną.
Zobacz strona pomocy Synapse
Obsługa SSL/TLS
Z Synapse możesz korzystać z obsługi OpenSSL, CryptLib, StreamSecII lub OpenStreamSecII SSL. Domyślnie nie jest używana obsługa SSL.
Wsparcie jest aktywowane poprzez umieszczenie wybranej nazwy modułu w sekcji uses w swoim projekcie. Musisz także umieścić plik biblioteki binarnej w ścieżce projektu (Windows) lub zainstalować go w ścieżce wyszukiwania biblioteki (Linux, macOS, FreeBSD).
Synapse ładuje pliki bibliotek SSL w środowisku wykonawczym jako biblioteki dynamiczne.
- Szczegółowe informacje można znaleźć na stronie Architektura wtyczek SSL/TLS
- Niektóre biblioteki kryptograficzne można uzyskać pod adresem: http://synapse.ararat.cz/files/crypt/
Brakująca biblioteka
W systemie Linux musisz upewnić się, że wymagana biblioteka dynamiczna jest obecna/zainstalowana w twoim systemie. Jeśli biblioteki nie ma w systemie, to w przypadku cryptlib, podczas linkowania pojawia się komunikat o błędzie:
/usr/bin/ld: cannot find -lcl
Podobny komunikat zostanie wyświetlony podczas korzystania z innych bibliotek dynamicznych.
Przykład serwera WWW
Zobacz ten przykład serwera internetowego (www).
Przykład zapytania serwera QOTD
Zobacz ten przykład zapytania serwera - Cytat dnia.
Wysyłanie email
Artykuł opisujący wysyłanie e-maili wraz z załącznikami za pomocą Synapse: http://www.freepascal.org/~michael/articles/lazmail/lazmail-en.pdf
Przykład z postu na forum; współpracuje np. z Gmailem (czy to jest aktualne?):
{Ten kod obsługuje szyfrowanie TLS/SSL; jeśli wysyłasz do portu 25, SMTP używa zwykłego tekstu.}
uses
..., smtpsend,ssl_openssl; //prawdopodobnie można użyć również inne moduły SSL.
// MailData to tekst wiadomości.
function SendMail(
User, Password,
MailFrom, MailTo,
SMTPHost, SMTPPort: string;
MailData: string): Boolean;
var
SMTP: TSMTPSend;
sl:TStringList;
begin
Result:=False;
SMTP:=TSMTPSend.Create;
sl:=TStringList.Create;
try
sl.text:=Maildata;
SMTP.UserName:=User;
SMTP.Password:=Password;
SMTP.TargetHost:=SMTPHost;
SMTP.TargetPort:=SMTPPort;
SMTP.AutoTLS:=true;
if Trim(SMTPPort)<>'25' then
SMTP.FullSSL:=true; // jeśli wysyłasz na port 25, nie używaj szyfrowania
if SMTP.Login then
begin
result:=SMTP.MailFrom(MailFrom, Length(MailData)) and
SMTP.MailTo(MailTo) and
SMTP.MailData(sl);
SMTP.Logout;
end;
finally
SMTP.Free;
sl.Free;
end;
end;Wysyłanie załączników
Proszę zapoznać się z tą dokumentacją Synapse.
Pobieranie plików
Z serwera FTP
Po podaniu adresu URL (i ścieżki) oraz nazwy pliku, zostanie on pobrany z serwera FTP. Jest to głównie opakowanie wokół kodu Synapse, które ma ułatwić pobieranie dowolnych plików. Jeśli dokładnie wiesz, co i gdzie chcesz pobrać, wystarczy wywołać Synapse:
FtpGetFilezaprowadzi cię bardzo daleko.
function DownloadFTP(URL, TargetFile: string): boolean;
const
FTPPort=21;
FTPScheme='ftp://'; //Nazwa schematu URI dla adresów URL FTP
var
Host: string;
Port: integer;
Source: string;
FoundPos: integer;
begin
// Usuń informacje o schemacie:
if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));
// Surowa analiza; można użyć kodu parsującego URI z pakietów FPC...
FoundPos:=pos('/', URL);
Host:=LeftStr(URL, FoundPos-1);
Source:=Copy(URL, FoundPos+1, Length(URL));
//Sprawdź numery portów:
FoundPos:=pos(':', Host);
Port:=FTPPort;
if FoundPos>0 then
begin
Host:=LeftStr(Host, FoundPos-1);
Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);
end;
Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');
if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);
end;Przykład pobrania listy plików dostępnych w podanej ścieżce
//Użyj modułu ftpsend
function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;
var
i: Integer;
s: string;
begin
Result := False;
with TFTPSend.Create do
try
Username := User;
Password := Pass;
TargetHost := IP;
TargetPort := Port;
if not Login then
Exit;
Result := List(Path, False);
for i := 0 to FtpList.Count -1 do
begin
s := FTPList[i].FileName;
DirList.Add(s);
end;
Logout;
finally
Free;
end;
end;Z serwera HTTP
Po podaniu adresu URL (i ścieżki) oraz nazwy pliku, zostanie on pobrany z serwera HTTP. Zauważ, że ten kod sprawdza kod statusu HTTP (np. 200, 404), aby sprawdzić, czy dokument, który otrzymaliśmy z serwera, jest żądanym plikiem czy stroną błędu.
Wersja prosta
...
uses httpsend,
...
function DownloadHTTP(URL, TargetFile: string): Boolean;
var
HTTPGetResult: Boolean;
HTTPSender: THTTPSend;
begin
Result := False;
HTTPSender := THTTPSend.Create;
try
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
HTTPSender.Document.SaveToFile(TargetFile);
Result := True;
end;
finally
HTTPSender.Free;
end;
end;Wersja zaawansowana
...
uses httpsend
...
function DownloadHTTP(URL, TargetFile: string): Boolean;
// Pobieranie pliku; w razie potrzeby spróbuj ponownie.
// Można próbować użyć funkcji Synapse HttpGetBinary, ale to nie działa
// z kodami wyników (tzn. szczęśliwie pobiera dokument błędu 404)
const
MaxRetries = 3;
var
HTTPGetResult: Boolean;
HTTPSender: THTTPSend;
RetryAttempt: Integer;
begin
Result := False;
RetryAttempt := 1;
HTTPSender := THTTPSend.Create;
try
try
// Spróbuj pobrać plik
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do
begin
Sleep(500 * RetryAttempt);
HTTPSender.Clear;
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
RetryAttempt := RetryAttempt + 1;
end;
// Jeśli mamy odpowiedź z serwera, sprawdź, czy plik został do nas wysłany.
case HTTPSender.Resultcode of
100..299:
begin
HTTPSender.Document.SaveToFile(TargetFile);
Result := True;
end; //informational, success
300..399: Result := False; // przekierowanie. Nie zaimplementowano, ale może się zdarzyć.
400..499: Result := False; // błąd klienta; 404 nie znaleziono itp.
500..599: Result := False; // wewnętrzny błąd serwera
else Result := False; // kod nieznany
end;
except
// Nie obchodzi nas przyczyna tego błędu; pobieranie nie powiodło się.
Result := False;
end;
finally
HTTPSender.Free;
end;
end;Prosta wersja z paskiem postępu
Poniższy przykład pokazuje, jak uzyskać informacje o postępie z pobierania HTTP, a także rozmiar pliku. Rozmiar pliku jest pobierany z informacji zawartej w nagłówku.
Note: Wcześniejsze wersje free pascala wymagały użycia trybu {$mode Delphi} do poprawnego działania poniższych przykładów. Obecnie bez problemu można używać trybu {$mode objfpc}{$H+}. Testowane na wersji FPC:3.2.2, Lazarus:2.0.12
unit uhttpdownloader;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, httpsend, blcksock, typinfo;
type
IProgress = interface
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
end;
type
{ THttpDownloader }
THttpDownloader = class
public
function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
private
Bytes : Integer;
MaxBytes : Integer;
HTTPSender: THTTPSend;
ProgressMonitor : IProgress;
procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
function GetSizeFromHeader(Header: String):integer;
end;
implementation
function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
var
HTTPGetResult: Boolean;
begin
Result := False;
Bytes:= 0;
MaxBytes:= -1;
Self.ProgressMonitor:= ProgressMonitor;
HTTPSender := THTTPSend.Create;
try
HTTPSender.Sock.OnStatus:= Status;
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
HTTPSender.Document.SaveToFile(TargetFile);
Result := True;
end;
finally
HTTPSender.Free;
end;
end;
procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
var
V, currentHeader: String;
i: integer;
begin
//spróbuj pobrać rozmiar pliku z nagłówków
if (MaxBytes = -1) then
begin
for i:= 0 to HTTPSender.Headers.Count - 1 do
begin
currentHeader:= HTTPSender.Headers[i];
MaxBytes:= GetSizeFromHeader(currentHeader);
if MaxBytes <> -1 then break;
end;
end;
V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;
if Reason = THookSocketReason.HR_ReadCount then
begin
Bytes:= Bytes + StrToInt(Value);
ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);
end;
end;
function THttpDownloader.GetSizeFromHeader(Header: String): integer;
var
item : TStringList;
begin
Result:= -1;
if Pos('Content-Length:', Header) <> 0 then
begin
item:= TStringList.Create();
item.Delimiter:= ':';
item.StrictDelimiter:=true;
item.DelimitedText:=Header;
if item.Count = 2 then
begin
Result:= StrToInt(Trim(item[1]));
end;
end;
end;
end.Co my tu robimy?
Przede wszystkim zaglądamy do nagłówków, aby uzyskać rozmiar pliku. Musimy poczekać i sprawdzić, czy nagłówek tam jest. Pierwsze zdarzenia nie zawierają informacji o treści Content-Length.
Po znalezieniu wyodrębniamy te informacje. Pojawia się tutaj kilka zdarzeń, na które możesz zareagować. Ale w tym przykładzie sprawdzamy tylko THookSocketReason.HR_ReadCount.
„HR_ReadCount” dostarcza nam informacji ile bajtów zostało odczytanych od ostatniego zdarzenia.
Postęp jest następnie zgłaszany do interfejsu użytkownika:
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
begin
if (MaxProgress <> -1) then
begin
ProgressBar.Max:= MaxProgress;
end;
ProgressBar.Position:= CurrentProgress;
memoStatus.Lines.Add(Text);
Application.ProcessMessages;
end;Tak więc ostatecznym głównym modułem będzie:
unit uMain;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,
uhttpdownloader;
type
{ TMainForm }
TMainForm = class(TForm, IProgress)
btnStartDownload: TButton;
edtUrl: TEdit;
labelUrl: TLabel;
memoStatus: TMemo;
ProgressBar: TProgressBar;
SaveDialog: TSaveDialog;
procedure btnStartDownloadClick(Sender: TObject);
private
{ private declarations }
function GetFileNameFromURL(url: String):string;
public
{ public declarations }
procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
{ TMainForm }
procedure TMainForm.btnStartDownloadClick(Sender: TObject);
var
fileName: String;
downloader: THttpDownloader;
success: boolean;
begin
fileName:= GetFileNameFromURL(edtUrl.Text);
SaveDialog.FileName:=fileName;
if (SaveDialog.Execute) then
begin
memoStatus.Lines.Clear;
ProgressBar.Position:=0;
downloader:= THttpDownloader.Create();
success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);
ProgressBar.Position:=0;
if Success then
memoStatus.Lines.Add('Download successful')
else
memoStatus.Lines.Add('Error during download');
end;
end;
function TMainForm.GetFileNameFromURL(url: String): string;
var i, l : integer;
fileName, current : String;
begin
fileName:= '';
l:= Length(url);
for i:= l downto 0 do begin
current:= url[i];
if current <> '/' then
begin
fileName:= current + fileName;
end else begin
Result:= fileName;
break;
end;
end;
end;
procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
begin
if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;
ProgressBar.Position:= CurrentProgress;
memoStatus.Lines.Add(Text);
Application.ProcessMessages;
end;
end.Odnosnik: https://andydunkel.net/2015/09/09/lazarus_synapse_progress/
Z serwera HTTP przez parsowanie adresów URL: Sourceforge
Zobacz Pobieranie z SourceForge, aby zapoznać się z przykładem pobierania z sourceforge.net.
Z serwera HTTPS
Jest to podobne do pobierania z serwera HTTP. Dodatkowo musisz aktywować obsługę SSL/TLS i uzyskać plik(i) binarny wymaganej biblioteki. Następnie możesz użyć tej samej funkcji DownloadHTTP do pobrania pliku z adresu URL zaczynającego się od https://.
Przykładowy program klienta SSH/Telnet
Poniżej znajdziesz moduł, który pozwala na korzystanie z funkcjonalności klienta telnet/SSH, która wykorzystuje moduł synapse tlntsend.pas. Przykładowy program pokazuje, jak tego używać. Inny, prostszy sposób ilustruje Leonardo Ramé na 1. Jego przykład nie może jednak używać telnetu i wysyła tylko jedno polecenie.
Wymagania
Oprócz źródeł Synapse (z których potrzebujesz tylko kilku), jeśli chcesz korzystać z funkcjonalności SSH, będziesz potrzebować biblioteki szyfrowania, której używa Synapse. Chyba, że używasz tylko usługi Telnet, wówczas tego nie potrzebujesz.
Istnieją 2 możliwości:
- Biblioteka Cryptlib. Zaleta: stabilna. Najwyraźniej można używać kluczy prywatnych, ale są one stosowane w pewnym formacie, który nie jest powszechnie obsługiwany.
- Biblioteka LibSSH2. Obsługa w Pascalu jest nadal w fazie rozwoju, ale do uwierzytelnienia możesz użyć pliku z kluczem prywatnym (w formacie OpenSSH).
Cryptlib
- W systemie Windows pobierz binarną wersję biblioteki DLL cryptlib (CL32.DLL) i umieść ją w katalogu źródłowym. Jeśli kompilujesz do innego katalogu lub rozprowadzasz swój program, będziesz musiał również rozesłać bibliotekę DLL.
- W systemach Linux i OSX zainstaluj cryptlib za pomocą menedżera pakietów lub z innych źródeł. Podczas dystrybucji aplikacji oznacz cryptlib jako wymaganie w swoim pakiecie .deb/.rpm/inny.
Będziesz także potrzebował dowiązań (cryptlib.pas), obecnych w źródłach dystrybucji cryptlib.
Wersje pliku binarnego cryptlib i dowiązania muszą być zgodne.
Note: Wygląda na to, że cryptlib nie nadaje się do łączenia z maszynami linuksowymi, chociaż w systemie AIX działa. Zamiast tego użyj SSH2.
LibSSH2
- W systemie Windows pobierz binarną wersję biblioteki DLL libssh2 (LIBSSH2.DLL) i umieść ją w katalogu źródłowym. Jeśli kompilujesz do innego katalogu lub rozprowadzasz swój program, będziesz musiał również rozesłać tą bibliotekę DLL.
- W systemach Linux i macOS zainstaluj libssh2 za pomocą menedżera pakietów lub z innych środków. Ponadto dystrybuując swoją aplikację:
- Linux: oznacz libssh2 jako wymaganie w swoim pakiecie .deb/.rpm/inny.
- macOS: dołącz libssh2 do katalogu zasobów Pakietu aplikacji.
Będziesz także potrzebował ssl_libssh2.pas (patrz poniżej) i dowiązań: (libssh2.pas, zobacz ten post na forum). Plik binarny libssh2 i dowiązania muszą być zgodne.
Wtyczka Synapse libssh2 SSL
Note: Wtyczka nie jest ukończona.
{
ssl_libssh2.pas wersja 0.2
Wtyczka obsługi SSH2 (zarys) dla biblioteki Synapse (http://www.ararat.cz/synapse) do LibSSH2 (http://libssh2.org)
Wymaga: interfejsu libssh2 pascal - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 oraz
libssh2.dll z OpenSSL.
(С) Alexey Suhinin http://x-alexey.narod.ru
}
{$IFDEF FPC}
{$MODE DELPHI}
{$ENDIF}
{$H+}
unit ssl_libssh2;
interface
uses
SysUtils,
blcksock, synsock,
libssh2;
type
{:@abstract(klasa implementująca wtyczkę CryptLib SSL/SSH.)
Instancja tej klasy zostanie utworzona dla każdego @link(TTCPBlockSocket).
Nie musisz tworzyć instancji tej klasy, wszystko robi sam Synapse!}
TSSLLibSSH2 = class(TCustomSSL)
protected
FSession: PLIBSSH2_SESSION;
FChannel: PLIBSSH2_CHANNEL;
function SSHCheck(Value: integer): Boolean;
function DeInit: Boolean;
public
{:Zobacz @inherited}
constructor Create(const Value: TTCPBlockSocket); override;
destructor Destroy; override;
function Connect: boolean; override;
function LibName: String; override;
function Shutdown: boolean; override;
{:Zobacz @inherited}
function BiShutdown: boolean; override;
{:Zobacz @inherited}
function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;
{:Zobacz @inherited}
function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;
{:Zobacz @inherited}
function WaitingData: Integer; override;
{:Zobacz @inherited}
function GetSSLVersion: string; override;
published
end;
implementation
{==============================================================================}
function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;
var
PLastError: PAnsiChar;
ErrMsgLen: Integer;
begin
Result := true;
FLastError := 0;
FLastErrorDesc := '';
if Value<0 then
begin
FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);
FLastErrorDesc := PLastError;
Result := false;
end;
end;
function TSSLLibSSH2.DeInit: Boolean;
begin
if Assigned(FChannel) then
begin
libssh2_channel_free(FChannel);
FChannel := nil;
end;
if Assigned(FSession) then
begin
libssh2_session_disconnect(FSession,'Goodbye');
libssh2_session_free(FSession);
FSession := nil;
end;
FSSLEnabled := False;
Result := true;
end;
constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);
begin
inherited Create(Value);
FSession := nil;
FChannel := nil;
end;
destructor TSSLLibSSH2.Destroy;
begin
DeInit;
inherited Destroy;
end;
function TSSLLibSSH2.Connect: boolean;
begin
Result := False;
if SSLEnabled then DeInit;
if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then
begin
FSession := libssh2_session_init();
if not Assigned(FSession) then
begin
FLastError := -999;
FLastErrorDesc := 'Cannot initialize SSH session';
exit;
end;
if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then
exit;
if (FSocket.SSL.PrivateKeyFile<>'') then
begin
if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then
exit;
end
else
if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then
begin
if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then
exit;
end;
FChannel := libssh2_channel_open_session(FSession);
if not assigned(FChannel) then
begin
SSHCheck(-1); // get error
if FLastError = 0 then
begin
FLastError := -999; // unknown error
FLastErrorDesc := 'Cannot open session';
end;
exit;
end;
if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then
exit;
if not SSHCheck(libssh2_channel_shell(FChannel)) then
exit;
FSSLEnabled := True;
Result := True;
end;
end;
function TSSLLibSSH2.LibName: String;
begin
Result := 'ssl_libssh2';
end;
function TSSLLibSSH2.Shutdown: boolean;
begin
Result := DeInit;
end;
function TSSLLibSSH2.BiShutdown: boolean;
begin
Result := DeInit;
end;
function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;
begin
Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);
SSHCheck(Result);
end;
function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;
begin
result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);
SSHCheck(Result);
end;
function TSSLLibSSH2.WaitingData: Integer;
begin
if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;
end;
function TSSLLibSSH2.GetSSLVersion: string;
begin
Result:=libssh2_version(0);
end;
initialization
if libssh2_init(0)=0 then
SSLImplementation := TSSLLibSSH2;
finalization
libssh2_exit;
end.Klasa klienta terminala
Poniższy moduł telnetsshclient.pas wykorzystuje moduł Synapse tlntsend.pas i opisuje logowanie, wysyłanie poleceń, odbieranie danych wyjściowych i wylogowywanie.
Jeśli potrzebujesz tylko klienta telnet i możesz żyć bez obsługi SSH, zakomentuj {$DEFINE HAS_SSH_SUPPORT} poniżej, aby kod nie wymagał biblioteki libssh2.
To urządzenie zostało lekko przetestowane na serwerze Linux ssh/telnet. Dodatkowe testy mile widziane.
unit telnetsshclient;
{ Wrapper wokół bibliotek Synapse i biblioteki SSL (obecnie używane są libssh2+libssl)
Pobierz skompilowaną bibliotekę Windows dll, np. z
http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip
Pobierz pliki interfejsu FreePascal:
http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465
Ten moduł umożliwia użytkownikowi wysyłanie poleceń Telnet lub SSH i uzyskiwanie danych wyjściowych
Dzięki Leonardo Rame
http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html
i Ludo Brands.
Napisany przez Reiniera Olislagersa 2011.
Zmodyfikowany dla libssh2 przez Alexeya Suhinina 2012.
Licencja kodu:
* MIT
* LGPLv2 lub nowszy (z wyjątkiem statycznego linkowania FreePascal)
* GPLv2 lub nowszy
zgodnie z twoim wyborem.
Dozwolone jest bezpłatne korzystanie, ale proszę nie pozywać mnie ani nie obwiniać.
Korzysta z innych bibliotek/komponentów, dla których mogą obowiązywać różne licencje, które również mogą wpływać na skompilowaną pracę.
}
{$mode objfpc}{$H+}
{$DEFINE HAS_SSH_SUPPORT} //zakomentuj, jeśli wymagana jest tylko obsługa telnet
{$DEFINE LIBSSH2}
interface
uses
Classes, SysUtils,
tlntsend
{$IFDEF HAS_SSH_SUPPORT}
{ssl - a właściwie teraz ssh - biblioteki wymagane przez tlntsend}
{$IFDEF LIBSSH2}
ssl_libssh2
{$ELSE}
ssl_cryptlib
{$ENDIF}
{$ENDIF HAS_SSH_SUPPORT} ;
type
TProtocolType = (Telnet, SSH); //Różne sposoby łączenia
TServerType = (Unix, Windows); //głównie dotyczy zakończenia linii tekstu
{ TelnetSSHClient }
{ TTelnetSSHClient }
TTelnetSSHClient = class(TTelnetSend)
protected
FConnected: boolean;
FOutputPosition: integer; //Śledzi pozycję w strumieniu wyjściowym
FProtocolType: TProtocolType;
FServerLineEnding: string; //zależy od FServerType
FServerType: TServerType;
FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;
procedure SetPrivateKeyFile(Value: string);
function GetPrivateKeyFile: string;
{ Na podstawie protokołu i typu serwera ustaw oczekiwane zakończenie linii po stronie serwera}
procedure DetermineLineEnding;
{ Ustawia port, jeśli portu nie ustawiono jawnie. Używa protokołu typu: SSH lub telnet}
procedure DeterminePort;
function GetSessionLog: string;
procedure ProtocolTypeChange(Value: TProtocolType);
function ReceiveData: string; //Może być używany do odbierania wiadomości powitalnej itp.
procedure SendData(Data: string);
procedure ServerTypeChange(Value: TServerType);
public
{Wszystkie dane wyjściowe wygenerowane podczas całej sesji do tej pory}
property AllOutput: string read GetSessionLog;
{Ma wartość True, jeśli jest podłączony do serwera}
property Connected: boolean read FConnected;
{Nazwa lub adres IP hosta, z którym chcesz się połączyć}
property HostName: string read FTargetHost write FTargetHost;
{Port na hoście używany do połączenia. Jeśli zostanie pozostawiony jako 0, zostanie określony przez typ protokołu (22 dla SSH, 23 dla Telnet}
property Port: String read FTargetPort write FTargetPort;
{Lokalizacja pliku klucza prywatnego}
property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;
{Pytanie o login Telnet}
property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;
{Pytanie o hasło Telnet}
property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;
{Nazwa użytkownika używana podczas łączenia}
property UserName: string read FUserName write FUserName;
{Hasło używane podczas łączenia. Używane jako hasło (passphrase), jeśli używany jest klucz prywatny}
property Password: string read FPassword write FPassword;
{Czy powinniśmy rozmawiać z serwerem przez Telnet czy SSH? Domyślnie jest to SSH}
property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;
{Serwer Windows czy Unix/Linux? Ma to wpływ na końcówki linii. Domyślnie jest to Unix. UWAGA: nieprzetestowane}
property Servertype: TServerType read FServerType write ServerTypeChange;
{Pierwsza wiadomość wyświetlana przy logowaniu}
property WelcomeMessage: string read FWelcomeMessage;
{Połącz/zaloguj się do serwera. Wymaga poprawnych wszystkich opcji uwierzytelniania, protokołu i nazwy hosta/portu.
Zwraca opisowy wynik. Następnie możesz użyć właściwości Connected.}
function Connect: string;
{Jeśli połączony, wyloguj się z serwera}
procedure Disconnect;
{Wyślij polecenie do serwera i odbierz wynik}
function CommandResult(Command: string): string; //Wyślij polecenie i uzyskaj wyniki
constructor Create;
destructor Destroy; override;
end;
implementation
{ TelnetSSHClient }
procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);
begin
Sock.SSL.PrivateKeyFile := value;
end;
function TTelnetSSHClient.GetPrivateKeyFile: string;
begin
Result := Sock.SSL.PrivateKeyFile;
end;
procedure TTelnetSSHClient.DetermineLineEnding;
begin
case FProtocolType of
SSH:
begin
if FServerType = Unix then
FServerLineEnding := #10 //Unix
else
FServerLineEnding := #13 + #10; //windows
end;
Telnet:
begin
if FServerType = Unix then
FServerLineEnding := #10 //Unix
else
FServerLineEnding := #13 + #10; //windows
end;
else
raise Exception.Create('Nieznany typ protokołu.');
end;
end;
procedure Ttelnetsshclient.DeterminePort;
begin
if FTargetPort = '' then
//Ustaw domyślny port dla protokołu
begin
case FProtocolType of
Telnet: FTargetPort := '23';
SSH: FTargetPort := '22';
else
raise Exception.Create('Nieznany typ protokołu.');
end;
end;
end;
procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);
begin
FServerType := Value;
DetermineLineEnding;
end;
function TTelnetSSHClient.Connect: string;
var
Received: string;
begin
result:='Nieznany błąd podczas łączenia';
FOutputPosition := 1; //Pierwszy znak w strumieniu wyjściowym
FWelcomeMessage := '';
//Tylko dla pewności:
DetermineLineEnding;
DeterminePort;
if FTargetPort='0' then
begin
result:='Port nie może być równy 0.';
exit; //wyskoczyć z funkcji
end;
case FProtocolType of
Telnet:
begin
try
if Login then
begin
FConnected := True;
result:='Połączono z serwerem telnet.';
end
else
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
except
on E: Exception do
begin
FConnected:=false;
result:='Błąd połączenia z serwerem telnet '+FTargetHost+':'+
FTargetPort+' jako użytkownik ' + FUserName +
'. Szczegóły techniczne: '+E.Message;
end;
end;
end;
SSH:
begin
{$IFNDEF HAS_SSH_SUPPORT}
raise Exception.Create(
'Obsługa SSH nie została skompilowana do biblioteki telnetsshclient.');
{$ENDIF HAS_SSH_SUPPORT}
try
if (PrivateKeyFile <> '') and (FPassword <> '') then
Sock.SSL.KeyPassword:=FPassword;
if SSHLogin then
begin
FConnected := True;
result:='Connected to SSH server.';
end
else
begin
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);
end;
except
on E: Exception do
begin
FConnected:=false;
result:='Błąd połączenia z serwerem SSH '+FTargetHost+':'+
FTargetPort+' jako użytkownik ' + FUserName +
'. Szczegóły techniczne: '+E.Message;
end;
end;
end;
else
raise Exception.Create('Nieznany typ protokołu.');
end;
if FConnected = True then
begin
FWelcomeMessage := ReceiveData;
if FProtocolType=Telnet then
begin
//Niestety, będziemy musieli sami wyodrębnić login
//Mam nadzieję, że dotyczy to wszystkich typów serwerów.
if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then
begin
SendData(UserName);
end;
Received:=ReceiveData;
if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then
begin
SendData(Password);
end;
//Otrzymuj dodatkową wiadomość powitalną/wiadomość dnia
FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;
end;
end;
end;
procedure TTelnetSSHClient.Disconnect;
begin
Logout;
FConnected := False;
end;
function TTelnetSSHClient.ReceiveData: string;
begin
Result := '';
while Sock.CanRead(1000) or (Sock.WaitingData > 0) do
begin
Sock.RecvPacket(1000);
Result := Result + Copy(SessionLog, FOutputPosition,
Length(SessionLog));
FOutputPosition := Length(SessionLog) + 1;
end;
end;
procedure Ttelnetsshclient.SendData(Data: String);
begin
Data := Data + FServerLineEnding; //Może to być linux, może być Windows
Send(Data);
end;
function TTelnetSSHClient.GetSessionLog: string;
begin
// Do tej pory uzyskuje pełne dane wyjściowe
Result := SessionLog;
end;
procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);
begin
FProtocolType := Value;
//W razie potrzeby automatycznie określ port i zakończenie linii
DeterminePort;
DetermineLineEnding;
end;
function TTelnetSSHClient.CommandResult(Command: string): string;
begin
Result := '';
if FConnected then
begin
SendData(Command);
Result := ReceiveData; //dostaje za dużo
end
else
begin
//podnieś wyjątek
Result := '';
raise Exception.Create('Polecenie można uruchomić tylko po podłączeniu');
end;
end;
constructor TTelnetSSHClient.Create;
begin
inherited;
FConnected := False;
FProtocolType := SSH; //Może to też być telnet
FServerType := Unix; //Prawdopodobnie bezpieczna wartość domyślna.
FTelnetLoginPrompt := 'login:';
FTelnetPasswordPrompt := 'password:';
DetermineLineEnding;
DeterminePort;
end;
destructor TTelnetSSHClient.Destroy;
begin
if FConnected then
Disconnect;
inherited Destroy;
end;
end.Przykładowy kod klienta
Aby użyć właśnie stworzonej przez nas klasy TTelnetSSHClient, możesz wypróbować przykładową aplikację, sshtest.lpr. Zauważ, że kod musi być skompilowany przez Lazarusa, ponieważ potrzebuje komponentów LCL do pracy z Synapse:
program sshtest;
{Program testowy dla telnetsshclient
Napisany przez Reiniera Olislagersa 2011.
Zmodyfikowany dla libssh2 przez Alexeya Suhinina 2012.
Licencja kodu:
* MIT
* LGPLv2 lub nowszy (z wyjątkiem statycznego linkowania FreePascal)
* GPLv2 lub nowszy
zgodnie z twoim wyborem.
Dozwolone jest bezpłatne korzystanie, ale proszę nie pozywać mnie ani nie obwiniać.
Korzysta z innych bibliotek/komponentów, dla których mogą obowiązywać różne licencje, które również mogą wpływać na skompilowaną pracę.
Uruchamianie: sshtest <serverIPorhostname> [PrivateKeyFile]
}
{$mode objfpc}{$H+}
{$APPTYPE CONSOLE}
uses
telnetsshclient;
var
comm: TTelnetSSHClient;
Command: string;
begin
writeln('Start.');
comm:=TTelnetSSHClient.Create;
comm.HostName:= ParamStr(1); //Pierwszy argument w wierszu poleceń
if comm.HostName='' then
begin
writeln('Proszę podać nazwę hosta w wierszu poleceń.');
halt(1);
end;
comm.PrivateKeyFile := ParamStr(2);
comm.TargetPort:='0'; //automatyczne określanie na podstawie typu protokołu
comm.UserName:='root'; //zmień dla swojej sytuacji
comm.Password:='password'; //zmień dla swojej sytuacji
comm.ProtocolType:=SSH; //Telnet lub SSH
writeln(comm.Connect); //Pokaż wynik połączenia
if comm.Connected then
begin
writeln('Serwer: ' + comm.HostName + ':'+comm.TargetPort+', użytkownik: '+comm.UserName);
writeln('Wiadomość powitalna:');
writeln(comm.WelcomeMessage);
Command:='ls -al';
writeln('*** Wysyłanie ' + Command);
writeln('*** Początek wyniku****');
writeln(comm.CommandResult(Command));
writeln('*** Koniec wyniku****');
writeln('');
writeln('');
Command:='df -h';
writeln('*** Wysyłanie ' + Command);
writeln('*** Początek wyniku****');
writeln(comm.CommandResult(Command));
writeln('*** Koniec wyniku****');
writeln('');
writeln('');
writeln('Wszystkie wyjścia:');
writeln('*** Początek wyniku****');
writeln(comm.AllOutput);
writeln('*** Koniec wyniku****');
comm.Disconnect;
end
else
begin
writeln('Połączenie z ' +
comm.HostName + ':' +
comm.TargetPort + ' nie udało się.');
end;
comm.Free;
end.Integracja z OAuth v1/Twitter/Plurk
Biblioteka OAuth v1 napisana w FPC, która używa Synapse (i jest gotowa do obsługi innych bibliotek sieciowych, takich jak lNet) jest dostępna w fpctwit. FPCtwit zawiera również przykładowe programy klienckie FPC jak Twitter i Plurk oraz klienta Lazarus Twitter.
Inne artykuły internetowe i sieciowe
- Portal na temat tworzenia stron internetowych
- Sieć
- Biblioteki sieciowe - porównanie różnych bibliotek sieciowych
- Brook Framework - Idealny framework Free Pascal dla Twoich aplikacji internetowych. To czysty Pascal. Nie musisz porzucać preferowanego języka programowania.
- Gniazda - Komponenty gniazd TCP/IP
- fcl-net - Biblioteka sieciowa dostarczana z FPC
- lNet - Lekkie komponenty sieciowe
- Synapse - Port szeregowy i synchroniczna biblioteka TCP/IP
- Poradnik XML - XML jest często używany w komunikacji sieciowej
- FPC i moduły Apache
- fcl-web - Znana również jako fpWeb, jest to biblioteka do tworzenia aplikacji internetowych, które można wdrażać jako moduły cgi, fastcgi lub apache.
- Bezpieczne programowanie
- Narzędzia internetowe - Wrapper wokół komponentów http Synapse/wininet/Android upraszczający korzystanie z https i przekierowań oraz silnik XPath/XQuery/CSS Selector/JSONiq do przetwarzania pobranych stron
Zobacz też
- Pobieranie z SourceForge Przykład, który używa Synapse do pobierania z serwera HTTP, który stosuje przekierowanie.
- Wizualny Synapse wrappery komponentów dla wielu elementów komunikacji szeregowej i sieciowej biblioteki Synapse (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualTCP, TvsVisualICMP, TvsSocksProxyInfo, TvsIPHelper, TvsSendMail i TvsSynPing).
- Komponent TCP/IP oparty na Synapse + mała aplikacja demonstracyjna
Linki zewnętrzne
- Oficjalna strona
- Oficjalna dokumentacja
- Użytkownik malcome stworzył rozwidlenie, aby szybciej poprawić synapse; ta gałąź znajduje się pod adresem
[Witryna z kodem Google dla Synapse4Lazarus](http://code.google.com/p/synapse4lazarus/)(aktualnie niedostępna)
From the Free Pascal wiki, page "Synapse/pl", as it stood on 2022-07-23 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.
Talk about this page
Nobody has opened a bench for this page yet. Sign in to open one.
