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
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/build/LinkSelector.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.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/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.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
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/build/LinkSelector.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.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/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.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 ntpDate from 'lib/ntpDate';
|
||||
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { time } = require('lib/time-utils');
|
||||
const { fileExtension, filename } = require('lib/path-utils.js');
|
||||
@ -98,8 +100,8 @@ export default class LockHandler {
|
||||
return output;
|
||||
}
|
||||
|
||||
private lockIsActive(lock:Lock):boolean {
|
||||
return Date.now() - lock.updatedTime < this.lockTtl;
|
||||
private lockIsActive(lock:Lock, currentDate:Date):boolean {
|
||||
return currentDate.getTime() - lock.updatedTime < this.lockTtl;
|
||||
}
|
||||
|
||||
async hasActiveLock(lockType:LockType, clientType:string = null, clientId:string = null) {
|
||||
@ -112,11 +114,12 @@ export default class LockHandler {
|
||||
// of that type instead.
|
||||
async activeLock(lockType:LockType, clientType:string = null, clientId:string = null) {
|
||||
const locks = await this.locks(lockType);
|
||||
const currentDate = await ntpDate();
|
||||
|
||||
if (lockType === LockType.Exclusive) {
|
||||
const activeLocks = locks
|
||||
.slice()
|
||||
.filter((lock:Lock) => this.lockIsActive(lock))
|
||||
.filter((lock:Lock) => this.lockIsActive(lock, currentDate))
|
||||
.sort((a:Lock, b:Lock) => {
|
||||
if (a.updatedTime === b.updatedTime) {
|
||||
return a.clientId < b.clientId ? -1 : +1;
|
||||
@ -134,7 +137,7 @@ export default class LockHandler {
|
||||
for (const lock of locks) {
|
||||
if (clientType && lock.clientType !== clientType) continue;
|
||||
if (clientId && lock.clientId !== clientId) continue;
|
||||
if (this.lockIsActive(lock)) return lock;
|
||||
if (this.lockIsActive(lock, currentDate)) return lock;
|
||||
}
|
||||
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