Sign in Join
Wiki copytemporary

Synapse/ru: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Synapse предоставляет простой в использовании последовательный порт и синхронную библиотеку TCP/IP.
22
33## Обзор
44
55Synapse предлагает [[Hardware Access|последовательный]] порт и возможность подключения по TCP/IP. Он отличается от других библиотек тем, что вам нужно только добавить в свой код несколько файлов исходного кода Synapse Pascal; нет необходимости устанавливать пакеты и т. д. Единственное исключение - вам понадобится внешняя криптографическая библиотека, если вы хотите использовать шифрование, такое как SSL/TLS/SSH.
66
77См. документацию на официальном сайте (ссылка ниже) для более подробной информации.
88
99## Установка
1010
1111Установка может быть такой же простой, как простое копирование всех файлов в каталог вашего приложения и добавление соответствующих модулей Synapse в ваш раздел *uses*.
1212
1313Более элегантный и рекомендуемый способ - это компиляция пакета laz_synapse.lpk, чтобы вы могли использовать одни и те же модули во всех своих проектах.
1414
1515Страница информации по загрузке/SVN: [Страница загрузки Synapse](http://www.ararat.cz/synapse/doku.php/download)
1616
1717## Поддержка и сообщения об ошибках
1818
1919У проекта Synapse есть список рассылки, в котором предоставляется поддержка и можно отправлять исправления.
2020
2121Отчеты об ошибках также можно отправлять по списку рассылки.
2222
2323См. [страницу поддержки Synapse](http://www.ararat.cz/synapse/doku.php/support)
2424
2525## Поддержка SSL/TLS
2626
2727Вы можете использовать поддержку OpenSSL, CryptLib, StreamSecII или OpenStreamSecII SSL с Synapse. По умолчанию поддержка SSL не используется.
2828
2929Поддержка активируется путем помещения выбранного имени модуля в раздел uses вашего проекта. Вы также должны поместить файл двоичной библиотеки в путь к вашему проекту(Windows) или установить его в путях поиска библиотеки (Linux, macOS, FreeBSD).
3030
3131Synapse загружает файлы библиотеки SSL во время выполнения как динамические библиотеки.
3232
3333- Для получения подробной информации см. [SSL/TLS Plugin Architecture](http://www.ararat.cz/synapse/doku.php/public:howto:sslplugin)
3434- Некоторые криптографические библиотеки можно получить [здесь](http://synapse.ararat.cz/files/crypt/)
3535
3636### Отсутствущая библиотека
3737
3838В Linux вам необходимо убедиться, что требуемая динамическая библиотека присутствует/установлена ​​в вашей системе. В случае cryptlib, если библиотека отсутствует в системе, при компоновке появляется сообщение об ошибке:
3939
40-`/usr/bin/ld: cannot find -lcl`
40+```text
41+/usr/bin/ld: cannot find -lcl
42+```
4143
4244Аналогичное сообщение будет отображаться при использовании других динамических библиотек.
4345
4446## Пример веб-сервера
4547
4648См. [[Networking|примеры Webserver]].
4749
4850## Пример запроса сервера QOTD
4951
5052См. [[QOTD|пример запроса сервера Quote of the Day]].
5153
5254## Отправка email
5355
5456Статья Michaël Van Canneyt об отправке электронной почты, включая вложения, с помощью Synapse: <http://www.freepascal.org/~michael/articles/lazmail/lazmail-en.pdf>
5557
5658Из сообщения [на форуме](http://forum.lazarus.freepascal.org/index.php/topic,21157.msg123501.html#msg123501); работает, например, с Gmail:
5759
5860``` pascal
5961{Этот код поддерживает использование шифрования TLS/SSL; при отправке на порт 25 используется простой текстовый SMTP.}
6062
6163uses
6264 ..., smtpsend,ssl_openssl; //вероятно, можно использовать и другие модули SSL.
6365
6466// MailData - это текст письма.
6567function SendMail(
6668 User, Password,
6769 MailFrom, MailTo,
6870 SMTPHost, SMTPPort: string;
6971 MailData: string): Boolean;
7072var
7173 SMTP: TSMTPSend;
7274 sl:TStringList;
7375begin
7476 Result:=False;
7577 SMTP:=TSMTPSend.Create;
7678 sl:=TStringList.Create;
7779 try
7880 sl.text:=Maildata;
7981 SMTP.UserName:=User;
8082 SMTP.Password:=Password;
8183 SMTP.TargetHost:=SMTPHost;
8284 SMTP.TargetPort:=SMTPPort;
8385 SMTP.AutoTLS:=true;
8486 if Trim(SMTPPort)<>'25' then
8587 SMTP.FullSSL:=true; // при отправке на порт 25 шифрование не используется
8688 if SMTP.Login then
8789 begin
8890 result:=SMTP.MailFrom(MailFrom, Length(MailData)) and
8991 SMTP.MailTo(MailTo) and
9092 SMTP.MailData(sl);
9193 SMTP.Logout;
9294 end;
9395 finally
9496 SMTP.Free;
9597 sl.Free;
9698 end;
9799end;
98100```
99101
100102### Отправка вложений
101103
102104См. [документацию Synapse](http://synapse.ararat.cz/doku.php/public:howto:tmimepart).
103105
104106## Загрузка файлов
105107
106108### С FTP-сервера
107109
108110Учитывая URL-адрес и (путь и) имя файла, это загрузит его с FTP-сервера. В основном это обертка кода Synapse, предназначенная для облегчения загрузки при работе с произвольными файлами. Если вы точно знаете, что будете скачивать, простой вызов Synapse:
109111
110112``` pascal
111113FtpGetFile
112114```
113115
114116доставит вас очень далеко.
115117
116118``` pascal
117119function DownloadFTP(URL, TargetFile: string): boolean;
118120const
119121 FTPPort=21;
120122 FTPScheme='ftp://'; //Имя схемы URI для URL-адресов FTP
121123var
122124 Host: string;
123125 Port: integer;
124126 Source: string;
125127 FoundPos: integer;
126128begin
127129 // Вычеркиваем информацию о схеме:
128130 if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));
129131
130132 // Грубый парсинг; мог использовать код синтаксического анализа URI в пакетах FPC ...
131133 FoundPos:=pos('/', URL);
132134 Host:=LeftStr(URL, FoundPos-1);
133135 Source:=Copy(URL, FoundPos+1, Length(URL));
134136
135137 //Проверка номера портов:
136138 FoundPos:=pos(':', Host);
137139 Port:=FTPPort;
138140 if FoundPos>0 then
139141 begin
140142 Host:=LeftStr(Host, FoundPos-1);
141143 Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);
142144 end;
143145 Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');
144146 if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);
145147end;
146148```
147149
148150Пример получения списка файлов по заданному пути
149151
150152``` pascal
151153//Используем модуль ftpsend
152154
153155function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;
154156var
155157 i: Integer;
156158 s: string;
157159begin
158160 Result := False;
159161 with TFTPSend.Create do
160162 try
161163 Username := User;
162164 Password := Pass;
163165 TargetHost := IP;
164166 TargetPort := Port;
165167 if not Login then
166168 Exit;
167169 Result := List(Path, False);
168170 for i := 0 to FtpList.Count -1 do
169171 begin
170172 s := FTPList[i].FileName;
171173 DirList.Add(s);
172174 end;
173175 Logout;
174176 finally
175177 Free;
176178 end;
177179end;
178180```
179181
180182### С HTTP-сервера
181183
182184Учитывая URL-адрес и (путь и) имя файла, это загрузит его с HTTP-сервера. Обратите внимание, что этот код проверяет код состояния HTTP (например, 200, 404), чтобы узнать, является ли документ, который мы получили обратно с сервера, желаемым файлом или же страницей с ошибкой.
183185
184186#### Простая версия
185187
186188``` pascal
187189...
188190uses httpsend,
189191...
190192function DownloadHTTP(URL, TargetFile: string): Boolean;
191193var
192194 HTTPGetResult: Boolean;
193195 HTTPSender: THTTPSend;
194196begin
195197 Result := False;
196198 HTTPSender := THTTPSend.Create;
197199 try
198200 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
199201 if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
200202 HTTPSender.Document.SaveToFile(TargetFile);
201203 Result := True;
202204 end;
203205 finally
204206 HTTPSender.Free;
205207 end;
206208end;
207209```
208210
209211#### Расширенная версия
210212
211213``` pascal
212214...
213215uses httpsend
214216...
215217function DownloadHTTP(URL, TargetFile: string): Boolean;
216218// Грузим файл; при необходимости повторяем попытку.
217219// Можно использовать Synapse HttpGetBinary, но это не работает
218220// с кодами результата (т.е. он успешно загружает документ с ошибкой 404)
219221const
220222 MaxRetries = 3;
221223var
222224 HTTPGetResult: Boolean;
223225 HTTPSender: THTTPSend;
224226 RetryAttempt: Integer;
225227begin
226228 Result := False;
227229 RetryAttempt := 1;
228230 HTTPSender := THTTPSend.Create;
229231 try
230232 try
231233 // Пробуем получить файл
232234 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
233235 while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do
234236 begin
235237 Sleep(500 * RetryAttempt);
236238 HTTPSender.Clear;
237239 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
238240 RetryAttempt := RetryAttempt + 1;
239241 end;
240242 // Если у нас есть ответ от сервера, проверяем, не
241243 // был ли файл уже отправлен нам.
242244 case HTTPSender.Resultcode of
243245 100..299:
244246 begin
245247 HTTPSender.Document.SaveToFile(TargetFile);
246248 Result := True;
247249 end; //информирование, успешно
248250 300..399: Result := False; // перенаправление. Не реализовано, но могло бы быть.
249251 400..499: Result := False; // ошибка клиента; 404 не найдено и т.д.
250252 500..599: Result := False; // внутренняя ошибка сервера
251253 else Result := False; // неизвестный код
252254 end;
253255 except
254256 // Нас не интересует причина этой ошибки; загрузка не удалась.
255257 Result := False;
256258 end;
257259 finally
258260 HTTPSender.Free;
259261 end;
260262end;
261263```
262264
263265#### Простая версия с показом прогресса загрузки
264266
265267В следующем примере показано, как получить информацию о ходе загрузки по HTTP, а также размер файла. Размер файла извлекается из информации заголовка.
266268
267269``` pascal
268270unit uhttpdownloader;
269271
270272// Необходимо изменить это. По умолчанию это - {$mode objfpc}{$H+}, и не работает.
271273{$mode Delphi}
272274
273275interface
274276
275277uses
276278 Classes, SysUtils, httpsend, blcksock, typinfo;
277279
278280type
279281 IProgress = interface
280282 procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
281283 end;
282284
283285type
284286 { THttpDownloader }
285287
286288 THttpDownloader = class
287289 public
288290 function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
289291 private
290292 Bytes : Integer;
291293 MaxBytes : Integer;
292294 HTTPSender: THTTPSend;
293295 ProgressMonitor : IProgress;
294296 procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
295297 function GetSizeFromHeader(Header: String):integer;
296298 end;
297299
298300implementation
299301
300302function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
301303var
302304 HTTPGetResult: Boolean;
303305begin
304306 Result := False;
305307 Bytes:= 0;
306308 MaxBytes:= -1;
307309 Self.ProgressMonitor:= ProgressMonitor;
308310
309311 HTTPSender := THTTPSend.Create;
310312 try
311313 HTTPSender.Sock.OnStatus:= Status;
312314 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
313315 if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
314316 HTTPSender.Document.SaveToFile(TargetFile);
315317 Result := True;
316318 end;
317319 finally
318320 HTTPSender.Free;
319321 end;
320322end;
321323
322324procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
323325var
324326 V, currentHeader: String;
325327 i: integer;
326328begin
327329 //try to get filesize from headers
328330 if (MaxBytes = -1) then
329331 begin
330332 for i:= 0 to HTTPSender.Headers.Count - 1 do
331333 begin
332334 currentHeader:= HTTPSender.Headers[i];
333335 MaxBytes:= GetSizeFromHeader(currentHeader);
334336 if MaxBytes <> -1 then break;
335337 end;
336338 end;
337339
338340 V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;
339341
340342 if Reason = THookSocketReason.HR_ReadCount then
341343 begin
342344 Bytes:= Bytes + StrToInt(Value);
343345 ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);
344346 end;
345347end;
346348
347349function THttpDownloader.GetSizeFromHeader(Header: String): integer;
348350var
349351 item : TStringList;
350352begin
351353 Result:= -1;
352354
353355 if Pos('Content-Length:', Header) <> 0 then
354356 begin
355357 item:= TStringList.Create();
356358 item.Delimiter:= ':';
357359 item.StrictDelimiter:=true;
358360 item.DelimitedText:=Header;
359361 if item.Count = 2 then
360362 begin
361363 Result:= StrToInt(Trim(item[1]));
362364 end;
363365 end;
364366end;
365367
366368end.
367369```
368370
369371Что мы здесь делаем?
370372
371373Прежде всего, мы смотрим на заголовки, чтобы узнать размер файла. Надо подождать и проверить, есть ли заголовок. Первые события не содержат Content-Length: information.
372374
373375Найдя, мы извлекаем эту информацию. Здесь появляется несколько событий, на которые вы можете реагировать. Но мы проверяем только THookSocketReason.HR_ReadCount в этом примере.
374376
375377HR_ReadCount предоставляет нам информацию о том, сколько байтов было прочитано с момента последнего события.
376378
377379Затем прогресс передается в пользовательский интерфейс:
378380
379381``` pascal
380382procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
381383begin
382384 if (MaxProgress <> -1) then
383385 begin
384386 ProgressBar.Max:= MaxProgress;
385387 end;
386388
387389 ProgressBar.Position:= CurrentProgress;
388390 memoStatus.Lines.Add(Text);
389391 Application.ProcessMessages;
390392end;
391393```
392394
393395Итак, окончательно основной модуль будет таким:
394396
395397``` pascal
396398unit uMain;
397399
398400{$mode Delphi}
399401
400402interface
401403
402404uses
403405 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,
404406 uhttpdownloader;
405407
406408type
407409
408410 { TMainForm }
409411
410412 TMainForm = class(TForm, IProgress)
411413 btnStartDownload: TButton;
412414 edtUrl: TEdit;
413415 labelUrl: TLabel;
414416 memoStatus: TMemo;
415417 ProgressBar: TProgressBar;
416418 SaveDialog: TSaveDialog;
417419 procedure btnStartDownloadClick(Sender: TObject);
418420 private
419421 { private declarations }
420422 function GetFileNameFromURL(url: String):string;
421423 public
422424 { public declarations }
423425 procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
424426 end;
425427
426428var
427429 MainForm: TMainForm;
428430
429431implementation
430432
431433{$R *.lfm}
432434
433435{ TMainForm }
434436
435437procedure TMainForm.btnStartDownloadClick(Sender: TObject);
436438var
437439 fileName: String;
438440 downloader: THttpDownloader;
439441 success: boolean;
440442begin
441443 fileName:= GetFileNameFromURL(edtUrl.Text);
442444 SaveDialog.FileName:=fileName;
443445 if (SaveDialog.Execute) then
444446 begin
445447 memoStatus.Lines.Clear;
446448 ProgressBar.Position:=0;
447449 downloader:= THttpDownloader.Create();
448450 success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);
449451
450452 ProgressBar.Position:=0;
451453 if Success then
452454 memoStatus.Lines.Add('Download successful')
453455 else
454456 memoStatus.Lines.Add('Error during download');
455457
456458 end;
457459end;
458460
459461function TMainForm.GetFileNameFromURL(url: String): string;
460462var i, l : integer;
461463 fileName, current : String;
462464begin
463465 fileName:= '';
464466 l:= Length(url);
465467 for i:= l downto 0 do begin
466468 current:= url[i];
467469 if current <> '/' then
468470 begin
469471 fileName:= current + fileName;
470472 end else begin
471473 Result:= fileName;
472474 break;
473475 end;
474476 end;
475477end;
476478
477479procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
478480begin
479481 if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;
480482 ProgressBar.Position:= CurrentProgress;
481483 memoStatus.Lines.Add(Text);
482484 Application.ProcessMessages;
483485end;
484486
485487end.
486488```
487489
488490Ссылка: <https://andydunkel.net/2015/09/09/lazarus_synapse_progress/>
489491
490492#### С HTTP-сервера путем анализа URL-адресов: Sourceforge
491493
492494См. статью [[Download from SourceForge|Загрузка с SourceForge]] для примера загрузки с sourceforge.net.
493495
494496### С HTTPS-сервера
495497
496498Это похоже на загрузку с HTTP-сервера. Кроме того, вам необходимо [[Synapse/ru|активировать поддержку SSL/TLS]] и получить двоичный файл(ы) для необходимой библиотеки. Затем вы можете использовать ту же функцию DownloadHTTP для загрузки файла с URL-адреса, начинающегося с **<https://>**.
497499
498500## Пример программы клиента SSH/Telnet
499501
500502Ниже вы найдете модуль, который позволяет использовать клиентские функции telnet/SSH, использующий модуль synapse `tlntsend.pas`. Пример программы показывает, как это использовать. Другой, более простой способ проиллюстрирован Leonardo Ramé на [1](http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html). Его пример не может использовать telnet и отправляет только одну команду.
501503
502504### Требования
503505
504506Помимо исходников Synapse (которых вам понадобится всего пара), если вы хотите использовать функциональность SSH, вам понадобится библиотека шифрования, которую использует Synapse. Если вы используете только Telnet, вам это не нужно.
505507
506508Есть 2 варианта:
507509
508510- Библиотека Cryptlib. Преимущество: стабильность. По-видимому, можно использовать закрытые ключи, но они имеют некоторый формат, который широко не поддерживается.
509511- Библиотека LibSSH2. Привязки Pascal все еще находятся в разработке, но вы можете использовать файл с вашим закрытым ключом (в формате OpenSSH) для аутентификации.
510512
511513#### Cryptlib
512514
513515- В Windows загрузите двоичную версию библиотеки DLL cryptlib (CL32.DLL) и поместите ее в исходный каталог. Если вы компилируете в другой каталог или распространяете свою программу, вам также необходимо будет распространить DLL.
514516- В Linux и OSX установите cryptlib через диспетчер пакетов/другими способами. При распространении приложения отметьте cryptlib как требование в вашем пакете .deb/.rpm/любой.
515517
516518Вам также потребуются привязки (cryptlib.pas), присутствующие в исходном дистрибутиве cryptlib.
517519
518520Версии двоичного файла cryptlib и привязок должны совпадать.
519521
520522> **Note:** Кажется, что cryptlib не подходит для подключения к машинам с Linux, хотя AIX работает. Вместо этого используйте SSH2.
521523
522524#### LibSSH2
523525
524526- В Windows загрузите двоичную версию библиотеки libssh2 (LIBSSH2.DLL) и поместите ее в исходный каталог. Если вы компилируете в другой каталог или распространяете свою программу, вам также необходимо будет распространить DLL.
525527- В Linux и macOS установите libssh2 через диспетчер пакетов/другими способами. При распространении вашего приложения:
526528 - Linux: отметьте libssh2 как требование в вашем .deb/.rpm/любом пакете.
527529 - macOS: включите libssh2 в каталог ресурсов [[Application Bundle]].
528530
529531Вам также понадобится ssl_libssh2.pas (см. ниже) и привязки: (libssh2.pas, см. [это сообщение на форуме](http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465)). Двоичный файл libssh2 и привязки должны совпадать.
530532
531533### Synapse libssh2 SSL plugin
532534
533535> **Note:** плагин не доработан.
534536
535537``` pascal
536538{
537539 ssl_libssh2.pas version 0.2
538540
539541 Плагин поддержки SSH2 (черновик) для библиотеки Synapse (http://www.ararat.cz/synapse) от LibSSH2 (http://libssh2.org)
540542 Требуется: интерфейс libssh2 pascal - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 и
541543 libssh2.dll с OpenSSL.
542544
543545 (С) Alexey Suhinin http://x-alexey.narod.ru
544546}
545547
546548{$IFDEF FPC}
547549 {$MODE DELPHI}
548550{$ENDIF}
549551{$H+}
550552
551553unit ssl_libssh2;
552554
553555interface
554556
555557uses
556558 SysUtils,
557559 blcksock, synsock,
558560 libssh2;
559561
560562type
561563 {: @abstract(класс, реализующий плагин CryptLib SSL/SSH.)
562564 Экземпляр этого класса будет создан для каждого @link(TTCPBlockSocket).
563565 Вам не нужно создавать экземпляр этого класса, все делает сам Synapse!}
564566 TSSLLibSSH2 = class(TCustomSSL)
565567 protected
566568 FSession: PLIBSSH2_SESSION;
567569 FChannel: PLIBSSH2_CHANNEL;
568570 function SSHCheck(Value: integer): Boolean;
569571 function DeInit: Boolean;
570572 public
571573 {:См. @inherited}
572574 constructor Create(const Value: TTCPBlockSocket); override;
573575 destructor Destroy; override;
574576 function Connect: boolean; override;
575577 function LibName: String; override;
576578 function Shutdown: boolean; override;
577579 {:See @inherited}
578580 function BiShutdown: boolean; override;
579581 {:See @inherited}
580582 function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;
581583 {:See @inherited}
582584 function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;
583585 {:See @inherited}
584586 function WaitingData: Integer; override;
585587 {:See @inherited}
586588 function GetSSLVersion: string; override;
587589 published
588590 end;
589591
590592implementation
591593
592594{==============================================================================}
593595function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;
594596var
595597 PLastError: PAnsiChar;
596598 ErrMsgLen: Integer;
597599begin
598600 Result := true;
599601 FLastError := 0;
600602 FLastErrorDesc := '';
601603 if Value<0 then
602604 begin
603605 FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);
604606 FLastErrorDesc := PLastError;
605607 Result := false;
606608 end;
607609end;
608610
609611function TSSLLibSSH2.DeInit: Boolean;
610612begin
611613 if Assigned(FChannel) then
612614 begin
613615 libssh2_channel_free(FChannel);
614616 FChannel := nil;
615617 end;
616618 if Assigned(FSession) then
617619 begin
618620 libssh2_session_disconnect(FSession,'Goodbye');
619621 libssh2_session_free(FSession);
620622 FSession := nil;
621623 end;
622624 FSSLEnabled := False;
623625 Result := true;
624626end;
625627
626628constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);
627629begin
628630 inherited Create(Value);
629631 FSession := nil;
630632 FChannel := nil;
631633end;
632634
633635destructor TSSLLibSSH2.Destroy;
634636begin
635637 DeInit;
636638 inherited Destroy;
637639end;
638640
639641function TSSLLibSSH2.Connect: boolean;
640642begin
641643 Result := False;
642644 if SSLEnabled then DeInit;
643645 if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then
644646 begin
645647 FSession := libssh2_session_init();
646648 if not Assigned(FSession) then
647649 begin
648650 FLastError := -999;
649651 FLastErrorDesc := 'Cannot initialize SSH session';
650652 exit;
651653 end;
652654 if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then
653655 exit;
654656 if (FSocket.SSL.PrivateKeyFile<>'') then
655657 begin
656658 if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then
657659 exit;
658660 end
659661 else
660662 if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then
661663 begin
662664 if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then
663665 exit;
664666 end;
665667 FChannel := libssh2_channel_open_session(FSession);
666668 if not assigned(FChannel) then
667669 begin
668670 SSHCheck(-1); // получаем ошибку
669671 if FLastError = 0 then
670672 begin
671673 FLastError := -999; // неизвестная ошибка
672674 FLastErrorDesc := 'Cannot open session';
673675 end;
674676 exit;
675677 end;
676678 if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then
677679 exit;
678680 if not SSHCheck(libssh2_channel_shell(FChannel)) then
679681 exit;
680682 FSSLEnabled := True;
681683 Result := True;
682684 end;
683685end;
684686
685687function TSSLLibSSH2.LibName: String;
686688begin
687689 Result := 'ssl_libssh2';
688690end;
689691
690692function TSSLLibSSH2.Shutdown: boolean;
691693begin
692694 Result := DeInit;
693695end;
694696
695697function TSSLLibSSH2.BiShutdown: boolean;
696698begin
697699 Result := DeInit;
698700end;
699701
700702function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;
701703begin
702704 Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);
703705 SSHCheck(Result);
704706end;
705707
706708function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;
707709begin
708710 result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);
709711 SSHCheck(Result);
710712end;
711713
712714function TSSLLibSSH2.WaitingData: Integer;
713715begin
714716 if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;
715717end;
716718
717719function TSSLLibSSH2.GetSSLVersion: string;
718720begin
719721 Result:=libssh2_version(0);
720722end;
721723
722724initialization
723725 if libssh2_init(0)=0 then
724726 SSLImplementation := TSSLLibSSH2;
725727
726728finalization
727729 libssh2_exit;
728730
729731end.
730732```
731733
732734### Класс клиента терминала
733735
734736Модуль telnetsshclient.pas, представленный ниже, охватывает модуль Synapse tlntsend.pas и абстрагирует вход в систему, отправку команд, получение вывода и выход из системы.
735737
736738Если вам нужен только telnet-клиент и вы можете жить без поддержки SSH, закомментируйте {\$DEFINE HAS_SSH_SUPPORT} ниже, чтобы вам не нужна была библиотека libssh2.
737739
738740Этот модуль было слегка протестировано на сервере Linux ssh/telnet. Дополнительные тесты приветствуются.
739741
740742``` pascal
741743unit telnetsshclient;
742744
743745{ Обертка для библиотек Synapse и SSL (libssh2 + libssl
744746используется прямо сейчас)
745747Загрузите скомпилированную dll Windows, например,
746748http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip
747749Загрузите файлы интерфейса FreePascal:
748750http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465
749751
750752Это устройство позволяет пользователю отправлять команды Telnet или SSH и получать результат
751753Спасибо Leonardo Rame
752754http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html
753755и Ludo Brands.
754756
755757Написано Reinier Olislagers 2011.
756758Изменено для libssh2 Алексеем Сухининым 2012.
757759
758760Лицензия кода:
759761* MIT
760762* LGPLv2 или новее (с исключением статического связывания FreePascal)
761763* GPLv2 или новее
762764по вашему выбору.
763765Бесплатное использование разрешено, но, пожалуйста, не предъявляйте иск и не обвиняйте меня.
764766
765767Использует другие библиотеки/компоненты; могут применяться разные лицензии, которые также могут влиять на комбинированную/скомпилированную работу.
766768}
767769
768770{$mode objfpc}{$H+}
769771{$DEFINE HAS_SSH_SUPPORT} //закомментируйте, если требуется только поддержка telnet
770772{$DEFINE LIBSSH2}
771773
772774interface
773775
774776uses
775777 Classes, SysUtils,
776778 tlntsend
777779 {$IFDEF HAS_SSH_SUPPORT}
778780 {ssl - или на самом деле ssh - библиотеки, требуемые tlntsend}
779781 {$IFDEF LIBSSH2}
780782 ssl_libssh2
781783 {$ELSE}
782784 ssl_cryptlib
783785 {$ENDIF}
784786 {$ENDIF HAS_SSH_SUPPORT} ;
785787
786788type
787789 TProtocolType = (Telnet, SSH); //Различные способы подключения
788790 TServerType = (Unix, Windows); //окончания строк, в основном
789791 { TelnetSSHClient }
790792
791793 { TTelnetSSHClient }
792794
793795 TTelnetSSHClient = class(TTelnetSend)
794796 protected
795797 FConnected: boolean;
796798 FOutputPosition: integer; //Отслеживает позицию в выходном потоке
797799 FProtocolType: TProtocolType;
798800 FServerLineEnding: string; //зависит от FServerType
799801 FServerType: TServerType;
800802 FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;
801803 procedure SetPrivateKeyFile(Value: string);
802804 function GetPrivateKeyFile: string;
803805 { В зависимости от протокола и типа сервера установите ожидаемое окончание строки на стороне сервера.}
804806 procedure DetermineLineEnding;
805807 { Устанавливает порт, если он явно не задан. Использует тип протокола: SSH или telnet}
806808 procedure DeterminePort;
807809 function GetSessionLog: string;
808810 procedure ProtocolTypeChange(Value: TProtocolType);
809811 function ReceiveData: string; //Может использоваться для получения приветственного сообщения и т.д.
810812 procedure SendData(Data: string);
811813 procedure ServerTypeChange(Value: TServerType);
812814 public
813815 {Весь вывод, созданный в течение всего сеанса до настоящего момента}
814816 property AllOutput: string read GetSessionLog;
815817 {Истинно, если есть подключение к серверу}
816818 property Connected: boolean read FConnected;
817819 {Имя или IP-адрес хоста для подключения}
818820 property HostName: string read FTargetHost write FTargetHost;
819821 {Порт на хосте, используемый для подключения. Если оставить 0, он будет определяться типом протокола (22 для SSH, 23 для Telnet)}
820822 property Port: String read FTargetPort write FTargetPort;
821823 {Расположение файла закрытого ключа.}
822824 property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;
823825 {Telnet login prompt}
824826 property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;
825827 {Подсказка входа в Telnet}
826828 property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;
827829 {Имя пользователя, используемое при подключении}
828830 property UserName: string read FUserName write FUserName;
829831 {Пароль, используемый при подключении. Используется как кодовая фраза, если используется PrivateKey}
830832 property Password: string read FPassword write FPassword;
831833 {Должны ли мы общаться с сервером через Telnet или SSH? По умолчанию SSH.}
832834 property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;
833835 {Сервер Windows или Unix/Linux? Влияет на окончания строк. По умолчанию Unix. ПРИМЕЧАНИЕ: непроверено}
834836 property Servertype: TServerType read FServerType write ServerTypeChange;
835837 {Первоначальное сообщение отображается при входе в систему}
836838 property WelcomeMessage: string read FWelcomeMessage;
837839 {Подключение/вход на сервер. Требуется, чтобы все параметры аутентификации, протокола и имени хоста/порта были правильными
838840 Возвращает описательный результат. Затем вы можете использовать свойство Connected.}
839841 function Connect: string;
840842 {Если подключено, выйти с сервера}
841843 procedure Disconnect;
842844 {Отправляет команду на сервер и получает результат}
843845 function CommandResult(Command: string): string; //Отправьте команду и получите результат
844846 constructor Create;
845847 destructor Destroy; override;
846848 end;
847849
848850implementation
849851
850852
851853{ TelnetSSHClient }
852854procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);
853855begin
854856 Sock.SSL.PrivateKeyFile := value;
855857end;
856858
857859function TTelnetSSHClient.GetPrivateKeyFile: string;
858860begin
859861 Result := Sock.SSL.PrivateKeyFile;
860862end;
861863
862864procedure TTelnetSSHClient.DetermineLineEnding;
863865begin
864866 case FProtocolType of
865867 SSH:
866868 begin
867869 if FServerType = Unix then
868870 FServerLineEnding := #10 //Unix
869871 else
870872 FServerLineEnding := #13 + #10; //windows
871873 end;
872874 Telnet:
873875 begin
874876 if FServerType = Unix then
875877 FServerLineEnding := #10 //Unix
876878 else
877879 FServerLineEnding := #13 + #10; //windows
878880 end;
879881 else
880882 raise Exception.Create('Unknown protocol type');
881883 end;
882884end;
883885
884886procedure Ttelnetsshclient.DeterminePort;
885887begin
886888 if FTargetPort = '' then
887889 //Задаем порт по умолчанию для протокола
888890 begin
889891 case FProtocolType of
890892 Telnet: FTargetPort := '23';
891893 SSH: FTargetPort := '22';
892894 else
893895 raise Exception.Create('Unknown protocol type.');
894896 end;
895897
896898 end;
897899end;
898900
899901procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);
900902begin
901903 FServerType := Value;
902904 DetermineLineEnding;
903905end;
904906
905907function TTelnetSSHClient.Connect: string;
906908var
907909 Received: string;
908910begin
909911 result:='Unknown error while connecting';
910912 FOutputPosition := 1; //Первый символ в потоке вывода
911913 FWelcomeMessage := '';
912914 //Просто чтобы убедиться:
913915 DetermineLineEnding;
914916 DeterminePort;
915917 if FTargetPort='0' then
916918 begin
917919 result:='Port may not be 0.';
918920 exit; //выскакиваем из функции
919921 end;
920922 case FProtocolType of
921923 Telnet:
922924 begin
923925 try
924926 if Login then
925927 begin
926928 FConnected := True;
927929 result:='Connected to telnet server.';
928930 end
929931 else
930932 if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
931933 except
932934 on E: Exception do
933935 begin
934936 FConnected:=false;
935937 result:='Error connecting to telnet server '+FTargetHost+':'+
936938 FTargetPort+' as user ' + FUserName +
937939 '. Technical details: '+E.Message;
938940 end;
939941 end;
940942 end;
941943 SSH:
942944 begin
943945 {$IFNDEF HAS_SSH_SUPPORT}
944946 raise Exception.Create(
945947 'SSH support has not been compiled into the telnetsshclient library.');
946948 {$ENDIF HAS_SSH_SUPPORT}
947949 try
948950 if (PrivateKeyFile <> '') and (FPassword <> '') then
949951 Sock.SSL.KeyPassword:=FPassword;
950952 if SSHLogin then
951953 begin
952954 FConnected := True;
953955 result:='Connected to SSH server.';
954956 end
955957 else
956958 begin
957959 if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
958960 if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);
959961 end;
960962 except
961963 on E: Exception do
962964 begin
963965 FConnected:=false;
964966 result:='Error connecting to SSH server '+FTargetHost+':'+
965967 FTargetPort+' as user ' + FUserName +
966968 '. Technical details: '+E.Message;
967969 end;
968970 end;
969971 end;
970972 else
971973 raise Exception.Create('Unknown protocol type');
972974 end;
973975 if FConnected = True then
974976 begin
975977 FWelcomeMessage := ReceiveData;
976978 if FProtocolType=Telnet then
977979 begin
978980 //К сожалению, нам придется извлекать логин самостоятельно
979981 //Надеюсь, это применимо ко всем типам серверов.
980982 if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then
981983 begin
982984 SendData(UserName);
983985 end;
984986 Received:=ReceiveData;
985987 if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then
986988 begin
987989 SendData(Password);
988990 end;
989991 //Получаем дополнительное приветственное сообщение/подсказку дня
990992 FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;
991993 end;
992994 end;
993995end;
994996
995997procedure TTelnetSSHClient.Disconnect;
996998begin
997999 Logout;
9981000 FConnected := False;
9991001end;
10001002
10011003function TTelnetSSHClient.ReceiveData: string;
10021004begin
10031005 Result := '';
10041006 while Sock.CanRead(1000) or (Sock.WaitingData > 0) do
10051007 begin
10061008 Sock.RecvPacket(1000);
10071009 Result := Result + Copy(SessionLog, FOutputPosition,
10081010 Length(SessionLog));
10091011 FOutputPosition := Length(SessionLog) + 1;
10101012 end;
10111013end;
10121014
10131015procedure Ttelnetsshclient.SendData(Data: String);
10141016begin
10151017 Data := Data + FServerLineEnding; //Может быть Linux, может быть Windows
10161018 Send(Data);
10171019end;
10181020
10191021function TTelnetSSHClient.GetSessionLog: string;
10201022begin
10211023 // Получает полный вывод на данный момент
10221024 Result := SessionLog;
10231025end;
10241026
10251027procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);
10261028begin
10271029 FProtocolType := Value;
10281030 //Автоматическое определение порта и символов конца строки, если необходимо
10291031 DeterminePort;
10301032 DetermineLineEnding;
10311033end;
10321034
10331035function TTelnetSSHClient.CommandResult(Command: string): string;
10341036begin
10351037 Result := '';
10361038 if FConnected then
10371039 begin
10381040 SendData(Command);
10391041 Result := ReceiveData; //получает слишком много
10401042 end
10411043 else
10421044 begin
10431045 //возбуждаем исключение
10441046 Result := '';
10451047 raise Exception.Create('Can only run command when connected');
10461048 end;
10471049end;
10481050
10491051constructor TTelnetSSHClient.Create;
10501052begin
10511053 inherited;
10521054 FConnected := False;
10531055 FProtocolType := SSH; //также может быть telnet
10541056 FServerType := Unix; //Probably a safe default.
10551057 FTelnetLoginPrompt := 'login:';
10561058 FTelnetPasswordPrompt := 'password:';
10571059 DetermineLineEnding;
10581060 DeterminePort;
10591061end;
10601062
10611063destructor TTelnetSSHClient.Destroy;
10621064begin
10631065 if FConnected then
10641066 Disconnect;
10651067 inherited Destroy;
10661068end;
10671069
10681070end.
10691071```
10701072
10711073### Пример клиентского кода
10721074
10731075Чтобы использовать только что созданный класс TTelnetSSHClient, вы можете использовать этот пример приложения, sshtest.lpr. Обратите внимание, что он должен быть скомпилирован Lazarus, так как ему нужны компоненты LCL для работы с Synapse:
10741076
10751077``` pascal
10761078program sshtest;
10771079
10781080{Программа тестирования для telnetsshclient
10791081
10801082Написано Reinier Olislagers 2011.
10811083Изменено для libssh2 Алексеем Сухининым 2012.
10821084
10831085Лицензия кода:
10841086* MIT
10851087* LGPLv2 или новее (с исключением статического связывания FreePascal)
10861088* GPLv2 или новее
10871089по вашему выбору.
10881090Бесплатное использование разрешено, но, пожалуйста, не предъявляйте претензии и не обвиняйте меня.
10891091
10901092Использует другие библиотеки/компоненты; могут применяться разные лицензии, которые также могут влиять на комбинированную/скомпилированную работу.
10911093
10921094Запустите: sshtest serverIPorhostname [PrivateKeyFile]
10931095}
10941096{$mode objfpc}{$H+}
10951097{$APPTYPE CONSOLE}
10961098
10971099uses
10981100 telnetsshclient;
10991101var
11001102 comm: TTelnetSSHClient;
11011103 Command: string;
11021104begin
11031105 writeln('Starting.');
11041106 comm:=TTelnetSSHClient.Create;
11051107 comm.HostName:= ParamStr(1); //Первый аргумент в командной строке
11061108 if comm.HostName='' then
11071109 begin
11081110 writeln('Please specify hostname on command line.');
11091111 halt(1);
11101112 end;
11111113
11121114 comm.PrivateKeyFile := ParamStr(2);
11131115
11141116 comm.TargetPort:='0'; //автоматическое определение на основе типа протокола
11151117 comm.UserName:='root'; //измените с соответствии с своей ситуацией
11161118 comm.Password:='password'; //измените с соответствии с своей ситуацией
11171119 comm.ProtocolType:=SSH; //Telnet или SSH
11181120 writeln(comm.Connect); //Показать результат подключения
11191121 if comm.Connected then
11201122 begin
11211123 writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);
11221124 writeln('Welcome message:');
11231125 writeln(comm.WelcomeMessage);
11241126 Command:='ls -al';
11251127 writeln('*** Sending ' + Command);
11261128 writeln('*** Begin result****');
11271129 writeln(comm.CommandResult(Command));
11281130 writeln('*** End result****');
11291131 writeln('');
11301132 writeln('');
11311133 Command:='df -h';
11321134 writeln('*** Sending ' + Command);
11331135 writeln('*** Begin result****');
11341136 writeln(comm.CommandResult(Command));
11351137 writeln('*** End result****');
11361138 writeln('');
11371139 writeln('');
11381140 writeln('All output:');
11391141 writeln('*** Begin result****');
11401142 writeln(comm.AllOutput);
11411143 writeln('*** End result****');
11421144 comm.Disconnect;
11431145 end
11441146 else
11451147 begin
11461148 writeln('Connection to ' +
11471149 comm.HostName + ':' +
11481150 comm.TargetPort + ' failed.');
11491151 end;
11501152 comm.Free;
11511153end.
11521154```
11531155
11541156## Интреграция OAuth с v1/Twitter/Plurk
11551157
11561158Библиотека OAuth v1, написанная на FPC, которая использует Synapse (и готова для других сетевых библиотек, таких как lnet) доступна [здесь](https://bitbucket.org/mararosas/fpctwit/src/default/). FPCTwit также содержит примеры клиентских программ FPC twitter и plurk и Twitter-клиент Lazarus.
11571159
11581160## Другие статьи о Интернете и сетях
11591161
11601162- [[Portal:Web Development|Web Development Portal]]
11611163- [[Networking]]
11621164- [[Networking libraries]] - сравнение различных сетевых библиотек
11631165- [[Brook Framework]] - Идеальный фреймворк Free Pascal для ваших веб-приложений. Это чистый Паскаль. Вам не нужно оставлять предпочитаемый язык программирования.
11641166- [[Sockets]] - Компоненты сокетов TCP/IP
11651167- [[fcl-net]] - Сетевая библиотека, поставляемая с FPC
11661168- [[lNet]] - Легковесные сетевые компоненты
11671169- [[Synapse]] - Последовательный порт и синхронная библиотека TCP/IP
11681170- [[XML Tutorial]] -XML, часто использующиеся в сетевых коммуникациях
11691171- [[FPC and Apache Modules]]
11701172- [[fcl-web]] - Также известная как fpWeb, это библиотека для разработки веб-приложений, которые можно развернуть как модули cgi, fastcgi или apache.
11711173- [[Secure programming|Безопасное программирование]]
11721174- [[Internet Tools]] - Обертка для HTTP-компонентов Synapse/wininet/Android, упрощающая https и перенаправления, и механизм XPath/XQuery/CSS Selector/JSONiq для обработки загруженных страниц.
11731175
11741176## См.также
11751177
11761178- [[Download from SourceForge]] Пример, который использует Synapse для загрузки с HTTP-сервера, который перенаправляет.
11771179- [Visual Synapse](https://sourceforge.net/projects/visualsynapse/) обертки компонентов для многих частей последовательной и сетевой библиотеки Synapse (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualHTTP, TvsVisualDUP, TvsViscksVisualTvs
11781180- [Компонент TCP/IP на базе Synapse + небольшое демо приложение](https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html)
11791181
11801182## Внешние ссылки
11811183
11821184- [Официальный сайт](http://www.ararat.cz/synapse/)
11831185- [Официальная документация](http://synapse.ararat.cz/doc/help/)
11841186- [Пользователь malcome создал форк, чтобы быстрее улучшить Synapse](http://lazarus.freepascal.org/index.php/topic,16032.msg87066.html#msg87066); этот форк расположен в [Google code site for Synapse4Lazarus](http://code.google.com/p/synapse4lazarus/)
11851187
11861188---
11871189*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