views:

746

answers:

4

Ok - this is in continuation from my earlier question about sending an email using a php script. I'm now using PEAR to send the mail. The php script i use is the following one (successfull if executed alone): PHPEMail.php

<?php
require_once "Mail.php"; // Pear Mail.php 

$from = "FromName <[email protected]>";
$to = $_POST["destination"]; // destination  
$subject = "Hello You!";
$body = $_POST["nicebody"]; // body of text sent 
$host = "ValidServerName";
$username = "User";         // validation at server    
$password = "Password";     // validation at server 

$headers = array ('From' => $from,
  'To' => $to,
  'Subject' => $subject);
$smtp = Mail::factory('smtp',
  array ('host' => $host,
    'auth' => true,
    'username' => $username,
    'password' => $password));

$mail = $smtp->send($to, $headers, $body);

if (PEAR::isError($mail)) {
  echo("<p>" . $mail->getMessage() . "</p>");
 } else {
  echo("<p>Message successfully sent!</p>");
 }
?>

I now need to execute this script (PHPEMail.php) from Delphi, passing some variables, using winsock. I'm going with this code - which has not been successfull up to now:

Procedure SendEmail;
var
  WSADat:WSAData;
  SomeText:TextFile;
  Client:TSocket;
  Info,TheData,Lina,Nicebody:String;
  SockAddrIn:SockAddr_In;
begin
  try
    if not FileExists(Log) then exit;     
    AssignFile(SomeText, Log);             // try to open log, assigned to SomeText
    Reset(SomeText);                       // Reopen SomeText for reading
    while not Eof(SomeText) do
    begin
      ReadLn(SomeText, Lina);             //read each line of SomeTextans place it in linha
      nicebody:=Nicebody+#13#10+Lina;     // nicebody = all line red from SomeText
    end;
    CloseFile(SomeText);                  // SomeText is closed
    DeleteFile(PChar(Log));               // log is deleted
//
    WSAStartUp(257,WSADat);
    Client:=Socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
    SockAddrIn.sin_family:=AF_INET;
    SockAddrIn.sin_port:=htons(80);
    SockAddrIn.sin_addr.S_addr:=inet_addr('66.66.66.66'); // server IP
    if Connect(Client,SockAddrIn,SizeOf(SockAddrIn))=0 then begin
      Info:='destination='+EmailDestAddressFromIni + '' +'Nicebody='+Nicebody;
      TheData:='POST PHPEMail.php HTTP/1.0'                   +#13#10+
             'Connection: close'                              +#13#10+
             'Content-Type: application/x-www-form-urlencoded'+#13#10+
             'Content-Length: '+IntToStr(Length(Info))       +#13#10+
             'Host: someEmailHostAddress'                         +#13#10+
             'Accept: text/html'                              +#13#10+#13#10+
              Info                                            +#13#10;
      Send(Client,Pointer(TheData)^,Length(TheData),0);
      end;
    CloseSocket(Client);
  except
    exit;
  end;
end;

[... more code not related]

I'm pretty sure the fault is in "TheData" that is sent to the web server. The PHP script is just not triggered. Anyone have an idea what is going wrong?

(note: i want to use winsock, i don't want third party components. The complete code, which is a server, weight about 12ko and is destinated to be embeded in some hardware).

SEE FINAL CODE AT END.

+2  A: 

The second token of the first HTTP line needs to be an absolute URL path. It needs to start with a slash.

POST /PHPEMail.php HTTP/1.0

You should also make some effort to ensure that the data you send really is URL-encoded, as the content-type says it is. Characters 10 and 13 are not valid characters in a URL. You also need to consider all the characters in the text file you're reading:

Info := 'destination=' + UrlEncode(EmailDestAddressFromIni) +
  '&' + 'Nicebody=' + UrlEncode(Nicebody);

I notice that you're not reading the response from the server. Don't ignore that. Sometimes it may tell you what's wrong. I also saw no mention of what the server logs said had occurred when you tried to run your code.

You're bound to make more mistakes like this along the way. Consider using a library that handles this sort of stuff for you, such as Indy, ICS, or Synapse. Don't re-implement HTTP unless you really have to. And if you really don't want third-party code, consider second-party code (or is it first-party?) by using the stuff built in to Windows. KB 165298 has a short example of using InternetConnect, HttpOpenRequest, and HttpSendRequest to post a URL-encoded form request.

Rob Kennedy
Thank you. The server error log says nothing - not even a connection is showing! Should i replace chr 10 and 13 by some <br/> ? I have tried Indy, ICS and Synapse, every one result in a server being more than 600ko - that is much too for me large since the whole server is put within a resource. Actual code is about 12-20ko max. I will have a look at KB 165298.
volvox
However i know it connects. Adding a ShowMessage at some point after "If connect" i can read the data sent, that is, the destination email and the NiceBody (the data). TheData looks nicely formated at first glance.
volvox
volvox
You're sending a URL not HTML. Are you familiar with what application/x-www-form-urlencoded data is supposed to look like? If not, then capture a POST request sent by your browser and take a look. I think Firebug can do it. Or use Wireshark to see what's sent across the wire. ASCII character 10 is sent as the three-character sequence "%0A", as you've probably seen in other URLs over the years.
Rob Kennedy
Using URLEncode on Info i get a long string of all of the data sent including the destination email at first position - exactly like a long URL. Are you sure it is the way it should be presented to the php script? Still, i have the connection but nothing the script care about.
volvox
In any case, since there is data transmitted to the server aiming the php mail function (i see data leaving my computer), this function should be triggered in any way (error or not). However, i see absolutly no sign of posting on the server.
volvox
If you see no signs on the server, then are you sure you're sending it to the right server? You've checked the HTTP logs, not just the PHP logs? Can you elicit _any_ response from the server? Try a plain old "GET /" request.
Rob Kennedy
And yes, will look a long URL. The content-type name "x-www-form-urlencoded" ends with "urlencoded" for a reason.
Rob Kennedy
OK - POST and GET to my phpmail.php coming from other sources shows on the server log. POST from my application don't show. I see the data leaving my computer looking at a TCP sniffer but no trace on the access log of the server!!!
volvox
+2  A: 

As I see from your Delphi code, all information of the email has been packed into one variable, but in your php script how do pass it your many php variables? You have to unpack/parse it to pass each variable (target address, subject, mail body etc) in your php script. (In your current php script there are many $_posts, but it receives only 1 actual post from Delphi code.)

Below is my working code to a php script.

result := 'POST /index.php HTTP/1.1' +
a + 'Host: somehost.com' +
a + 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080406 K-Meleon/1.1.5' +
a + 'Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5' +
a + 'Accept-Language: en-us,en;q=0.5' +
a + 'Accept-Encoding: gzip,deflate' +
a + 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7' +
a + 'Keep-Alive: 300' +
a + 'Connection: keep-alive' +
a + 'Referer: http://somehost.com/index.php' +
a + 'Content-Type: application/x-www-form-urlencoded' +
a + 'Content-Length: 73' + a +
a + 'link=' + MyHttpEnCodedStrData  + a + a;

a is #13#10.

Another important thing is "link='; it is the variable name in my php script that receives/holds the data I'm sending. Since I'm sending only 1 variable, and it is not email script, no php parsing needed, so I didn't paste my php code here.

Hope I made it clear :)

avar
He has populated Info with two querystring parameters, destination and nicebody. He left out the ampersand that should separate them. PHP knows how to parse URL-encoded post data, so it will split the parameters into $_POST['destination'] and $_POST['nicebody'].
Rob Kennedy
My first attemp at Info was indeed:Info:='Destination='+Email+'I have aslo tried (avar's suggestion):TheData:= 'POST /phpemail.php HTTP/1.0' +a+ 'Connection: close' +a+ 'Content-Type: application/x-www-form-urlencoded' +a+ 'Content-Length: '+IntToStr(Length(Email)+ Length(NiceBody)) +a+ 'Host: serverhostname' +a+ 'Accept: text/html' +a+ 'Destination=' +Email +a+ 'NiceBody=' +NiceBody + a + a;Send(Client,Pointer(TheData)^,Length(TheData),0);Again, data is sent (read on sniffer for tcp server hostname) but still, no interaction with the php script.
volvox
i'll try your php code with my delphi code and let you know . later
avar
A: 

Because i could not see the POST on the server log i have made some improvement in my code (plus some error message). NOW the server's log shows some trace of the packets sent... that is, the usual id and time plus the the lettre "P" ... probably the first letter of the word 'POST' (the Data sent). I thus have to investigate the 'Send()' command. (i'm on Delphi 2009). I get no error from winsock or the send command.

Procedure SendEmail;

const
  a = #13#10;

var
  WSAData:TWSAData;
  Texto:TextFile;
  ClientSocket :TSocket;
  Info,Data,Lina,Contenu:String;
  host:SockAddr_In;
  i_result:Integer;

begin
  try
    if not FileExists(Log) then exit;     
    AssignFile(Texto, Log);      
    Reset(Texto);  // Reopen texto for reading
    while not Eof(Texto) do
    begin
      ReadLn(Texto, Lina); //read each line of texto and place it in lina
      Contenu:=Contenu+#13#10+Lina;  // contenu is all lines of texto
    end;
    CloseFile(Texto); // close texto
    DeleteFile(PChar(Log)); // delete log


    // Initialize Winsock
    i_result := WSAStartUp(257,WSAData);
    if (i_Result <> NO_ERROR) then
    begin
     MessageBox(0,'Initialization of winsock failed.','Error',MB_OK Or MB_ICONERROR);
     Exit;
    end;

    // Create a SOCKET for connecting to server
    ClientSocket := Socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
    If ClientSocket = INVALID_SOCKET Then
    begin
     MessageBox(0,'ServerSocket creation failed.','Error',MB_OK Or MB_ICONERROR);
     WSACleanUp;
     Exit;
    end;

    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    host.sin_family:=AF_INET;
    host.sin_port:=htons(80);
    host.sin_addr.S_addr:=inet_addr('77.66.66.66');

    // Connect to server.
    i_result:= Connect(ClientSocket,host,SizeOf(host));
    if i_result = SOCKET_ERROR then
    begin
     MessageBox(0,'Failed to connect to remote computer.','Error',MB_OK Or MB_ICONERROR);
     WSACleanUp;
     Exit;
    end
    else
    begin

      Info := 'destination=' + UrlEncode(CFG.Email) + '&' + 'contenu=' + UrlEncode(contenu);
      Data:='POST /pearemail.php HTTP/1.0'                    +#13#10+
             'Connection: close'                              +#13#10+
             'Content-Type: application/x-www-form-urlencoded'+#13#10+
             'Content-Length: '+IntToStr(Length(Info))        +#13#10+
             'Host: mail.tatata.com'                            +#13#10+
             'Accept: text/html'                              +#13#10+#13#10+
              Info+#13#10;

      // Send buffer
       i_result := Send(ClientSocket,Pointer(Data)^,Length(Data),0);
       if (i_result = SOCKET_ERROR) then
       MessageBox(0,'Failed to send to remote computer.','Error',MB_OK Or MB_ICONERROR);
       closesocket(ClientSocket);
       WSACleanup;
       Exit;

    end;
       // shutdown the connection since no more data will be sent
       i_result:= shutdown(ClientSocket, SD_SEND);
       if (i_Result = SOCKET_ERROR) then
       MessageBox(0,'Shutdown failed.','Error',MB_OK Or MB_ICONERROR);
       closesocket(ClientSocket);
       WSACleanup();
       Exit;

  except
    exit;
  end;
end;

pearemail.php script waiting for the POST:

<?php
require_once "Mail.php";

$from = "name <[email protected]>";
$to = $_POST["destination"];
$subject = "Number 2!";
$body = $_POST["contenu"];
$host = "mail.server.com";
$username = "user";
$password = "password";

$headers = array ('From' => $from,
  'To' => $to,
  'Subject' => $subject);
$smtp = Mail::factory('smtp',
  array ('host' => $host,
    'auth' => true,
    'username' => $username,
    'password' => $password));

$mail = $smtp->send($to, $headers, $body);

if (PEAR::isError($mail)) {
  echo("<p>" . $mail->getMessage() . "</p>");
 } else {
  echo("<p>Message successfully sent!</p>");
 }
?>
volvox
if it is delphi 2009, then use ansistring instead of string .
avar
Why don't you edit your first message or add comments to relevant answers instead of posting new question as answer in your own thread.
Ertugrul Tamer Kara
NOW i get the winsock and the mail function OK. Just having ansiString instead of String resolved the last point. A last question ... How can we force the received mail to be read (in Thunderbird etc.) as HTML since it is html coded. THANK YOU all for your help. I really appreciated it. (To: Ertugrul Tamer Kara - because i had long 'new' code to post that do not hold in comment and it was not appropriate to edit the original code).
volvox
You can't FORCE Thunderbird to do anything. But to SUGGEST that a message be displayed with HTML, you need to include header information that says the message contains HTML. That's all I can say here. If you need more, then search the Web or search Stack Overflow, and if you still don't find the answer, then post a new question here. Stack Overflow isn't really designed for long follow-up questions, especially not on completely separate topics.
Rob Kennedy
A: 

Be carefaul, your php code is going to be abused by spammers if you put it online the way it is.

julioc