【问题标题】:Running an external php script from Delphi从 Delphi 运行外部 php 脚本
【发布时间】:2011-08-29 09:45:55
【问题描述】:

好的 - 这是我之前关于 sending an email using a php script 的问题的延续。我现在使用PEAR 发送邮件。我使用的php脚本如下(单独执行成功):PHPEMail.php

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

$from = "FromName <FromName@SomeAddress.com>";
$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>");
 }
?>

我现在需要从 Delphi 执行这个脚本 (PHPEMail.php),传递一些变量,使用 winsock。我要使用这段代码 - 到目前为止还没有成功:

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]

我很确定问题出在发送到 Web 服务器的“TheData”中。 PHP脚本只是没有被触发。有人知道出了什么问题吗?

(注意:我想用winsock,我不想要第三方组件。完整的代码,是一个服务器,重约12ko,注定要嵌入一些硬件中)。

见最后的代码。


因为我在服务器日志上看不到 POST,所以我对我的代码做了一些改进(加上一些错误消息)。现在服务器的日志显示发送的数据包的一些跟踪......也就是说,通常的 id 和时间加上字母“P”......可能是单词 'POST' 的第一个字母(发送的数据)。因此,我必须调查“Send()”命令。 (我在德尔福 2009 年)。我没有从 winsock 或发送命令中得到任何错误。

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 脚本等待 POST:

<?php
require_once "Mail.php";

$from = "name <name@tatata.com>";
$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>");
 }
?>

【问题讨论】:

  • 小心,如果你把它放在网上,你的 php 代码会被垃圾邮件发送者滥用。

标签: php delphi post winsock


【解决方案1】:

第一个 HTTP 行的第二个标记需要是绝对 URL 路径。它需要以斜线开头。

POST /PHPEMail.php HTTP/1.0

您还应该努力确保您发送的数据确实是 URL 编码的,正如内容类型所说的那样。字符 10 和 13 不是 URL 中的有效字符。您还需要考虑正在阅读的文本文件中的所有字符:

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

我注意到您没有读取来自服务器的响应。不要忽视这一点。有时它可能会告诉你出了什么问题。当你尝试运行你的代码时,我也没有看到服务器日志所说的发生了什么。

在此过程中,您一定会犯更多这样的错误。考虑使用为您处理此类内容的库,例如IndyICSSynapse。除非你真的必须,否则不要重新实现 HTTP。如果您真的不想要第三方代码,请考虑使用 Windows 内置的东西来使用第二方代码(或者是第一方代码?)。 KB 165298 有一个使用 InternetConnectHttpOpenRequestHttpSendRequest 发布 URL 编码的表单请求的简短示例。

【讨论】:

  • 谢谢。服务器错误日志什么也没说 - 甚至连连接都没有显示!我应该用一些
    替换 chr 10 和 13 吗?我尝试过 Indy、ICS 和 Synapse,每一个都会导致服务器超过 600ko——这对我来说太大了,因为整个服务器都放在一个资源中。实际代码约为 12-20ko max。我会看看 KB 165298。
  • 但是我知道它可以连接。在“如果连接”之后的某个时间添加 ShowMessage,我可以读取发送的数据,即目标电子邮件和 NiceBody(数据)。乍一看,TheData 的格式很好。
  • 我认为 TheData 的退货和馈线还可以,但我需要找到示例。 NiceBody 本身(传输的消息)实际上是 html 编码的。
  • 您发送的 URL 不是 HTML。您熟悉 application/x-www-form-urlencoded 数据应该是什么样子吗?如果没有,则捕获浏览器发送的 POST 请求并查看。我认为 Firebug 可以做到。或者使用 Wireshark 查看通过网络发送的内容。 ASCII 字符 10 以三字符序列“%0A”的形式发送,就像您多年来在其他 URL 中看到的那样。
  • 在 Info 上使用 URLEncode 我得到了所有发送的数据的长字符串,包括第一个位置的目标电子邮件 - 就像一个长 URL。你确定这是它应该呈现给 php 脚本的方式吗?不过,我有连接,但脚本并不关心。
【解决方案2】:

正如我从您的 Delphi 代码中看到的,电子邮件的所有信息都已打包到一个变量中,但是在您的 php 脚本中如何将您的许多 php 变量传递给它?您必须解压缩/解析它以在您的 php 脚本中传递每个变量(目标地址、主题、邮件正文等)。 (在你当前的 php 脚本中有很多 $_posts,但它只收到来自 Delphi 代码的 1 个实际帖子。)

下面是我的 php 脚本的工作代码。

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 是 #13#10。

另一个重要的事情是“link=';它是我的 php 脚本中的变量名,它接收/保存我正在发送的数据。因为我只发送 1 个变量,而且它不是电子邮件脚本,所以没有 php需要解析,所以我没有在这里粘贴我的php代码。

希望我说清楚:)

【讨论】:

  • 他用两个查询字符串参数,destination 和 nicebody 填充了 Info。他省略了应该将它们分开的&符号。 PHP 知道如何解析 URL 编码的 post 数据,因此它会将参数拆分为 $_POST['destination'] 和 $_POST['nicebody']。
  • 我在 Info 的第一次尝试确实是:Info:='Destination='+Email+'&NiceBody='+NiceBody;我也试过(avar 的建议): 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 +一个+一个;发送(客户端,指针(数据)^,长度(数据),0);再次发送数据(在嗅探器上读取 tcp 服务器主机名),但仍然没有与 php 脚本交互。
  • 我会用我的 delphi 代码试试你的 php 代码,然后告诉你。稍后
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-01
  • 2018-08-14
  • 2021-06-21
  • 2018-08-13
  • 1970-01-01
  • 2020-09-05
  • 2011-05-26
相关资源
最近更新 更多