From a67cd9193a6ac04d34c92c1a817fb0f5e44af30f Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Tue, 28 Oct 2008 16:51:52 +0100 Subject: Password callback support from all VFS modules Cleanup, remove unused dialogs --- UCore.pas | 503 ++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 293 insertions(+), 210 deletions(-) (limited to 'UCore.pas') diff --git a/UCore.pas b/UCore.pas index 5415535..86e896b 100644 --- a/UCore.pas +++ b/UCore.pas @@ -28,7 +28,42 @@ function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: procedure FindNextSelected(ListView: TGTKListView; DataList: TList; var Item1, Item2: string); procedure UnselectAll(ListView: TGTKListView; DataList: TList); -type TWorkerThread = class(TThread) + +type TVFSCallbackThread = class(TThread) + private + FThreadID: __pthread_t; + FCopyProgressFunc: TEngineProgressFunc; + procedure PrepareExecute; // Call this right after thread has been started + public + AEngine: TPanelEngine; + APlugin: TVFSPlugin; + + VFSCallbackEvent: TSimpleEvent; + VFSAskQuestion_Message: PChar; + VFSAskQuestion_Choices: PPChar; + VFSAskQuestion_Choice: PInteger; + VFSAskQuestion_Display: boolean; + + VFSAskPassword_Message: PChar; + VFSAskPassword_default_user: PChar; + VFSAskPassword_default_domain: PChar; + VFSAskPassword_default_password: PChar; + VFSAskPassword_flags: TVFSAskPasswordFlags; + VFSAskPassword_username: PPChar; + VFSAskPassword_password: PPChar; + VFSAskPassword_anonymous: PInteger; + VFSAskPassword_domain: PPChar; + VFSAskPassword_password_save: PVFSPasswordSave; + VFSAskPassword_Display: boolean; + VFSAskPassword_Result: LongBool; + + VFSCallbackCancelled: boolean; + + constructor Create(CreateSuspended: boolean); + destructor Destroy; override; + end; + + TWorkerThread = class(TVFSCallbackThread) private FCancelled: boolean; GUIMutex: TCriticalSection; @@ -119,15 +154,12 @@ type TWorkerThread = class(TThread) procedure CancelIt; end; - TOpenDirThread = class(TThread) + TOpenDirThread = class(TVFSCallbackThread) private - FThreadID: __pthread_t; function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer; protected procedure Execute; override; public - AEngine: TPanelEngine; - xEngine: TVFSEngine; APath: string; ASelItem: string; AAutoFallBack: boolean; @@ -135,35 +167,12 @@ type TWorkerThread = class(TThread) ChDirResult, ListingResult, VFSOpenResult: integer; Finished, CancelIt: boolean; RunningTime: Int64; - APlugin: TVFSPlugin; AFullPath, AHighlightItem: string; - Password: string; - - VFSCallbackEvent: TSimpleEvent; - VFSAskQuestion_Message: PChar; - VFSAskQuestion_Choices: PPChar; - VFSAskQuestion_Choice: PInteger; - VFSAskQuestion_Display: boolean; - - VFSAskPassword_Message: PChar; - VFSAskPassword_default_user: PChar; - VFSAskPassword_default_domain: PChar; - VFSAskPassword_flags: TVFSAskPasswordFlags; - VFSAskPassword_username: PPChar; - VFSAskPassword_password: PPChar; - VFSAskPassword_anonymous: PInteger; - VFSAskPassword_domain: PPChar; - VFSAskPassword_password_save: PVFSPasswordSave; - VFSAskPassword_Display: boolean; - VFSAskPassword_Result: LongBool; - - VFSCallbackCancelled: boolean; constructor Create; destructor Destroy; override; end; - // Thread aware functions (also half-thread-safe) without any piece of GTK code procedure DeleteFilesWorker(SenderThread: TWorkerThread); procedure CopyFilesWorker(SenderThread: TWorkerThread); @@ -176,7 +185,6 @@ procedure DummyThreadWorker(SenderThread: TWorkerThread); // Classic functions - don't need progress window function CreateSymlink(const FileName, PossibleNewName: string; Engine: TPanelEngine) : boolean; function EditSymlink(const FileName: string; Engine: TPanelEngine) : boolean; -function HandleLogin(Parent: TComponent; Engine: TPanelEngine; UserName, Password: string): boolean; procedure GetDirSize(AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; AllItems: boolean); @@ -233,9 +241,152 @@ var LeftLocalEngine, RightLocalEngine: TPanelEngine; implementation (********************************************************************************************************************************) uses SysUtils, DateUtils, StrUtils, UConfig, UDirDelete, UOverwrite, ULocale, - UNewDir, UFileAssoc, USymlink, UCoreClasses, ULogin, URemoteWait, UMain, UGnome; + UNewDir, UFileAssoc, USymlink, UCoreClasses, URemoteWait, UMain, UGnome; + + + + +(********************************************************************************************************************************) +constructor TVFSCallbackThread.Create(CreateSuspended: boolean); +begin + inherited Create(CreateSuspended); + APlugin := nil; + VFSCallbackEvent := TSimpleEvent.Create; + VFSAskQuestion_Display := False; + VFSAskPassword_Display := False; + VFSCallbackCancelled := False; +end; +destructor TVFSCallbackThread.Destroy; +begin + VFSCallbackEvent.Free; + inherited Destroy; +end; + +procedure TVFSCallbackThread.PrepareExecute; +begin + FThreadID := pthread_self; + VFSCallbackCancelled := False; +end; + +(********************************************************************************************************************************) +procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; cancel_choice: Integer; user_data: Pointer); cdecl; +var Thread: TVFSCallbackThread; +begin + Thread := user_data; + if (Thread = nil) { or (not (Thread is TVFSCallbackThread))} then begin + DebugMsg(['(ERROR): vfs_ask_question_callback: user_data is not TVFSCallbackThread, exiting.']); + Exit; + end; + + if pthread_self = Application.ThreadID then begin + DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a OpenDirThread']); + HandleVFSAskQuestionCallback(FMain.FWidget, AMessage, Choices, choice); + if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice); + Exit; + end; + + if pthread_self = Thread.FThreadID then begin + DebugMsg(['******* vfs_ask_question_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]); + Thread.VFSAskQuestion_Message := AMessage; + Thread.VFSAskQuestion_Choices := Choices; + Thread.VFSAskQuestion_Choice := choice; + Thread.VFSAskQuestion_Display := True; + Thread.VFSCallbackEvent.ResetEvent; + Thread.VFSCallbackEvent.WaitFor(INFINITE); + DebugMsg(['******* thread: resuming...']); + if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice); + Exit; + end; + + DebugMsg(['!! (ERROR): vfs_ask_question_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']); + DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); +end; + +function vfs_ask_password_callback(const AMessage: PChar; const default_user: PChar; const default_domain: PChar; const default_password: PChar; flags: TVFSAskPasswordFlags; + username, password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave; + user_data: Pointer): LongBool; cdecl; +var Thread: TVFSCallbackThread; + def_pass: PChar; +begin + Result := False; + Thread := user_data; + if (Thread = nil) { or (not (Thread is TVFSCallbackThread))} then begin + DebugMsg(['(ERROR): vfs_ask_question_callback: user_data is not TVFSCallbackThread, exiting.']); + Exit; + end; + + def_pass := default_password; + + // Use stored password, if previously set + if ((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) and (password <> nil) and + (Thread.AEngine is TVFSEngine) and (Length((Thread.AEngine as TVFSEngine).Password) > 0) then + begin + if not (Thread.AEngine as TVFSEngine).PasswordUsed then begin + DebugMsg([' (II) vfs_ask_password_callback: reusing manually set password']); + password^ := g_strdup(PChar((Thread.AEngine as TVFSEngine).Password)); + (Thread.AEngine as TVFSEngine).PasswordUsed := True; + Result := True; + Thread.VFSCallbackCancelled := False; + Exit; + end else def_pass := PChar((Thread.AEngine as TVFSEngine).Password); + end; + + // Ask for password + if pthread_self = Application.ThreadID then begin + DebugMsg(['!! (WARNING): vfs_ask_password_callback called from the main thread, expected spawn from a OpenDirThread']); + Result := HandleVFSAskPasswordCallback(FMain.FWidget, AMessage, default_user, default_domain, def_pass, flags, username, password, anonymous, domain, password_save); + Thread.VFSCallbackCancelled := Result = False; + end else + if pthread_self = Thread.FThreadID then begin + DebugMsg(['******* vfs_ask_password_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); + Thread.VFSAskPassword_Message := AMessage; + Thread.VFSAskPassword_default_user := default_user; + Thread.VFSAskPassword_default_domain := default_domain; + Thread.VFSAskPassword_default_password := def_pass; + Thread.VFSAskPassword_flags := flags; + Thread.VFSAskPassword_username := username; + Thread.VFSAskPassword_password := password; + Thread.VFSAskPassword_anonymous := anonymous; + Thread.VFSAskPassword_domain := domain; + Thread.VFSAskPassword_password_save := password_save; + Thread.VFSAskPassword_Display := True; + Thread.VFSAskPassword_Result := False; + Thread.VFSCallbackEvent.ResetEvent; + Thread.VFSCallbackEvent.WaitFor(INFINITE); + DebugMsg(['******* thread: resuming...']); + Result := Thread.VFSAskPassword_Result; + Thread.VFSCallbackCancelled := Result = False; + end else + begin + DebugMsg(['!! (ERROR): vfs_ask_password_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']); + DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); + end; + + // Save password back to the engine + if ((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) and + Result and (password <> nil) and (strlen(password^) > 0) and + (Thread.AEngine is TVFSEngine) then + begin + (Thread.AEngine as TVFSEngine).Password := string(password^); + (Thread.AEngine as TVFSEngine).PasswordUsed := True; + end; +end; + +function vfs_progress_callback(position, max: Int64; user_data: Pointer): LongBool; cdecl; +begin +// DebugMsg(['VFSCopyCallBackFunc called (iPos = ', iPos, ', iMax = ', iMax, ')']); + Result := True; + if not Assigned(user_data) then Exit; + + if Assigned(TVFSCallbackThread(user_data).FCopyProgressFunc) then + try + Result := TVFSCallbackThread(user_data).FCopyProgressFunc(user_data, position); + except + on E: Exception do DebugMsg(['*** Exception raised in vfs_progress_callback(position=', position, ', max=', max, ', user_data=', user_data, '): (', E.ClassName, '): ', E.Message]); + end; +end; (********************************************************************************************************************************) procedure ClearListData(List: TList); @@ -586,6 +737,10 @@ end; Exit; end; case ErrorType of + 0 : begin + CancelIt; + Exit; + end; 1 : s := LANGMemoryAllocationFailed; 2 : s := LANGCannotOpenSourceFile; 3 : s := LANGCannotOpenDestinationFile; @@ -598,8 +753,9 @@ end; else s2 := LANGMoveError; if ErrorType <> 1 then s3 := StrToUTF8(FileName) else s3 := ''; + case ShowDirDeleteDialog(3, s, s3, GetErrorString(ErrorNum), s2) of - 0 : begin // Cancel button + 0, 252 : begin // Cancel button, Escape Result := False; CancelIt; end; @@ -704,28 +860,48 @@ var DefResponse: integer; // Global variables for this function begin Result := False; try - with SenderThread do begin - if ((SrcEngine is TLocalTreeEngine) and (DestEngine is TLocalTreeEngine)) or - ((SrcEngine is TLocalTreeEngine) and (not (DestEngine is TLocalTreeEngine))) - then Result := DestEngine.CopyFileIn(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append) else -// DebugMsg(['2 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); - if (not (SrcEngine is TLocalTreeEngine)) and (DestEngine is TLocalTreeEngine) - then Result := SrcEngine.CopyFileOut(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append) - // both files are on different engines, we will have to handle the copy process ourselves - else Result := ManualCopyFile(SourceFile, DestFile, Append); -// DebugMsg(['3 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); - - // If size differs, then delete target file - if (not Append) and (not Result) then begin -// DebugMsg(['4 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); - DataSrc := SrcEngine.GetFileInfoSL(SourceFile); - if DataSrc = nil then Exit; - DataDest := DestEngine.GetFileInfoSL(DestFile); - if DataDest = nil then Exit; - if DataSrc^.Size <> DataDest^.Size then DestEngine.Remove(DestFile); + with SenderThread do begin + AEngine := nil; + FCopyProgressFunc := CopyFilesWorker_ProgressFunc; + + // local -> local + if (SrcEngine is TLocalTreeEngine) and (DestEngine is TLocalTreeEngine) + then Result := DestEngine.CopyFileIn(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append) + else + + // from local engine to VFS engine + if (SrcEngine is TLocalTreeEngine) and (DestEngine is TVFSEngine) then + begin + AEngine := DestEngine; + Result := (DestEngine as TVFSEngine).CopyFileInEx(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ErrorFunc, Append, + @vfs_ask_question_callback, @vfs_ask_password_callback, @vfs_progress_callback, SenderThread); + end else + + // from VFS engine to local (most common use) + if (SrcEngine is TVFSEngine) and (DestEngine is TLocalTreeEngine) then + begin + AEngine := SrcEngine; + Result := (SrcEngine as TVFSEngine).CopyFileOutEx(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ErrorFunc, Append, + @vfs_ask_question_callback, @vfs_ask_password_callback, @vfs_progress_callback, SenderThread); + end + + // VFS to VFS (not supported yet) + else + begin + AEngine := SrcEngine; + Result := ManualCopyFile(SourceFile, DestFile, Append); + end; + AEngine := nil; + + // Copy OK? (check size, otherwise delete target file) + if (not Append) and (not Result) then begin + DataSrc := SrcEngine.GetFileInfoSL(SourceFile); + if DataSrc = nil then Exit; + DataDest := DestEngine.GetFileInfoSL(DestFile); + if DataDest = nil then Exit; + if DataSrc^.Size <> DataDest^.Size then DestEngine.Remove(DestFile); + end; end; - end; -// DebugMsg(['(II) CopyFilesWorker.LocalCopyFile: finished']); except on E: Exception do DebugMsg(['*** Exception raised in LocalCopyFile(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]); end; @@ -2090,6 +2266,32 @@ begin Application.ProcessMessages; // DebugMsg(['After refresh']); + + // VFS callbacks + if SenderThread.VFSAskQuestion_Display then begin + SenderThread.VFSAskQuestion_Display := False; + DebugMsg(['ProcessProgressThread - Main thread: displaying question dialog']); + HandleVFSAskQuestionCallback(ProgressForm.FWidget, SenderThread.VFSAskQuestion_Message, SenderThread.VFSAskQuestion_Choices, SenderThread.VFSAskQuestion_Choice); + SenderThread.VFSCallbackEvent.SetEvent; + end; + if SenderThread.VFSAskPassword_Display then begin + SenderThread.VFSAskPassword_Display := False; + DebugMsg(['ProcessProgressThread - Main thread: displaying password prompt']); + SenderThread.VFSAskPassword_Result := HandleVFSAskPasswordCallback(ProgressForm.FWidget, + SenderThread.VFSAskPassword_Message, + SenderThread.VFSAskPassword_default_user, + SenderThread.VFSAskPassword_default_domain, + SenderThread.VFSAskPassword_default_password, + SenderThread.VFSAskPassword_flags, + SenderThread.VFSAskPassword_username, + SenderThread.VFSAskPassword_password, + SenderThread.VFSAskPassword_anonymous, + SenderThread.VFSAskPassword_domain, + SenderThread.VFSAskPassword_password_save); + SenderThread.VFSCallbackEvent.SetEvent; + end; + + try if SenderThread.FDialogShowDirDelete then begin AFDirDelete := nil; @@ -2182,6 +2384,7 @@ end; (********************************************************************************************************************************) procedure TWorkerThread.Execute; begin + PrepareExecute; if Assigned(WorkerProcedure) then WorkerProcedure(Self); end; @@ -2295,6 +2498,7 @@ begin if DirDeleteCaption = '' then FDirDeleteCaption := LANGRemoveDirectory else FDirDeleteCaption := DirDeleteCaption; FDialogShowDirDelete := True; + FCallbackLockEvent.ResetEvent; FCallbackLockEvent.WaitFor(INFINITE); Result := FDialogResultDirDelete; end; @@ -2311,6 +2515,7 @@ begin FOverwriteSourceFile := SourceFile; FOverwriteDestFile := DestFile; FDialogShowOverwrite := True; + FCallbackLockEvent.ResetEvent; FCallbackLockEvent.WaitFor(INFINITE); Result := FDialogResultOverwrite; end; @@ -2321,6 +2526,7 @@ begin FNewDirLabel := LabelCaption; FNewDirEdit := Edit; FDialogShowNewDir := True; + FCallbackLockEvent.ResetEvent; FCallbackLockEvent.WaitFor(INFINITE); Result := FDialogResultNewDir; end; @@ -2333,6 +2539,7 @@ begin FMsgBoxDefault := Default; FMsgBoxEscape := Escape; FDialogShowMsgBox := True; + FCallbackLockEvent.ResetEvent; FCallbackLockEvent.WaitFor(INFINITE); Result := FDialogResultMsgBox; end; @@ -2466,29 +2673,6 @@ begin Result := HandleSystemCommand(s, Format(LANGErrorEject, [StrToUTF8(MountPath)])); end; -(********************************************************************************************************************************) -(********************************************************************************************************************************) -function HandleLogin(Parent: TComponent; Engine: TPanelEngine; UserName, Password: string): boolean; -var b: boolean; -begin - Result := False; - b := Engine.Login(UserName, Password) = cVFS_OK; - if not b then - repeat - try - FLogin := TFLogin.Create(Parent); - b := FLogin.Run = mbOK; - UserName := FLogin.UserEntry.Text; - Password := FLogin.PasswordEntry.Text; - finally - FLogin.Free; - end; - if not b then Exit; - if b then b := Engine.Login(UserName, Password) = cVFS_OK; - until b; - Result := True; -end; - (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TGetDirSizeThread.Execute; @@ -2606,147 +2790,14 @@ begin ListingResult := 0; VFSOpenResult := 0; RunningTime := 0; - APlugin := nil; - xEngine := nil; - Password := ''; - VFSCallbackEvent := TSimpleEvent.Create; - VFSAskQuestion_Display := False; - VFSAskPassword_Display := False; - VFSCallbackCancelled := False; end; destructor TOpenDirThread.Destroy; begin -{ - if (APlugin <> nil) and (xEngine <> nil) then - try - xEngine.Free; - except - on E: Exception do DebugMsg(['*** TOpenDirThread.Destroy -Exception: ', E.Message]); - end; -} - VFSCallbackEvent.Free; inherited Destroy; end; -procedure TOpenDirThread.Execute; -var tt: TDateTime; -begin - FThreadID := pthread_self; - VFSCallbackCancelled := False; - try -// Writeln('execute.'); - tt := Now; - try -// WriteLn('x1'); - if APlugin <> nil then begin - xEngine := TVFSEngine.Create(APlugin); - xEngine.ParentEngine := AEngine; - xEngine.ArchiveMode := True; - AEngine.LastHighlightItem := AHighlightItem; - xEngine.SavePath := AEngine.Path; - AEngine := xEngine; - if Length(Password) > 0 then (xEngine as TVFSEngine).Password := Password; - VFSOpenResult := (xEngine as TVFSEngine).VFSOpenEx(AFullPath); - end else VFSOpenResult := 0; -// WriteLn('x2'); - - if (VFSOpenResult = 0) and (not CancelIt) then begin -// WriteLn('x3'); - ChDirResult := ChangeDir(AEngine, APath, ASelItem, AAutoFallBack); -// WriteLn('x4'); - - if (ChDirResult = 0) and (not CancelIt) then begin -// WriteLn('x5'); - ListingResult := AEngine.GetListing(ADirList, ConfShowDotFiles); -// WriteLn('x6'); - end; -// WriteLn('x7'); - end; - except - on E: Exception do DebugMsg(['*** TOpenDirThread.Execute -Exception: ', E.Message]); - end; - RunningTime := MilliSecondsBetween(tt, Now); -// WriteLn('x8'); - finally - Finished := True; - end; -{ except - on E: Exception do DebugMsg(['*** Exception raised in TOpenDirThread.Execute (', E.ClassName, '): ', E.Message]); - end; } -end; - (********************************************************************************************************************************) -procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; cancel_choice: Integer; user_data: Pointer); cdecl; -var Thread: TOpenDirThread; -begin - Thread := user_data; - - if pthread_self = Application.ThreadID then begin - DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a OpenDirThread']); - HandleVFSAskQuestionCallback(FMain.FWidget, AMessage, Choices, choice); - if (choice <> nil) and (Thread <> nil) and (Thread is TOpenDirThread) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice); - Exit; - end; - - if (Thread <> nil) and (Thread is TOpenDirThread) and (pthread_self = Thread.FThreadID) then begin - DebugMsg(['******* vfs_ask_question_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]); - Thread.VFSAskQuestion_Message := AMessage; - Thread.VFSAskQuestion_Choices := Choices; - Thread.VFSAskQuestion_Choice := choice; - Thread.VFSAskQuestion_Display := True; - Thread.VFSCallbackEvent.WaitFor(INFINITE); - DebugMsg(['******* thread: resuming...']); - if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice); - Exit; - end; - - DebugMsg(['!! (ERROR): vfs_ask_question_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']); - if (Thread <> nil) and (Thread is TOpenDirThread) - then DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); -end; - -function vfs_ask_password_callback(const AMessage: PChar; const default_user: PChar; const default_domain: PChar; flags: TVFSAskPasswordFlags; - username, password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave; - user_data: Pointer): LongBool; cdecl; -var Thread: TOpenDirThread; -begin - Thread := user_data; - - if pthread_self = Application.ThreadID then begin - DebugMsg(['!! (WARNING): vfs_ask_password_callback called from the main thread, expected spawn from a OpenDirThread']); - Result := HandleVFSAskPasswordCallback(FMain.FWidget, AMessage, default_user, default_domain, flags, username, password, anonymous, domain, password_save); - if (Thread <> nil) and (Thread is TOpenDirThread) then Thread.VFSCallbackCancelled := Result = False; - Exit; - end; - - if (Thread <> nil) and (Thread is TOpenDirThread) and (pthread_self = Thread.FThreadID) then begin - DebugMsg(['******* vfs_ask_password_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); - Thread.VFSAskPassword_Message := AMessage; - Thread.VFSAskPassword_default_user := default_user; - Thread.VFSAskPassword_default_domain := default_domain; - Thread.VFSAskPassword_flags := flags; - Thread.VFSAskPassword_username := username; - Thread.VFSAskPassword_password := password; - Thread.VFSAskPassword_anonymous := anonymous; - Thread.VFSAskPassword_domain := domain; - Thread.VFSAskPassword_password_save := password_save; - Thread.VFSAskPassword_Display := True; - Thread.VFSAskPassword_Result := False; - Thread.VFSCallbackEvent.WaitFor(INFINITE); - DebugMsg(['******* thread: resuming...']); - Result := Thread.VFSAskPassword_Result; - Thread.VFSCallbackCancelled := Result = False; - Exit; - end; - - DebugMsg(['!! (ERROR): vfs_ask_password_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']); - if (Thread <> nil) and (Thread is TOpenDirThread) - then DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); - Result := False; -end; - - function TOpenDirThread.ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer; procedure GoUp(var NewPath: string); @@ -2778,13 +2829,13 @@ begin // AutoFallback loop if Engine is TVFSEngine - then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, Self) + then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self) else Error := Engine.ChangeDir(APath); while AutoFallback and (Error <> 0) and (APath <> '/') do begin GoUp(APath); if Engine is TVFSEngine - then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, Self) + then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self) else Error := Engine.ChangeDir(APath); end; // Going on... @@ -2803,6 +2854,38 @@ begin end; end; +procedure TOpenDirThread.Execute; +var tt: TDateTime; + xEngine: TVFSEngine; +begin + PrepareExecute; + try + tt := Now; + try + if APlugin <> nil then begin + xEngine := TVFSEngine.Create(APlugin); + xEngine.ParentEngine := AEngine; + xEngine.ArchiveMode := True; + AEngine.LastHighlightItem := AHighlightItem; + xEngine.SavePath := AEngine.Path; + // AEngine must be set here since VFSOpenEx callbacks will reference it + AEngine := xEngine; + VFSOpenResult := (AEngine as TVFSEngine).VFSOpenEx(AFullPath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self); + end else VFSOpenResult := 0; + + if (VFSOpenResult = 0) and (not CancelIt) then begin + ChDirResult := ChangeDir(AEngine, APath, ASelItem, AAutoFallBack); + if (ChDirResult = 0) and (not CancelIt) then + ListingResult := AEngine.GetListing(ADirList, ConfShowDotFiles); + end; + except + on E: Exception do DebugMsg(['*** Exception raised in TOpenDirThread.Execute (', E.ClassName, '): ', E.Message]); + end; + RunningTime := MilliSecondsBetween(tt, Now); + finally + Finished := True; + end; +end; (********************************************************************************************************************************) (********************************************************************************************************************************) -- cgit v1.2.3