Simple XMPP client using Synapse - delphi

I'm trying to build my simple xmpp client to avoid using 3rd party components cause I need only some very basic functions like new account registration and sending messages (e.g. a basic jabber bot).
I'm not completely new to Delphi and even to Synapse (was using it's HTTPsend class before), but I am relatively new at sending data through TCP/IP socket and am stuck with some problems.
Basically, I've read about XMPP protocol specs and found out it is relatively simple to deal with, because it uses XML and is well-explained. I started with capturing some packets from a real client communicated with XMPP server. The very first task I decided to implement was new account creation.
Here is what the client sent to the server:
http://notepad.cc/faxije16
These are the initial packets.
Now comes the problem.
I wrote the following function to maintain sending xml to the server and read the response
function sndq(const query: string;sock: ttcpblocksocket): string;
var c: string;
i: integer;
begin
result:='';
sock.SendString(query);
c:='';
for i := 0 to 5000 do begin
c:=Sock.RecvBufferStr(1, 2500);
result := result + c;
end;// for i
end;:
First of all, I feel very wrong about doing that for...doing thing, but I tried using sock.waitingdata and waitingdataex, but it somehow returns wrong value (less then actual response size).
But the bigger problem is that I can only receive the first packet. So Here is the code I've written:
var sock: ttcpblocksocket;
begin
sock:=ttcpblocksocket.Create;
sock.Connect('exploit.im','5222');
writeln(sndq('<stream:stream to="exploit.im" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="ru" version="1.0" />',sock)); // goes well as I get that same initial packet as the client
writeln('----------');
writeln(sndq('<iq type="get" id="qip_0"><query xmlns="jabber:iq:register" /></iq>',sock)); // шлем запрос точно как у Квипа // prints empty line
readln; end.
I hope that I've explained my problem well.
Probably these questions are already answered but I wasn't able to find any way around, so please point out what do I have to correct in the code above (about determining actual response size and about being able to receive only a packet).
Thanks.

Related

Is it possible to enumerate all available DDE servers with `TDdeClientConv` class?

I was curious about all the running DDE servers, tried TDdeClientConv class but got confused with it (may be just didn't figured out how) and finally rolled my own low-level (normally applications should use DDEML abstraction layer) "client":
procedure TForm6.FormClick(Sender: TObject);
begin
{ initiate DDE conversation with all top-level windows }
SendMessage(
HWND_BROADCAST,
WM_DDE_INITIATE,
Handle,
MakeLParam(
0, // all services
0 // all topics
)
);
end;
procedure TForm6.WMDDE_Ack(var Message: TWMDDE_Ack);
begin
{ this message handler receives acknowledgements }
{ and prints service-topic pairs to console }
Writeln('"' + GetAtom(Message.App) + '"', #9, '"' + GetAtom(Message.Topic) + '"');
end;
Question: is it possible to do the same with TDdeClientConv class, that is, initiate a DDE conversation with all available services and receive multiple acknowledgements? Or TDdeClientConv merely represents client endpoint of DDE conversation and thus my scenario is out of the scope?
TDdeClientConv does not use any window messages, it uses the Dynamic Data Exchange Management Library (DDEML) instead. TDdeClientConv can connect only to a single server that implements a specified Service and/or Topic, as it establishes its connection using the DDEML DdeConnect() function:
Establishes a conversation with a server application that supports the specified service name and topic name pair. If more than one such server exists, the system selects only one.
DDEML's DdeConnectList() function, on the other hand, can establish a conversation with multiple servers supporting a given Service and/or Topic.
Establishes a conversation with all server applications that support the specified service name and topic name pair. An application can also use this function to obtain a list of conversation handles by passing the function an existing conversation handle. The Dynamic Data Exchange Management Library removes the handles of any terminated conversations from the conversation list. The resulting conversation list contains the handles of all currently established conversations that support the specified service name and topic name.
You can enumerate that list using the DdeQueryNextServer() and DdeQueryConvInfo() functions.

Delphi XE2 DataSnap Server - Log client connection user/properties

I'm building an XE2 DataSnap server which will serve connections from REST clients. My DSServerClass LifeCycle property is set to 'Invocation'. What I want to do is to log the details of all client connections to the server, including the following details : username, IP address, protocol, application name. I can currently get these details using the following events :
DSAuthenticationManager - UserAuthenticate() : username, protocol (using the standard parameters passed in)
DSServer - Connect() : protocol, IP address, application name (using DSConnectEventObject.ChannelInfo.ClientInfo)
What I want to do is just log once for all details, but it seems I can't get all the details I need in one event. I tried using a shared private variable in the class but as expected this gave inconsistent results - the wrong IP address against the wrong username. Is there another way to achieve what I want?
Jonathan
procedure TServerContainer1.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);
begin
Form1.Memo1.Lines.Add(Format('Conn->UserName=%s, Password=%s', [
DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName],
DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password]
]));
// 取 Client 端的IP 和 Port
Form1.Memo1.Lines.Add('IP =' + DSConnectEventObject.ChannelInfo.ClientInfo.IpAddress + ':'
+ DSConnectEventObject.ChannelInfo.ClientInfo.ClientPort);
end;
You can use TDSServer.OnConnect event (which is called after TDSAuthenticationManager.OnUserAuthenticate). There you have access to ChannelInfo.ClientInfo as you've discovered and also ConnectProperties from which you can read property values like this:
Scheme := ConnectProperties.Values[TDBXPropertyNames.DSAuthenticationScheme];
UserName := ConnectProperties.Values[TDBXPropertyNames.DSAuthenticationUser];
Password := ConnectProperties.Values[TDBXPropertyNames.DSAuthenticationPassword];

Send an email with attachment Client Agnostic

I currently have an application written that generates pdf vouchers and emails to their perspective recipients. However the function I use is client dependent (MS Outlook) and I would really like to make this email client agnostic as we have many customers and not all of them use Outlook.
I have looked at a few options but can't really find anything in searching that seems to solve my problem.
Does anyone know a good way to send email using the customers smtp connection regardless of the client and send an attachment with it without calling the client directly to do it?
Or you can use the Synapse library, for sending a mail using SMTP, ideally in its newest snapshot.
Here is the code which should send the mail with attached c:\voucher.pdf file from sender#from.com to recipient#to.com to the smtp.server.com with login login and password password. About the rest of the functions from the TMimeMess class I would refer you directly to the reference.
I hope this will work because I've simplified and localized much more complicated code I'm using and I can't verify it nor compile. If not, let's downvote it :)
uses
SMTPSend, MIMEPart, MIMEMess;
procedure TForm.SendEmailClick(Sender: TObject);
var
MIMEText: TStrings;
MIMEPart: TMimePart;
MIMEMessage: TMimeMess;
begin
MIMEText := TStringList.Create;
MIMEText.Add('Hello,');
MIMEText.Add('here is the text of your e-mail message,');
MIMEText.Add('if you want the HTML format, use AddPartHTML');
MIMEText.Add('or e.g. AddPartHTMLFromFile if you have your');
MIMEText.Add('HTML message content in a file.');
MIMEMessage := TMimeMess.Create;
with MIMEMessage do
try
Header.Date := Now;
Header.From := 'sender#from.com';
Header.ToList.Clear;
Header.ToList.Add('recipient#to.com');
Header.CcList.Clear;
Header.Subject := 'E-mail subject';
Header.XMailer := 'My mail client name';
MIMEPart := AddPartMultipart('mixed', nil);
AddPartText(MIMEText, MIMEPart);
AddPartBinaryFromFile('c:\voucher.pdf', MIMEPart);
EncodeMessage;
if SendToRaw(Header.From, // e-mail sender
Header.ToList.CommaText, // comma delimited recipient list
'smtp.server.com', // SMTP server
Lines, // MIME message data
'login', // server authentication
'password') // server authentication
then
ShowMessage('E-mail has been successfuly sent :)')
else
ShowMessage('E-mail sending failed :(');
finally
Free;
MIMEText.Free;
end;
end;
Update:
According to nice comment from Downvoter step into the light (man, change your nick please, it's not cool anymore :), would be really bad if you will send the list of all recipients to everyone. With synapse you cannot add BCCs to the message header; there's no Header.BCCList property in MIMEMessage.
Instead you can directly modify the data before sending them.
// First, you will remove the line where you are adding a recipient to the list
Header.ToList.Add('recipient#to.com');
// the rest between you can keep as it is and after the message encoding
EncodeMessage;
// and before sending the mail you'll insert the line with BCCs
Lines.Insert(1, 'Bcc: jane#invisiblecustomer.com, lisa#invisiblecustomer.com');
if SendToRaw ...
You could use Indy as your SMTP client, independent of the system default e-mail client. Here's a basic demo of sending e-mails without attachments, and there are detailed articles here and here about sending HTML or plain-text e-mails and with or without attachments.
If you want to integrate with the existent email client (e.g. see the sent messages in the sent, sent items, etc. folder of the email client), you could use Simple MAPI. The headers are translated in the Mapi unit in Delphi (at least in D2007).
But be careful to check if the actual client supports Simple MAPI.

How can my program log in to a Web site programmatically with Indy?

I am trying to log in to fileserve.com from my Delphi application.
I used the LiveHTTPHeader Firefox addon to see HTTP post data. I found
&autoLogin=on&recaptcha_response_field=&recaptcha_challenge_field=&recaptcha_shortencode_field=&loginFormSubmit=Login
I tried in my application like this:
Str := TStringList.Create;
Str.Add('loginUserName='+edit1.Text);
Str.Add('loginUserPassword='+edit2.Text);
Str.Add('autoLogin=on');
Str.Add('recaptcha_response_field=');
Str.Add('recaptcha_challenge_field=');
Str.Add('recaptcha_shortencode_field=');
Str.Add('loginFormSubmit=Login');
s:= IdHTTP1.Post('http://www.fileserve.com/login.php', Str);
FreeAndNil(str);
s1 := IdHTTP1.Get('http://www.fileserve.com/dashboard.php');
memo1.lines.add(s1);
In my my memo, it's not giving the data after I logged in. It just displays the source of the main site. Why doesn't it recognize that I logged in? (I used a working ID and password while testing.)
I am using Delphi 7 and Indy 9; The IdHTTP.HandleRedirect property is set to true.
The site you're logging in to probably sends a cookie in its response to the login request. You need to remember that cookie and send it back during all subsequent requests. Indy should have some sort of TIdCookieManager object that you can hook up to your TIdHTTP object to make it remember cookies automatically.

Any way to authenticate with a websense server from delphi?

We use a websense internet filter at my workplace. I have an application that tries to retrieve information from the internet.
On my client machine, I have to authenticate with websense manually (i.e., open firefox and give my username / password) or I'll get an error in my application when it tries to do the download.
The error message is:
HTTP/1.0 302 Moved.
Does anyone know of a way to authenticate with websense from code? Examples in any language are welcome- I am using Delphi and Indy's TIdHTTP component.
Answering my own question; this is what worked for me.
The custom user agent string is only required if you want the authentication to let MSN / Live messenger get through, as described under "notes" at the end of this article.
In a command line application:
uses
... IdHTTP ...;
...
var
httpGetter: TIdHTTP;
...
httpGetter.Request.Username := username;
httpGetter.Request.Password := password;
httpGetter.HandleRedirects := True;
httpGetter.Request.BasicAuthentication := True;
//custom useragent required to let live messenger work
httpGetter.Request.UserAgent := 'MSN Explorer/9.0 (MSN 8.0; TmstmpExt)';
httpGetter.Get(url,MS);
...
I would try HTTP authentication
http://en.wikipedia.org/wiki/Basic_access_authentication

Resources