Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
RemoteDesktopClient.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
7using Waher.Events;
11
13{
18 {
19 private readonly Cache<string, RemoteDesktopSession> sessions;
20 private readonly EndpointSecurity e2e;
21
25 public const string RemoteDesktopNamespace = "http://waher.se/rdp/1.0";
26
33 : base(Client)
34 {
35 this.e2e = E2e;
36
37 this.sessions = new Cache<string, RemoteDesktopSession>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromMinutes(15));
38 this.sessions.Removed += this.Sessions_Removed;
39
40 Client.RegisterMessageHandler("started", RemoteDesktopNamespace, this.StartedMessageHandler, true);
41 Client.RegisterMessageHandler("stopped", RemoteDesktopNamespace, this.StoppedMessageHandler, false);
42 Client.RegisterMessageHandler("tile", RemoteDesktopNamespace, this.TileMessageHandler, false);
43 Client.RegisterMessageHandler("tiles", RemoteDesktopNamespace, this.TilesMessageHandler, false);
44 Client.RegisterMessageHandler("scanComplete", RemoteDesktopNamespace, this.ScanCompleteHandler, false);
45 }
46
50 public override void Dispose()
51 {
52 this.client.UnregisterMessageHandler("started", RemoteDesktopNamespace, this.StartedMessageHandler, true);
53 this.client.UnregisterMessageHandler("stopped", RemoteDesktopNamespace, this.StoppedMessageHandler, false);
54 this.client.UnregisterMessageHandler("tile", RemoteDesktopNamespace, this.TileMessageHandler, false);
55 this.client.UnregisterMessageHandler("tiles", RemoteDesktopNamespace, this.TilesMessageHandler, false);
56 this.client.UnregisterMessageHandler("scanComplete", RemoteDesktopNamespace, this.ScanCompleteHandler, false);
57
58 this.sessions.Dispose();
59 base.Dispose();
60 }
61
65 public override string[] Extensions => new string[0];
66
70 public EndpointSecurity E2E => this.e2e;
71
77 public Task<RemoteDesktopSession> StartSessionAsync(string To)
78 {
79 return this.StartSessionAsync(To, Guid.NewGuid());
80 }
81
88 public async Task<RemoteDesktopSession> StartSessionAsync(string To, Guid SessionGuid)
89 {
90 StringBuilder sb = new StringBuilder();
91 string SessionId = SessionGuid.ToString();
92
93 sb.Append("<start xmlns='");
94 sb.Append(RemoteDesktopNamespace);
95 sb.Append("'>");
96 sb.Append(SessionId);
97 sb.Append("</start>");
98
99 TaskCompletionSource<RemoteDesktopSession> Result = new TaskCompletionSource<RemoteDesktopSession>();
100
101 await this.e2e.SendIqSet(this.client, E2ETransmission.NormalIfNotE2E, To, sb.ToString(), (Sender, e) =>
102 {
103 if (e.Ok)
104 {
105 RemoteDesktopSession Session = new RemoteDesktopSession(SessionId, To, this);
106 this.sessions[SessionId] = Session;
107 Result.TrySetResult(Session);
108 }
109 else
110 Result.TrySetException(e.StanzaError ?? new Exception("Unable to start Remote Desktop Session."));
111
112 return Task.CompletedTask;
113 }, null);
114
115 return await Result.Task;
116 }
117
118 private Task Sessions_Removed(object Sender, CacheItemEventArgs<string, RemoteDesktopSession> e)
119 {
120 return e.Value.SetState(RemoteDesktopSessionState.Stopped);
121 }
122
129 public async Task StopSessionAsync(string To, string SessionId)
130 {
131 StringBuilder sb = new StringBuilder();
132
133 sb.Append("<stop xmlns='");
134 sb.Append(RemoteDesktopNamespace);
135 sb.Append("'>");
136 sb.Append(SessionId);
137 sb.Append("</stop>");
138
139 if (this.sessions.TryGetValue(SessionId, out RemoteDesktopSession Session) && Session.State != RemoteDesktopSessionState.Stopped)
140 await Session.SetState(RemoteDesktopSessionState.Stopping);
141
142 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
143
144 await this.e2e.SendIqSet(this.client, E2ETransmission.NormalIfNotE2E, To, sb.ToString(), (Sender, e) =>
145 {
146 if (e.Ok)
147 Result.TrySetResult(true);
148 else
149 Result.TrySetException(e.StanzaError ?? new Exception("Unable to stop Remote Desktop Session."));
150
151 return Task.CompletedTask;
152 }, null);
153 }
154
155 private async Task StartedMessageHandler(object State, MessageEventArgs e)
156 {
157 List<ScreenInfo> Screens = new List<ScreenInfo>();
158 string SessionId = XML.Attribute(e.Content, "sessionId");
159
160 if (!this.sessions.TryGetValue(SessionId, out RemoteDesktopSession Session))
161 return;
162
163 Session.DeviceName = XML.Attribute(e.Content, "deviceName");
164 Session.BitsPerPixel = XML.Attribute(e.Content, "bitsPerPixel", 0);
165 Session.Left = XML.Attribute(e.Content, "left", 0);
166 Session.Top = XML.Attribute(e.Content, "top", 0);
167 Session.Width = XML.Attribute(e.Content, "width", 0);
168 Session.Height = XML.Attribute(e.Content, "height", 0);
169 Session.TileSize = XML.Attribute(e.Content, "tileSize", 0);
170
171 foreach (XmlNode N in e.Content.ChildNodes)
172 {
173 if (N is XmlElement E && E.LocalName == "screen" && E.NamespaceURI == RemoteDesktopNamespace)
174 {
175 string DeviceName2 = XML.Attribute(E, "deviceName");
176 int BitsPerPixel2 = XML.Attribute(E, "bitsPerPixel", 0);
177 int Left2 = XML.Attribute(E, "left", 0);
178 int Top2 = XML.Attribute(E, "top", 0);
179 int Width2 = XML.Attribute(E, "width", 0);
180 int Height2 = XML.Attribute(E, "height", 0);
181 bool Primary = XML.Attribute(E, "primary", false);
182
183 Screens.Add(new ScreenInfo(Primary, BitsPerPixel2, Left2, Top2, Width2, Height2, DeviceName2));
184 }
185 }
186
187 Session.Screens = Screens.ToArray();
188 await Session.SetState(RemoteDesktopSessionState.Started);
189
190 Log.Informational("Remote desktop session started.",
191 new KeyValuePair<string, object>("RemoteJid", Session.RemoteJid),
192 new KeyValuePair<string, object>("SessionId", Session.SessionId));
193 }
194
195 private async Task StoppedMessageHandler(object State, MessageEventArgs e)
196 {
197 string SessionId = XML.Attribute(e.Content, "sessionId");
198 string Reason = XML.Attribute(e.Content, "reason");
199
200 if (!this.sessions.TryGetValue(SessionId, out RemoteDesktopSession Session))
201 return;
202
203 await Session.SetState(RemoteDesktopSessionState.Stopped);
204 this.sessions.Remove(SessionId);
205
206 Log.Informational("Remote desktop session stopped.",
207 new KeyValuePair<string, object>("RemoteJid", Session.RemoteJid),
208 new KeyValuePair<string, object>("SessionId", Session.SessionId),
209 new KeyValuePair<string, object>("Reason", Reason));
210 }
211
212 private Task TileMessageHandler(object State, MessageEventArgs e)
213 {
214 RemoteDesktopSession Session = null;
215 string TileBase64 = e.Content.InnerText;
216 int X = 0;
217 int Y = 0;
218
219 foreach (XmlAttribute Attr in e.Content.Attributes)
220 {
221 switch (Attr.Name)
222 {
223 case "sessionId":
224 if (!this.sessions.TryGetValue(Attr.Value, out Session))
225 return Task.CompletedTask;
226 break;
227
228 case "x":
229 if (!int.TryParse(Attr.Value, out X))
230 return Task.CompletedTask;
231 break;
232
233 case "y":
234 if (!int.TryParse(Attr.Value, out Y))
235 return Task.CompletedTask;
236 break;
237 }
238 }
239
240 if (Session is null || TileBase64 is null)
241 return Task.CompletedTask;
242
243 Session.UpdateTile(X, Y, TileBase64);
244
245 return Task.CompletedTask;
246 }
247
248 private Task TilesMessageHandler(object State, MessageEventArgs e)
249 {
250 string SessionId = XML.Attribute(e.Content, "sessionId");
251 if (!this.sessions.TryGetValue(SessionId, out RemoteDesktopSession Session))
252 return Task.CompletedTask;
253
254 foreach (XmlNode N in e.Content.ChildNodes)
255 {
256 if (N is XmlElement E && E.LocalName == "tile" && E.NamespaceURI == e.Content.NamespaceURI)
257 {
258 string TileBase64 = E.InnerText;
259 int X = 0;
260 int Y = 0;
261
262 foreach (XmlAttribute Attr in E.Attributes)
263 {
264 switch (Attr.Name)
265 {
266 case "x":
267 if (!int.TryParse(Attr.Value, out X))
268 continue;
269 break;
270
271 case "y":
272 if (!int.TryParse(Attr.Value, out Y))
273 continue;
274 break;
275 }
276 }
277
278 Session.UpdateTile(X, Y, TileBase64);
279 }
280 }
281
282 return Task.CompletedTask;
283 }
284
285 private Task ScanCompleteHandler(object State, MessageEventArgs e)
286 {
287 string SessionId = XML.Attribute(e.Content, "sessionId");
288 if (!this.sessions.TryGetValue(SessionId, out RemoteDesktopSession Session))
289 return Task.CompletedTask;
290
291 return Session.ScanCompleted();
292 }
293 }
294}
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:334
Event arguments for message events.
XmlElement Content
Content of the message. For messages that are processed by registered message handlers,...
Class managing end-to-end encryption.
async Task StopSessionAsync(string To, string SessionId)
Stops a Remote Desktop session.
Task< RemoteDesktopSession > StartSessionAsync(string To)
Starts a Remote Desktop session.
async Task< RemoteDesktopSession > StartSessionAsync(string To, Guid SessionGuid)
Starts a Remote Desktop session.
override void Dispose()
Disposes of the extension.
RemoteDesktopClient(XmppClient Client, EndpointSecurity E2e)
Remote Desktop Client
const string RemoteDesktopNamespace
http://waher.se/rdp/1.0
EndpointSecurity E2E
End-to-end encryption
override string[] Extensions
Implemented extensions.
Maintains the client-side state of a Remote Desktop Session.
Information about a remote screen.
Definition: ScreenInfo.cs:9
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters a Message handler.
Definition: XmppClient.cs:2855
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers a Message handler.
Definition: XmppClient.cs:2828
Base class for XMPP Extensions.
XmppClient client
XMPP Client used by the extension.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
XmppClient Client
XMPP Client.
Implements an in-memory cache.
Definition: Cache.cs:15
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
RemoteDesktopSessionState
State of a Remote Desktop Session
E2ETransmission
End-to-end encryption mode.