Sign in Join
Wiki copytemporary

Synapse/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Synapse/ru

OldNewLine
1+Synapse предоставляет простой в использовании последовательный порт и синхронную библиотеку TCP/IP.
2+
3+## Обзор
4+
5+Synapse предлагает [[Hardware Access|последовательный]] порт и возможность подключения по TCP/IP. Он отличается от других библиотек тем, что вам нужно только добавить в свой код несколько файлов исходного кода Synapse Pascal; нет необходимости устанавливать пакеты и т. д. Единственное исключение - вам понадобится внешняя криптографическая библиотека, если вы хотите использовать шифрование, такое как SSL/TLS/SSH.
6+
7+См. документацию на официальном сайте (ссылка ниже) для более подробной информации.
8+
9+## Установка
10+
11+Установка может быть такой же простой, как простое копирование всех файлов в каталог вашего приложения и добавление соответствующих модулей Synapse в ваш раздел *uses*.
12+
13+Более элегантный и рекомендуемый способ - это компиляция пакета laz_synapse.lpk, чтобы вы могли использовать одни и те же модули во всех своих проектах.
14+
15+Страница информации по загрузке/SVN: [Страница загрузки Synapse](http://www.ararat.cz/synapse/doku.php/download)
16+
17+## Поддержка и сообщения об ошибках
18+
19+У проекта Synapse есть список рассылки, в котором предоставляется поддержка и можно отправлять исправления.
20+
21+Отчеты об ошибках также можно отправлять по списку рассылки.
22+
23+См. [страницу поддержки Synapse](http://www.ararat.cz/synapse/doku.php/support)
24+
25+## Поддержка SSL/TLS
26+
27+Вы можете использовать поддержку OpenSSL, CryptLib, StreamSecII или OpenStreamSecII SSL с Synapse. По умолчанию поддержка SSL не используется.
28+
29+Поддержка активируется путем помещения выбранного имени модуля в раздел uses вашего проекта. Вы также должны поместить файл двоичной библиотеки в путь к вашему проекту(Windows) или установить его в путях поиска библиотеки (Linux, macOS, FreeBSD).
30+
31+Synapse загружает файлы библиотеки SSL во время выполнения как динамические библиотеки.
32+
33+- Для получения подробной информации см. [SSL/TLS Plugin Architecture](http://www.ararat.cz/synapse/doku.php/public:howto:sslplugin)
34+- Некоторые криптографические библиотеки можно получить [здесь](http://synapse.ararat.cz/files/crypt/)
35+
36+### Отсутствущая библиотека
37+
38+В Linux вам необходимо убедиться, что требуемая динамическая библиотека присутствует/установлена ​​в вашей системе. В случае cryptlib, если библиотека отсутствует в системе, при компоновке появляется сообщение об ошибке:
39+
40+`/usr/bin/ld: cannot find -lcl`
41+
42+Аналогичное сообщение будет отображаться при использовании других динамических библиотек.
43+
44+## Пример веб-сервера
45+
46+См. [[Networking|примеры Webserver]].
47+
48+## Пример запроса сервера QOTD
49+
50+См. [[QOTD|пример запроса сервера Quote of the Day]].
51+
52+## Отправка email
53+
54+Статья <i>Michaël Van Canneyt</i> об отправке электронной почты, включая вложения, с помощью Synapse: <http://www.freepascal.org/~michael/articles/lazmail/lazmail-en.pdf>
55+
56+Из сообщения [на форуме](http://forum.lazarus.freepascal.org/index.php/topic,21157.msg123501.html#msg123501); работает, например, с Gmail:
57+
58+``` pascal
59+{Этот код поддерживает использование шифрования TLS/SSL; при отправке на порт 25 используется простой текстовый SMTP.}
60+
61+uses
62+ ..., smtpsend,ssl_openssl; //вероятно, можно использовать и другие модули SSL.
63+
64+// MailData - это текст письма.
65+function SendMail(
66+ User, Password,
67+ MailFrom, MailTo,
68+ SMTPHost, SMTPPort: string;
69+ MailData: string): Boolean;
70+var
71+ SMTP: TSMTPSend;
72+ sl:TStringList;
73+begin
74+ Result:=False;
75+ SMTP:=TSMTPSend.Create;
76+ sl:=TStringList.Create;
77+ try
78+ sl.text:=Maildata;
79+ SMTP.UserName:=User;
80+ SMTP.Password:=Password;
81+ SMTP.TargetHost:=SMTPHost;
82+ SMTP.TargetPort:=SMTPPort;
83+ SMTP.AutoTLS:=true;
84+ if Trim(SMTPPort)<>'25' then
85+ SMTP.FullSSL:=true; // при отправке на порт 25 шифрование не используется
86+ if SMTP.Login then
87+ begin
88+ result:=SMTP.MailFrom(MailFrom, Length(MailData)) and
89+ SMTP.MailTo(MailTo) and
90+ SMTP.MailData(sl);
91+ SMTP.Logout;
92+ end;
93+ finally
94+ SMTP.Free;
95+ sl.Free;
96+ end;
97+end;
98+```
99+
100+### Отправка вложений
101+
102+См. [документацию Synapse](http://synapse.ararat.cz/doku.php/public:howto:tmimepart).
103+
104+## Загрузка файлов
105+
106+### С FTP-сервера
107+
108+Учитывая URL-адрес и (путь и) имя файла, это загрузит его с FTP-сервера. В основном это обертка кода Synapse, предназначенная для облегчения загрузки при работе с произвольными файлами. Если вы точно знаете, что будете скачивать, простой вызов Synapse:
109+
110+``` pascal
111+FtpGetFile
112+```
113+
114+доставит вас очень далеко.
115+
116+``` pascal
117+function DownloadFTP(URL, TargetFile: string): boolean;
118+const
119+ FTPPort=21;
120+ FTPScheme='ftp://'; //Имя схемы URI для URL-адресов FTP
121+var
122+ Host: string;
123+ Port: integer;
124+ Source: string;
125+ FoundPos: integer;
126+begin
127+ // Вычеркиваем информацию о схеме:
128+ if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));
129+
130+ // Грубый парсинг; мог использовать код синтаксического анализа URI в пакетах FPC ...
131+ FoundPos:=pos('/', URL);
132+ Host:=LeftStr(URL, FoundPos-1);
133+ Source:=Copy(URL, FoundPos+1, Length(URL));
134+
135+ //Проверка номера портов:
136+ FoundPos:=pos(':', Host);
137+ Port:=FTPPort;
138+ if FoundPos>0 then
139+ begin
140+ Host:=LeftStr(Host, FoundPos-1);
141+ Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);
142+ end;
143+ Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');
144+ if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);
145+end;
146+```
147+
148+Пример получения списка файлов по заданному пути
149+
150+``` pascal
151+//Используем модуль ftpsend
152+
153+function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;
154+var
155+ i: Integer;
156+ s: string;
157+begin
158+ Result := False;
159+ with TFTPSend.Create do
160+ try
161+ Username := User;
162+ Password := Pass;
163+ TargetHost := IP;
164+ TargetPort := Port;
165+ if not Login then
166+ Exit;
167+ Result := List(Path, False);
168+ for i := 0 to FtpList.Count -1 do
169+ begin
170+ s := FTPList[i].FileName;
171+ DirList.Add(s);
172+ end;
173+ Logout;
174+ finally
175+ Free;
176+ end;
177+end;
178+```
179+
180+### С HTTP-сервера
181+
182+Учитывая URL-адрес и (путь и) имя файла, это загрузит его с HTTP-сервера. Обратите внимание, что этот код проверяет код состояния HTTP (например, 200, 404), чтобы узнать, является ли документ, который мы получили обратно с сервера, желаемым файлом или же страницей с ошибкой.
183+
184+#### Простая версия
185+
186+``` pascal
187+...
188+uses httpsend,
189+...
190+function DownloadHTTP(URL, TargetFile: string): Boolean;
191+var
192+ HTTPGetResult: Boolean;
193+ HTTPSender: THTTPSend;
194+begin
195+ Result := False;
196+ HTTPSender := THTTPSend.Create;
197+ try
198+ HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
199+ if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
200+ HTTPSender.Document.SaveToFile(TargetFile);
201+ Result := True;
202+ end;
203+ finally
204+ HTTPSender.Free;
205+ end;
206+end;
207+```
208+
209+#### Расширенная версия
210+
211+``` pascal
212+...
213+uses httpsend
214+...
215+function DownloadHTTP(URL, TargetFile: string): Boolean;
216+// Грузим файл; при необходимости повторяем попытку.
217+// Можно использовать Synapse HttpGetBinary, но это не работает
218+// с кодами результата (т.е. он успешно загружает документ с ошибкой 404)
219+const
220+ MaxRetries = 3;
221+var
222+ HTTPGetResult: Boolean;
223+ HTTPSender: THTTPSend;
224+ RetryAttempt: Integer;
225+begin
226+ Result := False;
227+ RetryAttempt := 1;
228+ HTTPSender := THTTPSend.Create;
229+ try
230+ try
231+ // Пробуем получить файл
232+ HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
233+ while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do
234+ begin
235+ Sleep(500 * RetryAttempt);
236+ HTTPSender.Clear;
237+ HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
238+ RetryAttempt := RetryAttempt + 1;
239+ end;
240+ // Если у нас есть ответ от сервера, проверяем, не
241+ // был ли файл уже отправлен нам.
242+ case HTTPSender.Resultcode of
243+ 100..299:
244+ begin
245+ HTTPSender.Document.SaveToFile(TargetFile);
246+ Result := True;
247+ end; //информирование, успешно
248+ 300..399: Result := False; // перенаправление. Не реализовано, но могло бы быть.
249+ 400..499: Result := False; // ошибка клиента; 404 не найдено и т.д.
250+ 500..599: Result := False; // внутренняя ошибка сервера
251+ else Result := False; // неизвестный код
252+ end;
253+ except
254+ // Нас не интересует причина этой ошибки; загрузка не удалась.
255+ Result := False;
256+ end;
257+ finally
258+ HTTPSender.Free;
259+ end;
260+end;
261+```
262+
263+#### Простая версия с показом прогресса загрузки
264+
265+В следующем примере показано, как получить информацию о ходе загрузки по HTTP, а также размер файла. Размер файла извлекается из информации заголовка.
266+
267+``` pascal
268+unit uhttpdownloader;
269+
270+// Необходимо изменить это. По умолчанию это - {$mode objfpc}{$H+}, и не работает.
271+{$mode Delphi}
272+
273+interface
274+
275+uses
276+ Classes, SysUtils, httpsend, blcksock, typinfo;
277+
278+type
279+ IProgress = interface
280+ procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
281+ end;
282+
283+type
284+ { THttpDownloader }
285+
286+ THttpDownloader = class
287+ public
288+ function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
289+ private
290+ Bytes : Integer;
291+ MaxBytes : Integer;
292+ HTTPSender: THTTPSend;
293+ ProgressMonitor : IProgress;
294+ procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
295+ function GetSizeFromHeader(Header: String):integer;
296+ end;
297+
298+implementation
299+
300+function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
301+var
302+ HTTPGetResult: Boolean;
303+begin
304+ Result := False;
305+ Bytes:= 0;
306+ MaxBytes:= -1;
307+ Self.ProgressMonitor:= ProgressMonitor;
308+
309+ HTTPSender := THTTPSend.Create;
310+ try
311+ HTTPSender.Sock.OnStatus:= Status;
312+ HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
313+ if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
314+ HTTPSender.Document.SaveToFile(TargetFile);
315+ Result := True;
316+ end;
317+ finally
318+ HTTPSender.Free;
319+ end;
320+end;
321+
322+procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
323+var
324+ V, currentHeader: String;
325+ i: integer;
326+begin
327+ //try to get filesize from headers
328+ if (MaxBytes = -1) then
329+ begin
330+ for i:= 0 to HTTPSender.Headers.Count - 1 do
331+ begin
332+ currentHeader:= HTTPSender.Headers[i];
333+ MaxBytes:= GetSizeFromHeader(currentHeader);
334+ if MaxBytes <> -1 then break;
335+ end;
336+ end;
337+
338+ V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;
339+
340+ if Reason = THookSocketReason.HR_ReadCount then
341+ begin
342+ Bytes:= Bytes + StrToInt(Value);
343+ ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);
344+ end;
345+end;
346+
347+function THttpDownloader.GetSizeFromHeader(Header: String): integer;
348+var
349+ item : TStringList;
350+begin
351+ Result:= -1;
352+
353+ if Pos('Content-Length:', Header) <> 0 then
354+ begin
355+ item:= TStringList.Create();
356+ item.Delimiter:= ':';
357+ item.StrictDelimiter:=true;
358+ item.DelimitedText:=Header;
359+ if item.Count = 2 then
360+ begin
361+ Result:= StrToInt(Trim(item[1]));
362+ end;
363+ end;
364+end;
365+
366+end.
367+```
368+
369+Что мы здесь делаем?
370+
371+Прежде всего, мы смотрим на заголовки, чтобы узнать размер файла. Надо подождать и проверить, есть ли заголовок. Первые события не содержат Content-Length: information.
372+
373+Найдя, мы извлекаем эту информацию. Здесь появляется несколько событий, на которые вы можете реагировать. Но мы проверяем только THookSocketReason.HR_ReadCount в этом примере.
374+
375+HR_ReadCount предоставляет нам информацию о том, сколько байтов было прочитано с момента последнего события.
376+
377+Затем прогресс передается в пользовательский интерфейс:
378+
379+``` pascal
380+procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
381+begin
382+ if (MaxProgress <> -1) then
383+ begin
384+ ProgressBar.Max:= MaxProgress;
385+ end;
386+
387+ ProgressBar.Position:= CurrentProgress;
388+ memoStatus.Lines.Add(Text);
389+ Application.ProcessMessages;
390+end;
391+```
392+
393+Итак, окончательно основной модуль будет таким:
394+
395+``` pascal
396+unit uMain;
397+
398+{$mode Delphi}
399+
400+interface
401+
402+uses
403+ Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,
404+ uhttpdownloader;
405+
406+type
407+
408+ { TMainForm }
409+
410+ TMainForm = class(TForm, IProgress)
411+ btnStartDownload: TButton;
412+ edtUrl: TEdit;
413+ labelUrl: TLabel;
414+ memoStatus: TMemo;
415+ ProgressBar: TProgressBar;
416+ SaveDialog: TSaveDialog;
417+ procedure btnStartDownloadClick(Sender: TObject);
418+ private
419+ { private declarations }
420+ function GetFileNameFromURL(url: String):string;
421+ public
422+ { public declarations }
423+ procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
424+ end;
425+
426+var
427+ MainForm: TMainForm;
428+
429+implementation
430+
431+{$R *.lfm}
432+
433+{ TMainForm }
434+
435+procedure TMainForm.btnStartDownloadClick(Sender: TObject);
436+var
437+ fileName: String;
438+ downloader: THttpDownloader;
439+ success: boolean;
440+begin
441+ fileName:= GetFileNameFromURL(edtUrl.Text);
442+ SaveDialog.FileName:=fileName;
443+ if (SaveDialog.Execute) then
444+ begin
445+ memoStatus.Lines.Clear;
446+ ProgressBar.Position:=0;
447+ downloader:= THttpDownloader.Create();
448+ success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);
449+
450+ ProgressBar.Position:=0;
451+ if Success then
452+ memoStatus.Lines.Add('Download successful')
453+ else
454+ memoStatus.Lines.Add('Error during download');
455+
456+ end;
457+end;
458+
459+function TMainForm.GetFileNameFromURL(url: String): string;
460+var i, l : integer;
461+ fileName, current : String;
462+begin
463+ fileName:= '';
464+ l:= Length(url);
465+ for i:= l downto 0 do begin
466+ current:= url[i];
467+ if current <> '/' then
468+ begin
469+ fileName:= current + fileName;
470+ end else begin
471+ Result:= fileName;
472+ break;
473+ end;
474+ end;
475+end;
476+
477+procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
478+begin
479+ if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;
480+ ProgressBar.Position:= CurrentProgress;
481+ memoStatus.Lines.Add(Text);
482+ Application.ProcessMessages;
483+end;
484+
485+end.
486+```
487+
488+Ссылка: <https://andydunkel.net/2015/09/09/lazarus_synapse_progress/>
489+
490+#### С HTTP-сервера путем анализа URL-адресов: Sourceforge
491+
492+См. статью [[Download from SourceForge|Загрузка с SourceForge]] для примера загрузки с sourceforge.net.
493+
494+### С HTTPS-сервера
495+
496+Это похоже на загрузку с HTTP-сервера. Кроме того, вам необходимо [[Synapse/ru|активировать поддержку SSL/TLS]] и получить двоичный файл(ы) для необходимой библиотеки. Затем вы можете использовать ту же функцию DownloadHTTP для загрузки файла с URL-адреса, начинающегося с **<https://>**.
497+
498+## Пример программы клиента SSH/Telnet
499+
500+Ниже вы найдете модуль, который позволяет использовать клиентские функции telnet/SSH, использующий модуль synapse `tlntsend.pas`. Пример программы показывает, как это использовать. Другой, более простой способ проиллюстрирован <i>Leonardo Ramé</i> на [1](http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html). Его пример не может использовать telnet и отправляет только одну команду.
501+
502+### Требования
503+
504+Помимо исходников Synapse (которых вам понадобится всего пара), если вы хотите использовать функциональность SSH, вам понадобится библиотека шифрования, которую использует Synapse. Если вы используете только Telnet, вам это не нужно.
505+
506+Есть 2 варианта:
507+
508+- Библиотека Cryptlib. Преимущество: стабильность. По-видимому, можно использовать закрытые ключи, но они имеют некоторый формат, который широко не поддерживается.
509+- Библиотека LibSSH2. Привязки Pascal все еще находятся в разработке, но вы можете использовать файл с вашим закрытым ключом (в формате OpenSSH) для аутентификации.
510+
511+#### Cryptlib
512+
513+- В Windows загрузите двоичную версию библиотеки DLL cryptlib (CL32.DLL) и поместите ее в исходный каталог. Если вы компилируете в другой каталог или распространяете свою программу, вам также необходимо будет распространить DLL.
514+- В Linux и OSX установите cryptlib через диспетчер пакетов/другими способами. При распространении приложения отметьте cryptlib как требование в вашем пакете .deb/.rpm/любой.
515+
516+Вам также потребуются привязки (cryptlib.pas), присутствующие в исходном дистрибутиве cryptlib.
517+
518+Версии двоичного файла cryptlib и привязок должны совпадать.
519+
520+> **Note:** Кажется, что cryptlib не подходит для подключения к машинам с Linux, хотя AIX работает. Вместо этого используйте SSH2.
521+
522+#### LibSSH2
523+
524+- В Windows загрузите двоичную версию библиотеки libssh2 (LIBSSH2.DLL) и поместите ее в исходный каталог. Если вы компилируете в другой каталог или распространяете свою программу, вам также необходимо будет распространить DLL.
525+- В Linux и macOS установите libssh2 через диспетчер пакетов/другими способами. При распространении вашего приложения:
526+ - Linux: отметьте libssh2 как требование в вашем .deb/.rpm/любом пакете.
527+ - macOS: включите libssh2 в каталог ресурсов [[Application Bundle]].
528+
529+Вам также понадобится ssl_libssh2.pas (см. ниже) и привязки: (libssh2.pas, см. [это сообщение на форуме](http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465)). Двоичный файл libssh2 и привязки должны совпадать.
530+
531+### Synapse libssh2 SSL plugin
532+
533+> **Note:** плагин не доработан.
534+
535+``` pascal
536+{
537+ ssl_libssh2.pas version 0.2
538+
539+ Плагин поддержки SSH2 (черновик) для библиотеки Synapse (http://www.ararat.cz/synapse) от LibSSH2 (http://libssh2.org)
540+ Требуется: интерфейс libssh2 pascal - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 и
541+ libssh2.dll с OpenSSL.
542+
543+ (С) Alexey Suhinin http://x-alexey.narod.ru
544+}
545+
546+{$IFDEF FPC}
547+ {$MODE DELPHI}
548+{$ENDIF}
549+{$H+}
550+
551+unit ssl_libssh2;
552+
553+interface
554+
555+uses
556+ SysUtils,
557+ blcksock, synsock,
558+ libssh2;
559+
560+type
561+ {: @abstract(класс, реализующий плагин CryptLib SSL/SSH.)
562+ Экземпляр этого класса будет создан для каждого @link(TTCPBlockSocket).
563+ Вам не нужно создавать экземпляр этого класса, все делает сам Synapse!}
564+ TSSLLibSSH2 = class(TCustomSSL)
565+ protected
566+ FSession: PLIBSSH2_SESSION;
567+ FChannel: PLIBSSH2_CHANNEL;
568+ function SSHCheck(Value: integer): Boolean;
569+ function DeInit: Boolean;
570+ public
571+ {:См. @inherited}
572+ constructor Create(const Value: TTCPBlockSocket); override;
573+ destructor Destroy; override;
574+ function Connect: boolean; override;
575+ function LibName: String; override;
576+ function Shutdown: boolean; override;
577+ {:See @inherited}
578+ function BiShutdown: boolean; override;
579+ {:See @inherited}
580+ function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;
581+ {:See @inherited}
582+ function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;
583+ {:See @inherited}
584+ function WaitingData: Integer; override;
585+ {:See @inherited}
586+ function GetSSLVersion: string; override;
587+ published
588+ end;
589+
590+implementation
591+
592+{==============================================================================}
593+function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;
594+var
595+ PLastError: PAnsiChar;
596+ ErrMsgLen: Integer;
597+begin
598+ Result := true;
599+ FLastError := 0;
600+ FLastErrorDesc := '';
601+ if Value<0 then
602+ begin
603+ FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);
604+ FLastErrorDesc := PLastError;
605+ Result := false;
606+ end;
607+end;
608+
609+function TSSLLibSSH2.DeInit: Boolean;
610+begin
611+ if Assigned(FChannel) then
612+ begin
613+ libssh2_channel_free(FChannel);
614+ FChannel := nil;
615+ end;
616+ if Assigned(FSession) then
617+ begin
618+ libssh2_session_disconnect(FSession,'Goodbye');
619+ libssh2_session_free(FSession);
620+ FSession := nil;
621+ end;
622+ FSSLEnabled := False;
623+ Result := true;
624+end;
625+
626+constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);
627+begin
628+ inherited Create(Value);
629+ FSession := nil;
630+ FChannel := nil;
631+end;
632+
633+destructor TSSLLibSSH2.Destroy;
634+begin
635+ DeInit;
636+ inherited Destroy;
637+end;
638+
639+function TSSLLibSSH2.Connect: boolean;
640+begin
641+ Result := False;
642+ if SSLEnabled then DeInit;
643+ if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then
644+ begin
645+ FSession := libssh2_session_init();
646+ if not Assigned(FSession) then
647+ begin
648+ FLastError := -999;
649+ FLastErrorDesc := 'Cannot initialize SSH session';
650+ exit;
651+ end;
652+ if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then
653+ exit;
654+ if (FSocket.SSL.PrivateKeyFile<>'') then
655+ begin
656+ if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then
657+ exit;
658+ end
659+ else
660+ if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then
661+ begin
662+ if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then
663+ exit;
664+ end;
665+ FChannel := libssh2_channel_open_session(FSession);
666+ if not assigned(FChannel) then
667+ begin
668+ SSHCheck(-1); // получаем ошибку
669+ if FLastError = 0 then
670+ begin
671+ FLastError := -999; // неизвестная ошибка
672+ FLastErrorDesc := 'Cannot open session';
673+ end;
674+ exit;
675+ end;
676+ if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then
677+ exit;
678+ if not SSHCheck(libssh2_channel_shell(FChannel)) then
679+ exit;
680+ FSSLEnabled := True;
681+ Result := True;
682+ end;
683+end;
684+
685+function TSSLLibSSH2.LibName: String;
686+begin
687+ Result := 'ssl_libssh2';
688+end;
689+
690+function TSSLLibSSH2.Shutdown: boolean;
691+begin
692+ Result := DeInit;
693+end;
694+
695+function TSSLLibSSH2.BiShutdown: boolean;
696+begin
697+ Result := DeInit;
698+end;
699+
700+function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;
701+begin
702+ Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);
703+ SSHCheck(Result);
704+end;
705+
706+function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;
707+begin
708+ result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);
709+ SSHCheck(Result);
710+end;
711+
712+function TSSLLibSSH2.WaitingData: Integer;
713+begin
714+ if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;
715+end;
716+
717+function TSSLLibSSH2.GetSSLVersion: string;
718+begin
719+ Result:=libssh2_version(0);
720+end;
721+
722+initialization
723+ if libssh2_init(0)=0 then
724+ SSLImplementation := TSSLLibSSH2;
725+
726+finalization
727+ libssh2_exit;
728+
729+end.
730+```
731+
732+### Класс клиента терминала
733+
734+Модуль telnetsshclient.pas, представленный ниже, охватывает модуль Synapse tlntsend.pas и абстрагирует вход в систему, отправку команд, получение вывода и выход из системы.
735+
736+Если вам нужен только telnet-клиент и вы можете жить без поддержки SSH, закомментируйте {\$DEFINE HAS_SSH_SUPPORT} ниже, чтобы вам не нужна была библиотека libssh2.
737+
738+Этот модуль было слегка протестировано на сервере Linux ssh/telnet. Дополнительные тесты приветствуются.
739+
740+``` pascal
741+unit telnetsshclient;
742+
743+{ Обертка для библиотек Synapse и SSL (libssh2 + libssl
744+используется прямо сейчас)
745+Загрузите скомпилированную dll Windows, например,
746+http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip
747+Загрузите файлы интерфейса FreePascal:
748+http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465
749+
750+Это устройство позволяет пользователю отправлять команды Telnet или SSH и получать результат
751+Спасибо Leonardo Rame
752+http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html
753+и Ludo Brands.
754+
755+Написано Reinier Olislagers 2011.
756+Изменено для libssh2 Алексеем Сухининым 2012.
757+
758+Лицензия кода:
759+* MIT
760+* LGPLv2 или новее (с исключением статического связывания FreePascal)
761+* GPLv2 или новее
762+по вашему выбору.
763+Бесплатное использование разрешено, но, пожалуйста, не предъявляйте иск и не обвиняйте меня.
764+
765+Использует другие библиотеки/компоненты; могут применяться разные лицензии, которые также могут влиять на комбинированную/скомпилированную работу.
766+}
767+
768+{$mode objfpc}{$H+}
769+{$DEFINE HAS_SSH_SUPPORT} //закомментируйте, если требуется только поддержка telnet
770+{$DEFINE LIBSSH2}
771+
772+interface
773+
774+uses
775+ Classes, SysUtils,
776+ tlntsend
777+ {$IFDEF HAS_SSH_SUPPORT}
778+ {ssl - или на самом деле ssh - библиотеки, требуемые tlntsend}
779+ {$IFDEF LIBSSH2}
780+ ssl_libssh2
781+ {$ELSE}
782+ ssl_cryptlib
783+ {$ENDIF}
784+ {$ENDIF HAS_SSH_SUPPORT} ;
785+
786+type
787+ TProtocolType = (Telnet, SSH); //Различные способы подключения
788+ TServerType = (Unix, Windows); //окончания строк, в основном
789+ { TelnetSSHClient }
790+
791+ { TTelnetSSHClient }
792+
793+ TTelnetSSHClient = class(TTelnetSend)
794+ protected
795+ FConnected: boolean;
796+ FOutputPosition: integer; //Отслеживает позицию в выходном потоке
797+ FProtocolType: TProtocolType;
798+ FServerLineEnding: string; //зависит от FServerType
799+ FServerType: TServerType;
800+ FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;
801+ procedure SetPrivateKeyFile(Value: string);
802+ function GetPrivateKeyFile: string;
803+ { В зависимости от протокола и типа сервера установите ожидаемое окончание строки на стороне сервера.}
804+ procedure DetermineLineEnding;
805+ { Устанавливает порт, если он явно не задан. Использует тип протокола: SSH или telnet}
806+ procedure DeterminePort;
807+ function GetSessionLog: string;
808+ procedure ProtocolTypeChange(Value: TProtocolType);
809+ function ReceiveData: string; //Может использоваться для получения приветственного сообщения и т.д.
810+ procedure SendData(Data: string);
811+ procedure ServerTypeChange(Value: TServerType);
812+ public
813+ {Весь вывод, созданный в течение всего сеанса до настоящего момента}
814+ property AllOutput: string read GetSessionLog;
815+ {Истинно, если есть подключение к серверу}
816+ property Connected: boolean read FConnected;
817+ {Имя или IP-адрес хоста для подключения}
818+ property HostName: string read FTargetHost write FTargetHost;
819+ {Порт на хосте, используемый для подключения. Если оставить 0, он будет определяться типом протокола (22 для SSH, 23 для Telnet)}
820+ property Port: String read FTargetPort write FTargetPort;
821+ {Расположение файла закрытого ключа.}
822+ property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;
823+ {Telnet login prompt}
824+ property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;
825+ {Подсказка входа в Telnet}
826+ property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;
827+ {Имя пользователя, используемое при подключении}
828+ property UserName: string read FUserName write FUserName;
829+ {Пароль, используемый при подключении. Используется как кодовая фраза, если используется PrivateKey}
830+ property Password: string read FPassword write FPassword;
831+ {Должны ли мы общаться с сервером через Telnet или SSH? По умолчанию SSH.}
832+ property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;
833+ {Сервер Windows или Unix/Linux? Влияет на окончания строк. По умолчанию Unix. ПРИМЕЧАНИЕ: непроверено}
834+ property Servertype: TServerType read FServerType write ServerTypeChange;
835+ {Первоначальное сообщение отображается при входе в систему}
836+ property WelcomeMessage: string read FWelcomeMessage;
837+ {Подключение/вход на сервер. Требуется, чтобы все параметры аутентификации, протокола и имени хоста/порта были правильными
838+ Возвращает описательный результат. Затем вы можете использовать свойство Connected.}
839+ function Connect: string;
840+ {Если подключено, выйти с сервера}
841+ procedure Disconnect;
842+ {Отправляет команду на сервер и получает результат}
843+ function CommandResult(Command: string): string; //Отправьте команду и получите результат
844+ constructor Create;
845+ destructor Destroy; override;
846+ end;
847+
848+implementation
849+
850+
851+{ TelnetSSHClient }
852+procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);
853+begin
854+ Sock.SSL.PrivateKeyFile := value;
855+end;
856+
857+function TTelnetSSHClient.GetPrivateKeyFile: string;
858+begin
859+ Result := Sock.SSL.PrivateKeyFile;
860+end;
861+
862+procedure TTelnetSSHClient.DetermineLineEnding;
863+begin
864+ case FProtocolType of
865+ SSH:
866+ begin
867+ if FServerType = Unix then
868+ FServerLineEnding := #10 //Unix
869+ else
870+ FServerLineEnding := #13 + #10; //windows
871+ end;
872+ Telnet:
873+ begin
874+ if FServerType = Unix then
875+ FServerLineEnding := #10 //Unix
876+ else
877+ FServerLineEnding := #13 + #10; //windows
878+ end;
879+ else
880+ raise Exception.Create('Unknown protocol type');
881+ end;
882+end;
883+
884+procedure Ttelnetsshclient.DeterminePort;
885+begin
886+ if FTargetPort = '' then
887+ //Задаем порт по умолчанию для протокола
888+ begin
889+ case FProtocolType of
890+ Telnet: FTargetPort := '23';
891+ SSH: FTargetPort := '22';
892+ else
893+ raise Exception.Create('Unknown protocol type.');
894+ end;
895+
896+ end;
897+end;
898+
899+procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);
900+begin
901+ FServerType := Value;
902+ DetermineLineEnding;
903+end;
904+
905+function TTelnetSSHClient.Connect: string;
906+var
907+ Received: string;
908+begin
909+ result:='Unknown error while connecting';
910+ FOutputPosition := 1; //Первый символ в потоке вывода
911+ FWelcomeMessage := '';
912+ //Просто чтобы убедиться:
913+ DetermineLineEnding;
914+ DeterminePort;
915+ if FTargetPort='0' then
916+ begin
917+ result:='Port may not be 0.';
918+ exit; //выскакиваем из функции
919+ end;
920+ case FProtocolType of
921+ Telnet:
922+ begin
923+ try
924+ if Login then
925+ begin
926+ FConnected := True;
927+ result:='Connected to telnet server.';
928+ end
929+ else
930+ if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
931+ except
932+ on E: Exception do
933+ begin
934+ FConnected:=false;
935+ result:='Error connecting to telnet server '+FTargetHost+':'+
936+ FTargetPort+' as user ' + FUserName +
937+ '. Technical details: '+E.Message;
938+ end;
939+ end;
940+ end;
941+ SSH:
942+ begin
943+ {$IFNDEF HAS_SSH_SUPPORT}
944+ raise Exception.Create(
945+ 'SSH support has not been compiled into the telnetsshclient library.');
946+ {$ENDIF HAS_SSH_SUPPORT}
947+ try
948+ if (PrivateKeyFile <> '') and (FPassword <> '') then
949+ Sock.SSL.KeyPassword:=FPassword;
950+ if SSHLogin then
951+ begin
952+ FConnected := True;
953+ result:='Connected to SSH server.';
954+ end
955+ else
956+ begin
957+ if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
958+ if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);
959+ end;
960+ except
961+ on E: Exception do
962+ begin
963+ FConnected:=false;
964+ result:='Error connecting to SSH server '+FTargetHost+':'+
965+ FTargetPort+' as user ' + FUserName +
966+ '. Technical details: '+E.Message;
967+ end;
968+ end;
969+ end;
970+ else
971+ raise Exception.Create('Unknown protocol type');
972+ end;
973+ if FConnected = True then
974+ begin
975+ FWelcomeMessage := ReceiveData;
976+ if FProtocolType=Telnet then
977+ begin
978+ //К сожалению, нам придется извлекать логин самостоятельно
979+ //Надеюсь, это применимо ко всем типам серверов.
980+ if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then
981+ begin
982+ SendData(UserName);
983+ end;
984+ Received:=ReceiveData;
985+ if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then
986+ begin
987+ SendData(Password);
988+ end;
989+ //Получаем дополнительное приветственное сообщение/подсказку дня
990+ FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;
991+ end;
992+ end;
993+end;
994+
995+procedure TTelnetSSHClient.Disconnect;
996+begin
997+ Logout;
998+ FConnected := False;
999+end;
1000+
1001+function TTelnetSSHClient.ReceiveData: string;
1002+begin
1003+ Result := '';
1004+ while Sock.CanRead(1000) or (Sock.WaitingData > 0) do
1005+ begin
1006+ Sock.RecvPacket(1000);
1007+ Result := Result + Copy(SessionLog, FOutputPosition,
1008+ Length(SessionLog));
1009+ FOutputPosition := Length(SessionLog) + 1;
1010+ end;
1011+end;
1012+
1013+procedure Ttelnetsshclient.SendData(Data: String);
1014+begin
1015+ Data := Data + FServerLineEnding; //Может быть Linux, может быть Windows
1016+ Send(Data);
1017+end;
1018+
1019+function TTelnetSSHClient.GetSessionLog: string;
1020+begin
1021+ // Получает полный вывод на данный момент
1022+ Result := SessionLog;
1023+end;
1024+
1025+procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);
1026+begin
1027+ FProtocolType := Value;
1028+ //Автоматическое определение порта и символов конца строки, если необходимо
1029+ DeterminePort;
1030+ DetermineLineEnding;
1031+end;
1032+
1033+function TTelnetSSHClient.CommandResult(Command: string): string;
1034+begin
1035+ Result := '';
1036+ if FConnected then
1037+ begin
1038+ SendData(Command);
1039+ Result := ReceiveData; //получает слишком много
1040+ end
1041+ else
1042+ begin
1043+ //возбуждаем исключение
1044+ Result := '';
1045+ raise Exception.Create('Can only run command when connected');
1046+ end;
1047+end;
1048+
1049+constructor TTelnetSSHClient.Create;
1050+begin
1051+ inherited;
1052+ FConnected := False;
1053+ FProtocolType := SSH; //также может быть telnet
1054+ FServerType := Unix; //Probably a safe default.
1055+ FTelnetLoginPrompt := 'login:';
1056+ FTelnetPasswordPrompt := 'password:';
1057+ DetermineLineEnding;
1058+ DeterminePort;
1059+end;
1060+
1061+destructor TTelnetSSHClient.Destroy;
1062+begin
1063+ if FConnected then
1064+ Disconnect;
1065+ inherited Destroy;
1066+end;
1067+
1068+end.
1069+```
1070+
1071+### Пример клиентского кода
1072+
1073+Чтобы использовать только что созданный класс TTelnetSSHClient, вы можете использовать этот пример приложения, sshtest.lpr. Обратите внимание, что он должен быть скомпилирован Lazarus, так как ему нужны компоненты LCL для работы с Synapse:
1074+
1075+``` pascal
1076+program sshtest;
1077+
1078+{Программа тестирования для telnetsshclient
1079+
1080+Написано Reinier Olislagers 2011.
1081+Изменено для libssh2 Алексеем Сухининым 2012.
1082+
1083+Лицензия кода:
1084+* MIT
1085+* LGPLv2 или новее (с исключением статического связывания FreePascal)
1086+* GPLv2 или новее
1087+по вашему выбору.
1088+Бесплатное использование разрешено, но, пожалуйста, не предъявляйте претензии и не обвиняйте меня.
1089+
1090+Использует другие библиотеки/компоненты; могут применяться разные лицензии, которые также могут влиять на комбинированную/скомпилированную работу.
1091+
1092+Запустите: sshtest serverIPorhostname [PrivateKeyFile]
1093+}
1094+{$mode objfpc}{$H+}
1095+{$APPTYPE CONSOLE}
1096+
1097+uses
1098+ telnetsshclient;
1099+var
1100+ comm: TTelnetSSHClient;
1101+ Command: string;
1102+begin
1103+ writeln('Starting.');
1104+ comm:=TTelnetSSHClient.Create;
1105+ comm.HostName:= ParamStr(1); //Первый аргумент в командной строке
1106+ if comm.HostName='' then
1107+ begin
1108+ writeln('Please specify hostname on command line.');
1109+ halt(1);
1110+ end;
1111+
1112+ comm.PrivateKeyFile := ParamStr(2);
1113+
1114+ comm.TargetPort:='0'; //автоматическое определение на основе типа протокола
1115+ comm.UserName:='root'; //измените с соответствии с своей ситуацией
1116+ comm.Password:='password'; //измените с соответствии с своей ситуацией
1117+ comm.ProtocolType:=SSH; //Telnet или SSH
1118+ writeln(comm.Connect); //Показать результат подключения
1119+ if comm.Connected then
1120+ begin
1121+ writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);
1122+ writeln('Welcome message:');
1123+ writeln(comm.WelcomeMessage);
1124+ Command:='ls -al';
1125+ writeln('*** Sending ' + Command);
1126+ writeln('*** Begin result****');
1127+ writeln(comm.CommandResult(Command));
1128+ writeln('*** End result****');
1129+ writeln('');
1130+ writeln('');
1131+ Command:='df -h';
1132+ writeln('*** Sending ' + Command);
1133+ writeln('*** Begin result****');
1134+ writeln(comm.CommandResult(Command));
1135+ writeln('*** End result****');
1136+ writeln('');
1137+ writeln('');
1138+ writeln('All output:');
1139+ writeln('*** Begin result****');
1140+ writeln(comm.AllOutput);
1141+ writeln('*** End result****');
1142+ comm.Disconnect;
1143+ end
1144+ else
1145+ begin
1146+ writeln('Connection to ' +
1147+ comm.HostName + ':' +
1148+ comm.TargetPort + ' failed.');
1149+ end;
1150+ comm.Free;
1151+end.
1152+```
1153+
1154+## Интреграция OAuth с v1/Twitter/Plurk
1155+
1156+Библиотека OAuth v1, написанная на FPC, которая использует Synapse (и готова для других сетевых библиотек, таких как lnet) доступна [здесь](https://bitbucket.org/mararosas/fpctwit/src/default/). FPCTwit также содержит примеры клиентских программ FPC twitter и plurk и Twitter-клиент Lazarus.
1157+
1158+## Другие статьи о Интернете и сетях
1159+
1160+- [[Portal:Web Development|Web Development Portal]]
1161+- [[Networking]]
1162+- [[Networking libraries]] - сравнение различных сетевых библиотек
1163+- [[Brook Framework]] - Идеальный фреймворк Free Pascal для ваших веб-приложений. Это чистый Паскаль. Вам не нужно оставлять предпочитаемый язык программирования.
1164+- [[Sockets]] - Компоненты сокетов TCP/IP
1165+- [[fcl-net]] - Сетевая библиотека, поставляемая с FPC
1166+- [[lNet]] - Легковесные сетевые компоненты
1167+- [[Synapse]] - Последовательный порт и синхронная библиотека TCP/IP
1168+- [[XML Tutorial]] -XML, часто использующиеся в сетевых коммуникациях
1169+- [[FPC and Apache Modules]]
1170+- [[fcl-web]] - Также известная как fpWeb, это библиотека для разработки веб-приложений, которые можно развернуть как модули cgi, fastcgi или apache.
1171+- [[Secure programming|Безопасное программирование]]
1172+- [[Internet Tools]] - Обертка для HTTP-компонентов Synapse/wininet/Android, упрощающая https и перенаправления, и механизм XPath/XQuery/CSS Selector/JSONiq для обработки загруженных страниц.
1173+
1174+## См.также
1175+
1176+- [[Download from SourceForge]] Пример, который использует Synapse для загрузки с HTTP-сервера, который перенаправляет.
1177+- [Visual Synapse](https://sourceforge.net/projects/visualsynapse/) обертки компонентов для многих частей последовательной и сетевой библиотеки Synapse (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualHTTP, TvsVisualDUP, TvsViscksVisualTvs
1178+- [Компонент TCP/IP на базе Synapse + небольшое демо приложение](https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html)
1179+
1180+## Внешние ссылки
1181+
1182+- [Официальный сайт](http://www.ararat.cz/synapse/)
1183+- [Официальная документация](http://synapse.ararat.cz/doc/help/)
1184+- [Пользователь malcome создал форк, чтобы быстрее улучшить Synapse](http://lazarus.freepascal.org/index.php/topic,16032.msg87066.html#msg87066); этот форк расположен в [Google code site for Synapse4Lazarus](http://code.google.com/p/synapse4lazarus/)
1185+
1186+---
1187+*From the Free Pascal wiki, page "Synapse/ru", as it stood on 2020-12-31 after a change by Zoltanleo, 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](/wiki-contact).*

Back to history