mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Desktop: Fixes #3729: Fix lock issue when device does not have the right time
This commit is contained in:
parent
3a33e5f416
commit
1f70a76c7e
@ -62,6 +62,7 @@ Modules/TinyMCE/langs/
|
|||||||
|
|
||||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||||
CliClient/app/LinkSelector.js
|
CliClient/app/LinkSelector.js
|
||||||
|
CliClient/build/LinkSelector.js
|
||||||
CliClient/tests/synchronizer_LockHandler.js
|
CliClient/tests/synchronizer_LockHandler.js
|
||||||
CliClient/tests/synchronizer_MigrationHandler.js
|
CliClient/tests/synchronizer_MigrationHandler.js
|
||||||
ElectronClient/commands/focusElement.js
|
ElectronClient/commands/focusElement.js
|
||||||
@ -151,6 +152,7 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
|||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||||
ReactNativeClient/lib/JoplinServerApi.js
|
ReactNativeClient/lib/JoplinServerApi.js
|
||||||
|
ReactNativeClient/lib/ntpDate.js
|
||||||
ReactNativeClient/lib/services/CommandService.js
|
ReactNativeClient/lib/services/CommandService.js
|
||||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -53,6 +53,7 @@ Tools/commit_hook.txt
|
|||||||
|
|
||||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||||
CliClient/app/LinkSelector.js
|
CliClient/app/LinkSelector.js
|
||||||
|
CliClient/build/LinkSelector.js
|
||||||
CliClient/tests/synchronizer_LockHandler.js
|
CliClient/tests/synchronizer_LockHandler.js
|
||||||
CliClient/tests/synchronizer_MigrationHandler.js
|
CliClient/tests/synchronizer_MigrationHandler.js
|
||||||
ElectronClient/commands/focusElement.js
|
ElectronClient/commands/focusElement.js
|
||||||
@ -142,6 +143,7 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
|||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||||
ReactNativeClient/lib/JoplinServerApi.js
|
ReactNativeClient/lib/JoplinServerApi.js
|
||||||
|
ReactNativeClient/lib/ntpDate.js
|
||||||
ReactNativeClient/lib/services/CommandService.js
|
ReactNativeClient/lib/services/CommandService.js
|
||||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||||
|
46
ReactNativeClient/lib/ntpDate.ts
Normal file
46
ReactNativeClient/lib/ntpDate.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const ntpClient = require('lib/vendor/ntp-client');
|
||||||
|
const Mutex = require('async-mutex').Mutex;
|
||||||
|
|
||||||
|
let lastSyncTime = 0;
|
||||||
|
let timeOffset = 0;
|
||||||
|
const fetchingTimeMutex = new Mutex();
|
||||||
|
|
||||||
|
const server = {
|
||||||
|
domain: 'pool.ntp.org',
|
||||||
|
port: 123,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function networkTime():Promise<Date> {
|
||||||
|
return new Promise(function(resolve:Function, reject:Function) {
|
||||||
|
ntpClient.getNetworkTime(server.domain, server.port, function(error:any, date:Date) {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(date);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldSyncTime() {
|
||||||
|
return !lastSyncTime || Date.now() - lastSyncTime >= 5 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function():Promise<Date> {
|
||||||
|
if (shouldSyncTime()) {
|
||||||
|
const release = await fetchingTimeMutex.acquire();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (shouldSyncTime()) {
|
||||||
|
const date = await networkTime();
|
||||||
|
lastSyncTime = Date.now();
|
||||||
|
timeOffset = date.getTime() - Date.now();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(Date.now() + timeOffset);
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
import { Dirnames } from './utils/types';
|
import { Dirnames } from './utils/types';
|
||||||
|
import ntpDate from 'lib/ntpDate';
|
||||||
|
|
||||||
const JoplinError = require('lib/JoplinError');
|
const JoplinError = require('lib/JoplinError');
|
||||||
const { time } = require('lib/time-utils');
|
const { time } = require('lib/time-utils');
|
||||||
const { fileExtension, filename } = require('lib/path-utils.js');
|
const { fileExtension, filename } = require('lib/path-utils.js');
|
||||||
@ -98,8 +100,8 @@ export default class LockHandler {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private lockIsActive(lock:Lock):boolean {
|
private lockIsActive(lock:Lock, currentDate:Date):boolean {
|
||||||
return Date.now() - lock.updatedTime < this.lockTtl;
|
return currentDate.getTime() - lock.updatedTime < this.lockTtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasActiveLock(lockType:LockType, clientType:string = null, clientId:string = null) {
|
async hasActiveLock(lockType:LockType, clientType:string = null, clientId:string = null) {
|
||||||
@ -112,11 +114,12 @@ export default class LockHandler {
|
|||||||
// of that type instead.
|
// of that type instead.
|
||||||
async activeLock(lockType:LockType, clientType:string = null, clientId:string = null) {
|
async activeLock(lockType:LockType, clientType:string = null, clientId:string = null) {
|
||||||
const locks = await this.locks(lockType);
|
const locks = await this.locks(lockType);
|
||||||
|
const currentDate = await ntpDate();
|
||||||
|
|
||||||
if (lockType === LockType.Exclusive) {
|
if (lockType === LockType.Exclusive) {
|
||||||
const activeLocks = locks
|
const activeLocks = locks
|
||||||
.slice()
|
.slice()
|
||||||
.filter((lock:Lock) => this.lockIsActive(lock))
|
.filter((lock:Lock) => this.lockIsActive(lock, currentDate))
|
||||||
.sort((a:Lock, b:Lock) => {
|
.sort((a:Lock, b:Lock) => {
|
||||||
if (a.updatedTime === b.updatedTime) {
|
if (a.updatedTime === b.updatedTime) {
|
||||||
return a.clientId < b.clientId ? -1 : +1;
|
return a.clientId < b.clientId ? -1 : +1;
|
||||||
@ -134,7 +137,7 @@ export default class LockHandler {
|
|||||||
for (const lock of locks) {
|
for (const lock of locks) {
|
||||||
if (clientType && lock.clientType !== clientType) continue;
|
if (clientType && lock.clientType !== clientType) continue;
|
||||||
if (clientId && lock.clientId !== clientId) continue;
|
if (clientId && lock.clientId !== clientId) continue;
|
||||||
if (this.lockIsActive(lock)) return lock;
|
if (this.lockIsActive(lock, currentDate)) return lock;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
146
ReactNativeClient/lib/vendor/ntp-client.js
vendored
Normal file
146
ReactNativeClient/lib/vendor/ntp-client.js
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* ntp-client
|
||||||
|
* https://github.com/moonpyk/node-ntp-client
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Clément Bourgeois
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
// 2020-08-09: We vendor the package because although it works
|
||||||
|
// it has several bugs and is currently unmaintained
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(function (exports) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var dgram = require('dgram');
|
||||||
|
|
||||||
|
exports.defaultNtpPort = 123;
|
||||||
|
exports.defaultNtpServer = "pool.ntp.org";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of acceptable time to await for a response from the remote server.
|
||||||
|
* Configured default to 10 seconds.
|
||||||
|
*/
|
||||||
|
exports.ntpReplyTimeout = 10000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current NTP Time from the given server and port.
|
||||||
|
* @param {string} server IP/Hostname of the remote NTP Server
|
||||||
|
* @param {number} port Remote NTP Server port number
|
||||||
|
* @param {function(Object, Date)} callback(err, date) Async callback for
|
||||||
|
* the result date or eventually error.
|
||||||
|
*/
|
||||||
|
exports.getNetworkTime = function (server, port, callback) {
|
||||||
|
if (callback === null || typeof callback !== "function") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = server || exports.defaultNtpServer;
|
||||||
|
port = port || exports.defaultNtpPort;
|
||||||
|
|
||||||
|
var client = dgram.createSocket("udp4"),
|
||||||
|
ntpData = new Buffer(48);
|
||||||
|
|
||||||
|
// RFC 2030 -> LI = 0 (no warning, 2 bits), VN = 3 (IPv4 only, 3 bits), Mode = 3 (Client Mode, 3 bits) -> 1 byte
|
||||||
|
// -> rtol(LI, 6) ^ rotl(VN, 3) ^ rotl(Mode, 0)
|
||||||
|
// -> = 0x00 ^ 0x18 ^ 0x03
|
||||||
|
ntpData[0] = 0x1B;
|
||||||
|
|
||||||
|
for (var i = 1; i < 48; i++) {
|
||||||
|
ntpData[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some errors can happen before/after send() or cause send() to was impossible.
|
||||||
|
// Some errors will also be given to the send() callback.
|
||||||
|
// We keep a flag, therefore, to prevent multiple callbacks.
|
||||||
|
// NOTE : the error callback is not generalised, as the client has to lose the connection also, apparently.
|
||||||
|
var errorFired = false;
|
||||||
|
|
||||||
|
function closeClient(client) {
|
||||||
|
try {
|
||||||
|
client.close();
|
||||||
|
} catch (error) {
|
||||||
|
// Doesn't mater if it could not be closed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout = setTimeout(function () {
|
||||||
|
closeClient(client);
|
||||||
|
|
||||||
|
if (errorFired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(new Error("Timeout waiting for NTP response."), null);
|
||||||
|
errorFired = true;
|
||||||
|
}, exports.ntpReplyTimeout);
|
||||||
|
|
||||||
|
client.on('error', function (err) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
if (errorFired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(err, null);
|
||||||
|
errorFired = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.send(ntpData, 0, ntpData.length, port, server, function (err) {
|
||||||
|
if (err) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
if (errorFired) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(err, null);
|
||||||
|
errorFired = true;
|
||||||
|
closeClient(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.once('message', function (msg) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
closeClient(client);
|
||||||
|
|
||||||
|
// Offset to get to the "Transmit Timestamp" field (time at which the reply
|
||||||
|
// departed the server for the client, in 64-bit timestamp format."
|
||||||
|
var offsetTransmitTime = 40,
|
||||||
|
intpart = 0,
|
||||||
|
fractpart = 0;
|
||||||
|
|
||||||
|
// Get the seconds part
|
||||||
|
for (var i = 0; i <= 3; i++) {
|
||||||
|
intpart = 256 * intpart + msg[offsetTransmitTime + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the seconds fraction
|
||||||
|
for (i = 4; i <= 7; i++) {
|
||||||
|
fractpart = 256 * fractpart + msg[offsetTransmitTime + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
var milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000);
|
||||||
|
|
||||||
|
// **UTC** time
|
||||||
|
var date = new Date("Jan 01 1900 GMT");
|
||||||
|
date.setUTCMilliseconds(date.getUTCMilliseconds() + milliseconds);
|
||||||
|
|
||||||
|
callback(null, date);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.demo = function (argv) {
|
||||||
|
exports.getNetworkTime(
|
||||||
|
exports.defaultNtpServer,
|
||||||
|
exports.defaultNtpPort,
|
||||||
|
function (err, date) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(date);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}(exports));
|
Loading…
Reference in New Issue
Block a user