IMAP4 не проходит аутентификацию в Gmail в Delphi 2010

Я новичок в Инди, поэтому прошу прощения, если здесь есть какие-то явные проблемы.

Я пытаюсь получить электронные письма из своего почтового ящика Gmail, а затем сохранить их в массиве объектов TIdMessage.

Я не могу найти никакой документации, подробно описывающей, как его настроить, или рабочий пример (документация Indy не работает), а мои знания о работе с электронной почтой практически равны нулю.

procedure TfrmEmail.LoadInbox;
var
  IMAP:TIdIMAP4;
  iLength,i:integer;
  SSL:TIdSSLIOHandlerSocketOpenSSL;
begin
  SSL:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  //Set-Up SSL
  with SSL.SSLOptions do
  begin
    Method:=sslvTLSv1;
    Mode:=sslmClient;
    VerifyMode:=[];
    VerifyDepth:=0;
  end;

  IMAP:=TIdIMAP4.Create(Nil);
  //IMAP settings
  with IMAP do
  begin
    IOHandler:=SSL;
    Host:='imap.gmail.com';
    Port:=993;
    Username: = {[email protected]};
    Password: = {app password};
    UseTLS:=utUseImplicitTLS;
    Connect; //works up until here
    SelectMailBox('INBOX');//error here
  end;

  iLength:=IMAP.RetrieveMailBoxSize;
  ShowMessage(inttostr(iLength));
  SetLength(ArrInboxMessages,iLength);
  for i := 1 to iLength do
  begin
    IMAP.Retrieve(i,ArrInboxMessages[i]);
  end;
  IMAP.Disconnect;
  IMAP.Free;
end;

Мой код работает до части Connect, но как только я пытаюсь выбрать почтовый ящик, получаю исключение: EIdConnectionStateError.

Состояние моего соединения не аутентифицировано, и я думаю, это потому, что я не использую аутентификацию SASL.

Другая проблема может заключаться в том, что я использую TLSv1, но он работает с SMTP, и я не могу обновить Delphi 2010 до последней версии.

Если бы кто-нибудь мог определить проблему и предоставить рабочий пример/где его найти, это было бы очень полезно.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
92
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Использование пароля приложения с установленным для TIdIMAP4.AuthType значением iatUserPass (настройка по умолчанию) должно работать нормально, вам не нужно использовать SASL+OAuth (хотя GMail предпочел бы это).

TIdIMAP4.Connect() имеет необязательный параметр AAutoLogin, который по умолчанию равен True, поэтому TIdIMAP4.Login() должен вызываться до выхода Connect(). Если бы Login() произошел сбой, вы бы получили другое исключение и с самого начала не смогли бы достичь TIdIMAP4.SelectMailBox().

Исключение EIdConnectionStateError, которое вы получаете, означает, что TIdIMAP4.ConnectionState не является csAuthenticated, когда вы звоните SelectMailBox(). ConnectionState устанавливается в csNonAuthenticated в начале Connect() и Login(), а затем Login() обновляет его до csAuthenticated, если аутентификация прошла успешно. Таким образом, ваша ошибка не должна быть возможной, если Login() не обходится, т. е. если AAutoLogin=False.

При необходимости вы можете попробовать вызвать Login() явно после Connect() и перед SelectMailBox(), например:

Connect;
if ConnectionState <> csAuthenticated then
  Login;
SelectMailBox('INBOX');

Альтернативно:

Connect(False);
Login;
SelectMailBox('INBOX');

При этом, как только вы решите проблему Login, в вашем коде возникнут еще две проблемы:

  • вы сливаете объект SSL. TIdIMAP4 не берет на себя ответственность за него.

  • вы не создаете никаких TIdMessage объектов для TIdIMAP4.Retrieve() для заполнения. Выделение массива объектов не приводит к созданию самих объектов, вам необходимо создать их самостоятельно.

  • TIdIMAP4.(UID)RetrieveMailBoxSize() извлекает общий размер всех писем в почтовом ящике в байтах. Чтобы получить количество сообщений в почтовом ящике, вместо этого используйте TIdIMAP4.MailBox.TotalMsgs, который SelectMailBox() заполняет.

Однако вы вручную копируете функцию, которая TIdIMAP4 вам уже предоставлена. TIdIMAP4 имеет публичные RetrieveAllHeaders() и RetrieveAllMsgs() методы. А если вы установите TIdIMAP4.RetrieveOnSelect на rsHeaders или rsMessages, то SelectMailBox() автоматически загрузит для вас все доступные заголовки/тела электронной почты в TIdIMAP4.Mailbox.MessageList.


ОБНОВЛЯТЬ:

Я не заметил, что вы используете очень старую версию Delphi.

Я просмотрел исходный код TIdIMAP4 в версии Indy, которая поставлялась бы с Delphi 2010 (см. здесь ), и заметил, что Login() на самом деле не вызывал исключения в случае сбоя в то время. Эта проблема была исправлена ​​2 года спустя ( см. здесь) и, скорее всего, будет включена в Delphi XE2.

Итак, в вашем случае Login(), скорее всего, вызывается Connect(), но аутентификация не удалась, и Login() не вызывает исключения по этому поводу, поэтому ConnectionState остается как csNonAuthenticated. Это объясняет вашу ситуацию.

Итак, вам необходимо дважды проверить правильность ваших учетных данных. Вызов Login() второй раз будет излишним, но вы можете проверить, является ли TIdIMAP4.LastCmdResult.Code'OK' или 'PREAUTH', когда Connect() выходит, например:

Connect; // <-- AAutoLogin=True
if PosInStrArray(LastCmdResult.Code, ['OK', 'PREAUTH']) = -1 then
  RaiseExceptionForLastCmdResult;
SelectMailBox('INBOX');

Альтернативно:

Connect(False);
if ConnectionState <> csAuthenticated then
begin
  Login;
  if ConnectionState <> csAuthenticated then
  //if LastCmdResult.Code <> 'OK' then
    RaiseExceptionForLastCmdResult;
end;
SelectMailBox('INBOX');

Тем не менее, я бы посоветовал обновить Indy до последней версии из репозитория GitHub, чтобы убедиться, что у вас есть все последние исправления, включая то, где Login() вызывает исключение в случае сбоя. Используя последнюю версию, я могу использовать TIdIMAP4 для успешного входа в свою Gmail с паролем приложения и без ошибок выбрать Inbox.

Вы можете обновить Indy, даже если используете старую версию Delphi, поскольку Indy по-прежнему поддерживает Delphi 2010. См. документацию Обновление Indy в Wiki репозитория.

Явный вызов Login; оказался бесполезным. Дважды проверил учетные данные, даже дошел до генерации нового пароля приложения, но тоже безуспешно. Я использовал ваш код для проверки TIdIMAP4.LastCmdResult.Code, получил ошибку List Index out of bounds, поэтому изменил -1 на 0 (насколько я понимаю, это должен быть вариант «ОК»), а затем я получил исключение. Что касается обновления, файлы репозитория Github не содержат группового проекта для Delphi 2010 (версия пакета 140 согласно Wiki), смогу ли я вручную установить файлы без него?

Rayhaan Bhoola 26.08.2024 18:24

«Явный вызов Login оказался бесплодным» — да, потому что Connect уже позвонил. «Я дважды проверил учетные данные, даже дошел до создания нового пароля приложения, но тоже безуспешно» — я точно знаю, что пароли приложений работают с Gmail IMAP. Я могу успешно войти в свой GMail и выбрать Inbox, используя TIdIMAP4 в последней версии Indy. Это еще одна причина для вас обновиться. Итак, еще раз убедитесь, что ваш пароль правильный. «Я использовал ваш код для проверки TIdIMAP4.LastCmdResult.Code, получил ошибку List Index out of bounds» — вы не можете получить эту ошибку с помощью PosInStrArray()...

Remy Lebeau 26.08.2024 19:46

... «Файлы Github Repo не содержат группового проекта для Delphi 2010... смогу ли я вручную установить файлы без него?» - да, конечно, просто скомпилируйте и установите 5 пакетов по отдельности. Я обновил Wiki, чтобы упомянуть об этом. В конце концов, это и есть вся группа проектов — просто группа ссылок на файлы пакета, не более того.

Remy Lebeau 26.08.2024 19:47

Обновление Indy помогло, спасибо за помощь.

Rayhaan Bhoola 26.08.2024 22:03

Другие вопросы по теме