diff --git a/packages/delphiXE5/heidisql.dpr b/packages/delphiXE5/heidisql.dpr
index 9df566f1..501997dd 100644
--- a/packages/delphiXE5/heidisql.dpr
+++ b/packages/delphiXE5/heidisql.dpr
@@ -41,7 +41,8 @@ uses
syncdb in '..\..\source\syncdb.pas' {frmSyncDB},
gnugettext in '..\..\source\gnugettext.pas',
JumpList in '..\..\source\JumpList.pas',
- extra_controls in '..\..\source\extra_controls.pas';
+ extra_controls in '..\..\source\extra_controls.pas',
+ change_password in '..\..\source\change_password.pas' {frmPasswordChange};
{$R ..\..\res\icon.RES}
{$R ..\..\res\icon-question.RES}
diff --git a/packages/delphiXE5/heidisql.dproj b/packages/delphiXE5/heidisql.dproj
index 63711d09..f19373d0 100644
--- a/packages/delphiXE5/heidisql.dproj
+++ b/packages/delphiXE5/heidisql.dproj
@@ -194,6 +194,10 @@
+
+
+ dfm
+
Cfg_2
Base
diff --git a/source/change_password.dfm b/source/change_password.dfm
new file mode 100644
index 00000000..025adfba
--- /dev/null
+++ b/source/change_password.dfm
@@ -0,0 +1,162 @@
+object frmPasswordChange: TfrmPasswordChange
+ Left = 0
+ Top = 0
+ BorderStyle = bsDialog
+ Caption = 'Change expired password'
+ ClientHeight = 187
+ ClientWidth = 456
+ Color = clBtnFace
+ Constraints.MaxHeight = 300
+ Constraints.MaxWidth = 600
+ Constraints.MinHeight = 185
+ Constraints.MinWidth = 400
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ Position = poMainFormCenter
+ OnShow = FormShow
+ DesignSize = (
+ 456
+ 187)
+ PixelsPerInch = 96
+ TextHeight = 13
+ object lblHeading: TLabel
+ Left = 8
+ Top = 16
+ Width = 440
+ Height = 51
+ Anchors = [akLeft, akTop, akRight, akBottom]
+ AutoSize = False
+ Caption = 'lblHeading'
+ WordWrap = True
+ end
+ object lblPassword: TLabel
+ Left = 8
+ Top = 76
+ Width = 74
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = 'New password:'
+ end
+ object lblRepeatPassword: TLabel
+ Left = 8
+ Top = 103
+ Width = 111
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = 'Repeat new password:'
+ end
+ object lblStatus: TLabel
+ Left = 8
+ Top = 132
+ Width = 41
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = 'lblStatus'
+ end
+ object editPassword: TButtonedEdit
+ Left = 146
+ Top = 73
+ Width = 302
+ Height = 21
+ Anchors = [akLeft, akRight, akBottom]
+ Images = MainForm.ImageListMain
+ RightButton.DropDownMenu = popupPassword
+ RightButton.ImageIndex = 75
+ RightButton.Visible = True
+ TabOrder = 0
+ TextHint = 'Type new password or select a suggestion'
+ OnChange = editPasswordChange
+ OnClick = editPasswordChange
+ OnKeyDown = editPasswordKeyDown
+ end
+ object editRepeatPassword: TButtonedEdit
+ Left = 146
+ Top = 100
+ Width = 302
+ Height = 21
+ Anchors = [akLeft, akRight, akBottom]
+ Images = MainForm.ImageListMain
+ PasswordChar = '*'
+ TabOrder = 1
+ OnChange = editPasswordChange
+ OnClick = editPasswordChange
+ OnKeyDown = editPasswordKeyDown
+ end
+ object btnCancel: TButton
+ Left = 292
+ Top = 154
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'Cancel'
+ ModalResult = 2
+ TabOrder = 2
+ end
+ object btnOK: TButton
+ Left = 213
+ Top = 154
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'OK'
+ Default = True
+ ModalResult = 1
+ TabOrder = 3
+ end
+ object btnCopyToClipboard: TButton
+ Left = 373
+ Top = 154
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'Copy'
+ ImageIndex = 3
+ Images = MainForm.ImageListMain
+ TabOrder = 4
+ OnClick = btnCopyToClipboardClick
+ end
+ object popupPassword: TPopupMenu
+ Left = 344
+ Top = 8
+ object N6characters1: TMenuItem
+ Caption = '6 characters'
+ OnClick = menuPasswordClick
+ object menuDummy1: TMenuItem
+ Caption = 'dummy'
+ OnClick = menuPasswordInsert
+ end
+ end
+ object N8characters1: TMenuItem
+ Caption = '8 characters'
+ OnClick = menuPasswordClick
+ object menuDummy2: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ object N10characters1: TMenuItem
+ Caption = '10 characters'
+ OnClick = menuPasswordClick
+ object menuDummy3: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ object N12characters1: TMenuItem
+ Caption = '12 characters'
+ OnClick = menuPasswordClick
+ object menuDummy4: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ object N30characters1: TMenuItem
+ Caption = '30 characters'
+ OnClick = menuPasswordClick
+ object menuDummy5: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ end
+end
diff --git a/source/change_password.pas b/source/change_password.pas
new file mode 100644
index 00000000..7c7bd4dd
--- /dev/null
+++ b/source/change_password.pas
@@ -0,0 +1,137 @@
+unit change_password;
+
+interface
+
+uses
+ Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
+ Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, extra_controls, gnugettext,
+ Vcl.Menus, Clipbrd;
+
+type
+ TfrmPasswordChange = class(TFormWithSizeGrip)
+ lblHeading: TLabel;
+ lblPassword: TLabel;
+ lblRepeatPassword: TLabel;
+ editPassword: TButtonedEdit;
+ editRepeatPassword: TButtonedEdit;
+ btnCancel: TButton;
+ btnOK: TButton;
+ lblStatus: TLabel;
+ popupPassword: TPopupMenu;
+ N6characters1: TMenuItem;
+ N8characters1: TMenuItem;
+ N10characters1: TMenuItem;
+ N12characters1: TMenuItem;
+ N30characters1: TMenuItem;
+ menuDummy1: TMenuItem;
+ menuDummy2: TMenuItem;
+ menuDummy3: TMenuItem;
+ menuDummy4: TMenuItem;
+ menuDummy5: TMenuItem;
+ btnCopyToClipboard: TButton;
+ procedure editPasswordChange(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure editPasswordKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+ procedure menuPasswordClick(Sender: TObject);
+ procedure menuPasswordInsert(Sender: TObject);
+ procedure btnCopyToClipboardClick(Sender: TObject);
+ private
+ { Private-Deklarationen }
+ public
+ { Public-Deklarationen }
+ end;
+
+var
+ frmPasswordChange: TfrmPasswordChange;
+
+implementation
+
+uses main, helpers;
+
+{$R *.dfm}
+
+
+procedure TfrmPasswordChange.FormShow(Sender: TObject);
+begin
+ // Manually trigger change event on password box
+ editPassword.OnChange(Sender);
+end;
+
+
+procedure TfrmPasswordChange.menuPasswordClick(Sender: TObject);
+var
+ Parent, Item: TMenuItem;
+ PasswordLen, i: Integer;
+begin
+ // Create menu items with random passwords
+ Parent := Sender as TMenuItem;
+ PasswordLen := MakeInt(Parent.Caption);
+ for i:=0 to 19 do begin
+ if Parent.Count > i then
+ Item := Parent[i]
+ else begin
+ Item := TMenuItem.Create(Parent);
+ Parent.Add(Item);
+ end;
+ Item.OnClick := menuPasswordInsert;
+ Item.Caption := GeneratePassword(PasswordLen);
+ end;
+end;
+
+
+procedure TfrmPasswordChange.menuPasswordInsert(Sender: TObject);
+var
+ Item: TMenuItem;
+begin
+ // Insert password from menu item
+ Item := Sender as TMenuItem;
+ editPassword.Text := Item.Caption;
+ editRepeatPassword.Text := editPassword.Text;
+end;
+
+
+procedure TfrmPasswordChange.btnCopyToClipboardClick(Sender: TObject);
+var
+ OldImageIndex: Integer;
+begin
+ // Copy new password to clipboard
+ Clipboard.AsText := editPassword.Text;
+ OldImageIndex := btnCopyToClipboard.ImageIndex;
+ btnCopyToClipboard.ImageIndex := 55;
+ btnCopyToClipboard.Repaint;
+ Sleep(500);
+ btnCopyToClipboard.ImageIndex := OldImageIndex;
+end;
+
+
+procedure TfrmPasswordChange.editPasswordChange(Sender: TObject);
+begin
+ // User has entered something on one or both password fields
+ btnOK.Enabled := False;
+ btnCopyToClipboard.Enabled := False;
+
+ if editPassword.Text = '' then begin
+ editPassword.PasswordChar := #0;
+ lblStatus.Caption := _('Please change your password')
+ end else begin
+ editPassword.PasswordChar := '*';
+ if editPassword.Text <> editRepeatPassword.Text then
+ lblStatus.Caption := _('Error: Passwords do not match!')
+ else begin
+ lblStatus.Caption := '';
+ btnOK.Enabled := True;
+ btnCopyToClipboard.Enabled := True;
+ end;
+ end;
+end;
+
+
+procedure TfrmPasswordChange.editPasswordKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+begin
+ editPassword.OnChange(Sender);
+end;
+
+
+end.
diff --git a/source/dbconnection.pas b/source/dbconnection.pas
index 2805767f..e427a782 100644
--- a/source/dbconnection.pas
+++ b/source/dbconnection.pas
@@ -757,7 +757,7 @@ var
implementation
-uses helpers, loginform;
+uses helpers, loginform, change_password;
{ TProcessPipe }
@@ -1614,6 +1614,7 @@ var
sslca, sslkey, sslcert, sslcipher: PAnsiChar;
PluginDir: AnsiString;
Vars, Status: TDBQuery;
+ PasswordChangeDialog: TfrmPasswordChange;
begin
if Value and (FHandle = nil) then begin
DoBeforeConnect;
@@ -1667,7 +1668,7 @@ begin
end;
// Gather client options
- ClientFlags := CLIENT_LOCAL_FILES or CLIENT_INTERACTIVE or CLIENT_PROTOCOL_41 or CLIENT_MULTI_STATEMENTS;
+ ClientFlags := CLIENT_LOCAL_FILES or CLIENT_INTERACTIVE or CLIENT_PROTOCOL_41 or CLIENT_MULTI_STATEMENTS or CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
if Parameters.Compressed then
ClientFlags := ClientFlags or CLIENT_COMPRESS;
if Parameters.WantSSL then
@@ -1708,6 +1709,28 @@ begin
'* either to fix the "%s" variable,'+CRLF+
'* or to grant you missing privileges.'),
['init_connect', 'init_connect']);
+ // Try to fire the very first query against the server, which probably run into the following error:
+ // "Error 1820: You must SET PASSWORD before executing this statement"
+ try
+ ThreadId;
+ except
+ on E:EDatabaseError do begin
+ if GetLastErrorCode = 1820 then begin
+ PasswordChangeDialog := TfrmPasswordChange.Create(Self);
+ PasswordChangeDialog.lblHeading.Caption := GetLastError;
+ PasswordChangeDialog.ShowModal;
+ if PasswordChangeDialog.ModalResult = mrOk then begin
+ if ExecRegExpr('\sALTER USER\s', GetLastError) then
+ Query('ALTER USER USER() IDENTIFIED BY '+EscapeString(PasswordChangeDialog.editPassword.Text))
+ else
+ Query('SET PASSWORD=PASSWORD('+EscapeString(PasswordChangeDialog.editPassword.Text)+')');
+ end else // Dialog cancelled
+ Raise;
+ PasswordChangeDialog.Free;
+ end else
+ Raise;
+ end;
+ end;
Log(lcInfo, f_('Connected. Thread-ID: %d', [ThreadId]));
try
CharacterSet := 'utf8mb4';