diff --git a/out/heidisql.iss b/out/heidisql.iss index 1afa5a07..1191765e 100644 --- a/out/heidisql.iss +++ b/out/heidisql.iss @@ -62,6 +62,7 @@ Source: "readme.txt"; DestDir: "{app}"; Flags: ignoreversion isreadme Source: "license.txt"; DestDir: "{app}"; Flags: ignoreversion Source: "gpl.txt"; DestDir: "{app}"; Flags: ignoreversion Source: "libmysql.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "plugins\*.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion Source: "Snippets\*.sql"; DestDir: "{commonappdata}\{#ProgName}\Snippets"; [Icons] diff --git a/out/libmysql.dll b/out/libmysql.dll index 922f1c14..a86b798e 100644 Binary files a/out/libmysql.dll and b/out/libmysql.dll differ diff --git a/out/plugins/dialog.dll b/out/plugins/dialog.dll new file mode 100644 index 00000000..7ec957ba Binary files /dev/null and b/out/plugins/dialog.dll differ diff --git a/source/dbconnection.pas b/source/dbconnection.pas index aca5b3be..4e1d992c 100644 --- a/source/dbconnection.pas +++ b/source/dbconnection.pas @@ -166,6 +166,16 @@ type end; PMYSQL_RES = ^MYSQL_RES; + TMySQLOption = (MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, MYSQL_OPT_NAMED_PIPE, + MYSQL_INIT_COMMAND, MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP, + MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_OPT_LOCAL_INFILE, + MYSQL_OPT_PROTOCOL, MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_OPT_READ_TIMEOUT, + MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT, + MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, + MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, + MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT, + MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH); + { TDBObjectList and friends } @@ -448,6 +458,7 @@ type mysql_init: function(Handle: PMYSQL): PMYSQL; stdcall; mysql_num_fields: function(Result: PMYSQL_RES): Integer; stdcall; mysql_num_rows: function(Result: PMYSQL_RES): Int64; stdcall; + mysql_options: function(Handle: PMYSQL; Option: TMySQLOption; arg: PAnsiChar): Integer; stdcall; mysql_ping: function(Handle: PMYSQL): Integer; stdcall; mysql_real_connect: function(Handle: PMYSQL; const Host, User, Passwd, Db: PAnsiChar; Port: Cardinal; const UnixSocket: PAnsiChar; ClientFlag: Cardinal): PMYSQL; stdcall; mysql_real_query: function(Handle: PMYSQL; const Query: PAnsiChar; Length: Cardinal): Integer; stdcall; @@ -718,6 +729,17 @@ type function TableName: String; override; end; +function mysql_authentication_dialog_ask( + Handle: PMYSQL; + _type: Integer; + prompt: PAnsiChar; + buf: PAnsiChar; + buf_len: Integer + ): PAnsiChar; cdecl; + +exports + mysql_authentication_dialog_ask; + const MsgSQLError: String = 'SQL Error (%d): %s'; MsgUnhandledNetType: String = 'Unhandled connection type (%d)'; @@ -990,6 +1012,7 @@ var StartupInfo: TStartupInfo; ExitCode: LongWord; sslca, sslkey, sslcert: PAnsiChar; + PluginDir: AnsiString; DoSSL, SSLsettingsComplete: Boolean; Vars: TDBQuery; begin @@ -1079,6 +1102,10 @@ begin if FIsSSL then ClientFlags := ClientFlags or CLIENT_SSL; + // Point libmysql to the folder with client plugins + PluginDir := AnsiString(ExtractFilePath(ParamStr(0))+'plugins\'); + mysql_options(FHandle, MYSQL_PLUGIN_DIR, PAnsiChar(PluginDir)); + Connected := mysql_real_connect( FHandle, PAnsiChar(Utf8Encode(FinalHost)), @@ -1287,6 +1314,7 @@ begin AssignProc(@mysql_num_fields, 'mysql_num_fields'); AssignProc(@mysql_num_rows, 'mysql_num_rows'); AssignProc(@mysql_ping, 'mysql_ping'); + AssignProc(@mysql_options, 'mysql_options'); AssignProc(@mysql_real_connect, 'mysql_real_connect'); AssignProc(@mysql_real_query, 'mysql_real_query'); AssignProc(@mysql_ssl_set, 'mysql_ssl_set'); @@ -4734,4 +4762,46 @@ end; +function mysql_authentication_dialog_ask; +var + Username, Password: String; +begin + { + From client_plugin.h: + The C function with the name "mysql_authentication_dialog_ask", if exists, + will be used by the "dialog" client authentication plugin when user + input is needed. This function should be of mysql_authentication_dialog_ask_t + type. If the function does not exists, a built-in implementation will be + used. + @param mysql mysql + @param type type of the input + 1 - normal string input + 2 - password string + @param prompt prompt + @param buf a buffer to store the use input + @param buf_len the length of the buffer + @retval a pointer to the user input string. + It may be equal to 'buf' or to 'mysql->password'. + In all other cases it is assumed to be an allocated + string, and the "dialog" plugin will free() it. + Test suite: + INSTALL PLUGIN three_attempts SONAME 'dialog.dll'; + CREATE USER test_dialog IDENTIFIED VIA three_attempts USING 'SECRET'; + } + Username := ''; + Password := ''; + case _type of + 1: Username := String(buf); + 2: Password := String(buf); + else raise EDatabaseError.Create('Unsupported type ('+IntToStr(_type)+') in mysql_authentication_dialog_ask.'); + end; + LoginPrompt(String(prompt), Username, Password, _type=1, _type=2); + Result := buf; + case _type of + 1: Result := PAnsiChar(AnsiString(Username)); + 2: Result := PAnsiChar(AnsiString(Password)); + end; +end; + + end. diff --git a/source/loginform.pas b/source/loginform.pas index dff09349..afd5918a 100644 --- a/source/loginform.pas +++ b/source/loginform.pas @@ -22,7 +22,7 @@ type { Public declarations } end; - procedure LoginPrompt(ACaption: String; var AUsername, APassword: String); + procedure LoginPrompt(ACaption: String; var AUsername, APassword: String; UsernameEnabled: Boolean=True; PasswordEnabled: Boolean=True); implementation @@ -31,7 +31,7 @@ uses helpers; {$R *.dfm} {$I const.inc} -procedure LoginPrompt(ACaption: String; var AUsername, APassword: String); +procedure LoginPrompt(ACaption: String; var AUsername, APassword: String; UsernameEnabled: Boolean=True; PasswordEnabled: Boolean=True); var frm: TfrmLogin; begin @@ -40,6 +40,10 @@ begin frm.lblPrompt.Caption := ACaption; frm.editUsername.Text := AUsername; frm.editPassword.Text := APassword; + frm.editUsername.Enabled := UsernameEnabled; + frm.lblUsername.Enabled := UsernameEnabled; + frm.editPassword.Enabled := PasswordEnabled; + frm.lblPassword.Enabled := PasswordEnabled; frm.ShowModal; AUsername := frm.editUsername.Text; APassword := frm.editPassword.Text; @@ -55,9 +59,9 @@ end; procedure TfrmLogin.FormShow(Sender: TObject); begin - if (editUsername.GetTextLen > 0) and (editPassword.GetTextLen = 0) then + if editPassword.CanFocus and (editUsername.GetTextLen > 0) and (editPassword.GetTextLen = 0) then editPassword.SetFocus - else + else if editUsername.CanFocus then editUsername.SetFocus; end;