FTP Watcher



Another small tool. Ceritanya gini. Ada undangan meeting di client. Mau ada improvement fitur. Pas mau pulang, temen mengeluh ftp server dari suatu institusi keuangan suka mati. Ketahuan baru dua tiga hari. Mau bongkar code yang udah ada untuk generate email, udah naik ke production. Ya udah batal mau pulang lagi ke kantor abis makan siang. Jadi deh buka laptop lagi dan buka IDE Borland Delphi 2006. Tergoda untuk buat toolnya.

Idenya adalah membuat sebuah tool yang :

  1. Mengamati sebuah FTP server, koneksinya ok apa nggak
  2. Periode pengamatan bisa disetting
  3. Email destination bisa disetting
  4. Setiap kali dicek gagal, kirim email
  5. Kalo dah nyambung, kirim udah ok koneksinya pas nyambung yang pertama kali itu
  6. Konfigurasi disave ke IniFile
  7. Udah gitu doang. Hehehe…

Simple kan? Yeah..
Oh ya komponen untuk FTP aku pake TIdFTP dari Indy. Untuk SMTPnya pake TIdSMTP. Dua-duanya bawaan D2006. Bisa sih pake Synapse. Tapi yaa gitu deeh..

Eniwei, berikut ini adalah source codenya:

unit MainForm;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls,
IdExplicitTLSClientServerBase, IdFTP, ComCtrls, Buttons, ExtCtrls,
IdMessageClient, IdSMTPBase, IdSMTP, IdMessage;

type
TformMain = class(TForm)
pcLog: TPageControl;
tsLog: TTabSheet;

… deleted …

procedure smtpClientStatus(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
procedure tmrWatcherTimer(Sender: TObject);
procedure mmLogChange(Sender: TObject);
procedure ftpClientStatus(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
procedure bbCheckClick(Sender: TObject);
procedure bbSaveClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FAutoEmail: boolean;
FMaxLine: integer;
FLastFailed: boolean;
procedure LoadConfiguration;
procedure Log(messageLog: String);
procedure SendMessageOnFailure(logMessage: String);
procedure SendMessageOnSuccess(logMessage: String);
procedure SetEventListener;
procedure OnConfigurationChanges(Sender: TObject);
procedure SaveConfiguration;
function ValidateInput: boolean;
procedure WatchFTP;
procedure PrepareTheEmailBody(logMessage: string);
procedure PrepareTheSenderAndDestination;
procedure SendEmailNotification;
procedure PrepareAndSendEmail(logMessage: string);
public
{ Public declarations }
end;

var
formMain: TformMain;

implementation

uses IniFiles;

{$R *.dfm}

procedure TformMain.bbCheckClick(Sender: TObject);
begin
try
ftpClient.Username := edFTPUserName.Text;
ftpClient.Password := edFTPPassword.Text;
ftpClient.Connect;
Sleep(100);
ftpClient.Disconnect;
except on E : Exception do
log(‘There was an exception when trying to connect to ‘ + ftpClient.Host +
‘ (‘ + E.Message + ‘).’);
end;
end;

procedure TformMain.bbSaveClick(Sender: TObject);
begin
if ValidateInput then
begin
SaveConfiguration;
LoadConfiguration;
end;
bbSave.Enabled := False;
end;

procedure TformMain.FormCreate(Sender: TObject);
begin
pcLog.ActivePageIndex := 0;
FLastFailed := False;

LoadConfiguration;
SetEventListener;
end;

procedure TformMain.ftpClientStatus(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
begin
if AStatus = hsConnecting then
log(‘Trying to connect to ftp host : ‘ + ftpClient.Host + ‘…’)
else if AStatus = hsConnected then
log(‘Successfully connected to ftp host : ‘ + ftpClient.Host + ‘.’)
else if AStatus = hsDisconnected then
log(‘Disconnected from ftp host : ‘ + ftpClient.Host + ‘.’);
end;

procedure TformMain.LoadConfiguration;
var
indexFrequency: Integer;
iniFile: TIniFile;
frequency: integer;
begin
iniFile := TIniFile.Create(ExtractFileDir(ParamStr(0)) + ‘FTPWATCH.INI’);

edFTPHostName.Text := iniFile.ReadString(‘FTP’, ‘HOST’, ‘localhost’);
ftpClient.Host := edFTPHostName.Text;

edFTPPortNumber.Text := iniFile.ReadString(‘FTP’, ‘PORT’, ’21’);
ftpClient.Port := StrToIntDef(edFTPPortNumber.Text, 21);

edFTPUserName.Text := iniFile.ReadString(‘FTP’, ‘USERNAME’, ”);
ftpClient.Username := edFTPUserName.Text;

edFTPPassword.Text := iniFile.ReadString(‘FTP’, ‘PASSWORD’, ”);
ftpClient.Password := edFTPPassword.Text;

edSMTPHostName.Text := iniFile.ReadString(‘SMTP’, ‘HOST’, ‘localhost’);
smtpClient.Host := edSMTPHostName.Text;

edSMTPPort.Text := iniFile.ReadString(‘SMTP’, ‘PORT’, ’25’);
smtpClient.Port := StrToIntDef(edSMTPPort.Text, 25);

edSMTPUserName.Text := iniFile.ReadString(‘SMTP’, ‘USERNAME’, ”);
smtpClient.Username := edSMTPUserName.Text;

edSMTPPassword.Text := iniFile.ReadString(‘SMTP’, ‘PASSWORD’, ”);
smtpClient.Password := edSMTPPassword.Text;

edPrimaryEmail.Text := iniFile.ReadString(‘WATCHER’, ‘PRIMARYEMAIL’, ”);
edSecondaryEmail.Text := iniFile.ReadString(‘WATCHER’, ‘SECONDARYEMAIL’, ”);

frequency := iniFile.ReadInteger(‘WATCHER’, ‘FREQUENCY’, 60);
indexFrequency := cmbFrequency.Items.IndexOf(VarToStr(frequency));
if indexFrequency >= 0 then
cmbFrequency.ItemIndex := indexFrequency;

tmrWatcher.Interval := frequency * 1000 * 60;
tmrWatcher.Enabled := iniFile.ReadBool(‘WATCHER’, ‘ENABLEWATCHER’, False);
cbFTPWatcherEnabled.Checked := tmrWatcher.Enabled;

FAutoEmail := iniFile.ReadBool(‘WATCHER’, ‘AUTOEMAIL’, False);
cbAutoEmailEnabled.Checked := FAutoEmail;

FMaxLine := iniFile.ReadInteger(‘WATCHER’, ‘MAXLINE’, 1000);
if cmbMaxLine.Items.IndexOf(VarToStr(FMaxLine)) >= 0 then
cmbMaxLine.ItemIndex := FMaxLine;

iniFile.Free;
end;

procedure TformMain.Log(messageLog: String);
begin
mmLog.Lines.Add(FormatDateTime(‘DD-MMM-YYYY HH:MM:SS | ‘, Now) + messageLog);
end;

procedure TformMain.mmLogChange(Sender: TObject);
begin
if mmLog.Lines.Count >= FMaxLine then
mmLog.Lines.Delete(0);
end;

procedure TformMain.OnConfigurationChanges(Sender: TObject);
begin
bbSave.Enabled := True;
end;

procedure TformMain.SaveConfiguration;
var
iniFile: TIniFile;
begin
iniFile := TIniFile.Create(ExtractFileDir(ParamStr(0)) + ‘FTPWATCH.INI’);

iniFile.WriteString(‘FTP’, ‘HOST’, edFTPHostName.Text);
iniFile.WriteInteger(‘FTP’, ‘PORT’, StrToIntDef(edFTPPortNumber.Text, 21));
iniFile.WriteString(‘FTP’, ‘USERNAME’, edFTPUserName.Text);
iniFile.WriteString(‘FTP’, ‘PASSWORD’, edFTPPassword.Text);

iniFile.WriteString(‘SMTP’, ‘HOST’, edSMTPHostName.Text);
iniFile.WriteInteger(‘SMTP’, ‘PORT’, StrToIntDef(edSMTPPort.Text, 25));
iniFile.WriteString(‘SMTP’, ‘USERNAME’, edSMTPUserName.Text);
iniFile.WriteString(‘SMTP’, ‘PASSWORD’, edSMTPPassword.Text);

iniFile.WriteString(‘WATCHER’, ‘PRIMARYEMAIL’, edPrimaryEmail.Text);
iniFile.WriteString(‘WATCH
ER’, ‘SECONDARYEMAIL’, edSecondaryEmail.Text);
iniFile.WriteInteger(‘WATCHER’, ‘FREQUENCY’, StrToIntDef(cmbFrequency.Text, 60));
iniFile.WriteBool(‘WATCHER’, ‘ENABLEWATCHER’, cbFTPWatcherEnabled.Checked);
iniFile.WriteBool(‘WATCHER’, ‘AUTOEMAIL’, cbAutoEmailEnabled.Checked);
iniFile.WriteInteger(‘WATCHER’, ‘MAXLINE’, StrToIntDef(cmbMaxLine.text, 1000));

iniFile.Free;
end;

procedure TformMain.SendMessageOnFailure(logMessage: String);
begin
emailMessage.Subject := ‘[FTP WATCHER] Alert: FTP Connection is down’;
PrepareAndSendEmail(logMessage);
end;

procedure TformMain.SendMessageOnSuccess(logMessage: String);
begin
emailMessage.Subject := ‘[FTP WATCHER] Alert: FTP Connection is already up’;
PrepareAndSendEmail(logMessage);
end;

procedure TformMain.SetEventListener;
begin
edFTPHostName.OnChange := Self.OnConfigurationChanges;
edFTPPortNumber.OnChange := Self.OnConfigurationChanges;
edSMTPHostName.OnChange := Self.OnConfigurationChanges;
edSMTPPort.OnChange := Self.OnConfigurationChanges;
edSMTPUserName.OnChange := Self.OnConfigurationChanges;
edSMTPPassword.OnChange := Self.OnConfigurationChanges;
edPrimaryEmail.OnChange := Self.OnConfigurationChanges;
edSecondaryEmail.OnChange := Self.OnConfigurationChanges;
edFTPUserName.OnChange := Self.OnConfigurationChanges;
edFTPPassword.OnChange := Self.OnConfigurationChanges;

cmbFrequency.OnChange := Self.OnConfigurationChanges;
cmbFrequency.OnChange := Self.OnConfigurationChanges;

cbAutoEmailEnabled.OnClick := Self.OnConfigurationChanges;
cbFTPWatcherEnabled.OnClick := Self.OnConfigurationChanges;
end;

procedure TformMain.smtpClientStatus(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
begin
if AStatus = hsConnecting then
log(‘Trying to connect to smtp host : ‘ + smtpClient.Host + ‘…’)
else if AStatus = hsConnected then
log(‘Successfully connected to smtp host : ‘ + smtpClient.Host + ‘.’)
else if AStatus = hsDisconnected then
log(‘Disconnected from smtp host : ‘ + smtpClient.Host + ‘.’);
end;

procedure TformMain.tmrWatcherTimer(Sender: TObject);
begin
watchFTP;
end;

function TformMain.ValidateInput: boolean;
begin
Result := True;
if StrToIntDef(edFTPPortNumber.Text, 0) <= 0 then
begin
Result := False;
MessageDlg(‘Port number must be a valid non zero integer (usually is 21)’, mtError, [mbOK], 0);
edFTPPortNumber.SetFocus;
end
else if StrToIntDef(edSMTPPort.Text, 0) <= 0 then
begin
Result := False;
MessageDlg(‘Port number must be a valid non zero integer (usually is 25)’, mtError, [mbOK], 0);
edSMTPPort.SetFocus;
end
else if cbAutoEmailEnabled.Checked then
begin
if (Trim(edPrimaryEmail.Text) = ”) or (Pos(‘@’, edPrimaryEmail.Text) <= 0) then
begin
Result := False;
MessageDlg(‘Primary email address must be valid and not blank when auto email is enabled’, mtError, [mbOK], 0);
end;

if (Trim(edSecondaryEmail.Text) <> ”) and (Pos(‘@’, edSecondaryEmail.Text) <= 0) then
begin
Result := False;
MessageDlg(‘Secondary email address must be valid and not blank when auto email is enabled’, mtError, [mbOK], 0);
end;
end
else if cbFTPWatcherEnabled.Checked then
begin
if (Trim(edFTPHostName.Text) = ”) or (edFTPPortNumber.Text = ”) then
begin
MessageDlg(‘If you enable ftp watcher, ftp configuration must be valid’, mtError, [mbOK], 0);
end;
end;
end;

procedure TformMain.WatchFTP;
var
logMessage: String;
begin
try
ftpClient.Connect;
Sleep(100);
ftpClient.Disconnect;

if FLastFailed then
begin
logMessage := ‘Successfully connected to FTP Server : ‘ + ftpClient.Host;
SendMessageOnSuccess(logMessage);
FLastFailed := False;
end;

except on E : Exception do
begin
FLastFailed := true;
logMessage := ‘There was an exception when trying to connect to ‘ + ftpClient.Host +
‘ (‘ + E.Message + ‘).’;
log(logMessage);
if FAutoEmail then
begin
SendMessageOnFailure(logMessage);
end;
end;
end;
end;

procedure TformMain.PrepareTheEmailBody(logMessage: string);
begin
emailMessage.Body.Clear;
emailMessage.Body.Add(‘Dear Admin / FTP Watcher,’);
emailMessage.Body.Add(”);
emailMessage.Body.Add(logMessage);
emailMessage.Body.Add(”);
emailMessage.Body.Add(‘This message was sent by FTP Watcher automatically from ‘ + ‘host ‘ + edSMTPHostName.Text + ‘.’);
end;

procedure TformMain.PrepareTheSenderAndDestination;
begin
emailMessage.Recipients.EMailAddresses := edPrimaryEmail.Text;
emailMessage.CCList.EMailAddresses := edSecondaryEmail.Text;
emailMessage.From.Name := ‘FTP Watcher’;
end;

procedure TformMain.SendEmailNotification;
begin
try
smtpClient.Connect;
smtpClient.Send(emailMessage);
smtpClient.Disconnect;
except
on E: Exception do
log(‘Failed to send email notification to ‘ + edPrimaryEmail.Text + ‘.’);
end;
end;

procedure TformMain.PrepareAndSendEmail(logMessage: string);
begin
PrepareTheEmailBody(logMessage);
PrepareTheSenderAndDestination;
SendEmailNotification;
end;

end.

============================================================
Yang bisa diimprove dari tool ini:

  • Email Destination bisa dibanyakin (gak cuma dua)
  • IniFile bisa diganti registry, atau dienkripsi (supaya passwordnya gak plain)
  • Isi memo log bisa disave ke file (bisa pake Delphi Logger, mirip Log4Jnya Java)
  • Code di atas dibuat dengan fun, ngikutin mood stylenya (ada beberapa method yang kalau ganti mood, bisa beda tuh)
  • Dibuat Tray Icon (yang kalo koneksi putus ada balloon muncul di icon traynya)
  • dsb dsb dsb…

Kalau ada bugs kasih tahu ya…

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout /  Ubah )

Foto Google+

You are commenting using your Google+ account. Logout /  Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout /  Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout /  Ubah )

Connecting to %s