Повторният опит за грешна парола в PDFlibPas отхвърля неуспешния TPDFDocument и създава изцяло нов обект за следващия опит чрез OnPassword (TPDFlibPasswordEvent), който работи до шестнадесет опита. Така всяка кандидат-парола се проверява в чист жизнен цикъл без остатъчно състояние от предишния отказ
Защо не можете просто да опитате отново със същия обект на документа?
Повторното използване на TPDFDocument не работи, защото неуспешното отваряне вече е освободило вътрешните reader, cross-reference table и crypt handler. След грешна парола няма паузирано състояние, което да чака нова стойност, затова всеки опит започва с нов документ и избягва трудно проследими остатъци от неуспешния parse
Как callback-ът OnPassword заявява следващата парола?
TPDFlibPasswordEvent се извиква от TPDFlib.LoadFromFile, LoadFromStream и LoadFromString при грешна парола и получава номера на предстоящия опит, параметър Password за следващия кандидат и флага Retry, който по подразбиране е false
TPDFlibPasswordEvent = procedure(Sender: TObject; AttemptNumber: Integer;
var Password: WideString; var Retry: Boolean) of object;
property OnPassword: TPDFlibPasswordEvent read FOnPassword write FOnPassword;Паролата от първоначалното извикване на LoadFromFile е опит едно, затова първото извикване на OnPassword получава AttemptNumber 2. Ако Retry остане false, зареждането завършва с LastErrorCode 404, а при true PDFlibPas използва стойността, записана в Password
В цикъла за повторен опит: нов TPDFDocument при всеки опит
При LoadFromFile, LoadFromStream и LoadFromString PDFlibPas създава нов TPDFDocument при всеки опит, изпълнява пълната последователност за отваряне и запазва обекта само ако паролата е потвърдена. Отхвърленият документ се освобождава веднага заедно с неговите reader, cross-reference table и crypt handler
// Simplified excerpt from inside LoadFromFile: every attempt gets a
// document that has never seen a previously rejected password. FileName,
// AttemptNumber and AttemptPassword come from the enclosing method.
Var
Doc: TPDFDocument;
LoadResult: TPLLoadResult;
Success: Boolean;
Begin
Success := False;
Repeat
Doc := TPDFDocument.Create;
Doc.DecodeMode := FDefaultDecodeMode;
Try
LoadResult := Doc.LoadFromFile(FileName, AttemptPassword);
Success := LoadResult = lrOkay;
if Success then
begin
FDocs.Add(Doc); // hand the verified document to the
Doc := nil; // caller's collection; skip the Free below
end;
Finally
Doc.Free; // a rejected attempt's reader, xref table
End; // and crypt handler are torn down right here
if Success or (LoadResult <> lrWrongPassword) then
Break; // success, or a non-password failure: stop
Inc(AttemptNumber);
Until not RequestPasswordRetry(AttemptNumber, AttemptPassword);
End;Редът Doc := nil непосредствено преди блока Finally е целият договор за жизнения цикъл. Само потвърденият документ се добавя към FDocs, а отхвърленият не оставя външно видим reader, page count или crypt handler
Колко пъти PDFlibPas повтаря грешна парола?
PDFlibPas разрешава общо шестнадесет опита за едно извикване на LoadFromFile, LoadFromStream или LoadFromString, като първоначалната парола е опит едно. OnPassword се извиква най-много петнадесет пъти, а седемнадесетият опит не се допуска. При изчерпване или отказ LoadFromFile връща 0 и LastErrorCode е 404. Abort от обработчика прекратява цикъла, докато повредена cross-reference table спира още след първия опит с LastErrorCode 401
Работи ли цикълът еднакво за файлове, потоци и низове?
OnPassword и ограничението от шестнадесет опита са еднакви за LoadFromFile, LoadFromStream и LoadFromString. Пътят към файл се отваря отново, низът вече е в паметта, а LoadFromStream копира входа преди първия parse и го връща към позиция нула за всеки следващ TPDFDocument
Вписване на повторния опит за парола в екран за приемане на документи
Този callback естествено принадлежи към работния поток за приемане на документи: файлът идва отвън, паролата не е известна предварително и операторът трябва да може да предложи няколко кандидата без собствен външен цикъл около LoadFromFile
procedure TIntakeForm.SupplyPassword(Sender: TObject; AttemptNumber: Integer;
var Password: WideString; var Retry: Boolean);
var
Typed: string;
begin
// AttemptNumber counts from 2: the password already tried was attempt 1.
Typed := '';
Retry := InputQuery('Password required',
Format('Attempt %d of 16 - enter the document password', [AttemptNumber]), Typed);
if Retry then
Password := Typed;
// Retry is False when the operator cancels, which leaves
// LastErrorCode at 404 for the caller to report.
end;procedure TIntakeForm.LoadInboundDocument;
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.OnPassword := SupplyPassword;
if Lib.LoadFromFile('inbound-invoice.pdf', '') = 1 then
RegisterIntakeDocument(Lib) // only a verified document reaches here
else
LogRejectedIntake('inbound-invoice.pdf', Lib.LastErrorCode);
finally
Lib.Free;
end;
end;RegisterIntakeDocument получава Lib само след като LoadFromFile върне 1, тоест паролата действително е потвърдена от crypt handler. За следваща проверка на защитата може да се прочете /Encrypt dictionary чрез одит на алгоритъма, ревизията и permission bits Свързана статия
Този механизъм е част от по-широката дисциплина на parser слоя: файлът не получава доверие, преди да се докаже безопасен, независимо дали се проверява парола или подозрително поле за дължина на буфер Свързана статия
OnPassword и цикълът за повторен опит са част от стандартната PDF библиотека PDFlibPas за Delphi и C++Builder и не изискват отделен модул или лиценз Свързана статия