【问题标题】:How can I send email over IPv6 properly?如何通过 IPv6 正确发送电子邮件?
【发布时间】:2018-08-01 19:13:00
【问题描述】:

我正在我的 iOS 和 Android 应用程序中开发电子邮件发送功能。

这是一个使用 OpenSSL 通过 Gmail 发送电子邮件的功能。

我正在使用 Delphi 10.2.3 Tokyo 和 Indy 10。

我向 iTunes Connect 提交了我的 iOS 应用程序,但他们拒绝了我的应用程序,因为此功能在 IPv6 中不起作用。

他们说

在通过连接到 IPv6 网络的 Wi-Fi 运行 iOS 11.4.1 的 iPad 和 iPhone 上进行审查时,我们发现您的应用存在一个或多个错误。

他们还给我发了一张错误提示的屏幕截图

解析地址smtp.gmail.com时出错:(8)

如何解决此错误以正确使用 IPv6?我的代码如下:

Procedure MailSend; 
Var
  Connected: Boolean; 
Begin 
  IdSMTP := TIdSMTP.Create(nil); 
  try 
    IdSMTP.Host     := 'smtp.gmail.com'; 
    IdSMTP.Port     := 587; 
    IdSMTP.Username := 'xxxx@gmail.com'; // UserName 
    IdSMTP.Password := 'xxxx';       // Password 
    SSL := TIdSSLIOHandlerSocketOpenSSL.Create; 

    try 
      SSL.Host := IdSMTP.Host; 
      SSL.Port := IdSMTP.Port; 
      SSL.Destination := SSL.Host + ':' + IntToStr(SSL.Port); 
      IdSMTP.IOHandler := SSL; 
      IdSMTP.UseTLS := utUseExplicitTLS; 

      IdSMTP.Socket.IPVersion := Id_IPv6; 
      try 
        IdSMTP.Connect; 
        Connected := True; 
      except 
        Connected := False; 
      end; 

      If Connected = False then 
      Begin 
        IdSMTP.Socket.IPVersion := Id_IPv4; 
        IdSMTP.Connect; 
      End; 

      Msg := TIdMessage.Create(IdSMTP); 
      try 
        Msg.OnInitializeISO           := IdMessage_InitializeISO; 
        Msg.ContentType               := 'text/plain'; 
        Msg.CharSet                   := 'UTF-8'; 
        Msg.ContentTransferEncoding   := 'BASE64'; // BASE64 (7bit) 
        //Msg.ContentTransferEncoding   := '8bit';   // RAW(8bit) 
        Msg.From.Name                 := SsNoSt; 
        Msg.From.Address              := 'xxxx@gmail.com'; 
        Msg.Recipients.EMailAddresses := 'xxxx@gmail.com'; 
        Msg.Subject                   := SsNoSt; 
        Msg.Body.Text                 := 'Unicode String (body)'; 
        IdSMTP.Send(Msg); 
      finally 
        Msg.Free; 
      end; 
      IdSMTP.Disconnect; 
    finally 
      SSL.Free; 
    end; 
  finally 
    IdSMTP.Free; 
  End; 
End; 

【问题讨论】:

  • 您在问两个完全独立且不相关的问题。 StackOverflow 指南要求每个帖子一个问题。所以,请删除关于打开网址的第二个问题并单独重新发布,它不属于关于通过 IPv6 连接到 SMTP 的第一个问题。
  • 在第二个问题中,错误消息告诉您正在访问 nil 指针,因此您需要找到它。但是完全没有理由在打开一个 url 之前执行手动 TCP 检查。您正在生成一个外部应用程序来打开 url,因此让该应用程序根据需要处理连接错误。特别是因为如果两个 Connect() 调用都失败了,您仍然会继续打开 url。因此,只需从您的程序中完全删除TIdTCPClient,它不属于那里。这可能是您的 nil 指针的来源。
  • @Remy 非常感谢你教我 StackOverFlow 的指导方针。首先,我按照您的指示将第二个问题分开并重新发布。
  • 没有理由从这个问题的正文链接到另一个问题,因为它与这个问题无关。我已将其删除。如果有的话,它应该已经发布在评论中。有兴趣的朋友请看stackoverflow.com/questions/51643713

标签: ios delphi ipv6


【解决方案1】:

我发现您的 SMTP 代码存在一些问题:

  • 您需要设置IdSMTP.IPVersion 属性而不是IdSMTP.Socket.IPVersion 属性。 IPVersion 属性的默认值为 Id_IPv4bug - 它不遵守 IdGlobal 单元中的 ID_DEFAULT_IP_VERSION 常量)。 Connect()IPVersion 属性值覆盖 Socket.IPVersion 属性值,因此您实际上尝试使用 Id_IPv4 连接两次,这将在纯 IPv6 网络上失败(Apple 需要应用程序支持)。

  • 您没有发现来自第二个Connect() 的任何错误。这很可能是 Apple 最终看到的错误。

  • 您不应手动设置SSL.HostSSL.PortSSL.Destination 属性。让Connect() 为您处理。

试试这个:

// this accessor class is needed because TIdSMTP derives from TIdTCPClientCustom
// instead of TIdTCPClient.  The IPVersion property is protected in
// TIdTCPClientCustom and not published by TIdSMTP or its ancestors.
//
// See https://github.com/IndySockets/Indy/issues/184 ...
//
type
  TIdSMTPAccess = class(TIdSMTP)
  end;

procedure MailSend; 
var
  IdSMTP: TIdSMTP;
  Msg: TIdMessage;
begin 
  IdSMTP := TIdSMTP.Create(nil);
  try
    SSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP);
    IdSMTP.IOHandler := SSL;

    IdSMTP.Host     := 'smtp.gmail.com';
    IdSMTP.Port     := 587;
    IdSMTP.Username := 'xxxx@gmail.com';
    IdSMTP.Password := 'xxxx';
    IdSMTP.UseTLS := utUseExplicitTLS; 

    TIdSMTPAccess(IdSMTP).IPVersion := Id_IPv6; 
    try 
      IdSMTP.Connect; 
    except 
      TIdSMTPAccess(IdSMTP).IPVersion := Id_IPv4; 
      try
        IdSMTP.Connect; 
      except
        // unable to connect!
        Exit;
      end;
    end; 

    try
      Msg := TIdMessage.Create(nil); 
      try 
        Msg.OnInitializeISO           := IdMessage_InitializeISO; 
        Msg.ContentType               := 'text/plain'; 
        Msg.CharSet                   := 'UTF-8'; 
        Msg.ContentTransferEncoding   := 'BASE64'; // BASE64 (7bit) 
        //Msg.ContentTransferEncoding   := '8bit';   // RAW(8bit) 
        Msg.From.Name                 := SsNoSt; 
        Msg.From.Address              := 'xxxx@gmail.com'; 
        Msg.Recipients.EMailAddresses := 'xxxx@gmail.com'; 
        Msg.Subject                   := SsNoSt; 
        Msg.Body.Text                 := 'Unicode String (body)'; 

        IdSMTP.Send(Msg); 
      finally 
        Msg.Free; 
      end; 
    finally
      IdSMTP.Disconnect;
    end;
  finally 
    IdSMTP.Free; 
  end; 
end; 

或者:

type
  TIdSMTPAccess = class(TIdSMTP)
  end;

procedure MailSend; 
var
  IdSMTP: TIdSMTP;
  Msg: TIdMessage;
  Connected: Boolean;
begin 
  IdSMTP := TIdSMTP.Create(nil);
  try
    SSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP);
    IdSMTP.IOHandler := SSL;

    IdSMTP.Host     := 'smtp.gmail.com';
    IdSMTP.Port     := 587;
    IdSMTP.Username := 'xxxx@gmail.com';
    IdSMTP.Password := 'xxxx';
    IdSMTP.UseTLS := utUseExplicitTLS; 

    Connected := False;

    if GStack.SupportsIPv6 then
    begin
      TIdSMTPAccess(IdSMTP).IPVersion := Id_IPv6; 
      try 
        IdSMTP.Connect; 
        Connected := True;
      except 
      end; 
    end;

    if (not Connected) and GStack.SupportsIPv4 then
    begin
      TIdSMTPAccess(IdSMTP).IPVersion := Id_IPv4; 
      try 
        IdSMTP.Connect; 
        Connected := True;
      except 
      end; 
    end;

    if not Connected then
    begin
      // unable to connect!
      Exit;
    end; 

    try
      Msg := TIdMessage.Create(nil); 
      try 
        Msg.OnInitializeISO           := IdMessage_InitializeISO; 
        Msg.ContentType               := 'text/plain'; 
        Msg.CharSet                   := 'UTF-8'; 
        Msg.ContentTransferEncoding   := 'BASE64'; // BASE64 (7bit) 
        //Msg.ContentTransferEncoding   := '8bit';   // RAW(8bit) 
        Msg.From.Name                 := SsNoSt; 
        Msg.From.Address              := 'xxxx@gmail.com'; 
        Msg.Recipients.EMailAddresses := 'xxxx@gmail.com'; 
        Msg.Subject                   := SsNoSt; 
        Msg.Body.Text                 := 'Unicode String (body)'; 

        IdSMTP.Send(Msg); 
      finally 
        Msg.Free; 
      end; 
    finally
      IdSMTP.Disconnect;
    end;
  finally 
    IdSMTP.Free; 
  end; 
end;

【讨论】:

  • 感谢您的发布代码。但是,我有您的第一个错误消息,说“[DCC 错误] URegistration.pas (142): E2362 Can not access protected symbol TIdTCPClientCustom.IPVersion', at "IdSMTP.IPVersion := Id_IPv6;".在 Delphi 的结构部分中说“IdSMTP 不包含名为 IPVersion 的成员”。 “IdSMTP.IPVersion := Id_IPv4;”也是错误的。我该如何解决它们?
  • @miro 糟糕,我忘了这是 Indy 中的一个已知问题。我更新了我的答案
  • 非常感谢您的辛勤工作。我按照您在此处编写的内容修改了我的代码。它在我的 iPhone 6s Plus 上运行良好。我将我的应用程序提交给 iTunes Connect 进行审核。现在我等待结果。我会在这里告诉你它是如何进行的。我希望它也顺利。
  • 非常感谢您的辛勤工作。您的代码运行良好并被 iTunes Connect 接受。然而,苹果要求另一个问题。我会尝试击中它。我可能会为你请求一些帮助。在这种情况下,请再次帮助我。
猜你喜欢
  • 2010-10-16
  • 2012-03-10
  • 1970-01-01
  • 2015-10-22
  • 2011-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多