Я новичок в Инди, поэтому прошу прощения, если здесь есть какие-то явные проблемы.
Я пытаюсь получить электронные письма из своего почтового ящика 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 до последней версии.
Если бы кто-нибудь мог определить проблему и предоставить рабочий пример/где его найти, это было бы очень полезно.
Использование пароля приложения с установленным для 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
оказался бесплодным» — да, потому что Connect
уже позвонил. «Я дважды проверил учетные данные, даже дошел до создания нового пароля приложения, но тоже безуспешно» — я точно знаю, что пароли приложений работают с Gmail IMAP. Я могу успешно войти в свой GMail и выбрать Inbox
, используя TIdIMAP4
в последней версии Indy. Это еще одна причина для вас обновиться. Итак, еще раз убедитесь, что ваш пароль правильный. «Я использовал ваш код для проверки TIdIMAP4.LastCmdResult.Code
, получил ошибку List Index out of bounds
» — вы не можете получить эту ошибку с помощью PosInStrArray()
...
... «Файлы Github Repo не содержат группового проекта для Delphi 2010... смогу ли я вручную установить файлы без него?» - да, конечно, просто скомпилируйте и установите 5 пакетов по отдельности. Я обновил Wiki, чтобы упомянуть об этом. В конце концов, это и есть вся группа проектов — просто группа ссылок на файлы пакета, не более того.
Обновление Indy помогло, спасибо за помощь.
Явный вызов
Login;
оказался бесполезным. Дважды проверил учетные данные, даже дошел до генерации нового пароля приложения, но тоже безуспешно. Я использовал ваш код для проверкиTIdIMAP4.LastCmdResult.Code
, получил ошибкуList Index out of bounds
, поэтому изменил -1 на 0 (насколько я понимаю, это должен быть вариант «ОК»), а затем я получил исключение. Что касается обновления, файлы репозитория Github не содержат группового проекта для Delphi 2010 (версия пакета 140 согласно Wiki), смогу ли я вручную установить файлы без него?