tabby-rdp 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +516 -0
- package/README.md +216 -58
- package/TROUBLESHOOTING.md +77 -0
- package/dist/accountForm.js +0 -1
- package/dist/accountRevisions.js +46 -0
- package/dist/accounts.js +2 -2
- package/dist/audio.js +0 -1
- package/dist/authorities.js +331 -0
- package/dist/byteQueue.js +102 -0
- package/dist/clipboard.js +106 -0
- package/dist/connectionStatus.js +0 -1
- package/dist/desk.js +16 -47
- package/dist/deskScript.js +196 -6
- package/dist/desktop.service.js +1499 -269
- package/dist/desktopForm.js +53 -18
- package/dist/desktops.js +232 -24
- package/dist/diskStorage.js +212 -0
- package/dist/drives.js +271 -30
- package/dist/fileTransfer.js +960 -132
- package/dist/gateway.js +216 -57
- package/dist/header.js +76 -5
- package/dist/help.js +16 -47
- package/dist/hostCompat.js +86 -0
- package/dist/hyperv.js +65 -15
- package/dist/index.js +7 -4
- package/dist/keyboard.js +0 -1
- package/dist/microphone.js +57 -13
- package/dist/ntlm.js +0 -1
- package/dist/osd.js +56 -15
- package/dist/packageChangeDialog.js +186 -0
- package/dist/probe.js +15 -3
- package/dist/rdcleanpath.js +319 -44
- package/dist/rdpFile.js +22 -7
- package/dist/rdpProfile.js +39 -21
- package/dist/releaseSettings.js +72 -0
- package/dist/releases.js +408 -0
- package/dist/remoteSetup.js +103 -23
- package/dist/sessionLog.js +53 -0
- package/dist/settingsPage.js +148 -122
- package/dist/signin.js +478 -48
- package/dist/ssh.js +99 -18
- package/dist/sshChannelStream.js +40 -6
- package/dist/targets.js +194 -40
- package/dist/ui.js +88 -20
- package/dist/unshowable.js +41 -0
- package/dist/updates.js +79 -56
- package/dist/vms.js +17 -3
- package/dist/wake.js +15 -8
- package/docs/desk.md +157 -0
- package/package.json +14 -4
- package/remote/trd-pty.py +74 -13
- package/vendor/SHA256SUMS +26 -0
- package/vendor/ironrdp_web_bg.wasm +0 -0
- package/dist/accountForm.js.map +0 -1
- package/dist/accounts.js.map +0 -1
- package/dist/audio.js.map +0 -1
- package/dist/connectionStatus.js.map +0 -1
- package/dist/desk.js.map +0 -1
- package/dist/deskScript.js.map +0 -1
- package/dist/desktop.service.js.map +0 -1
- package/dist/desktopForm.js.map +0 -1
- package/dist/desktops.js.map +0 -1
- package/dist/drives.js.map +0 -1
- package/dist/fileTransfer.js.map +0 -1
- package/dist/gateway.js.map +0 -1
- package/dist/header.js.map +0 -1
- package/dist/help.js.map +0 -1
- package/dist/hyperv.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/keyboard.js.map +0 -1
- package/dist/microphone.js.map +0 -1
- package/dist/ntlm.js.map +0 -1
- package/dist/osd.js.map +0 -1
- package/dist/probe.js.map +0 -1
- package/dist/rdcleanpath.js.map +0 -1
- package/dist/rdpFile.js.map +0 -1
- package/dist/rdpProfile.js.map +0 -1
- package/dist/remoteSetup.js.map +0 -1
- package/dist/settingsPage.js.map +0 -1
- package/dist/signin.js.map +0 -1
- package/dist/ssh.js.map +0 -1
- package/dist/sshChannelStream.js.map +0 -1
- package/dist/targets.js.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/updates.js.map +0 -1
- package/dist/vms.js.map +0 -1
- package/dist/wake.js.map +0 -1
package/dist/desk.js
CHANGED
|
@@ -11,78 +11,48 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.DeskTriggerDecorator = void 0;
|
|
13
13
|
const core_1 = require("@angular/core");
|
|
14
|
-
const tabby_core_1 = require("tabby-core");
|
|
15
14
|
const tabby_terminal_1 = require("tabby-terminal");
|
|
16
15
|
const desktop_service_1 = require("./desktop.service");
|
|
17
16
|
const targets_1 = require("./targets");
|
|
17
|
+
const deskScript_1 = require("./deskScript");
|
|
18
18
|
const probe_1 = require("./probe");
|
|
19
|
-
const PREFIX = Buffer.from('\x1b]7777;');
|
|
20
|
-
const BEL = 0x07;
|
|
21
|
-
const ST = Buffer.from('\x1b\\');
|
|
22
|
-
function parse(payload) {
|
|
23
|
-
const [session, socket, cwd, kind, machine, hostname] = payload.split(';').map(f => Buffer.from(f ?? '', 'base64').toString('utf8'));
|
|
24
|
-
// Older `desk` scripts sent no kind (they only knew tmux), and no machine.
|
|
25
|
-
return {
|
|
26
|
-
session: session ?? '', socket: socket ?? '', cwd: cwd ?? '', kind: kind ?? (session ? 'tmux' : ''),
|
|
27
|
-
machine: machine?.trim() ?? '', hostname: hostname?.trim() ?? '',
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
19
|
/** Strips `desk` requests (and probes, see probe.ts) out of a session's output and reports them. */
|
|
31
20
|
class DeskTrigger extends tabby_terminal_1.SessionMiddleware {
|
|
32
|
-
constructor(onDesk, onProbe) {
|
|
21
|
+
constructor(known, onDesk, onProbe) {
|
|
33
22
|
super();
|
|
34
23
|
this.onDesk = onDesk;
|
|
35
24
|
this.onProbe = onProbe;
|
|
25
|
+
this.requests = new deskScript_1.DeskRequests(known);
|
|
36
26
|
}
|
|
37
27
|
feedFromSession(data) {
|
|
38
28
|
// Tabby's OSC processor runs first and passes unknown OSCs through whole, so each request
|
|
39
29
|
// arrives complete within one chunk.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const st = data.indexOf(ST, body);
|
|
45
|
-
const end = bel === -1 ? st : st === -1 ? bel : Math.min(bel, st);
|
|
46
|
-
if (end === -1) {
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
const payload = data.subarray(body, end).toString('utf8');
|
|
50
|
-
data = Buffer.concat([data.subarray(0, start), data.subarray(end + (end === st ? ST.length : 1))]);
|
|
51
|
-
if (payload.startsWith('desk;')) {
|
|
52
|
-
const request = parse(payload.slice('desk;'.length));
|
|
53
|
-
setTimeout(() => this.onDesk(request));
|
|
54
|
-
}
|
|
55
|
-
else if (payload.startsWith('probe;')) {
|
|
56
|
-
this.onProbe(payload.slice('probe;'.length));
|
|
57
|
-
}
|
|
58
|
-
start = data.indexOf(PREFIX, start);
|
|
30
|
+
const { output, desk, probes } = this.requests.take(data);
|
|
31
|
+
probes.forEach(id => this.onProbe(id));
|
|
32
|
+
if (desk) {
|
|
33
|
+
setTimeout(() => this.onDesk(desk));
|
|
59
34
|
}
|
|
60
|
-
if (
|
|
61
|
-
super.feedFromSession(
|
|
35
|
+
if (output.length) {
|
|
36
|
+
super.feedFromSession(output);
|
|
62
37
|
}
|
|
63
38
|
}
|
|
64
39
|
}
|
|
65
40
|
/** Listens for `desk` in every terminal (SSH tabs and local terminals running ssh). */
|
|
66
41
|
let DeskTriggerDecorator = class DeskTriggerDecorator extends tabby_terminal_1.TerminalDecorator {
|
|
67
|
-
constructor(desktop, zone
|
|
42
|
+
constructor(desktop, zone) {
|
|
68
43
|
super();
|
|
69
44
|
this.desktop = desktop;
|
|
70
45
|
this.zone = zone;
|
|
71
|
-
this.notifications = notifications;
|
|
72
46
|
}
|
|
73
47
|
attach(terminal) {
|
|
74
48
|
const install = () => {
|
|
75
|
-
|
|
49
|
+
// Only requests with a key a host gave get this far, one in a while (see DeskRequests); the service checks
|
|
50
|
+
// the rest, the `desk` setting included.
|
|
51
|
+
terminal.session?.middleware.push(new DeskTrigger(key => this.desktop.knowsDeskKey(key), request => this.zone.run(() => {
|
|
76
52
|
const pane = (0, targets_1.desktopPaneOf)(terminal);
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
if (!this.desktop.settings().desk) {
|
|
81
|
-
// A hook left from when it was on; it goes away the next time a desktop opens there.
|
|
82
|
-
this.notifications.info('`desk` is off: turn it on in the menu under Remote Desktop › Settings');
|
|
83
|
-
return;
|
|
53
|
+
if (pane) {
|
|
54
|
+
this.desktop.openConsole(pane, request);
|
|
84
55
|
}
|
|
85
|
-
this.desktop.openConsole(pane, request);
|
|
86
56
|
}), id => (0, probe_1.probeArrived)(id, terminal)));
|
|
87
57
|
};
|
|
88
58
|
install();
|
|
@@ -92,6 +62,5 @@ let DeskTriggerDecorator = class DeskTriggerDecorator extends tabby_terminal_1.T
|
|
|
92
62
|
exports.DeskTriggerDecorator = DeskTriggerDecorator;
|
|
93
63
|
exports.DeskTriggerDecorator = DeskTriggerDecorator = __decorate([
|
|
94
64
|
(0, core_1.Injectable)(),
|
|
95
|
-
__metadata("design:paramtypes", [desktop_service_1.RemoteDesktopService, core_1.NgZone
|
|
65
|
+
__metadata("design:paramtypes", [desktop_service_1.RemoteDesktopService, core_1.NgZone])
|
|
96
66
|
], DeskTriggerDecorator);
|
|
97
|
-
//# sourceMappingURL=desk.js.map
|
package/dist/deskScript.js
CHANGED
|
@@ -1,8 +1,171 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MACHINE_ID_COMMAND = void 0;
|
|
3
|
+
exports.MACHINE_ID_COMMAND = exports.DeskGate = exports.DeskRequests = exports.DESK_COOLDOWN_MS = void 0;
|
|
4
|
+
exports.parseDeskRequest = parseDeskRequest;
|
|
5
|
+
exports.deskKeyDigest = deskKeyDigest;
|
|
6
|
+
exports.takeDeskMessages = takeDeskMessages;
|
|
4
7
|
exports.shq = shq;
|
|
5
8
|
exports.consoleScript = consoleScript;
|
|
9
|
+
const crypto_1 = require("crypto");
|
|
10
|
+
/** desk's key: 128 random bits in hex, made by the setup (remoteSetup.ts). */
|
|
11
|
+
const DESK_KEY = /^[0-9a-f]{32}$/;
|
|
12
|
+
/** A request's fields, from what follows `desk;`. */
|
|
13
|
+
function parseDeskRequest(payload) {
|
|
14
|
+
const [session, socket, cwd, kind, machine, hostname, key] = payload.split(';').map(f => Buffer.from(f, 'base64').toString('utf8'));
|
|
15
|
+
return {
|
|
16
|
+
session: session ?? '', socket: socket ?? '', cwd: cwd ?? '', kind: kind ?? '',
|
|
17
|
+
machine: machine?.trim() ?? '', hostname: hostname?.trim() ?? '', key: DESK_KEY.test(key ?? '') ? key : '',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** What the plugin keeps of a host's desk key: its SHA-256, so that the config never holds the key. '' for no key. */
|
|
21
|
+
function deskKeyDigest(key) {
|
|
22
|
+
return key && DESK_KEY.test(key) ? (0, crypto_1.createHash)('sha256').update(key).digest('hex') : '';
|
|
23
|
+
}
|
|
24
|
+
const PREFIX = Buffer.from('\x1b]7777;');
|
|
25
|
+
const DESK = Buffer.from('desk;');
|
|
26
|
+
const PROBE = Buffer.from('probe;');
|
|
27
|
+
const BEL = 0x07;
|
|
28
|
+
const ESC = 0x1b;
|
|
29
|
+
/** Whether `data` has `prefix` at `at`, before `end`. */
|
|
30
|
+
function startsAt(data, prefix, at, end) {
|
|
31
|
+
return end - at >= prefix.length && data.compare(prefix, 0, prefix.length, at, at + prefix.length) === 0;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Takes the plugin's OSC 7777 messages out of a chunk of terminal output: one `desk` request (output can repeat one any
|
|
35
|
+
* number of times; one is all a chunk gets), the last one that `counts` (its key: see DeskRequests), so that a
|
|
36
|
+
* look-alike after it can't stand in its place; how many others were looked at and didn't count; and probes (see
|
|
37
|
+
* probe.ts).
|
|
38
|
+
*/
|
|
39
|
+
function takeDeskMessages(data, counts = () => true) {
|
|
40
|
+
const kept = [];
|
|
41
|
+
const probes = [];
|
|
42
|
+
// Where each desk request's fields are (start, end), read from the last one back once all are out of the output.
|
|
43
|
+
const desks = [];
|
|
44
|
+
let from = 0;
|
|
45
|
+
for (let start = data.indexOf(PREFIX); start !== -1; start = data.indexOf(PREFIX, from)) {
|
|
46
|
+
const body = start + PREFIX.length;
|
|
47
|
+
// The first BEL or ST (ESC \) ends it; one pass, so many messages in a chunk cost no more than its length.
|
|
48
|
+
let end = body;
|
|
49
|
+
while (end < data.length && data[end] !== BEL && !(data[end] === ESC && data[end + 1] === 0x5c)) {
|
|
50
|
+
end++;
|
|
51
|
+
}
|
|
52
|
+
if (end >= data.length) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
kept.push(data.subarray(from, start));
|
|
56
|
+
from = end + (data[end] === BEL ? 1 : 2);
|
|
57
|
+
if (startsAt(data, DESK, body, end)) {
|
|
58
|
+
desks.push(body + DESK.length, end);
|
|
59
|
+
}
|
|
60
|
+
else if (startsAt(data, PROBE, body, end)) {
|
|
61
|
+
probes.push(data.subarray(body + PROBE.length, end).toString('utf8'));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let desk = null;
|
|
65
|
+
let ignored = 0;
|
|
66
|
+
for (let i = desks.length - 2; i >= 0 && !desk; i -= 2) {
|
|
67
|
+
const request = parseDeskRequest(data.subarray(desks[i], desks[i + 1]).toString('utf8'));
|
|
68
|
+
if (counts(request)) {
|
|
69
|
+
desk = request;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
ignored++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const output = from === 0 ? data : Buffer.concat([...kept, data.subarray(from)]);
|
|
76
|
+
return { output, desk, ignored, probes };
|
|
77
|
+
}
|
|
78
|
+
/** How long a pane ignores `desk` requests after handling one. */
|
|
79
|
+
exports.DESK_COOLDOWN_MS = 2000;
|
|
80
|
+
/** How many keys' lookups a chunk of output keeps, to look each of them up once (see DeskRequests.take). */
|
|
81
|
+
const KEYS_KEPT = 16;
|
|
82
|
+
/**
|
|
83
|
+
* The time the cooldowns go by: a clock that only goes forward. The time of day can step back (set by hand, or
|
|
84
|
+
* corrected after sleep), which would keep `desk` quiet for as long as the step.
|
|
85
|
+
*/
|
|
86
|
+
const monotonic = () => performance.now();
|
|
87
|
+
/**
|
|
88
|
+
* A terminal's `desk` requests as its output brings them, before any goes further: only one with a key `known` (a key
|
|
89
|
+
* some host's setup gave this plugin, see RemoteDesktopService.knowsDeskKey), and then none for the cooldown. Output
|
|
90
|
+
* can carry any number of look-alikes, in one chunk or in many, and each request let through takes a turn of Tabby's
|
|
91
|
+
* window (and, past the key check there, commands on a host): the others end here, as they are read. Those without a
|
|
92
|
+
* known key get a line in the developer console, at most one per cooldown.
|
|
93
|
+
*/
|
|
94
|
+
class DeskRequests {
|
|
95
|
+
constructor(known, cooldownMs = exports.DESK_COOLDOWN_MS, now = monotonic, warn = message => console.warn(message)) {
|
|
96
|
+
this.known = known;
|
|
97
|
+
this.cooldownMs = cooldownMs;
|
|
98
|
+
this.now = now;
|
|
99
|
+
this.warn = warn;
|
|
100
|
+
this.quietUntil = 0;
|
|
101
|
+
this.unknown = 0;
|
|
102
|
+
this.warnAfter = 0;
|
|
103
|
+
}
|
|
104
|
+
/** Takes the plugin's messages out of a chunk of output (see takeDeskMessages): the request to act on, if any. */
|
|
105
|
+
take(data) {
|
|
106
|
+
// A flood repeats a few keys, one or several in turn: each is looked up once per chunk. A few are kept, not all
|
|
107
|
+
// of them (a flood of different ones costs a lookup each whatever is kept, and holds no more memory for it),
|
|
108
|
+
// and the last one besides, so that one repeated past them isn't looked up again each time either.
|
|
109
|
+
const looked = new Map();
|
|
110
|
+
let last = null;
|
|
111
|
+
const counts = (request) => {
|
|
112
|
+
if (last?.key === request.key) {
|
|
113
|
+
return last.known;
|
|
114
|
+
}
|
|
115
|
+
let known = looked.get(request.key);
|
|
116
|
+
if (known === undefined) {
|
|
117
|
+
known = !!request.key && this.known(request.key);
|
|
118
|
+
if (looked.size < KEYS_KEPT) {
|
|
119
|
+
looked.set(request.key, known);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
last = { key: request.key, known };
|
|
123
|
+
return known;
|
|
124
|
+
};
|
|
125
|
+
const { output, desk, ignored, probes } = takeDeskMessages(data, counts);
|
|
126
|
+
const now = this.now();
|
|
127
|
+
this.unknown += ignored;
|
|
128
|
+
if (this.unknown && now >= this.warnAfter) {
|
|
129
|
+
this.warn(`desk: ignored ${this.unknown === 1 ? 'a request' : `${this.unknown} requests`} without a key from a host whose desktop this Tabby set desk up on`);
|
|
130
|
+
this.unknown = 0;
|
|
131
|
+
this.warnAfter = now + this.cooldownMs;
|
|
132
|
+
}
|
|
133
|
+
if (!desk || now < this.quietUntil) {
|
|
134
|
+
return { output, desk: null, probes };
|
|
135
|
+
}
|
|
136
|
+
this.quietUntil = now + this.cooldownMs;
|
|
137
|
+
return { output, desk, probes };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.DeskRequests = DeskRequests;
|
|
141
|
+
/**
|
|
142
|
+
* One `desk` request at a time per pane, and none for a moment after one: output can repeat a request any number of
|
|
143
|
+
* times, and each one costs commands on the host (and a `ps` here, for a local terminal).
|
|
144
|
+
*/
|
|
145
|
+
class DeskGate {
|
|
146
|
+
constructor(cooldownMs = exports.DESK_COOLDOWN_MS, now = monotonic) {
|
|
147
|
+
this.cooldownMs = cooldownMs;
|
|
148
|
+
this.now = now;
|
|
149
|
+
this.busy = false;
|
|
150
|
+
this.until = 0;
|
|
151
|
+
}
|
|
152
|
+
/** Runs `task`, unless one runs or the last one ended less than the cooldown ago (false: dropped). */
|
|
153
|
+
async run(task) {
|
|
154
|
+
if (this.busy || this.now() < this.until) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
this.busy = true;
|
|
158
|
+
try {
|
|
159
|
+
await task();
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
this.busy = false;
|
|
163
|
+
this.until = this.now() + this.cooldownMs;
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.DeskGate = DeskGate;
|
|
6
169
|
/** Remote command printing what `desk` sends as `machine`. */
|
|
7
170
|
exports.MACHINE_ID_COMMAND = 'cat /etc/machine-id 2>/dev/null || hostname';
|
|
8
171
|
/** Single-quotes a value for sh. */
|
|
@@ -10,8 +173,10 @@ function shq(value) {
|
|
|
10
173
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11
174
|
}
|
|
12
175
|
/**
|
|
13
|
-
* Remote script: open a maximized terminal on the headless desktop, attached to the given
|
|
14
|
-
*
|
|
176
|
+
* Remote script: open a maximized terminal on the headless desktop, attached to the given shared session (unless
|
|
177
|
+
* something already is), or in the given folder. Only sessions of this user's own: a trd-pty session by its id (trd-pty
|
|
178
|
+
* looks it up in its private folder and checks who serves it), or a tmux server whose socket this user owns, in tmux's
|
|
179
|
+
* folder for them. Prints `RD_OK …` or `RD_ERR …`.
|
|
15
180
|
*/
|
|
16
181
|
function consoleScript(request) {
|
|
17
182
|
return `SESS=${shq(request.session)}
|
|
@@ -23,14 +188,40 @@ command -v gnome-terminal >/dev/null 2>&1 || { echo "RD_ERR gnome-terminal is no
|
|
|
23
188
|
W=$(systemctl --user show-environment 2>/dev/null | sed -n 's/^WAYLAND_DISPLAY=//p')
|
|
24
189
|
export WAYLAND_DISPLAY=${"$"}{W:-wayland-0}
|
|
25
190
|
TRD="$HOME/.local/share/tabby-rdp/bin/trd-pty"
|
|
26
|
-
|
|
191
|
+
# A tmux client attaches to whatever server answers on the socket it is given, and another user can let anyone in to
|
|
192
|
+
# theirs. So only a socket of this user's, in tmux's folder for them (theirs, no access for others, as tmux makes it),
|
|
193
|
+
# with no symbolic link on the way, and every folder above it root's or this user's, where others can't rename or
|
|
194
|
+
# replace anything (or only their own entries: sticky, like /tmp): nobody else can swap the path out before the attach,
|
|
195
|
+
# later, finds the socket again. (ls -n's first and third fields, its mode and owner, are the portable way to read them.)
|
|
196
|
+
own_tmux () {
|
|
197
|
+
[ -S "$1" ] && [ ! -L "$1" ] && [ -O "$1" ] && [ "$(readlink -f "$1" 2>/dev/null)" = "$1" ] || return 1
|
|
198
|
+
ot_uid=$(id -u)
|
|
199
|
+
ot_dir=${"$"}{1%/*}
|
|
200
|
+
[ "${"$"}{ot_dir##*/}" = "tmux-$ot_uid" ] && [ -O "$ot_dir" ] || return 1
|
|
201
|
+
case $(ls -ld "$ot_dir") in d???------*) ;; *) return 1 ;; esac
|
|
202
|
+
ot_up=${"$"}{ot_dir%/*}
|
|
203
|
+
while :; do
|
|
204
|
+
set -- $(ls -ldn "$ot_up/")
|
|
205
|
+
[ "$3" = "$ot_uid" ] || [ "$3" = 0 ] || return 1
|
|
206
|
+
case $1 in d????[!w]??[!w]*|d????????[tT]*) ;; *) return 1 ;; esac
|
|
207
|
+
case $ot_up in '') return 0 ;; /*) ot_up=${"$"}{ot_up%/*} ;; *) return 1 ;; esac
|
|
208
|
+
done
|
|
209
|
+
}
|
|
210
|
+
# Session names as trd-pty and tmux make them (tmux never puts : or . in one); anything else opens a terminal in the
|
|
211
|
+
# folder instead. A tmux name is otherwise any: it only picks among the sessions of a server of this user's own.
|
|
212
|
+
case $KIND in
|
|
213
|
+
native) case $SESS in ''|*[!0-9a-f]*) KIND= ;; esac ;;
|
|
214
|
+
tmux) case $SESS in ''|*[:.]*) KIND= ;; esac ;;
|
|
215
|
+
*) KIND= ;;
|
|
216
|
+
esac
|
|
217
|
+
if [ "$KIND" = native ] && [ -x "$TRD" ] && [ "$("$TRD" clients "$SESS")" -ge 1 ]; then
|
|
27
218
|
# The console itself is one attached terminal; a second one means a desktop terminal is already attached.
|
|
28
219
|
if [ "$("$TRD" clients "$SESS")" -ge 2 ]; then
|
|
29
220
|
echo "RD_OK attached"; exit 0
|
|
30
221
|
fi
|
|
31
222
|
gnome-terminal --maximize -- "$TRD" attach "$SESS" >/dev/null 2>&1 \
|
|
32
223
|
&& echo "RD_OK opened" || echo "RD_ERR could not open a terminal"
|
|
33
|
-
elif [
|
|
224
|
+
elif [ "$KIND" = tmux ] && own_tmux "$SOCK" && tmux -S "$SOCK" has-session -t "=$SESS" 2>/dev/null; then
|
|
34
225
|
# The console itself is one client; a second one means a desktop terminal is already attached.
|
|
35
226
|
if [ "$(tmux -S "$SOCK" list-clients -t "=$SESS" 2>/dev/null | wc -l)" -ge 2 ]; then
|
|
36
227
|
echo "RD_OK attached"; exit 0
|
|
@@ -44,4 +235,3 @@ else
|
|
|
44
235
|
fi
|
|
45
236
|
`;
|
|
46
237
|
}
|
|
47
|
-
//# sourceMappingURL=deskScript.js.map
|