Sign in Join
Wiki copytemporary

Synapse/ru: what changed

Revision 1 to 2. 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
4040`/usr/bin/ld: cannot find -lcl`
4141
4242Аналогичное сообщение будет отображаться при использовании других динамических библиотек.
4343
4444## Пример веб-сервера
4545
4646См. [[Networking|примеры Webserver]].
4747
4848## Пример запроса сервера QOTD
4949
5050См. [[QOTD|пример запроса сервера Quote of the Day]].
5151
5252## Отправка email
5353
54-Статья <i>Michaël Van Canneyt</i> об отправке электронной почты, включая вложения, с помощью Synapse: <http://www.freepascal.org/~michael/articles/lazmail/lazmail-en.pdf>
54+Статья Michaël Van Canneyt об отправке электронной почты, включая вложения, с помощью Synapse: <http://www.freepascal.org/~michael/articles/lazmail/lazmail-en.pdf>
5555
5656Из сообщения [на форуме](http://forum.lazarus.freepascal.org/index.php/topic,21157.msg123501.html#msg123501); работает, например, с Gmail:
5757
5858``` pascal
5959{Этот код поддерживает использование шифрования TLS/SSL; при отправке на порт 25 используется простой текстовый SMTP.}
6060
6161uses
6262 ..., smtpsend,ssl_openssl; //вероятно, можно использовать и другие модули SSL.
6363
6464// MailData - это текст письма.
6565function SendMail(
6666 User, Password,
6767 MailFrom, MailTo,
6868 SMTPHost, SMTPPort: string;
6969 MailData: string): Boolean;
7070var
7171 SMTP: TSMTPSend;
7272 sl:TStringList;
7373begin
7474 Result:=False;
7575 SMTP:=TSMTPSend.Create;
7676 sl:=TStringList.Create;
7777 try
7878 sl.text:=Maildata;
7979 SMTP.UserName:=User;
8080 SMTP.Password:=Password;
8181 SMTP.TargetHost:=SMTPHost;
8282 SMTP.TargetPort:=SMTPPort;
8383 SMTP.AutoTLS:=true;
8484 if Trim(SMTPPort)<>'25' then
8585 SMTP.FullSSL:=true; // при отправке на порт 25 шифрование не используется
8686 if SMTP.Login then
8787 begin
8888 result:=SMTP.MailFrom(MailFrom, Length(MailData)) and
8989 SMTP.MailTo(MailTo) and
9090 SMTP.MailData(sl);
9191 SMTP.Logout;
9292 end;
9393 finally
9494 SMTP.Free;
9595 sl.Free;
9696 end;
9797end;
9898```
9999
100100### Отправка вложений
101101
102102См. [документацию Synapse](http://synapse.ararat.cz/doku.php/public:howto:tmimepart).
103103
104104## Загрузка файлов
105105
106106### С FTP-сервера
107107
108108Учитывая URL-адрес и (путь и) имя файла, это загрузит его с FTP-сервера. В основном это обертка кода Synapse, предназначенная для облегчения загрузки при работе с произвольными файлами. Если вы точно знаете, что будете скачивать, простой вызов Synapse:
109109
110110``` pascal
111111FtpGetFile
112112```
113113
114114доставит вас очень далеко.
115115
116116``` pascal
117117function DownloadFTP(URL, TargetFile: string): boolean;
118118const
119119 FTPPort=21;
120120 FTPScheme='ftp://'; //Имя схемы URI для URL-адресов FTP
121121var
122122 Host: string;
123123 Port: integer;
124124 Source: string;
125125 FoundPos: integer;
126126begin
127127 // Вычеркиваем информацию о схеме:
128128 if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));
129129
130130 // Грубый парсинг; мог использовать код синтаксического анализа URI в пакетах FPC ...
131131 FoundPos:=pos('/', URL);
132132 Host:=LeftStr(URL, FoundPos-1);
133133 Source:=Copy(URL, FoundPos+1, Length(URL));
134134
135135 //Проверка номера портов:
136136 FoundPos:=pos(':', Host);
137137 Port:=FTPPort;
138138 if FoundPos>0 then
139139 begin
140140 Host:=LeftStr(Host, FoundPos-1);
141141 Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);
142142 end;
143143 Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');
144144 if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);
145145end;
146146```
147147
148148Пример получения списка файлов по заданному пути
149149
150150``` pascal
151151//Используем модуль ftpsend
152152
153153function FtpGetDir(const IP, Port, Path, User, Pass: string; DirList: TStringList): Boolean;
154154var
155155 i: Integer;
156156 s: string;
157157begin
158158 Result := False;
159159 with TFTPSend.Create do
160160 try
161161 Username := User;
162162 Password := Pass;
163163 TargetHost := IP;
164164 TargetPort := Port;
165165 if not Login then
166166 Exit;
167167 Result := List(Path, False);
168168 for i := 0 to FtpList.Count -1 do
169169 begin
170170 s := FTPList[i].FileName;
171171 DirList.Add(s);
172172 end;
173173 Logout;
174174 finally
175175 Free;
176176 end;
177177end;
178178```
179179
180180### С HTTP-сервера
181181
182182Учитывая URL-адрес и (путь и) имя файла, это загрузит его с HTTP-сервера. Обратите внимание, что этот код проверяет код состояния HTTP (например, 200, 404), чтобы узнать, является ли документ, который мы получили обратно с сервера, желаемым файлом или же страницей с ошибкой.
183183
184184#### Простая версия
185185
186186``` pascal
187187...
188188uses httpsend,
189189...
190190function DownloadHTTP(URL, TargetFile: string): Boolean;
191191var
192192 HTTPGetResult: Boolean;
193193 HTTPSender: THTTPSend;
194194begin
195195 Result := False;
196196 HTTPSender := THTTPSend.Create;
197197 try
198198 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
199199 if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
200200 HTTPSender.Document.SaveToFile(TargetFile);
201201 Result := True;
202202 end;
203203 finally
204204 HTTPSender.Free;
205205 end;
206206end;
207207```
208208
209209#### Расширенная версия
210210
211211``` pascal
212212...
213213uses httpsend
214214...
215215function DownloadHTTP(URL, TargetFile: string): Boolean;
216216// Грузим файл; при необходимости повторяем попытку.
217217// Можно использовать Synapse HttpGetBinary, но это не работает
218218// с кодами результата (т.е. он успешно загружает документ с ошибкой 404)
219219const
220220 MaxRetries = 3;
221221var
222222 HTTPGetResult: Boolean;
223223 HTTPSender: THTTPSend;
224224 RetryAttempt: Integer;
225225begin
226226 Result := False;
227227 RetryAttempt := 1;
228228 HTTPSender := THTTPSend.Create;
229229 try
230230 try
231231 // Пробуем получить файл
232232 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
233233 while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do
234234 begin
235235 Sleep(500 * RetryAttempt);
236236 HTTPSender.Clear;
237237 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
238238 RetryAttempt := RetryAttempt + 1;
239239 end;
240240 // Если у нас есть ответ от сервера, проверяем, не
241241 // был ли файл уже отправлен нам.
242242 case HTTPSender.Resultcode of
243243 100..299:
244244 begin
245245 HTTPSender.Document.SaveToFile(TargetFile);
246246 Result := True;
247247 end; //информирование, успешно
248248 300..399: Result := False; // перенаправление. Не реализовано, но могло бы быть.
249249 400..499: Result := False; // ошибка клиента; 404 не найдено и т.д.
250250 500..599: Result := False; // внутренняя ошибка сервера
251251 else Result := False; // неизвестный код
252252 end;
253253 except
254254 // Нас не интересует причина этой ошибки; загрузка не удалась.
255255 Result := False;
256256 end;
257257 finally
258258 HTTPSender.Free;
259259 end;
260260end;
261261```
262262
263263#### Простая версия с показом прогресса загрузки
264264
265265В следующем примере показано, как получить информацию о ходе загрузки по HTTP, а также размер файла. Размер файла извлекается из информации заголовка.
266266
267267``` pascal
268268unit uhttpdownloader;
269269
270270// Необходимо изменить это. По умолчанию это - {$mode objfpc}{$H+}, и не работает.
271271{$mode Delphi}
272272
273273interface
274274
275275uses
276276 Classes, SysUtils, httpsend, blcksock, typinfo;
277277
278278type
279279 IProgress = interface
280280 procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
281281 end;
282282
283283type
284284 { THttpDownloader }
285285
286286 THttpDownloader = class
287287 public
288288 function DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
289289 private
290290 Bytes : Integer;
291291 MaxBytes : Integer;
292292 HTTPSender: THTTPSend;
293293 ProgressMonitor : IProgress;
294294 procedure Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
295295 function GetSizeFromHeader(Header: String):integer;
296296 end;
297297
298298implementation
299299
300300function THttpDownloader.DownloadHTTP(URL, TargetFile: string; ProgressMonitor : IProgress): Boolean;
301301var
302302 HTTPGetResult: Boolean;
303303begin
304304 Result := False;
305305 Bytes:= 0;
306306 MaxBytes:= -1;
307307 Self.ProgressMonitor:= ProgressMonitor;
308308
309309 HTTPSender := THTTPSend.Create;
310310 try
311311 HTTPSender.Sock.OnStatus:= Status;
312312 HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
313313 if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then begin
314314 HTTPSender.Document.SaveToFile(TargetFile);
315315 Result := True;
316316 end;
317317 finally
318318 HTTPSender.Free;
319319 end;
320320end;
321321
322322procedure THttpDownloader.Status(Sender: TObject; Reason: THookSocketReason; const Value: String);
323323var
324324 V, currentHeader: String;
325325 i: integer;
326326begin
327327 //try to get filesize from headers
328328 if (MaxBytes = -1) then
329329 begin
330330 for i:= 0 to HTTPSender.Headers.Count - 1 do
331331 begin
332332 currentHeader:= HTTPSender.Headers[i];
333333 MaxBytes:= GetSizeFromHeader(currentHeader);
334334 if MaxBytes <> -1 then break;
335335 end;
336336 end;
337337
338338 V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;
339339
340340 if Reason = THookSocketReason.HR_ReadCount then
341341 begin
342342 Bytes:= Bytes + StrToInt(Value);
343343 ProgressMonitor.ProgressNotification(V, Bytes, MaxBytes);
344344 end;
345345end;
346346
347347function THttpDownloader.GetSizeFromHeader(Header: String): integer;
348348var
349349 item : TStringList;
350350begin
351351 Result:= -1;
352352
353353 if Pos('Content-Length:', Header) <> 0 then
354354 begin
355355 item:= TStringList.Create();
356356 item.Delimiter:= ':';
357357 item.StrictDelimiter:=true;
358358 item.DelimitedText:=Header;
359359 if item.Count = 2 then
360360 begin
361361 Result:= StrToInt(Trim(item[1]));
362362 end;
363363 end;
364364end;
365365
366366end.
367367```
368368
369369Что мы здесь делаем?
370370
371371Прежде всего, мы смотрим на заголовки, чтобы узнать размер файла. Надо подождать и проверить, есть ли заголовок. Первые события не содержат Content-Length: information.
372372
373373Найдя, мы извлекаем эту информацию. Здесь появляется несколько событий, на которые вы можете реагировать. Но мы проверяем только THookSocketReason.HR_ReadCount в этом примере.
374374
375375HR_ReadCount предоставляет нам информацию о том, сколько байтов было прочитано с момента последнего события.
376376
377377Затем прогресс передается в пользовательский интерфейс:
378378
379379``` pascal
380380procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
381381begin
382382 if (MaxProgress <> -1) then
383383 begin
384384 ProgressBar.Max:= MaxProgress;
385385 end;
386386
387387 ProgressBar.Position:= CurrentProgress;
388388 memoStatus.Lines.Add(Text);
389389 Application.ProcessMessages;
390390end;
391391```
392392
393393Итак, окончательно основной модуль будет таким:
394394
395395``` pascal
396396unit uMain;
397397
398398{$mode Delphi}
399399
400400interface
401401
402402uses
403403 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, httpsend, blcksock, typinfo,
404404 uhttpdownloader;
405405
406406type
407407
408408 { TMainForm }
409409
410410 TMainForm = class(TForm, IProgress)
411411 btnStartDownload: TButton;
412412 edtUrl: TEdit;
413413 labelUrl: TLabel;
414414 memoStatus: TMemo;
415415 ProgressBar: TProgressBar;
416416 SaveDialog: TSaveDialog;
417417 procedure btnStartDownloadClick(Sender: TObject);
418418 private
419419 { private declarations }
420420 function GetFileNameFromURL(url: String):string;
421421 public
422422 { public declarations }
423423 procedure ProgressNotification(Text: String; CurrentProgress : integer; MaxProgress : integer);
424424 end;
425425
426426var
427427 MainForm: TMainForm;
428428
429429implementation
430430
431431{$R *.lfm}
432432
433433{ TMainForm }
434434
435435procedure TMainForm.btnStartDownloadClick(Sender: TObject);
436436var
437437 fileName: String;
438438 downloader: THttpDownloader;
439439 success: boolean;
440440begin
441441 fileName:= GetFileNameFromURL(edtUrl.Text);
442442 SaveDialog.FileName:=fileName;
443443 if (SaveDialog.Execute) then
444444 begin
445445 memoStatus.Lines.Clear;
446446 ProgressBar.Position:=0;
447447 downloader:= THttpDownloader.Create();
448448 success:= downloader.DownloadHTTP(edtUrl.Text, SaveDialog.FileName, Self);
449449
450450 ProgressBar.Position:=0;
451451 if Success then
452452 memoStatus.Lines.Add('Download successful')
453453 else
454454 memoStatus.Lines.Add('Error during download');
455455
456456 end;
457457end;
458458
459459function TMainForm.GetFileNameFromURL(url: String): string;
460460var i, l : integer;
461461 fileName, current : String;
462462begin
463463 fileName:= '';
464464 l:= Length(url);
465465 for i:= l downto 0 do begin
466466 current:= url[i];
467467 if current <> '/' then
468468 begin
469469 fileName:= current + fileName;
470470 end else begin
471471 Result:= fileName;
472472 break;
473473 end;
474474 end;
475475end;
476476
477477procedure TMainForm.ProgressNotification(Text: String; CurrentProgress: integer; MaxProgress: integer);
478478begin
479479 if (MaxProgress <> -1) then ProgressBar.Max:= MaxProgress;
480480 ProgressBar.Position:= CurrentProgress;
481481 memoStatus.Lines.Add(Text);
482482 Application.ProcessMessages;
483483end;
484484
485485end.
486486```
487487
488488Ссылка: <https://andydunkel.net/2015/09/09/lazarus_synapse_progress/>
489489
490490#### С HTTP-сервера путем анализа URL-адресов: Sourceforge
491491
492492См. статью [[Download from SourceForge|Загрузка с SourceForge]] для примера загрузки с sourceforge.net.
493493
494494### С HTTPS-сервера
495495
496496Это похоже на загрузку с HTTP-сервера. Кроме того, вам необходимо [[Synapse/ru|активировать поддержку SSL/TLS]] и получить двоичный файл(ы) для необходимой библиотеки. Затем вы можете использовать ту же функцию DownloadHTTP для загрузки файла с URL-адреса, начинающегося с **<https://>**.
497497
498498## Пример программы клиента SSH/Telnet
499499
500-Ниже вы найдете модуль, который позволяет использовать клиентские функции telnet/SSH, использующий модуль synapse `tlntsend.pas`. Пример программы показывает, как это использовать. Другой, более простой способ проиллюстрирован <i>Leonardo Ramé</i> на [1](http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html). Его пример не может использовать telnet и отправляет только одну команду.
500+Ниже вы найдете модуль, который позволяет использовать клиентские функции telnet/SSH, использующий модуль synapse `tlntsend.pas`. Пример программы показывает, как это использовать. Другой, более простой способ проиллюстрирован Leonardo Ramé на [1](http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html). Его пример не может использовать telnet и отправляет только одну команду.
501501
502502### Требования
503503
504504Помимо исходников Synapse (которых вам понадобится всего пара), если вы хотите использовать функциональность SSH, вам понадобится библиотека шифрования, которую использует Synapse. Если вы используете только Telnet, вам это не нужно.
505505
506506Есть 2 варианта:
507507
508508- Библиотека Cryptlib. Преимущество: стабильность. По-видимому, можно использовать закрытые ключи, но они имеют некоторый формат, который широко не поддерживается.
509509- Библиотека LibSSH2. Привязки Pascal все еще находятся в разработке, но вы можете использовать файл с вашим закрытым ключом (в формате OpenSSH) для аутентификации.
510510
511511#### Cryptlib
512512
513513- В Windows загрузите двоичную версию библиотеки DLL cryptlib (CL32.DLL) и поместите ее в исходный каталог. Если вы компилируете в другой каталог или распространяете свою программу, вам также необходимо будет распространить DLL.
514514- В Linux и OSX установите cryptlib через диспетчер пакетов/другими способами. При распространении приложения отметьте cryptlib как требование в вашем пакете .deb/.rpm/любой.
515515
516516Вам также потребуются привязки (cryptlib.pas), присутствующие в исходном дистрибутиве cryptlib.
517517
518518Версии двоичного файла cryptlib и привязок должны совпадать.
519519
520520> **Note:** Кажется, что cryptlib не подходит для подключения к машинам с Linux, хотя AIX работает. Вместо этого используйте SSH2.
521521
522522#### LibSSH2
523523
524524- В Windows загрузите двоичную версию библиотеки libssh2 (LIBSSH2.DLL) и поместите ее в исходный каталог. Если вы компилируете в другой каталог или распространяете свою программу, вам также необходимо будет распространить DLL.
525525- В Linux и macOS установите libssh2 через диспетчер пакетов/другими способами. При распространении вашего приложения:
526526 - Linux: отметьте libssh2 как требование в вашем .deb/.rpm/любом пакете.
527527 - macOS: включите libssh2 в каталог ресурсов [[Application Bundle]].
528528
529529Вам также понадобится ssl_libssh2.pas (см. ниже) и привязки: (libssh2.pas, см. [это сообщение на форуме](http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465)). Двоичный файл libssh2 и привязки должны совпадать.
530530
531531### Synapse libssh2 SSL plugin
532532
533533> **Note:** плагин не доработан.
534534
535535``` pascal
536536{
537537 ssl_libssh2.pas version 0.2
538538
539539 Плагин поддержки SSH2 (черновик) для библиотеки Synapse (http://www.ararat.cz/synapse) от LibSSH2 (http://libssh2.org)
540540 Требуется: интерфейс libssh2 pascal - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 и
541541 libssh2.dll с OpenSSL.
542542
543543 (С) Alexey Suhinin http://x-alexey.narod.ru
544544}
545545
546546{$IFDEF FPC}
547547 {$MODE DELPHI}
548548{$ENDIF}
549549{$H+}
550550
551551unit ssl_libssh2;
552552
553553interface
554554
555555uses
556556 SysUtils,
557557 blcksock, synsock,
558558 libssh2;
559559
560560type
561561 {: @abstract(класс, реализующий плагин CryptLib SSL/SSH.)
562562 Экземпляр этого класса будет создан для каждого @link(TTCPBlockSocket).
563563 Вам не нужно создавать экземпляр этого класса, все делает сам Synapse!}
564564 TSSLLibSSH2 = class(TCustomSSL)
565565 protected
566566 FSession: PLIBSSH2_SESSION;
567567 FChannel: PLIBSSH2_CHANNEL;
568568 function SSHCheck(Value: integer): Boolean;
569569 function DeInit: Boolean;
570570 public
571571 {:См. @inherited}
572572 constructor Create(const Value: TTCPBlockSocket); override;
573573 destructor Destroy; override;
574574 function Connect: boolean; override;
575575 function LibName: String; override;
576576 function Shutdown: boolean; override;
577577 {:See @inherited}
578578 function BiShutdown: boolean; override;
579579 {:See @inherited}
580580 function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;
581581 {:See @inherited}
582582 function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;
583583 {:See @inherited}
584584 function WaitingData: Integer; override;
585585 {:See @inherited}
586586 function GetSSLVersion: string; override;
587587 published
588588 end;
589589
590590implementation
591591
592592{==============================================================================}
593593function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;
594594var
595595 PLastError: PAnsiChar;
596596 ErrMsgLen: Integer;
597597begin
598598 Result := true;
599599 FLastError := 0;
600600 FLastErrorDesc := '';
601601 if Value<0 then
602602 begin
603603 FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);
604604 FLastErrorDesc := PLastError;
605605 Result := false;
606606 end;
607607end;
608608
609609function TSSLLibSSH2.DeInit: Boolean;
610610begin
611611 if Assigned(FChannel) then
612612 begin
613613 libssh2_channel_free(FChannel);
614614 FChannel := nil;
615615 end;
616616 if Assigned(FSession) then
617617 begin
618618 libssh2_session_disconnect(FSession,'Goodbye');
619619 libssh2_session_free(FSession);
620620 FSession := nil;
621621 end;
622622 FSSLEnabled := False;
623623 Result := true;
624624end;
625625
626626constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);
627627begin
628628 inherited Create(Value);
629629 FSession := nil;
630630 FChannel := nil;
631631end;
632632
633633destructor TSSLLibSSH2.Destroy;
634634begin
635635 DeInit;
636636 inherited Destroy;
637637end;
638638
639639function TSSLLibSSH2.Connect: boolean;
640640begin
641641 Result := False;
642642 if SSLEnabled then DeInit;
643643 if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then
644644 begin
645645 FSession := libssh2_session_init();
646646 if not Assigned(FSession) then
647647 begin
648648 FLastError := -999;
649649 FLastErrorDesc := 'Cannot initialize SSH session';
650650 exit;
651651 end;
652652 if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then
653653 exit;
654654 if (FSocket.SSL.PrivateKeyFile<>'') then
655655 begin
656656 if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) then
657657 exit;
658658 end
659659 else
660660 if (FSocket.SSL.Username<>'') and (FSocket.SSL.Password<>'') then
661661 begin
662662 if (not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then
663663 exit;
664664 end;
665665 FChannel := libssh2_channel_open_session(FSession);
666666 if not assigned(FChannel) then
667667 begin
668668 SSHCheck(-1); // получаем ошибку
669669 if FLastError = 0 then
670670 begin
671671 FLastError := -999; // неизвестная ошибка
672672 FLastErrorDesc := 'Cannot open session';
673673 end;
674674 exit;
675675 end;
676676 if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then
677677 exit;
678678 if not SSHCheck(libssh2_channel_shell(FChannel)) then
679679 exit;
680680 FSSLEnabled := True;
681681 Result := True;
682682 end;
683683end;
684684
685685function TSSLLibSSH2.LibName: String;
686686begin
687687 Result := 'ssl_libssh2';
688688end;
689689
690690function TSSLLibSSH2.Shutdown: boolean;
691691begin
692692 Result := DeInit;
693693end;
694694
695695function TSSLLibSSH2.BiShutdown: boolean;
696696begin
697697 Result := DeInit;
698698end;
699699
700700function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;
701701begin
702702 Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);
703703 SSHCheck(Result);
704704end;
705705
706706function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;
707707begin
708708 result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);
709709 SSHCheck(Result);
710710end;
711711
712712function TSSLLibSSH2.WaitingData: Integer;
713713begin
714714 if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;
715715end;
716716
717717function TSSLLibSSH2.GetSSLVersion: string;
718718begin
719719 Result:=libssh2_version(0);
720720end;
721721
722722initialization
723723 if libssh2_init(0)=0 then
724724 SSLImplementation := TSSLLibSSH2;
725725
726726finalization
727727 libssh2_exit;
728728
729729end.
730730```
731731
732732### Класс клиента терминала
733733
734734Модуль telnetsshclient.pas, представленный ниже, охватывает модуль Synapse tlntsend.pas и абстрагирует вход в систему, отправку команд, получение вывода и выход из системы.
735735
736736Если вам нужен только telnet-клиент и вы можете жить без поддержки SSH, закомментируйте {\$DEFINE HAS_SSH_SUPPORT} ниже, чтобы вам не нужна была библиотека libssh2.
737737
738738Этот модуль было слегка протестировано на сервере Linux ssh/telnet. Дополнительные тесты приветствуются.
739739
740740``` pascal
741741unit telnetsshclient;
742742
743743{ Обертка для библиотек Synapse и SSL (libssh2 + libssl
744744используется прямо сейчас)
745745Загрузите скомпилированную dll Windows, например,
746746http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip
747747Загрузите файлы интерфейса FreePascal:
748748http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465
749749
750750Это устройство позволяет пользователю отправлять команды Telnet или SSH и получать результат
751751Спасибо Leonardo Rame
752752http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html
753753и Ludo Brands.
754754
755755Написано Reinier Olislagers 2011.
756756Изменено для libssh2 Алексеем Сухининым 2012.
757757
758758Лицензия кода:
759759* MIT
760760* LGPLv2 или новее (с исключением статического связывания FreePascal)
761761* GPLv2 или новее
762762по вашему выбору.
763763Бесплатное использование разрешено, но, пожалуйста, не предъявляйте иск и не обвиняйте меня.
764764
765765Использует другие библиотеки/компоненты; могут применяться разные лицензии, которые также могут влиять на комбинированную/скомпилированную работу.
766766}
767767
768768{$mode objfpc}{$H+}
769769{$DEFINE HAS_SSH_SUPPORT} //закомментируйте, если требуется только поддержка telnet
770770{$DEFINE LIBSSH2}
771771
772772interface
773773
774774uses
775775 Classes, SysUtils,
776776 tlntsend
777777 {$IFDEF HAS_SSH_SUPPORT}
778778 {ssl - или на самом деле ssh - библиотеки, требуемые tlntsend}
779779 {$IFDEF LIBSSH2}
780780 ssl_libssh2
781781 {$ELSE}
782782 ssl_cryptlib
783783 {$ENDIF}
784784 {$ENDIF HAS_SSH_SUPPORT} ;
785785
786786type
787787 TProtocolType = (Telnet, SSH); //Различные способы подключения
788788 TServerType = (Unix, Windows); //окончания строк, в основном
789789 { TelnetSSHClient }
790790
791791 { TTelnetSSHClient }
792792
793793 TTelnetSSHClient = class(TTelnetSend)
794794 protected
795795 FConnected: boolean;
796796 FOutputPosition: integer; //Отслеживает позицию в выходном потоке
797797 FProtocolType: TProtocolType;
798798 FServerLineEnding: string; //зависит от FServerType
799799 FServerType: TServerType;
800800 FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;
801801 procedure SetPrivateKeyFile(Value: string);
802802 function GetPrivateKeyFile: string;
803803 { В зависимости от протокола и типа сервера установите ожидаемое окончание строки на стороне сервера.}
804804 procedure DetermineLineEnding;
805805 { Устанавливает порт, если он явно не задан. Использует тип протокола: SSH или telnet}
806806 procedure DeterminePort;
807807 function GetSessionLog: string;
808808 procedure ProtocolTypeChange(Value: TProtocolType);
809809 function ReceiveData: string; //Может использоваться для получения приветственного сообщения и т.д.
810810 procedure SendData(Data: string);
811811 procedure ServerTypeChange(Value: TServerType);
812812 public
813813 {Весь вывод, созданный в течение всего сеанса до настоящего момента}
814814 property AllOutput: string read GetSessionLog;
815815 {Истинно, если есть подключение к серверу}
816816 property Connected: boolean read FConnected;
817817 {Имя или IP-адрес хоста для подключения}
818818 property HostName: string read FTargetHost write FTargetHost;
819819 {Порт на хосте, используемый для подключения. Если оставить 0, он будет определяться типом протокола (22 для SSH, 23 для Telnet)}
820820 property Port: String read FTargetPort write FTargetPort;
821821 {Расположение файла закрытого ключа.}
822822 property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;
823823 {Telnet login prompt}
824824 property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;
825825 {Подсказка входа в Telnet}
826826 property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;
827827 {Имя пользователя, используемое при подключении}
828828 property UserName: string read FUserName write FUserName;
829829 {Пароль, используемый при подключении. Используется как кодовая фраза, если используется PrivateKey}
830830 property Password: string read FPassword write FPassword;
831831 {Должны ли мы общаться с сервером через Telnet или SSH? По умолчанию SSH.}
832832 property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;
833833 {Сервер Windows или Unix/Linux? Влияет на окончания строк. По умолчанию Unix. ПРИМЕЧАНИЕ: непроверено}
834834 property Servertype: TServerType read FServerType write ServerTypeChange;
835835 {Первоначальное сообщение отображается при входе в систему}
836836 property WelcomeMessage: string read FWelcomeMessage;
837837 {Подключение/вход на сервер. Требуется, чтобы все параметры аутентификации, протокола и имени хоста/порта были правильными
838838 Возвращает описательный результат. Затем вы можете использовать свойство Connected.}
839839 function Connect: string;
840840 {Если подключено, выйти с сервера}
841841 procedure Disconnect;
842842 {Отправляет команду на сервер и получает результат}
843843 function CommandResult(Command: string): string; //Отправьте команду и получите результат
844844 constructor Create;
845845 destructor Destroy; override;
846846 end;
847847
848848implementation
849849
850850
851851{ TelnetSSHClient }
852852procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);
853853begin
854854 Sock.SSL.PrivateKeyFile := value;
855855end;
856856
857857function TTelnetSSHClient.GetPrivateKeyFile: string;
858858begin
859859 Result := Sock.SSL.PrivateKeyFile;
860860end;
861861
862862procedure TTelnetSSHClient.DetermineLineEnding;
863863begin
864864 case FProtocolType of
865865 SSH:
866866 begin
867867 if FServerType = Unix then
868868 FServerLineEnding := #10 //Unix
869869 else
870870 FServerLineEnding := #13 + #10; //windows
871871 end;
872872 Telnet:
873873 begin
874874 if FServerType = Unix then
875875 FServerLineEnding := #10 //Unix
876876 else
877877 FServerLineEnding := #13 + #10; //windows
878878 end;
879879 else
880880 raise Exception.Create('Unknown protocol type');
881881 end;
882882end;
883883
884884procedure Ttelnetsshclient.DeterminePort;
885885begin
886886 if FTargetPort = '' then
887887 //Задаем порт по умолчанию для протокола
888888 begin
889889 case FProtocolType of
890890 Telnet: FTargetPort := '23';
891891 SSH: FTargetPort := '22';
892892 else
893893 raise Exception.Create('Unknown protocol type.');
894894 end;
895895
896896 end;
897897end;
898898
899899procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);
900900begin
901901 FServerType := Value;
902902 DetermineLineEnding;
903903end;
904904
905905function TTelnetSSHClient.Connect: string;
906906var
907907 Received: string;
908908begin
909909 result:='Unknown error while connecting';
910910 FOutputPosition := 1; //Первый символ в потоке вывода
911911 FWelcomeMessage := '';
912912 //Просто чтобы убедиться:
913913 DetermineLineEnding;
914914 DeterminePort;
915915 if FTargetPort='0' then
916916 begin
917917 result:='Port may not be 0.';
918918 exit; //выскакиваем из функции
919919 end;
920920 case FProtocolType of
921921 Telnet:
922922 begin
923923 try
924924 if Login then
925925 begin
926926 FConnected := True;
927927 result:='Connected to telnet server.';
928928 end
929929 else
930930 if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
931931 except
932932 on E: Exception do
933933 begin
934934 FConnected:=false;
935935 result:='Error connecting to telnet server '+FTargetHost+':'+
936936 FTargetPort+' as user ' + FUserName +
937937 '. Technical details: '+E.Message;
938938 end;
939939 end;
940940 end;
941941 SSH:
942942 begin
943943 {$IFNDEF HAS_SSH_SUPPORT}
944944 raise Exception.Create(
945945 'SSH support has not been compiled into the telnetsshclient library.');
946946 {$ENDIF HAS_SSH_SUPPORT}
947947 try
948948 if (PrivateKeyFile <> '') and (FPassword <> '') then
949949 Sock.SSL.KeyPassword:=FPassword;
950950 if SSHLogin then
951951 begin
952952 FConnected := True;
953953 result:='Connected to SSH server.';
954954 end
955955 else
956956 begin
957957 if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
958958 if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);
959959 end;
960960 except
961961 on E: Exception do
962962 begin
963963 FConnected:=false;
964964 result:='Error connecting to SSH server '+FTargetHost+':'+
965965 FTargetPort+' as user ' + FUserName +
966966 '. Technical details: '+E.Message;
967967 end;
968968 end;
969969 end;
970970 else
971971 raise Exception.Create('Unknown protocol type');
972972 end;
973973 if FConnected = True then
974974 begin
975975 FWelcomeMessage := ReceiveData;
976976 if FProtocolType=Telnet then
977977 begin
978978 //К сожалению, нам придется извлекать логин самостоятельно
979979 //Надеюсь, это применимо ко всем типам серверов.
980980 if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then
981981 begin
982982 SendData(UserName);
983983 end;
984984 Received:=ReceiveData;
985985 if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then
986986 begin
987987 SendData(Password);
988988 end;
989989 //Получаем дополнительное приветственное сообщение/подсказку дня
990990 FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;
991991 end;
992992 end;
993993end;
994994
995995procedure TTelnetSSHClient.Disconnect;
996996begin
997997 Logout;
998998 FConnected := False;
999999end;
10001000
10011001function TTelnetSSHClient.ReceiveData: string;
10021002begin
10031003 Result := '';
10041004 while Sock.CanRead(1000) or (Sock.WaitingData > 0) do
10051005 begin
10061006 Sock.RecvPacket(1000);
10071007 Result := Result + Copy(SessionLog, FOutputPosition,
10081008 Length(SessionLog));
10091009 FOutputPosition := Length(SessionLog) + 1;
10101010 end;
10111011end;
10121012
10131013procedure Ttelnetsshclient.SendData(Data: String);
10141014begin
10151015 Data := Data + FServerLineEnding; //Может быть Linux, может быть Windows
10161016 Send(Data);
10171017end;
10181018
10191019function TTelnetSSHClient.GetSessionLog: string;
10201020begin
10211021 // Получает полный вывод на данный момент
10221022 Result := SessionLog;
10231023end;
10241024
10251025procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);
10261026begin
10271027 FProtocolType := Value;
10281028 //Автоматическое определение порта и символов конца строки, если необходимо
10291029 DeterminePort;
10301030 DetermineLineEnding;
10311031end;
10321032
10331033function TTelnetSSHClient.CommandResult(Command: string): string;
10341034begin
10351035 Result := '';
10361036 if FConnected then
10371037 begin
10381038 SendData(Command);
10391039 Result := ReceiveData; //получает слишком много
10401040 end
10411041 else
10421042 begin
10431043 //возбуждаем исключение
10441044 Result := '';
10451045 raise Exception.Create('Can only run command when connected');
10461046 end;
10471047end;
10481048
10491049constructor TTelnetSSHClient.Create;
10501050begin
10511051 inherited;
10521052 FConnected := False;
10531053 FProtocolType := SSH; //также может быть telnet
10541054 FServerType := Unix; //Probably a safe default.
10551055 FTelnetLoginPrompt := 'login:';
10561056 FTelnetPasswordPrompt := 'password:';
10571057 DetermineLineEnding;
10581058 DeterminePort;
10591059end;
10601060
10611061destructor TTelnetSSHClient.Destroy;
10621062begin
10631063 if FConnected then
10641064 Disconnect;
10651065 inherited Destroy;
10661066end;
10671067
10681068end.
10691069```
10701070
10711071### Пример клиентского кода
10721072
10731073Чтобы использовать только что созданный класс TTelnetSSHClient, вы можете использовать этот пример приложения, sshtest.lpr. Обратите внимание, что он должен быть скомпилирован Lazarus, так как ему нужны компоненты LCL для работы с Synapse:
10741074
10751075``` pascal
10761076program sshtest;
10771077
10781078{Программа тестирования для telnetsshclient
10791079
10801080Написано Reinier Olislagers 2011.
10811081Изменено для libssh2 Алексеем Сухининым 2012.
10821082
10831083Лицензия кода:
10841084* MIT
10851085* LGPLv2 или новее (с исключением статического связывания FreePascal)
10861086* GPLv2 или новее
10871087по вашему выбору.
10881088Бесплатное использование разрешено, но, пожалуйста, не предъявляйте претензии и не обвиняйте меня.
10891089
10901090Использует другие библиотеки/компоненты; могут применяться разные лицензии, которые также могут влиять на комбинированную/скомпилированную работу.
10911091
10921092Запустите: sshtest serverIPorhostname [PrivateKeyFile]
10931093}
10941094{$mode objfpc}{$H+}
10951095{$APPTYPE CONSOLE}
10961096
10971097uses
10981098 telnetsshclient;
10991099var
11001100 comm: TTelnetSSHClient;
11011101 Command: string;
11021102begin
11031103 writeln('Starting.');
11041104 comm:=TTelnetSSHClient.Create;
11051105 comm.HostName:= ParamStr(1); //Первый аргумент в командной строке
11061106 if comm.HostName='' then
11071107 begin
11081108 writeln('Please specify hostname on command line.');
11091109 halt(1);
11101110 end;
11111111
11121112 comm.PrivateKeyFile := ParamStr(2);
11131113
11141114 comm.TargetPort:='0'; //автоматическое определение на основе типа протокола
11151115 comm.UserName:='root'; //измените с соответствии с своей ситуацией
11161116 comm.Password:='password'; //измените с соответствии с своей ситуацией
11171117 comm.ProtocolType:=SSH; //Telnet или SSH
11181118 writeln(comm.Connect); //Показать результат подключения
11191119 if comm.Connected then
11201120 begin
11211121 writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);
11221122 writeln('Welcome message:');
11231123 writeln(comm.WelcomeMessage);
11241124 Command:='ls -al';
11251125 writeln('*** Sending ' + Command);
11261126 writeln('*** Begin result****');
11271127 writeln(comm.CommandResult(Command));
11281128 writeln('*** End result****');
11291129 writeln('');
11301130 writeln('');
11311131 Command:='df -h';
11321132 writeln('*** Sending ' + Command);
11331133 writeln('*** Begin result****');
11341134 writeln(comm.CommandResult(Command));
11351135 writeln('*** End result****');
11361136 writeln('');
11371137 writeln('');
11381138 writeln('All output:');
11391139 writeln('*** Begin result****');
11401140 writeln(comm.AllOutput);
11411141 writeln('*** End result****');
11421142 comm.Disconnect;
11431143 end
11441144 else
11451145 begin
11461146 writeln('Connection to ' +
11471147 comm.HostName + ':' +
11481148 comm.TargetPort + ' failed.');
11491149 end;
11501150 comm.Free;
11511151end.
11521152```
11531153
11541154## Интреграция OAuth с v1/Twitter/Plurk
11551155
11561156Библиотека OAuth v1, написанная на FPC, которая использует Synapse (и готова для других сетевых библиотек, таких как lnet) доступна [здесь](https://bitbucket.org/mararosas/fpctwit/src/default/). FPCTwit также содержит примеры клиентских программ FPC twitter и plurk и Twitter-клиент Lazarus.
11571157
11581158## Другие статьи о Интернете и сетях
11591159
11601160- [[Portal:Web Development|Web Development Portal]]
11611161- [[Networking]]
11621162- [[Networking libraries]] - сравнение различных сетевых библиотек
11631163- [[Brook Framework]] - Идеальный фреймворк Free Pascal для ваших веб-приложений. Это чистый Паскаль. Вам не нужно оставлять предпочитаемый язык программирования.
11641164- [[Sockets]] - Компоненты сокетов TCP/IP
11651165- [[fcl-net]] - Сетевая библиотека, поставляемая с FPC
11661166- [[lNet]] - Легковесные сетевые компоненты
11671167- [[Synapse]] - Последовательный порт и синхронная библиотека TCP/IP
11681168- [[XML Tutorial]] -XML, часто использующиеся в сетевых коммуникациях
11691169- [[FPC and Apache Modules]]
11701170- [[fcl-web]] - Также известная как fpWeb, это библиотека для разработки веб-приложений, которые можно развернуть как модули cgi, fastcgi или apache.
11711171- [[Secure programming|Безопасное программирование]]
11721172- [[Internet Tools]] - Обертка для HTTP-компонентов Synapse/wininet/Android, упрощающая https и перенаправления, и механизм XPath/XQuery/CSS Selector/JSONiq для обработки загруженных страниц.
11731173
11741174## См.также
11751175
11761176- [[Download from SourceForge]] Пример, который использует Synapse для загрузки с HTTP-сервера, который перенаправляет.
11771177- [Visual Synapse](https://sourceforge.net/projects/visualsynapse/) обертки компонентов для многих частей последовательной и сетевой библиотеки Synapse (TvsComPort, TvsWebClient, TvsSniffer, TvsHTTPServer, TvsFTPServer, TvsAuthentication, TvsVisualDNS, TvsVisualHTTP, TvsVisualDUP, TvsVisualHTTP, TvsVisualDUP, TvsViscksVisualTvs
11781178- [Компонент TCP/IP на базе Synapse + небольшое демо приложение](https://forum.lazarus.freepascal.org/index.php/topic,48677.0.html)
11791179
11801180## Внешние ссылки
11811181
11821182- [Официальный сайт](http://www.ararat.cz/synapse/)
11831183- [Официальная документация](http://synapse.ararat.cz/doc/help/)
11841184- [Пользователь malcome создал форк, чтобы быстрее улучшить Synapse](http://lazarus.freepascal.org/index.php/topic,16032.msg87066.html#msg87066); этот форк расположен в [Google code site for Synapse4Lazarus](http://code.google.com/p/synapse4lazarus/)
11851185
11861186---
11871187*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