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 @@ + +
frmPasswordChange
+ 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';