@@ -19,7 +19,9 @@ import AccountsService 0.1
1919import GSettings 1.0
2020import Unity.Application 0.1
2121import Ubuntu.Components 0.1
22+import Ubuntu.Components.Popups 1.0
2223import Ubuntu.Gestures 0.1
24+import Ubuntu.SystemImage 0.1
2325import Unity.Launcher 0.1
2426import Utils 0.1
2527import LightDM 0.1 as LightDM
@@ -53,6 +55,10 @@ Item {
5355 property bool sideStageEnabled: shell.width >= units.gu(100)
5456 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
5557
58+ property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
59+ property int failedLoginsDelayAttempts: 7 // number of failed logins
60+ property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
61+
5662 function activateApplication(appId) {
5763 if (ApplicationManager.findApplication(appId)) {
5864 ApplicationManager.requestFocusApplication(appId);
@@ -255,7 +261,15 @@ Item {
255261
256262 onShowPrompt: {
257263 if (greeter.narrowMode) {
258− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
264+ var promptText = text.toLowerCase()
265+ if (isDefaultPrompt) {
266+ promptText = lockscreen.alphaNumeric ?
267+ i18n.tr("passphrase") : i18n.tr("passcode")
268+ }
269+ lockscreen.placeholderText = i18n.tr("Enter your %1").arg(promptText)
270+ lockscreen.wrongPlaceholderText = i18n.tr("Incorrect %1").arg(promptText) +
271+ "\n" +
272+ i18n.tr("Please re-enter")
259273 lockscreen.show();
260274 }
261275 }
@@ -270,13 +284,40 @@ Item {
270284 }
271285
272286 onAuthenticationComplete: {
287+ if (LightDM.Greeter.authenticated) {
288+ AccountsService.failedLogins = 0
289+ }
290+ // Else only penalize user for a failed login if they actually were
291+ // prompted for a password. We do this below after the promptless
292+ // early exit.
293+
273294 if (LightDM.Greeter.promptless) {
274295 return;
275296 }
297+
276298 if (LightDM.Greeter.authenticated) {
277299 lockscreen.hide();
278300 greeter.login();
279301 } else {
302+ AccountsService.failedLogins++
303+ if (maxFailedLogins >= 2) { // require at least a warning
304+ if (AccountsService.failedLogins === maxFailedLogins - 1) {
305+ var title = lockscreen.alphaNumeric ?
306+ i18n.tr("Sorry, incorrect passphrase.") :
307+ i18n.tr("Sorry, incorrect passcode.")
308+ var text = i18n.tr("This will be your last attempt.") + " " +
309+ (lockscreen.alphaNumeric ?
310+ i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
311+ i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
312+ lockscreen.showInfoPopup(title, text)
313+ } else if (AccountsService.failedLogins >= maxFailedLogins) {
314+ SystemImage.factoryReset() // Ouch!
315+ }
316+ }
317+ if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
318+ lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
319+ }
320+
280321 lockscreen.clear(true);
281322 if (greeter.narrowMode) {
282323 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -311,6 +352,14 @@ Item {
311352 }
312353
313354 property bool fullyShown: showProgress === 1.0
355+ onFullyShownChanged: {
356+ // Wait until the greeter is completely covering lockscreen before resetting it.
357+ if (fullyShown && !LightDM.Greeter.authenticated) {
358+ lockscreen.reset();
359+ lockscreen.show();
360+ }
361+ }
362+
314363 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
315364 onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
316365
@@ -350,10 +399,6 @@ Item {
350399 if (greeter.narrowMode) {
351400 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
352401 }
353− if (!LightDM.Greeter.authenticated) {
354− lockscreen.reset();
355− lockscreen.show();
356− }
357402 greeter.fakeActiveForApp = "";
358403 greeter.forceActiveFocus();
359404 }