Synapse
Synapse provides an easy to use serial port and synchronous TCP/IP library.
Template:Web and Networking Programming
Overview
Synapse offers serial port and TCP/IP connectivity. It differs from other libraries that you only require to add some Synapse Pascal source code files to your code; no need for installing packages etc. The only exception is that you will need an external crypto library if you want to use encryption such as SSL/TLS/SSH. See the documentation on the official site (link below) for more details.
SSL/TLS support
You can use OpenSSL, CryptLib, StreamSecII or OpenStreamSecII SSL support with synapse. By default no SSL support is used. The support is activated by putting chosen unit name to uses section in your project. You also have to put binary library file to your project path. SSL library files are loaded by synapse in runtime as dynamic library.
- For detailed information refer to Plugin Architecture
- Some crypt libraries can be obtained from: http://synapse.ararat.cz/files/crypt/
Missing library
On Linux you need used dynamic library present on your system. In case of cryptlib if the library is not present on the system, an error message appears during linking:
/usr/bin/ld: cannot find -lcl
Similar message will be displayed with use other dynamic libraries.
Web server example
See Networking#Webserver example
Sending email
Example for a command line program; from forum post [1]
If you want to use SSL/TLS connections to e.g. gmail mail servers, you'll probably need to adapt this code.
uses
...smtpsend... //and possibly other synapse units
var from, receiver,subject,line: string;
sl: TStringList;
i: Integer;
begin
from := 'default sender @ somewhere';
receiver:= 'default receiver @ somewhere';
for i:=1 to Paramcount do
if ParamStr(i) = '--from' then from := ParamStr(i+1)
else if ParamStr(i) = '--to' then receiver:= ParamStr(i+1)
else if ParamStr(i) = '--subject' then subject:= ParamStr(i+1);
sl:= TStringList.Create;
while not eof(Input) do begin
ReadLn(line);
sl.Add(Utf8ToAnsi(line));
end;
SendToEx(from,receiver,'=?utf-8?B?'+ EncodeBase64(subject)+ '?=','<your smtp server>',sl,'<your username>','<your password>');
end.
Downloading files
From an FTP server
Given an URL and a (path and) file name, this will download it from an FTP server. It's mostly a wrapper around the Synapse code meant to make downloading easier when handling arbitrary files. If you know exactly what you're going to download where, just a call to Synapse
FtpGetFile
will get you very far.
function DownloadFTP(URL, TargetFile: string): boolean;
const
FTPPort=21;
FTPScheme='ftp://'; //URI scheme name for FTP URLs
var
Host: string;
Port: integer;
Source: string;
FoundPos: integer;
begin
// Strip out scheme info:
if LeftStr(URL, length(FTPScheme))=FTPScheme then URL:=Copy(URL, length(FTPScheme)+1, length(URL));
// Crude parsing; could have used URI parsing code in FPC packages...
FoundPos:=pos('/', URL);
Host:=LeftStr(URL, FoundPos-1);
Source:=Copy(URL, FoundPos+1, Length(URL));
//Check for port numbers:
FoundPos:=pos(':', Host);
Port:=FTPPort;
if FoundPos>0 then
begin
Host:=LeftStr(Host, FoundPos-1);
Port:=StrToIntDef(Copy(Host, FoundPos+1, Length(Host)),21);
end;
Result:=FtpGetFile(Host, IntToStr(Port), Source, TargetFile, 'anonymous', 'fpc@example.com');
if result=false then writeln('DownloadFTP: error downloading '+URL+'. Details: host: '+Host+'; port: '+Inttostr(Port)+'; remote path: '+Source+' to '+TargetFile);
end;
From an HTTP server
Given an URL and a (path and) file name, this will download it from an HTTP server. Note that this code checks the HTTP status code (like 200, 404) to see if the document we got back from the server is the desired file or an error page.
Simple version
...
uses httpsend,
...
function DownloadHTTP(URL, TargetFile: string): Boolean;
var
HTTPGetResult: Boolean;
HTTPSender: THTTPSend;
begin
Result := False;
HTTPSender := THTTPSend.Create;
try
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
if HTTPSender.ResultCode = 100..299 then begin
HTTPGetResult.Document.SaveToFile(TargetFile);
Result := True;
end;
finally
HTTPSender.Free;
end;
end;
Advanced version
...
uses httpsend
...
function DownloadHTTP(URL, TargetFile: string): Boolean;
// Download file; retry if necessary.
// Could use Synapse HttpGetBinary, but that doesn't deal
// with result codes (i.e. it happily downloads a 404 error document)
const
MaxRetries = 3;
var
HTTPGetResult: Boolean;
HTTPSender: THTTPSend;
RetryAttempt: Integer;
begin
Result := False;
RetryAttempt := 1;
HTTPSender := THTTPSend.Create;
try
try
// Try to get the file
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do
begin
Sleep(500 * RetryAttempt);
HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);
RetryAttempt := RetryAttempt + 1;
end;
// If we have an answer from the server, check if the file
// was sent to us.
case HTTPSender.Resultcode of
100..299:
begin
HTTPGetResult.Document.SaveToFile(TargetFile);
Result := True;
end; //informational, success
300..399: Result := False; // redirection. Not implemented, but could be.
400..499: Result := False; // client error; 404 not found etc
500..599: Result := False; // internal server error
else Result := False; // unknown code
end;
except
// We don't care for the reason for this error; the download failed.
Result := False;
end;
finally
HTTPSender.Free;
end;
end;
From an HTTP server by parsing URLs: Sourceforge
Please see Download from SourceForge for an example of downloading from sourceforge.net.
From an HTTPS server
This is similar to downloading from the HTTP server. In addition you need activate one of Synapse#SSL.2FTLS_support SSL/TLC plugin and obtain binary files of need library. Then you can same DownloadHTTP function for downloading file from URL starting with https://.
SSH/Telnet client sample program
Below you will find a unit that allows you to use telnet/SSH client functionality that uses the synapse tlntsend.pas unit. An example program shows how to use this. A different, simpler way is illustrated by Leonardo Ramé at [2]. His example cannot use telnet and only sends one command, though.
Requirements
Apart from the Synapse sources (of which you only need a couple), if you want to use SSH functionality, you will need an encryption library that Synapse uses. If you only use Telnet, you don't need this.
There are 2 choices:
- Cryptlib library. Advantage: stable
- LibSSH2 library. Pascal bindings still in development, but you can use a file with your private key (in OpenSSH format) to authenticate.
Cryptlib
- On Windows, download a binary version of the cryptlib DLL (CL32.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.
- On Linux and OSX, install cryptlib via your package manager/other means. When distributing your application, mark cryptlib as a requirement in your .deb/.rpm/whatever package.
You will also need the bindings (cryptlib.pas), present in the source distribution of cryptlib.
The versions of the cryptlib binary and the bindings must match.
LibSSH2
- On Windows, download a binary version of the libssh2 DLL (LIBSSH2.DLL) and put it in your source directory. If you compile to a different directory or distribute your program, you will need to distribute the DLL as well.
- On Linux and OSX, install libssh2 via your package manager/other means. When distributing your application, mark libssh2 as a requirement in your .deb/.rpm/whatever package.
You will also need ssl_libssh2.pas (see below) and the bindings: (libssh2.pas, see [3]).
The libssh2 binary and the bindings must match.
Synapse libssh2 SSL plugin

{
ssl_libssh2.pas version 0.2
SSH2 support (draft) plugin for Synapse Library (http://www.ararat.cz/synapse) by LibSSH2 (http://libssh2.org)
Requires: libssh2 pascal interface - http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465 and
libssh2.dll with OpenSSL.
(С) Alexey Suhinin http://x-alexey.narod.ru
}
{$IFDEF FPC}
{$MODE DELPHI}
{$ENDIF}
{$H+}
unit ssl_libssh2;
interface
uses
SysUtils,
blcksock, synsock,
libssh2;
type
{:@abstract(class implementing CryptLib SSL/SSH plugin.)
Instance of this class will be created for each @link(TTCPBlockSocket).
You not need to create instance of this class, all is done by Synapse itself!}
TSSLLibSSH2 = class(TCustomSSL)
protected
FSession: PLIBSSH2_SESSION;
FChannel: PLIBSSH2_CHANNEL;
function SSHCheck(Value: integer): Boolean;
function DeInit: Boolean;
public
{:See @inherited}
constructor Create(const Value: TTCPBlockSocket); override;
destructor Destroy; override;
function Connect: boolean; override;
function LibName: String; override;
function Shutdown: boolean; override;
{:See @inherited}
function BiShutdown: boolean; override;
{:See @inherited}
function SendBuffer(Buffer: TMemory; Len: Integer): Integer; override;
{:See @inherited}
function RecvBuffer(Buffer: TMemory; Len: Integer): Integer; override;
{:See @inherited}
function WaitingData: Integer; override;
{:See @inherited}
function GetSSLVersion: string; override;
published
end;
implementation
{==============================================================================}
function TSSLLibSSH2.SSHCheck(Value: integer): Boolean;
var
PLastError: PAnsiChar;
ErrMsgLen: Integer;
begin
Result := true;
FLastError := 0;
FLastErrorDesc := '';
if Value<0 then
begin
FLastError := libssh2_session_last_error(FSession, PLastError, ErrMsglen, 0);
FLastErrorDesc := PLastError;
Result := false;
end;
end;
function TSSLLibSSH2.DeInit: Boolean;
begin
if Assigned(FChannel) then
begin
libssh2_channel_free(FChannel);
FChannel := nil;
end;
if Assigned(FSession) then
begin
libssh2_session_disconnect(FSession,'Goodbye');
libssh2_session_free(FSession);
FSession := nil;
end;
FSSLEnabled := False;
Result := true;
end;
constructor TSSLLibSSH2.Create(const Value: TTCPBlockSocket);
begin
inherited Create(Value);
FSession := nil;
FChannel := nil;
end;
destructor TSSLLibSSH2.Destroy;
begin
DeInit;
inherited Destroy;
end;
function TSSLLibSSH2.Connect: boolean;
begin
Result := False;
if SSLEnabled then DeInit;
if (FSocket.Socket <> INVALID_SOCKET) and (FSocket.SSL.SSLType = LT_SSHv2) then
begin
FSession := libssh2_session_init();
if not Assigned(FSession) then
begin
FLastError := -999;
FLastErrorDesc := 'Cannot initialize SSH session';
exit;
end;
if not SSHCheck(libssh2_session_startup(FSession, FSocket.Socket)) then
exit;
if FSocket.SSL.PrivateKeyFile<>'' then
if (not SSHCheck(libssh2_userauth_publickey_fromfile(FSession, PChar(FSocket.SSL.Username), nil, PChar(FSocket.SSL.PrivateKeyFile), PChar(FSocket.SSL.KeyPassword)))) and
(not SSHCheck(libssh2_userauth_password(FSession, PChar(FSocket.SSL.Username), PChar(FSocket.SSL.Password)))) then
exit;
FChannel := libssh2_channel_open_session(FSession);
if not assigned(FChannel) then
begin
// SSHCheck(-1);
FLastError:=-999;
FLastErrorDesc := 'Cannot open session';
exit;
end;
if not SSHCheck(libssh2_channel_request_pty(FChannel, 'vanilla')) then
exit;
if not SSHCheck(libssh2_channel_shell(FChannel)) then
exit;
FSSLEnabled := True;
Result := True;
end;
end;
function TSSLLibSSH2.LibName: String;
begin
Result := 'ssl_libssh2';
end;
function TSSLLibSSH2.Shutdown: boolean;
begin
Result := DeInit;
end;
function TSSLLibSSH2.BiShutdown: boolean;
begin
Result := DeInit;
end;
function TSSLLibSSH2.SendBuffer(Buffer: TMemory; Len: Integer): Integer;
begin
Result:=libssh2_channel_write(FChannel, PChar(Buffer), Len);
SSHCheck(Result);
end;
function TSSLLibSSH2.RecvBuffer(Buffer: TMemory; Len: Integer): Integer;
begin
result:=libssh2_channel_read(FChannel, PChar(Buffer), Len);
SSHCheck(Result);
end;
function TSSLLibSSH2.WaitingData: Integer;
begin
if libssh2_poll_channel_read(FChannel, Result) <> 1 then Result := 0;
end;
function TSSLLibSSH2.GetSSLVersion: string;
begin
Result:=libssh2_version(0);
end;
initialization
if libssh2_init(0)=0 then
SSLImplementation := TSSLLibSSH2;
finalization
libssh2_exit;
end.
Terminal client class
The telnetsshclient.pas unit below wraps around the Synapse tlntsend.pas unit and abstracts logging in, sending commands and receiving output and logging out.
If you only need a telnet client and can live without SSH support, comment out {$DEFINE HAS_SSH_SUPPORT} below so you don't need to have the libssh2 dll.
This unit has been lightly tested on a Linux ssh/telnet server. Additional tests welcome.
unit telnetsshclient;
{ Wrapper around Synapse libraries and SSL library (libssh2+libssl
is used right now)
Download compiled Windows dll from e.g.
http://alxdm.dyndns-at-work.com:808/files/windll_libssh2.zip
Download FreePascal interface files:
http://www.lazarus.freepascal.org/index.php/topic,15935.msg86465.html#msg86465
This unit allows the user to send Telnet or SSH commands and get the output
Thanks to Leonardo Rame
http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html
and Ludo Brands.
Written by Reinier Olislagers 2011.
Modified for libssh2 by Alexey Suhinin 2012.
License of code:
* MIT
* LGPLv2 or later (with FreePascal static linking exception)
* GPLv2 or later
according to your choice.
Free use allowed but please don't sue or blame me.
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.
}
{$mode objfpc}{$H+}
{$DEFINE HAS_SSH_SUPPORT} //comment out if only telnet support required
{$DEFINE LIBSSH2}
interface
uses
Classes, SysUtils,
tlntsend
{$IFDEF HAS_SSH_SUPPORT}
{ssl - or actually ssh - libs required by tlntsend}
{$IFDEF LIBSSH2}
ssl_libssh2
{$ELSE}
ssl_cryptlib
{$ENDIF}
{$ENDIF HAS_SSH_SUPPORT} ;
type
TProtocolType = (Telnet, SSH); //Different means of connecting
TServerType = (Unix, Windows); //line endings, mostly
{ TelnetSSHClient }
{ TTelnetSSHClient }
TTelnetSSHClient = class(TTelnetSend)
protected
FConnected: boolean;
FOutputPosition: integer; //Keeps track of position in output stream
FProtocolType: TProtocolType;
FServerLineEnding: string; //depends on FServerType
FServerType: TServerType;
FWelcomeMessage, FTelnetLoginPrompt, FTelnetPasswordPrompt: string;
procedure SetPrivateKeyFile(Value: string);
function GetPrivateKeyFile: string;
{ Based on protocol and servertype, set expected serverside line ending}
procedure DetermineLineEnding;
{ Sets port if no explicit port set. Uses protocol type: SSH or telnet}
procedure DeterminePort;
function GetSessionLog: string;
procedure ProtocolTypeChange(Value: TProtocolType);
function ReceiveData: string; //Can be used to get welcome message etc.
procedure SendData(Data: string);
procedure ServerTypeChange(Value: TServerType);
public
{All output generated during the entire session up to now}
property AllOutput: string read GetSessionLog;
{True if connected to server}
property Connected: boolean read FConnected;
{Name or IP address of host to connect to}
property HostName: string read FTargetHost write FTargetHost;
{Port on host used for connection. If left as 0, it will be determined by protocol type (22 for SSH, 23 for Telnet}
property Port: String read FTargetPort write FTargetPort;
{Location of private key file.}
property PrivateKeyFile: string read GetPrivateKeyFile write SetPrivateKeyFile;
{Telnet login prompt}
property TelnetLoginPrompt: string read FTelnetLoginPrompt write FTelnetLoginPrompt;
{Telnet password prompt}
property TelnetPasswordPrompt: string read FTelnetPasswordPrompt write FTelnetPasswordPrompt;
{Username used when connecting}
property UserName: string read FUserName write FUserName;
{Password used when connecting. Used as passphrase if PrivateKey is used}
property Password: string read FPassword write FPassword;
{Should we talk Telnet or SSH to the server? Defaults to SSH.}
property ProtocolType: TProtocolType read FProtocolType write ProtocolTypeChange;
{Windows or Unix/Linux server? Has effect on line endings. Defaults to Unix. NOTE: untested}
property Servertype: TServerType read FServerType write ServerTypeChange;
{Initial message displayed on logon}
property WelcomeMessage: string read FWelcomeMessage;
{Connect/logon to server. Requires that all authentication, protocol and hostname/port options are correct
Returns descriptive result. You can then use the Connected property.}
function Connect: string;
{If connected, logoff from server}
procedure Disconnect;
{Send command to server and receive result}
function CommandResult(Command: string): string; //Send command and get results
constructor Create;
destructor Destroy; override;
end;
implementation
{ TelnetSSHClient }
procedure TTelnetSSHClient.SetPrivateKeyFile(value: string);
begin
Sock.SSL.PrivateKeyFile := value;
end;
function TTelnetSSHClient.GetPrivateKeyFile: string;
begin
Result := Sock.SSL.PrivateKeyFile;
end;
procedure TTelnetSSHClient.DetermineLineEnding;
begin
case FProtocolType of
SSH:
begin
if FServerType = Unix then
FServerLineEnding := #10 //Unix
else
FServerLineEnding := #13 + #10; //windows
end;
Telnet:
begin
if FServerType = Unix then
FServerLineEnding := #10 //Unix
else
FServerLineEnding := #13 + #10; //windows
end;
else
raise Exception.Create('Unknown protocol type');
end;
end;
procedure Ttelnetsshclient.DeterminePort;
begin
if FTargetPort = '' then
//Set default port for protocol
begin
case FProtocolType of
Telnet: FTargetPort := '23';
SSH: FTargetPort := '22';
else
raise Exception.Create('Unknown protocol type.');
end;
end;
end;
procedure TTelnetSSHClient.ServerTypeChange(Value: Tservertype);
begin
FServerType := Value;
DetermineLineEnding;
end;
function TTelnetSSHClient.Connect: string;
var
Received: string;
begin
result:='Unknown error while connecting';
FOutputPosition := 1; //First character in output stream
FWelcomeMessage := '';
//Just to make sure:
DetermineLineEnding;
DeterminePort;
if FTargetPort='0' then
begin
result:='Port may not be 0.';
exit; //jump out of function
end;
case FProtocolType of
Telnet:
begin
try
if Login then
begin
FConnected := True;
result:='Connected to telnet server.';
end
else
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
except
on E: Exception do
begin
FConnected:=false;
result:='Error connecting to telnet server '+FTargetHost+':'+
FTargetPort+' as user ' + FUserName +
'. Technical details: '+E.Message;
end;
end;
end;
SSH:
begin
{$IFNDEF HAS_SSH_SUPPORT}
raise Exception.Create(
'SSH support has not been compiled into the telnetsshclient library.');
{$ENDIF HAS_SSH_SUPPORT}
try
if (PrivateKeyFile <> '') and (FPassword <> '') then
Sock.SSL.KeyPassword:=FPassword;
if SSHLogin then
begin
FConnected := True;
result:='Connected to SSH server.';
end
else
begin
if Sock.LastError<>0 then raise Exception.Create(Sock.LastErrorDesc);
if Sock.SSL.LastError<0 then raise Exception.Create(Sock.SSL.LastErrorDesc);
end;
except
on E: Exception do
begin
FConnected:=false;
result:='Error connecting to SSH server '+FTargetHost+':'+
FTargetPort+' as user ' + FUserName +
'. Technical details: '+E.Message;
end;
end;
end;
else
raise Exception.Create('Unknown protocol type');
end;
if FConnected = True then
begin
FWelcomeMessage := ReceiveData;
if FProtocolType=Telnet then
begin
//Unfortunately, we'll have to extract login ourselves
//Hope it applies to all server types.
if (AnsiPos(AnsiLowerCase(FTelnetLoginPrompt),AnsiLowerCase(FWelcomeMessage))>0) then
begin
SendData(UserName);
end;
Received:=ReceiveData;
if (AnsiPos(AnsiLowerCase(FTelnetPasswordPrompt),AnsiLowerCase(Received))>0) then
begin
SendData(Password);
end;
//Receive additional welcome message/message of the day
FWelcomeMessage:=FWelcomeMessage+LineEnding+ReceiveData;
end;
end;
end;
procedure TTelnetSSHClient.Disconnect;
begin
Logout;
FConnected := False;
end;
function TTelnetSSHClient.ReceiveData: string;
begin
Result := '';
while Sock.CanRead(1000) or (Sock.WaitingData > 0) do
begin
Sock.RecvPacket(1000);
Result := Result + Copy(SessionLog, FOutputPosition,
Length(SessionLog));
FOutputPosition := Length(SessionLog) + 1;
end;
end;
procedure Ttelnetsshclient.SendData(Data: String);
begin
Data := Data + FServerLineEnding; //Could be linux, could be Windows
Send(Data);
end;
function TTelnetSSHClient.GetSessionLog: string;
begin
// Gets complete output up to now
Result := SessionLog;
end;
procedure TTelnetSSHClient.ProtocolTypeChange(Value: Tprotocoltype);
begin
FProtocolType := Value;
//Auto-determine port and line ending, if necessary
DeterminePort;
DetermineLineEnding;
end;
function TTelnetSSHClient.CommandResult(Command: string): string;
begin
Result := '';
if FConnected then
begin
SendData(Command);
Result := ReceiveData; //gets too much
end
else
begin
//raise exception
Result := '';
raise Exception.Create('Can only run command when connected');
end;
end;
constructor TTelnetSSHClient.Create;
begin
inherited;
FConnected := False;
FProtocolType := SSH; //Could be telnet, too
FServerType := Unix; //Probably a safe default.
FTelnetLoginPrompt := 'login:';
FTelnetPasswordPrompt := 'password:';
DetermineLineEnding;
DeterminePort;
end;
destructor TTelnetSSHClient.Destroy;
begin
if FConnected then
Disconnect;
inherited Destroy;
end;
end.
Example program
To use the class we just made, you can use this example application, sshtest.lpr. Note that it needs to be compiled by Lazarus as it needs the LCL components to work with Synapse:
program sshtest;
{Test program for telnetsshclient
Written by Reinier Olislagers 2011.
Modified for libssh2 by Alexey Suhinin 2012.
License of code:
* MIT
* LGPLv2 or later (with FreePascal static linking exception)
* GPLv2 or later
according to your choice.
Free use allowed but please don't sue or blame me.
Uses other libraries/components; different licenses may apply that also can influence the combined/compiled work.
Run: sshtest <serverIPorhostname> [PrivateKeyFile]
}
{$mode objfpc}{$H+}
{$APPTYPE CONSOLE}
uses
telnetsshclient;
var
comm: TTelnetSSHClient;
Command: string;
begin
writeln('Starting.');
comm:=TTelnetSSHClient.Create;
comm.HostName:= ParamStr(1); //First argument on command line
if comm.HostName='' then
begin
writeln('Please specify hostname on command line.');
halt(1);
end;
comm.PrivateKeyFile := ParamStr(2);
comm.TargetPort:='0'; //auto determine based on protocoltype
comm.UserName:='root'; //change to your situation
comm.Password:='password'; //change to your situation
comm.ProtocolType:=SSH; //Telnet or SSH
writeln(comm.Connect); //Show result of connection
if comm.Connected then
begin
writeln('Server: ' + comm.HostName + ':'+comm.TargetPort+', user: '+comm.UserName);
writeln('Welcome message:');
writeln(comm.WelcomeMessage);
Command:='ls -al';
writeln('*** Sending ' + Command);
writeln('*** Begin result****');
writeln(comm.CommandResult(Command));
writeln('*** End result****');
writeln('');
writeln('');
Command:='df -h';
writeln('*** Sending ' + Command);
writeln('*** Begin result****');
writeln(comm.CommandResult(Command));
writeln('*** End result****');
writeln('');
writeln('');
writeln('All output:');
writeln('*** Begin result****');
writeln(comm.AllOutput);
writeln('*** End result****');
comm.Disconnect;
end
else
begin
writeln('Connection to ' +
comm.HostName + ':' +
comm.TargetPort + ' failed.');
end;
comm.Free;
end.
OAuth v1/Twitter/Plurk integration
An OAuth v1 library written in FPC that uses Synapse (and is ready for other network libaries like lnet) is available here.
FPCTwit also contains FPC twitter and plurk example client programs and a Lazarus twitter client.