我该如何正确地通过IPv6发送电子邮件?

3
我正在开发一款iOS和Android应用程序中的电子邮件发送功能。这是一个使用OpenSSL通过Gmail发送电子邮件的功能。我使用Delphi 10.2.3 Tokyo和 Indy 10。但是,我提交我的iOS应用程序到iTunes Connect后,他们拒绝了我的应用程序,因为此功能在IPv6下不起作用。他们说:“我们在iPad和iPhone上回顾运行iOS 11.4.1,并连接到IPv6网络的Wi-Fi时,在您的应用程序中发现一个或多个错误。” 他们还向我发送了一个显示错误的屏幕截图,显示“解析地址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; 

3
你提出了两个完全独立且无关的问题。StackOverflow指南要求一个帖子只能有一个问题。因此,请删除关于打开URL的第二个问题,并单独重新发布它,它与第一个关于通过IPv6连接到SMTP的问题不相关。 - Remy Lebeau
1
在第二个问题中,错误信息告诉你正在访问一个nil指针,所以你需要找到它。但是,在打开URL之前没有任何理由执行手动TCP检查。你正在生成一个外部应用程序来打开URL,因此让该应用程序根据需要处理连接错误。特别是当两个Connect()调用都失败时,您仍然会继续打开URL。因此,完全摆脱您的过程中的TIdTCPClient,它不属于那里。这可能是您的nil指针来自的地方。 - Remy Lebeau
@Remy非常感谢您教我StackOverFlow的指南。首先,我按照您的指示将第二个问题分开并重新发布了它。 - miro
1
在这个问题的正文中没有理由链接到另一个问题,因为它与这个问题无关。我已经将其删除。如果有任何人感兴趣,请参见 https://stackoverflow.com/questions/51643713/。 - Remy Lebeau
1个回答

5
我看到您的SMTP代码存在几个问题:
  • 您需要设置IdSMTP.IPVersion属性,而不是IdSMTP.Socket.IPVersion属性。 IPVersion属性的默认值为Id_IPv4bug - 它没有遵守IdGlobal单元中的ID_DEFAULT_IP_VERSION常量)。Connect()会覆盖Socket.IPVersion属性值,并使用IPVersion属性值进行连接,因此实际上您正在尝试两次使用Id_IPv4进行连接,这将在仅支持IPv6的网络上失败(Apple要求应用程序支持IPv6)。

  • 您没有捕获第二个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 error] URegistration.pas (142):E2362无法访问受保护的符号TIdTCPClientCustom.IPVersion”,在“IdSMTP.IPVersion:= Id_IPv6;”处。在Delphi的结构部分中说,“IdSMTP不包括名为IPVersion的成员”。在“IdSMTP.IPVersion:= Id_IPv4;”处也有错误。我该如何修复它们? - miro
@miro 不好意思,我忘记了这是Indy中已知的问题。我更新了我的回答。 - Remy Lebeau
我非常感谢你的辛勤工作。我根据你在这里写的内容修改了我的代码。它在我的iPhone 6s Plus上运行良好。我已经将我的应用程序提交到iTunes Connect进行审核。现在我等待结果。我会在这里通知进展情况。我希望一切都顺利。 - miro
非常感谢您的辛勤工作。您的代码运行良好,并已被iTunes Connect接受。然而,苹果公司提出了另一个问题。我会尝试解决它。如果需要您的帮助,我可能会再次向您求助。 - miro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接