tabby-rdp 0.2.7 → 0.3.1
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 +72 -2
- package/README.md +182 -21
- package/dist/connectionStatus.js +140 -0
- package/dist/connectionStatus.js.map +1 -0
- package/dist/desk.js +21 -10
- package/dist/desk.js.map +1 -1
- package/dist/deskScript.js +3 -0
- package/dist/deskScript.js.map +1 -1
- package/dist/desktop.service.js +994 -53
- package/dist/desktop.service.js.map +1 -1
- package/dist/desktopForm.js +64 -19
- package/dist/desktopForm.js.map +1 -1
- package/dist/desktops.js +60 -21
- package/dist/desktops.js.map +1 -1
- package/dist/fileTransfer.js +152 -5
- package/dist/fileTransfer.js.map +1 -1
- package/dist/header.js +22 -7
- package/dist/header.js.map +1 -1
- package/dist/help.js +241 -0
- package/dist/help.js.map +1 -0
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/keyboard.js +142 -5
- package/dist/keyboard.js.map +1 -1
- package/dist/microphone.js +295 -0
- package/dist/microphone.js.map +1 -0
- package/dist/osd.js +79 -0
- package/dist/osd.js.map +1 -0
- package/dist/probe.js +33 -0
- package/dist/probe.js.map +1 -0
- package/dist/rdcleanpath.js +132 -7
- package/dist/rdcleanpath.js.map +1 -1
- package/dist/rdpFile.js +47 -0
- package/dist/rdpFile.js.map +1 -0
- package/dist/rdpProfile.js +367 -0
- package/dist/rdpProfile.js.map +1 -0
- package/dist/remoteSetup.js +90 -11
- package/dist/remoteSetup.js.map +1 -1
- package/dist/settingsPage.js +554 -0
- package/dist/settingsPage.js.map +1 -0
- package/dist/signin.js +21 -2
- package/dist/signin.js.map +1 -1
- package/dist/ssh.js +28 -0
- package/dist/ssh.js.map +1 -1
- package/dist/targets.js +148 -8
- package/dist/targets.js.map +1 -1
- package/dist/ui.js +163 -24
- package/dist/ui.js.map +1 -1
- package/dist/updates.js +123 -0
- package/dist/updates.js.map +1 -0
- package/dist/vms.js +90 -0
- package/dist/vms.js.map +1 -0
- package/dist/wake.js +124 -0
- package/dist/wake.js.map +1 -0
- package/package.json +2 -1
- package/vendor/IRONRDP-LICENSE +3 -3
- package/vendor/iron-remote-desktop-rdp.js +698 -451
- package/vendor/iron-remote-desktop.js +87 -81
- package/vendor/ironrdp_web_bg.wasm +0 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RDPProfileOpener = exports.RDPCommands = exports.RDPProfilesService = exports.RDPProfileSettingsComponent = exports.RDPTabRecovery = exports.RDPTabComponent = void 0;
|
|
13
|
+
const core_1 = require("@angular/core");
|
|
14
|
+
const tabby_core_1 = require("tabby-core");
|
|
15
|
+
const tabby_terminal_1 = require("tabby-terminal");
|
|
16
|
+
const desktop_service_1 = require("./desktop.service");
|
|
17
|
+
const desktopForm_1 = require("./desktopForm");
|
|
18
|
+
const help_1 = require("./help");
|
|
19
|
+
const desktops_1 = require("./desktops");
|
|
20
|
+
const signin_1 = require("./signin");
|
|
21
|
+
const ssh_1 = require("./ssh");
|
|
22
|
+
const targets_1 = require("./targets");
|
|
23
|
+
/** Input of an SSH tab opened for an RDP profile with `via`: the desktop to show once SSH is connected. */
|
|
24
|
+
const OPEN_DESKTOP = 'trdOpenDesktop';
|
|
25
|
+
const RECOVERY_TYPE = 'app:rdp-tab';
|
|
26
|
+
// ---- the tab -----------------------------------------------------------------------------------
|
|
27
|
+
const TAB_STYLE = `
|
|
28
|
+
rdp-tab { flex: auto; display: flex; position: relative; overflow: hidden; background: #111; }
|
|
29
|
+
rdp-tab .trd-rdp-idle { position: absolute; inset: 0; display: flex; flex-direction: column; gap: 14px; align-items: center;
|
|
30
|
+
justify-content: center; padding: 2em; text-align: center; color: #bbb; font-size: 13px; white-space: pre-line; }
|
|
31
|
+
`;
|
|
32
|
+
/**
|
|
33
|
+
* A remote desktop tab: the desktop layer (the same one SSH tabs get) fills it, and there is no console under it.
|
|
34
|
+
* A component because Tabby opens profiles as tab components; it holds no UI of its own beyond a line and a Connect
|
|
35
|
+
* button for when no desktop is open (after Disconnect).
|
|
36
|
+
*/
|
|
37
|
+
let RDPTabComponent = class RDPTabComponent extends tabby_core_1.BaseTabComponent {
|
|
38
|
+
constructor(injector, element, desktop, targets, zone) {
|
|
39
|
+
super(injector);
|
|
40
|
+
this.element = element;
|
|
41
|
+
this.desktop = desktop;
|
|
42
|
+
this.targets = targets;
|
|
43
|
+
this.zone = zone;
|
|
44
|
+
}
|
|
45
|
+
ngOnInit() {
|
|
46
|
+
if (!this.title) {
|
|
47
|
+
this.setTitle(this.profile.name);
|
|
48
|
+
}
|
|
49
|
+
this.idle = document.createElement('div');
|
|
50
|
+
this.idle.className = 'trd-rdp-idle';
|
|
51
|
+
this.idle.innerHTML = '<div class="trd-rdp-idle-text"></div><button class="btn btn-secondary">Connect</button>';
|
|
52
|
+
this.idle.querySelector('button').addEventListener('click', () => this.zone.run(() => this.open()));
|
|
53
|
+
this.element.nativeElement.appendChild(this.idle);
|
|
54
|
+
this.subscribeUntilDestroyed(this.desktop.changed$, () => this.update());
|
|
55
|
+
this.update();
|
|
56
|
+
// Once the tab is laid out, so that the first connection gets the pane's size.
|
|
57
|
+
setTimeout(() => this.open(true));
|
|
58
|
+
}
|
|
59
|
+
update() {
|
|
60
|
+
const o = this.profile.options;
|
|
61
|
+
const text = o.via
|
|
62
|
+
? `${this.profile.name} goes through an SSH profile that no longer exists. Choose another in its profile settings.`
|
|
63
|
+
: `${this.profile.name} (${(0, desktopForm_1.formatAddress)(o.host, o.port || 3389)}) is not connected.`;
|
|
64
|
+
this.idle.querySelector('.trd-rdp-idle-text').textContent = text;
|
|
65
|
+
this.idle.querySelector('button').style.display = o.via ? 'none' : '';
|
|
66
|
+
this.idle.style.display = this.desktop.has(this) ? 'none' : '';
|
|
67
|
+
}
|
|
68
|
+
async open(first = false) {
|
|
69
|
+
if (this.profile.options.via || !this.profile.options.host) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
await this.targets.targetOf(this);
|
|
73
|
+
const elsewhere = first && this.desktop.isOpenElsewhere(this);
|
|
74
|
+
// Opened again while another tab has it: that tab comes forward (one desktop per server), and this one goes.
|
|
75
|
+
await this.desktop.showDesktop(this);
|
|
76
|
+
if (elsewhere) {
|
|
77
|
+
this.destroy();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async getRecoveryToken() {
|
|
81
|
+
return { type: RECOVERY_TYPE, profile: this.profile };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
exports.RDPTabComponent = RDPTabComponent;
|
|
85
|
+
exports.RDPTabComponent = RDPTabComponent = __decorate([
|
|
86
|
+
(0, core_1.Component)({
|
|
87
|
+
selector: 'rdp-tab',
|
|
88
|
+
template: '',
|
|
89
|
+
styles: [TAB_STYLE],
|
|
90
|
+
// The idle message is built by hand, outside the template, so its styles can't be scoped to it.
|
|
91
|
+
encapsulation: core_1.ViewEncapsulation.None,
|
|
92
|
+
}),
|
|
93
|
+
__metadata("design:paramtypes", [core_1.Injector,
|
|
94
|
+
core_1.ElementRef,
|
|
95
|
+
desktop_service_1.RemoteDesktopService,
|
|
96
|
+
targets_1.RemoteTargets,
|
|
97
|
+
core_1.NgZone])
|
|
98
|
+
], RDPTabComponent);
|
|
99
|
+
/** Reopens remote desktop tabs when Tabby restores its tabs (and for Duplicate). */
|
|
100
|
+
let RDPTabRecovery = class RDPTabRecovery extends tabby_core_1.TabRecoveryProvider {
|
|
101
|
+
constructor(injector) {
|
|
102
|
+
super();
|
|
103
|
+
this.injector = injector;
|
|
104
|
+
}
|
|
105
|
+
async applicableTo(token) {
|
|
106
|
+
return token.type === RECOVERY_TYPE;
|
|
107
|
+
}
|
|
108
|
+
async recover(token) {
|
|
109
|
+
return {
|
|
110
|
+
type: RDPTabComponent,
|
|
111
|
+
inputs: { profile: this.injector.get(tabby_core_1.ProfilesService).getConfigProxyForProfile(token.profile) },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
exports.RDPTabRecovery = RDPTabRecovery;
|
|
116
|
+
exports.RDPTabRecovery = RDPTabRecovery = __decorate([
|
|
117
|
+
(0, core_1.Injectable)(),
|
|
118
|
+
__metadata("design:paramtypes", [core_1.Injector])
|
|
119
|
+
], RDPTabRecovery);
|
|
120
|
+
// ---- profile settings --------------------------------------------------------------------------
|
|
121
|
+
/**
|
|
122
|
+
* The profile's own fields in Tabby's profile editor (name, group, icon and color are Tabby's). Plain DOM like the
|
|
123
|
+
* rest of the plugin, writing to the profile as you type.
|
|
124
|
+
*/
|
|
125
|
+
let RDPProfileSettingsComponent = class RDPProfileSettingsComponent {
|
|
126
|
+
constructor(element, injector) {
|
|
127
|
+
this.element = element;
|
|
128
|
+
this.injector = injector;
|
|
129
|
+
}
|
|
130
|
+
ngOnInit() {
|
|
131
|
+
const o = this.profile.options;
|
|
132
|
+
const account = () => `${o.username ?? ''}\n${o.domain ?? ''}`;
|
|
133
|
+
// No id for a new profile (no address yet): it has nothing saved to take along.
|
|
134
|
+
this.before = { id: o.host ? (0, desktops_1.desktopIdOf)(o) : '', account: account(), via: o.via ?? '' };
|
|
135
|
+
const root = this.element.nativeElement;
|
|
136
|
+
root.innerHTML = `
|
|
137
|
+
<div class="mb-3">
|
|
138
|
+
<label>Address</label>
|
|
139
|
+
<input class="form-control" name="address" spellcheck="false" placeholder="e.g. 192.168.1.20, or pc.local:3390">
|
|
140
|
+
<div class="text-danger small" data-for="address"></div>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="mb-3">
|
|
143
|
+
<label>Connect</label>
|
|
144
|
+
<select class="form-control" name="via"><option value="">Directly from this computer</option></select>
|
|
145
|
+
<div class="text-muted small">Through an SSH profile, the address is as that host sees it, and the desktop opens over its SSH tab.</div>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="mb-3">
|
|
148
|
+
<label>Kind</label>
|
|
149
|
+
<select class="form-control" name="kind">
|
|
150
|
+
<option value="windows">Windows (or another RDP server)</option>
|
|
151
|
+
<option value="xrdp">xrdp (Linux: KDE, XFCE, MATE, …)</option>
|
|
152
|
+
<option value="gnome">GNOME Remote Desktop (needs the graphics pipeline)</option>
|
|
153
|
+
</select>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="mb-3">
|
|
156
|
+
<label>User name</label>
|
|
157
|
+
<input class="form-control" name="username" spellcheck="false" placeholder="Optional; asked when connecting. DOMAIN\\user works too">
|
|
158
|
+
</div>
|
|
159
|
+
<div class="mb-3">
|
|
160
|
+
<label>Domain</label>
|
|
161
|
+
<input class="form-control" name="domain" spellcheck="false" placeholder="Optional">
|
|
162
|
+
</div>`;
|
|
163
|
+
const field = (name) => root.querySelector(`[name="${name}"]`);
|
|
164
|
+
field('address').value = o.host ? (0, desktopForm_1.formatAddress)(o.host, o.port || 3389) : '';
|
|
165
|
+
field('kind').value = o.kind === 'gnome' || o.kind === 'xrdp' ? o.kind : 'windows';
|
|
166
|
+
field('username').value = o.username ?? '';
|
|
167
|
+
field('domain').value = o.domain ?? '';
|
|
168
|
+
field('address').addEventListener('input', () => {
|
|
169
|
+
const text = field('address').value;
|
|
170
|
+
const address = (0, desktopForm_1.parseAddress)(text);
|
|
171
|
+
root.querySelector('[data-for=address]').textContent = text.trim() && !address ? 'This doesn\'t look like an address.' : '';
|
|
172
|
+
if (address) {
|
|
173
|
+
o.host = address.host;
|
|
174
|
+
o.port = address.port;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
field('kind').addEventListener('change', () => {
|
|
178
|
+
const kind = field('kind').value;
|
|
179
|
+
o.kind = kind === 'gnome' || kind === 'xrdp' ? kind : 'windows';
|
|
180
|
+
});
|
|
181
|
+
field('username').addEventListener('input', () => { o.username = field('username').value.trim(); });
|
|
182
|
+
field('domain').addEventListener('input', () => { o.domain = field('domain').value.trim(); });
|
|
183
|
+
const via = field('via');
|
|
184
|
+
via.addEventListener('change', () => { o.via = via.value; });
|
|
185
|
+
this.injector.get(tabby_core_1.ProfilesService).getProfiles().then(profiles => {
|
|
186
|
+
const ssh = profiles.filter(p => p.type === 'ssh' && !p.isTemplate && p.id);
|
|
187
|
+
if (o.via && !ssh.some(p => p.id === o.via)) {
|
|
188
|
+
via.append(new Option('(an SSH profile that no longer exists)', o.via));
|
|
189
|
+
}
|
|
190
|
+
via.append(...ssh.map(p => new Option(`Through ${p.name}`, p.id)));
|
|
191
|
+
via.value = o.via ?? '';
|
|
192
|
+
}, () => null);
|
|
193
|
+
}
|
|
194
|
+
save() {
|
|
195
|
+
const o = this.profile.options;
|
|
196
|
+
const { id, account, via } = this.before;
|
|
197
|
+
if (!id) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if ((o.via ?? '') !== via) {
|
|
201
|
+
// Reached another way, it is another desktop. A direct one's saved account and certificate were its own; one
|
|
202
|
+
// behind a host shares them with that host's desktop at the same address, so they stay.
|
|
203
|
+
if (!via) {
|
|
204
|
+
(0, signin_1.forgetCredentials)(`${desktops_1.DIRECT_KEY}#${id}`);
|
|
205
|
+
this.injector.get(desktop_service_1.RemoteDesktopService).forgetCertificatesFor(id, true);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const accountChanged = `${o.username ?? ''}\n${o.domain ?? ''}` !== account;
|
|
210
|
+
this.injector.get(desktop_service_1.RemoteDesktopService).desktopEdited(id, (0, desktops_1.desktopIdOf)(o), accountChanged, !via).catch(() => null);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
exports.RDPProfileSettingsComponent = RDPProfileSettingsComponent;
|
|
214
|
+
exports.RDPProfileSettingsComponent = RDPProfileSettingsComponent = __decorate([
|
|
215
|
+
(0, core_1.Component)({
|
|
216
|
+
selector: 'rdp-profile-settings',
|
|
217
|
+
template: '',
|
|
218
|
+
}),
|
|
219
|
+
__metadata("design:paramtypes", [core_1.ElementRef, core_1.Injector])
|
|
220
|
+
], RDPProfileSettingsComponent);
|
|
221
|
+
// ---- the profile provider ----------------------------------------------------------------------
|
|
222
|
+
let RDPProfilesService = class RDPProfilesService extends tabby_core_1.QuickConnectProfileProvider {
|
|
223
|
+
// ProfilesService depends on this provider: it is looked up when needed, not injected.
|
|
224
|
+
constructor(injector, config) {
|
|
225
|
+
super();
|
|
226
|
+
this.injector = injector;
|
|
227
|
+
this.config = config;
|
|
228
|
+
this.id = targets_1.RDP_PROFILE_TYPE;
|
|
229
|
+
this.name = 'Remote desktop (RDP)';
|
|
230
|
+
this.settingsComponent = RDPProfileSettingsComponent;
|
|
231
|
+
this.configDefaults = {
|
|
232
|
+
options: { host: '', port: 3389, kind: 'windows', username: '', domain: '', via: '' },
|
|
233
|
+
clearServiceMessagesOnConnect: false,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
async getBuiltinProfiles() {
|
|
237
|
+
return [{
|
|
238
|
+
id: `${targets_1.RDP_PROFILE_TYPE}:template`,
|
|
239
|
+
type: targets_1.RDP_PROFILE_TYPE,
|
|
240
|
+
name: 'Remote desktop (RDP)',
|
|
241
|
+
icon: 'fas fa-desktop',
|
|
242
|
+
options: { host: '', port: 3389, kind: 'windows', username: '', domain: '', via: '' },
|
|
243
|
+
isBuiltin: true,
|
|
244
|
+
isTemplate: true,
|
|
245
|
+
}];
|
|
246
|
+
}
|
|
247
|
+
async getNewTabParameters(profile) {
|
|
248
|
+
if (profile.options.via) {
|
|
249
|
+
// The desktop behind an SSH host opens over that host's SSH tab, like any desktop behind a host.
|
|
250
|
+
const profiles = this.injector.get(tabby_core_1.ProfilesService);
|
|
251
|
+
const ssh = (await profiles.getProfiles()).find(p => p.id === profile.options.via);
|
|
252
|
+
const params = ssh && await profiles.newTabParametersForProfile(ssh);
|
|
253
|
+
if (params) {
|
|
254
|
+
params.inputs = { ...params.inputs, [OPEN_DESKTOP]: (0, desktops_1.desktopIdOf)(profile.options) };
|
|
255
|
+
return params;
|
|
256
|
+
}
|
|
257
|
+
// The SSH profile is gone: the tab says so.
|
|
258
|
+
}
|
|
259
|
+
return { type: RDPTabComponent, inputs: { profile } };
|
|
260
|
+
}
|
|
261
|
+
getSuggestedName(profile) {
|
|
262
|
+
return profile.options?.host || null;
|
|
263
|
+
}
|
|
264
|
+
getDescription(profile) {
|
|
265
|
+
const o = profile.options ?? {};
|
|
266
|
+
if (!o.host) {
|
|
267
|
+
return '';
|
|
268
|
+
}
|
|
269
|
+
const address = (0, desktopForm_1.formatAddress)(o.host, o.port || 3389);
|
|
270
|
+
const via = o.via ? (this.config.store.profiles ?? []).find((p) => p.id === o.via)?.name ?? 'an SSH profile' : null;
|
|
271
|
+
return via ? `${address} via ${via}` : address;
|
|
272
|
+
}
|
|
273
|
+
/** `[user@]host[:port]`, typed in Tabby's profile selector. */
|
|
274
|
+
quickConnect(query) {
|
|
275
|
+
const at = query.lastIndexOf('@');
|
|
276
|
+
const address = (0, desktopForm_1.parseAddress)(query.slice(at + 1));
|
|
277
|
+
if (!address) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
name: query.trim(),
|
|
282
|
+
type: targets_1.RDP_PROFILE_TYPE,
|
|
283
|
+
options: { host: address.host, port: address.port, username: at > 0 ? query.slice(0, at).trim() : '' },
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
intoQuickConnectString(profile) {
|
|
287
|
+
const o = profile.options;
|
|
288
|
+
return o.via ? null : `${o.username ? `${o.username}@` : ''}${(0, desktopForm_1.formatAddress)(o.host, o.port || 3389)}`;
|
|
289
|
+
}
|
|
290
|
+
deleteProfile(profile) {
|
|
291
|
+
// A direct desktop's saved account and certificate are its own; one behind a host shares them with that host's
|
|
292
|
+
// desktop at the same address.
|
|
293
|
+
if (!profile.options.via) {
|
|
294
|
+
const id = (0, desktops_1.desktopIdOf)(profile.options);
|
|
295
|
+
(0, signin_1.forgetCredentials)(`${desktops_1.DIRECT_KEY}#${id}`);
|
|
296
|
+
this.injector.get(desktop_service_1.RemoteDesktopService).forgetCertificatesFor(id, true);
|
|
297
|
+
this.config.save();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
exports.RDPProfilesService = RDPProfilesService;
|
|
302
|
+
exports.RDPProfilesService = RDPProfilesService = __decorate([
|
|
303
|
+
(0, core_1.Injectable)({ providedIn: 'root' }),
|
|
304
|
+
__metadata("design:paramtypes", [core_1.Injector, tabby_core_1.ConfigService])
|
|
305
|
+
], RDPProfilesService);
|
|
306
|
+
/** In Tabby's command palette, also where no SSH tab (and so no Remote Desktop › Settings menu) is at hand. */
|
|
307
|
+
let RDPCommands = class RDPCommands extends tabby_core_1.CommandProvider {
|
|
308
|
+
constructor(desktop, help) {
|
|
309
|
+
super();
|
|
310
|
+
this.desktop = desktop;
|
|
311
|
+
this.help = help;
|
|
312
|
+
}
|
|
313
|
+
async provide() {
|
|
314
|
+
return [{
|
|
315
|
+
id: 'tabby-rdp:import-rdp-file',
|
|
316
|
+
label: 'Remote desktop: import an .rdp file…',
|
|
317
|
+
run: () => this.desktop.importRdpFile(),
|
|
318
|
+
}, {
|
|
319
|
+
id: 'tabby-rdp:help',
|
|
320
|
+
label: 'Remote desktop: settings, keys and help',
|
|
321
|
+
run: async () => this.help.open(),
|
|
322
|
+
}];
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
exports.RDPCommands = RDPCommands;
|
|
326
|
+
exports.RDPCommands = RDPCommands = __decorate([
|
|
327
|
+
(0, core_1.Injectable)(),
|
|
328
|
+
__metadata("design:paramtypes", [desktop_service_1.RemoteDesktopService, help_1.RemoteDesktopHelp])
|
|
329
|
+
], RDPCommands);
|
|
330
|
+
/** Shows the desktop an RDP profile with `via` asked for, over the SSH tab opened for it, once SSH is connected. */
|
|
331
|
+
let RDPProfileOpener = class RDPProfileOpener extends tabby_terminal_1.TerminalDecorator {
|
|
332
|
+
constructor(desktop, zone) {
|
|
333
|
+
super();
|
|
334
|
+
this.desktop = desktop;
|
|
335
|
+
this.zone = zone;
|
|
336
|
+
}
|
|
337
|
+
attach(terminal) {
|
|
338
|
+
const tab = terminal;
|
|
339
|
+
const id = tab[OPEN_DESKTOP];
|
|
340
|
+
if (!id || !(0, ssh_1.isSSHTab)(tab)) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
// Only for the first connection, not after a reconnect.
|
|
344
|
+
delete tab[OPEN_DESKTOP];
|
|
345
|
+
let closed = false;
|
|
346
|
+
tab.destroyed$.subscribe(() => { closed = true; });
|
|
347
|
+
// Not before: the desktop layer would cover a password prompt or host key question in the terminal.
|
|
348
|
+
const wait = (tries) => {
|
|
349
|
+
if (closed) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if ((0, ssh_1.isConnected)(tab)) {
|
|
353
|
+
this.zone.run(() => this.desktop.showDesktop(tab, id));
|
|
354
|
+
}
|
|
355
|
+
else if (tries > 0) {
|
|
356
|
+
setTimeout(() => wait(tries - 1), 250);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
this.zone.runOutsideAngular(() => wait(4 * 60 * 5));
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
exports.RDPProfileOpener = RDPProfileOpener;
|
|
363
|
+
exports.RDPProfileOpener = RDPProfileOpener = __decorate([
|
|
364
|
+
(0, core_1.Injectable)(),
|
|
365
|
+
__metadata("design:paramtypes", [desktop_service_1.RemoteDesktopService, core_1.NgZone])
|
|
366
|
+
], RDPProfileOpener);
|
|
367
|
+
//# sourceMappingURL=rdpProfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rdpProfile.js","sourceRoot":"","sources":["../src/rdpProfile.ts"],"names":[],"mappings":"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"}
|
package/dist/remoteSetup.js
CHANGED
|
@@ -3,15 +3,51 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.prepareRemoteDesktop = prepareRemoteDesktop;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
|
|
6
|
+
const rdcleanpath_1 = require("./rdcleanpath");
|
|
7
|
+
// Runs as the SSH user; needs no root. Prints exactly one `RD_OK port=N user=U pass=P cert=SHA256`,
|
|
8
|
+
// `RD_XRDP port=N user=U` (no GNOME, but xrdp), `RD_WINDOWS` (a Windows host's POSIX sh) or `RD_ERR <reason>` line;
|
|
9
|
+
// with RD_OK, also an RD_XRDP line when xrdp runs besides GNOME.
|
|
7
10
|
// The RDP password is generated once per remote user and kept in a 0600 file; grd reads credentials only
|
|
8
11
|
// at startup, so grd is restarted only when its configuration actually changes (that drops live sessions).
|
|
9
12
|
// (grdctl takes the credentials as arguments, so they are briefly visible in the remote's process list.)
|
|
10
13
|
const SETUP_SCRIPT = String.raw `
|
|
11
14
|
say () { printf '%s\n' "$*"; }
|
|
12
15
|
fail () { say "RD_ERR $*"; exit 0; }
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
# A Windows host whose PATH has a POSIX sh (Git, MSYS2, Cygwin): its desktop is Windows' own RDP server.
|
|
17
|
+
case "$(uname -s 2>/dev/null)" in CYGWIN*|MINGW*|MSYS*) say "RD_WINDOWS"; exit 0 ;; esac
|
|
18
|
+
# xrdp serves the Linux desktops other than GNOME (KDE, XFCE, MATE, …). Its port is [Globals] port= in xrdp.ini
|
|
19
|
+
# (default 3389): a number, or a listener such as tcp://:3390 (the first one, when there are several). Only used when
|
|
20
|
+
# it runs and listens, so the host's own desktop isn't one that can't answer.
|
|
21
|
+
XRDP_PORT= XRDP_WHY=
|
|
22
|
+
if [ -f /etc/xrdp/xrdp.ini ]; then
|
|
23
|
+
P=$(sed -n '/^\[Globals\]/,/^\[/s/^[[:space:]]*port[[:space:]]*=[[:space:]]*//p' /etc/xrdp/xrdp.ini 2>/dev/null | tr -d '\r' | head -1)
|
|
24
|
+
P=${"$"}{P%%[[:space:]]*}
|
|
25
|
+
case $P in
|
|
26
|
+
'') P=3389 ;;
|
|
27
|
+
vsock://*) P= ;;
|
|
28
|
+
*:*) P=${"$"}{P##*:} ;;
|
|
29
|
+
esac
|
|
30
|
+
case $P in *[!0-9]*) P= ;; esac
|
|
31
|
+
if ! systemctl is-active -q xrdp 2>/dev/null && ! pgrep -x xrdp >/dev/null 2>&1; then
|
|
32
|
+
XRDP_WHY="xrdp is installed but not running (start it: sudo systemctl enable --now xrdp)"
|
|
33
|
+
elif [ -z "$P" ]; then
|
|
34
|
+
XRDP_WHY="xrdp does not listen on a TCP port (see port= in /etc/xrdp/xrdp.ini)"
|
|
35
|
+
elif command -v ss >/dev/null 2>&1 && ! ss -ltnH "sport = :$P" 2>/dev/null | grep -q .; then
|
|
36
|
+
XRDP_WHY="xrdp runs, but nothing listens on its port $P"
|
|
37
|
+
else
|
|
38
|
+
XRDP_PORT=$P
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
NO_GNOME=
|
|
42
|
+
command -v grdctl >/dev/null 2>&1 || NO_GNOME="GNOME Remote Desktop (grdctl) is not installed"
|
|
43
|
+
[ -n "$NO_GNOME" ] || command -v gnome-shell >/dev/null 2>&1 || NO_GNOME="gnome-shell is not installed"
|
|
44
|
+
if [ -n "$NO_GNOME" ]; then
|
|
45
|
+
# No GNOME: the host's own desktop is xrdp's, if it runs. Nothing to set up for it: xrdp starts a session
|
|
46
|
+
# when the user signs in.
|
|
47
|
+
[ -z "$XRDP_PORT" ] || { say "RD_XRDP port=$XRDP_PORT user=$(id -un)"; exit 0; }
|
|
48
|
+
[ -z "$XRDP_WHY" ] || fail "$NO_GNOME, and $XRDP_WHY"
|
|
49
|
+
fail "$NO_GNOME. For other Linux desktops, install xrdp and a desktop (Debian, Ubuntu: sudo apt install xrdp xfce4)"
|
|
50
|
+
fi
|
|
15
51
|
if systemctl --user is-active -q gnome-remote-desktop.service; then
|
|
16
52
|
fail "a desktop-session GNOME Remote Desktop is already running for this user"
|
|
17
53
|
fi
|
|
@@ -28,10 +64,15 @@ for rc in "$HOME/.bashrc" "$HOME/.zshrc"; do
|
|
|
28
64
|
fi
|
|
29
65
|
done
|
|
30
66
|
mkdir -p "$D" && chmod 700 "$D" || fail "could not create $D"
|
|
67
|
+
NEW_CERT=0
|
|
31
68
|
if [ ! -s "$D/tls.key" ]; then
|
|
32
69
|
openssl req -new -newkey rsa:3072 -days 3650 -nodes -x509 -subj "/CN=$(hostname)" \
|
|
33
70
|
-keyout "$D/tls.key" -out "$D/tls.crt" >/dev/null 2>&1 || fail "could not create a TLS certificate"
|
|
71
|
+
NEW_CERT=1
|
|
34
72
|
fi
|
|
73
|
+
# The plugin accepts only this certificate on grd's port.
|
|
74
|
+
CERT=$(openssl x509 -in "$D/tls.crt" -noout -fingerprint -sha256 2>/dev/null | sed 's/^[^=]*=//')
|
|
75
|
+
[ -n "$CERT" ] || fail "could not read the TLS certificate $D/tls.crt (openssl x509)"
|
|
35
76
|
# 'desk' support (setting, off by default): interactive SSH logins start inside a shareable session (trd-pty,
|
|
36
77
|
# or tmux), so 'desk' can attach this exact console to a terminal on the desktop. Rewritten on every connect;
|
|
37
78
|
# opt out per login with TABBY_NO_SESSION=1. When the setting is off, the hook and helpers are removed.
|
|
@@ -83,7 +124,10 @@ elif [ -n "$TMUX" ]; then
|
|
|
83
124
|
SOCKET=$(tmux display-message -p '#{socket_path}' 2>/dev/null)
|
|
84
125
|
tmux set-option -p allow-passthrough on 2>/dev/null
|
|
85
126
|
fi
|
|
86
|
-
|
|
127
|
+
# Which machine this is: a shared home folder puts this script on others too, and the tab may be connected to another
|
|
128
|
+
# machine than the one this runs on (ssh typed in its console).
|
|
129
|
+
MACHINE=$(cat /etc/machine-id 2>/dev/null || hostname)
|
|
130
|
+
SEQ=$(printf '\033]7777;desk;%s;%s;%s;%s;%s;%s\007' "$(b64 "$SESSION")" "$(b64 "$SOCKET")" "$(b64 "$PWD")" "$(b64 "$KIND")" "$(b64 "$MACHINE")" "$(b64 "$(hostname)")")
|
|
87
131
|
if [ "$KIND" = tmux ]; then
|
|
88
132
|
printf '\033Ptmux;\033%s\033\\' "$SEQ" # tmux only lets it through as passthrough
|
|
89
133
|
else
|
|
@@ -122,7 +166,9 @@ STATUS=$(grdctl --headless status --show-credentials 2>/dev/null)
|
|
|
122
166
|
has () { printf '%s\n' "$STATUS" | grep -qxF "$(printf '\t%s' "$1")"; }
|
|
123
167
|
g () { grdctl --headless rdp "$@" >/dev/null 2>&1; }
|
|
124
168
|
RESTART=0
|
|
125
|
-
|
|
169
|
+
# grd reads the certificate at startup: a new one at the same path needs a restart too, or grd would keep serving
|
|
170
|
+
# the old one, which the plugin would then refuse.
|
|
171
|
+
if [ $NEW_CERT = 1 ] || ! has "TLS key: $D/tls.key" || ! has "TLS certificate: $D/tls.crt"; then
|
|
126
172
|
g set-tls-key "$D/tls.key" && g set-tls-cert "$D/tls.crt" || fail "grdctl could not set the TLS certificate"
|
|
127
173
|
RESTART=1
|
|
128
174
|
fi
|
|
@@ -194,26 +240,59 @@ PORT=$(grdctl --headless status 2>/dev/null | awk '/Port:/ { print $2; exit }')
|
|
|
194
240
|
PORT=${"$"}{PORT:-3389}
|
|
195
241
|
i=0; while [ $i -lt 40 ] && ! ss -ltnH "sport = :$PORT" 2>/dev/null | grep -q .; do sleep 0.25; i=$((i+1)); done
|
|
196
242
|
ss -ltnH "sport = :$PORT" 2>/dev/null | grep -q . || fail "nothing is listening on port $PORT"
|
|
197
|
-
|
|
243
|
+
# xrdp besides GNOME (on a port of its own; on grd's, one of them couldn't listen): offered as another desktop.
|
|
244
|
+
[ -z "$XRDP_PORT" ] || [ "$XRDP_PORT" = "$PORT" ] || say "RD_XRDP port=$XRDP_PORT user=$(id -un)"
|
|
245
|
+
say "RD_OK port=$PORT user=$RD_USER pass=$RD_PASS cert=$CERT"
|
|
198
246
|
`;
|
|
199
247
|
let trdPtySource = null;
|
|
200
248
|
function trdPty() {
|
|
201
249
|
trdPtySource ??= fs.readFileSync(path.join(__dirname, '..', 'remote', 'trd-pty.py'), 'utf8');
|
|
202
250
|
return trdPtySource;
|
|
203
251
|
}
|
|
204
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* Whether the SSH host is Windows, i.e. its shell is cmd.exe or PowerShell. One command each of the three shells answers
|
|
254
|
+
* differently: cmd expands %OS%, PowerShell $env:OS (both to Windows_NT), sh neither.
|
|
255
|
+
*/
|
|
256
|
+
async function isWindows(target) {
|
|
257
|
+
try {
|
|
258
|
+
return /Windows_NT/.test(await target.exec('echo %OS% $env:OS', ''));
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Makes sure the remote user has a headless GNOME session served by grd, with fresh credentials. Without GNOME,
|
|
266
|
+
* reports xrdp's port instead, when xrdp runs there; on a Windows host (OpenSSH Server on Windows), Windows' own RDP
|
|
267
|
+
* server, which signs in with the Windows account (the SSH user, to start with).
|
|
268
|
+
*/
|
|
205
269
|
async function prepareRemoteDesktop(target, desk, backend = 'native') {
|
|
206
270
|
const script = `TRD_DESK=${desk ? 1 : 0}\nTRD_BACKEND=${backend === 'tmux' ? 'tmux' : 'native'}\n` +
|
|
207
271
|
SETUP_SCRIPT.replace('@@TRD_PTY_PY@@', trdPty().trimEnd());
|
|
208
272
|
const out = await target.exec('sh -s', script);
|
|
273
|
+
// Without a sh, Windows prints its complaint on stderr, which exec() doesn't return: nothing came back. Only then is
|
|
274
|
+
// it worth asking, so a Linux host costs nothing extra.
|
|
275
|
+
if (/^RD_WINDOWS$/m.test(out) || !/^RD_(OK|XRDP|ERR) /m.test(out) && await isWindows(target)) {
|
|
276
|
+
// Its RDP server makes its own certificate: trusted on first use, like a desktop behind the host.
|
|
277
|
+
return { kind: 'windows', port: 3389, username: target.key.replace(/@[^@]*$/, ''), password: '', certificate: '' };
|
|
278
|
+
}
|
|
209
279
|
const err = /^RD_ERR (.*)$/m.exec(out);
|
|
210
280
|
if (err) {
|
|
211
281
|
throw new Error(err[1]);
|
|
212
282
|
}
|
|
213
|
-
const ok = /^RD_OK port=(\d+) user=(\S+) pass=(\S+)$/m.exec(out);
|
|
214
|
-
|
|
215
|
-
|
|
283
|
+
const ok = /^RD_OK port=(\d+) user=(\S+) pass=(\S+) cert=(\S+)$/m.exec(out);
|
|
284
|
+
const xrdp = /^RD_XRDP port=(\d+) user=(\S*)$/m.exec(out);
|
|
285
|
+
if (ok) {
|
|
286
|
+
const certificate = (0, rdcleanpath_1.normalizeFingerprint)(ok[4]);
|
|
287
|
+
if (!certificate) {
|
|
288
|
+
throw new Error(`Remote setup reported no usable certificate fingerprint (${ok[4]})`);
|
|
289
|
+
}
|
|
290
|
+
return { kind: 'gnome', port: Number(ok[1]), username: ok[2], password: ok[3], certificate, xrdpPort: xrdp ? Number(xrdp[1]) : undefined, xrdpUser: xrdp?.[2] };
|
|
291
|
+
}
|
|
292
|
+
if (xrdp) {
|
|
293
|
+
// xrdp makes its own certificate: trusted on first use, like a desktop behind the host.
|
|
294
|
+
return { kind: 'xrdp', port: Number(xrdp[1]), username: xrdp[2], password: '', certificate: '', xrdpPort: Number(xrdp[1]), xrdpUser: xrdp[2] };
|
|
216
295
|
}
|
|
217
|
-
|
|
296
|
+
throw new Error('Remote setup gave no result');
|
|
218
297
|
}
|
|
219
298
|
//# sourceMappingURL=remoteSetup.js.map
|
package/dist/remoteSetup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteSetup.js","sourceRoot":"","sources":["../src/remoteSetup.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"remoteSetup.js","sourceRoot":"","sources":["../src/remoteSetup.ts"],"names":[],"mappings":"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"}
|