diff options
author | UbitUmarov | 2015-09-01 11:43:07 +0100 |
---|---|---|
committer | UbitUmarov | 2015-09-01 11:43:07 +0100 |
commit | fb78b182520fc9bb0f971afd0322029c70278ea6 (patch) | |
tree | b4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Framework/Console/RemoteConsole.cs | |
parent | lixo (diff) | |
parent | Mantis #7713: fixed bug introduced by 1st MOSES patch. (diff) | |
download | opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2 opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz |
Merge remote-tracking branch 'os/master'
Diffstat (limited to 'OpenSim/Framework/Console/RemoteConsole.cs')
-rw-r--r-- | OpenSim/Framework/Console/RemoteConsole.cs | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs new file mode 100644 index 0000000..8ad7b0d --- /dev/null +++ b/OpenSim/Framework/Console/RemoteConsole.cs | |||
@@ -0,0 +1,513 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Xml; | ||
30 | using System.Collections; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Diagnostics; | ||
33 | using System.Reflection; | ||
34 | using System.Text; | ||
35 | using System.Text.RegularExpressions; | ||
36 | using System.Threading; | ||
37 | using OpenMetaverse; | ||
38 | using Nini.Config; | ||
39 | using OpenSim.Framework.Servers.HttpServer; | ||
40 | using log4net; | ||
41 | |||
42 | namespace OpenSim.Framework.Console | ||
43 | { | ||
44 | public class ConsoleConnection | ||
45 | { | ||
46 | public int last; | ||
47 | public long lastLineSeen; | ||
48 | public bool newConnection = true; | ||
49 | } | ||
50 | |||
51 | // A console that uses REST interfaces | ||
52 | // | ||
53 | public class RemoteConsole : CommandConsole | ||
54 | { | ||
55 | private IHttpServer m_Server = null; | ||
56 | private IConfigSource m_Config = null; | ||
57 | |||
58 | private List<string> m_Scrollback = new List<string>(); | ||
59 | private ManualResetEvent m_DataEvent = new ManualResetEvent(false); | ||
60 | private List<string> m_InputData = new List<string>(); | ||
61 | private long m_LineNumber = 0; | ||
62 | private Dictionary<UUID, ConsoleConnection> m_Connections = | ||
63 | new Dictionary<UUID, ConsoleConnection>(); | ||
64 | private string m_UserName = String.Empty; | ||
65 | private string m_Password = String.Empty; | ||
66 | private string m_AllowedOrigin = String.Empty; | ||
67 | |||
68 | public RemoteConsole(string defaultPrompt) : base(defaultPrompt) | ||
69 | { | ||
70 | } | ||
71 | |||
72 | public void ReadConfig(IConfigSource config) | ||
73 | { | ||
74 | m_Config = config; | ||
75 | |||
76 | IConfig netConfig = m_Config.Configs["Network"]; | ||
77 | if (netConfig == null) | ||
78 | return; | ||
79 | |||
80 | m_UserName = netConfig.GetString("ConsoleUser", String.Empty); | ||
81 | m_Password = netConfig.GetString("ConsolePass", String.Empty); | ||
82 | m_AllowedOrigin = netConfig.GetString("ConsoleAllowedOrigin", String.Empty); | ||
83 | } | ||
84 | |||
85 | public void SetServer(IHttpServer server) | ||
86 | { | ||
87 | m_Server = server; | ||
88 | |||
89 | m_Server.AddHTTPHandler("/StartSession/", HandleHttpStartSession); | ||
90 | m_Server.AddHTTPHandler("/CloseSession/", HandleHttpCloseSession); | ||
91 | m_Server.AddHTTPHandler("/SessionCommand/", HandleHttpSessionCommand); | ||
92 | } | ||
93 | |||
94 | public override void Output(string text, string level) | ||
95 | { | ||
96 | lock (m_Scrollback) | ||
97 | { | ||
98 | while (m_Scrollback.Count >= 1000) | ||
99 | m_Scrollback.RemoveAt(0); | ||
100 | m_LineNumber++; | ||
101 | m_Scrollback.Add(String.Format("{0}", m_LineNumber)+":"+level+":"+text); | ||
102 | } | ||
103 | FireOnOutput(text.Trim()); | ||
104 | System.Console.WriteLine(text.Trim()); | ||
105 | } | ||
106 | |||
107 | public override void Output(string text) | ||
108 | { | ||
109 | Output(text, "normal"); | ||
110 | } | ||
111 | |||
112 | public override string ReadLine(string p, bool isCommand, bool e) | ||
113 | { | ||
114 | if (isCommand) | ||
115 | Output("+++"+p); | ||
116 | else | ||
117 | Output("-++"+p); | ||
118 | |||
119 | m_DataEvent.WaitOne(); | ||
120 | |||
121 | string cmdinput; | ||
122 | |||
123 | lock (m_InputData) | ||
124 | { | ||
125 | if (m_InputData.Count == 0) | ||
126 | { | ||
127 | m_DataEvent.Reset(); | ||
128 | return ""; | ||
129 | } | ||
130 | |||
131 | cmdinput = m_InputData[0]; | ||
132 | m_InputData.RemoveAt(0); | ||
133 | if (m_InputData.Count == 0) | ||
134 | m_DataEvent.Reset(); | ||
135 | |||
136 | } | ||
137 | |||
138 | if (isCommand) | ||
139 | { | ||
140 | string[] cmd = Commands.Resolve(Parser.Parse(cmdinput)); | ||
141 | |||
142 | if (cmd.Length != 0) | ||
143 | { | ||
144 | int i; | ||
145 | |||
146 | for (i=0 ; i < cmd.Length ; i++) | ||
147 | { | ||
148 | if (cmd[i].Contains(" ")) | ||
149 | cmd[i] = "\"" + cmd[i] + "\""; | ||
150 | } | ||
151 | return String.Empty; | ||
152 | } | ||
153 | } | ||
154 | return cmdinput; | ||
155 | } | ||
156 | |||
157 | private Hashtable CheckOrigin(Hashtable result) | ||
158 | { | ||
159 | if (!string.IsNullOrEmpty(m_AllowedOrigin)) | ||
160 | result["access_control_allow_origin"] = m_AllowedOrigin; | ||
161 | return result; | ||
162 | } | ||
163 | /* TODO: Figure out how PollServiceHTTPHandler can access the request headers | ||
164 | * in order to use m_AllowedOrigin as a regular expression | ||
165 | private Hashtable CheckOrigin(Hashtable headers, Hashtable result) | ||
166 | { | ||
167 | if (!string.IsNullOrEmpty(m_AllowedOrigin)) | ||
168 | { | ||
169 | if (headers.ContainsKey("origin")) | ||
170 | { | ||
171 | string origin = headers["origin"].ToString(); | ||
172 | if (Regex.IsMatch(origin, m_AllowedOrigin)) | ||
173 | result["access_control_allow_origin"] = origin; | ||
174 | } | ||
175 | } | ||
176 | return result; | ||
177 | } | ||
178 | */ | ||
179 | |||
180 | private void DoExpire() | ||
181 | { | ||
182 | List<UUID> expired = new List<UUID>(); | ||
183 | |||
184 | lock (m_Connections) | ||
185 | { | ||
186 | foreach (KeyValuePair<UUID, ConsoleConnection> kvp in m_Connections) | ||
187 | { | ||
188 | if (System.Environment.TickCount - kvp.Value.last > 500000) | ||
189 | expired.Add(kvp.Key); | ||
190 | } | ||
191 | |||
192 | foreach (UUID id in expired) | ||
193 | { | ||
194 | m_Connections.Remove(id); | ||
195 | CloseConnection(id); | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | private Hashtable HandleHttpStartSession(Hashtable request) | ||
201 | { | ||
202 | DoExpire(); | ||
203 | |||
204 | Hashtable post = DecodePostString(request["body"].ToString()); | ||
205 | Hashtable reply = new Hashtable(); | ||
206 | |||
207 | reply["str_response_string"] = ""; | ||
208 | reply["int_response_code"] = 401; | ||
209 | reply["content_type"] = "text/plain"; | ||
210 | |||
211 | if (m_UserName == String.Empty) | ||
212 | return reply; | ||
213 | |||
214 | if (post["USER"] == null || post["PASS"] == null) | ||
215 | return reply; | ||
216 | |||
217 | if (m_UserName != post["USER"].ToString() || | ||
218 | m_Password != post["PASS"].ToString()) | ||
219 | { | ||
220 | return reply; | ||
221 | } | ||
222 | |||
223 | ConsoleConnection c = new ConsoleConnection(); | ||
224 | c.last = System.Environment.TickCount; | ||
225 | c.lastLineSeen = 0; | ||
226 | |||
227 | UUID sessionID = UUID.Random(); | ||
228 | |||
229 | lock (m_Connections) | ||
230 | { | ||
231 | m_Connections[sessionID] = c; | ||
232 | } | ||
233 | |||
234 | string uri = "/ReadResponses/" + sessionID.ToString() + "/"; | ||
235 | |||
236 | m_Server.AddPollServiceHTTPHandler( | ||
237 | uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout | ||
238 | |||
239 | XmlDocument xmldoc = new XmlDocument(); | ||
240 | XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, | ||
241 | "", ""); | ||
242 | |||
243 | xmldoc.AppendChild(xmlnode); | ||
244 | XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", | ||
245 | ""); | ||
246 | |||
247 | xmldoc.AppendChild(rootElement); | ||
248 | |||
249 | XmlElement id = xmldoc.CreateElement("", "SessionID", ""); | ||
250 | id.AppendChild(xmldoc.CreateTextNode(sessionID.ToString())); | ||
251 | |||
252 | rootElement.AppendChild(id); | ||
253 | |||
254 | XmlElement prompt = xmldoc.CreateElement("", "Prompt", ""); | ||
255 | prompt.AppendChild(xmldoc.CreateTextNode(DefaultPrompt)); | ||
256 | |||
257 | rootElement.AppendChild(prompt); | ||
258 | |||
259 | rootElement.AppendChild(MainConsole.Instance.Commands.GetXml(xmldoc)); | ||
260 | |||
261 | reply["str_response_string"] = xmldoc.InnerXml; | ||
262 | reply["int_response_code"] = 200; | ||
263 | reply["content_type"] = "text/xml"; | ||
264 | reply = CheckOrigin(reply); | ||
265 | |||
266 | return reply; | ||
267 | } | ||
268 | |||
269 | private Hashtable HandleHttpCloseSession(Hashtable request) | ||
270 | { | ||
271 | DoExpire(); | ||
272 | |||
273 | Hashtable post = DecodePostString(request["body"].ToString()); | ||
274 | Hashtable reply = new Hashtable(); | ||
275 | |||
276 | reply["str_response_string"] = ""; | ||
277 | reply["int_response_code"] = 404; | ||
278 | reply["content_type"] = "text/plain"; | ||
279 | |||
280 | if (post["ID"] == null) | ||
281 | return reply; | ||
282 | |||
283 | UUID id; | ||
284 | if (!UUID.TryParse(post["ID"].ToString(), out id)) | ||
285 | return reply; | ||
286 | |||
287 | lock (m_Connections) | ||
288 | { | ||
289 | if (m_Connections.ContainsKey(id)) | ||
290 | { | ||
291 | m_Connections.Remove(id); | ||
292 | CloseConnection(id); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | XmlDocument xmldoc = new XmlDocument(); | ||
297 | XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, | ||
298 | "", ""); | ||
299 | |||
300 | xmldoc.AppendChild(xmlnode); | ||
301 | XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", | ||
302 | ""); | ||
303 | |||
304 | xmldoc.AppendChild(rootElement); | ||
305 | |||
306 | XmlElement res = xmldoc.CreateElement("", "Result", ""); | ||
307 | res.AppendChild(xmldoc.CreateTextNode("OK")); | ||
308 | |||
309 | rootElement.AppendChild(res); | ||
310 | |||
311 | reply["str_response_string"] = xmldoc.InnerXml; | ||
312 | reply["int_response_code"] = 200; | ||
313 | reply["content_type"] = "text/xml"; | ||
314 | reply = CheckOrigin(reply); | ||
315 | |||
316 | return reply; | ||
317 | } | ||
318 | |||
319 | private Hashtable HandleHttpSessionCommand(Hashtable request) | ||
320 | { | ||
321 | DoExpire(); | ||
322 | |||
323 | Hashtable post = DecodePostString(request["body"].ToString()); | ||
324 | Hashtable reply = new Hashtable(); | ||
325 | |||
326 | reply["str_response_string"] = ""; | ||
327 | reply["int_response_code"] = 404; | ||
328 | reply["content_type"] = "text/plain"; | ||
329 | |||
330 | if (post["ID"] == null) | ||
331 | return reply; | ||
332 | |||
333 | UUID id; | ||
334 | if (!UUID.TryParse(post["ID"].ToString(), out id)) | ||
335 | return reply; | ||
336 | |||
337 | lock (m_Connections) | ||
338 | { | ||
339 | if (!m_Connections.ContainsKey(id)) | ||
340 | return reply; | ||
341 | } | ||
342 | |||
343 | if (post["COMMAND"] == null) | ||
344 | return reply; | ||
345 | |||
346 | lock (m_InputData) | ||
347 | { | ||
348 | m_DataEvent.Set(); | ||
349 | m_InputData.Add(post["COMMAND"].ToString()); | ||
350 | } | ||
351 | |||
352 | XmlDocument xmldoc = new XmlDocument(); | ||
353 | XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, | ||
354 | "", ""); | ||
355 | |||
356 | xmldoc.AppendChild(xmlnode); | ||
357 | XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", | ||
358 | ""); | ||
359 | |||
360 | xmldoc.AppendChild(rootElement); | ||
361 | |||
362 | XmlElement res = xmldoc.CreateElement("", "Result", ""); | ||
363 | res.AppendChild(xmldoc.CreateTextNode("OK")); | ||
364 | |||
365 | rootElement.AppendChild(res); | ||
366 | |||
367 | reply["str_response_string"] = xmldoc.InnerXml; | ||
368 | reply["int_response_code"] = 200; | ||
369 | reply["content_type"] = "text/xml"; | ||
370 | reply = CheckOrigin(reply); | ||
371 | |||
372 | return reply; | ||
373 | } | ||
374 | |||
375 | private Hashtable DecodePostString(string data) | ||
376 | { | ||
377 | Hashtable result = new Hashtable(); | ||
378 | |||
379 | string[] terms = data.Split(new char[] {'&'}); | ||
380 | |||
381 | foreach (string term in terms) | ||
382 | { | ||
383 | string[] elems = term.Split(new char[] {'='}); | ||
384 | if (elems.Length == 0) | ||
385 | continue; | ||
386 | |||
387 | string name = System.Web.HttpUtility.UrlDecode(elems[0]); | ||
388 | string value = String.Empty; | ||
389 | |||
390 | if (elems.Length > 1) | ||
391 | value = System.Web.HttpUtility.UrlDecode(elems[1]); | ||
392 | |||
393 | result[name] = value; | ||
394 | } | ||
395 | |||
396 | return result; | ||
397 | } | ||
398 | |||
399 | public void CloseConnection(UUID id) | ||
400 | { | ||
401 | try | ||
402 | { | ||
403 | string uri = "/ReadResponses/" + id.ToString() + "/"; | ||
404 | |||
405 | m_Server.RemovePollServiceHTTPHandler("", uri); | ||
406 | } | ||
407 | catch (Exception) | ||
408 | { | ||
409 | } | ||
410 | } | ||
411 | |||
412 | private bool HasEvents(UUID RequestID, UUID sessionID) | ||
413 | { | ||
414 | ConsoleConnection c = null; | ||
415 | |||
416 | lock (m_Connections) | ||
417 | { | ||
418 | if (!m_Connections.ContainsKey(sessionID)) | ||
419 | return false; | ||
420 | c = m_Connections[sessionID]; | ||
421 | } | ||
422 | c.last = System.Environment.TickCount; | ||
423 | if (c.lastLineSeen < m_LineNumber) | ||
424 | return true; | ||
425 | return false; | ||
426 | } | ||
427 | |||
428 | private Hashtable GetEvents(UUID RequestID, UUID sessionID) | ||
429 | { | ||
430 | ConsoleConnection c = null; | ||
431 | |||
432 | lock (m_Connections) | ||
433 | { | ||
434 | if (!m_Connections.ContainsKey(sessionID)) | ||
435 | return NoEvents(RequestID, UUID.Zero); | ||
436 | c = m_Connections[sessionID]; | ||
437 | } | ||
438 | c.last = System.Environment.TickCount; | ||
439 | if (c.lastLineSeen >= m_LineNumber) | ||
440 | return NoEvents(RequestID, UUID.Zero); | ||
441 | |||
442 | Hashtable result = new Hashtable(); | ||
443 | |||
444 | XmlDocument xmldoc = new XmlDocument(); | ||
445 | XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, | ||
446 | "", ""); | ||
447 | |||
448 | xmldoc.AppendChild(xmlnode); | ||
449 | XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", | ||
450 | ""); | ||
451 | |||
452 | if (c.newConnection) | ||
453 | { | ||
454 | c.newConnection = false; | ||
455 | Output("+++" + DefaultPrompt); | ||
456 | } | ||
457 | |||
458 | lock (m_Scrollback) | ||
459 | { | ||
460 | long startLine = m_LineNumber - m_Scrollback.Count; | ||
461 | long sendStart = startLine; | ||
462 | if (sendStart < c.lastLineSeen) | ||
463 | sendStart = c.lastLineSeen; | ||
464 | |||
465 | for (long i = sendStart ; i < m_LineNumber ; i++) | ||
466 | { | ||
467 | XmlElement res = xmldoc.CreateElement("", "Line", ""); | ||
468 | long line = i + 1; | ||
469 | res.SetAttribute("Number", line.ToString()); | ||
470 | res.AppendChild(xmldoc.CreateTextNode(m_Scrollback[(int)(i - startLine)])); | ||
471 | |||
472 | rootElement.AppendChild(res); | ||
473 | } | ||
474 | } | ||
475 | c.lastLineSeen = m_LineNumber; | ||
476 | |||
477 | xmldoc.AppendChild(rootElement); | ||
478 | |||
479 | result["str_response_string"] = xmldoc.InnerXml; | ||
480 | result["int_response_code"] = 200; | ||
481 | result["content_type"] = "application/xml"; | ||
482 | result["keepalive"] = false; | ||
483 | result["reusecontext"] = false; | ||
484 | result = CheckOrigin(result); | ||
485 | |||
486 | return result; | ||
487 | } | ||
488 | |||
489 | private Hashtable NoEvents(UUID RequestID, UUID id) | ||
490 | { | ||
491 | Hashtable result = new Hashtable(); | ||
492 | |||
493 | XmlDocument xmldoc = new XmlDocument(); | ||
494 | XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, | ||
495 | "", ""); | ||
496 | |||
497 | xmldoc.AppendChild(xmlnode); | ||
498 | XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", | ||
499 | ""); | ||
500 | |||
501 | xmldoc.AppendChild(rootElement); | ||
502 | |||
503 | result["str_response_string"] = xmldoc.InnerXml; | ||
504 | result["int_response_code"] = 200; | ||
505 | result["content_type"] = "text/xml"; | ||
506 | result["keepalive"] = false; | ||
507 | result["reusecontext"] = false; | ||
508 | result = CheckOrigin(result); | ||
509 | |||
510 | return result; | ||
511 | } | ||
512 | } | ||
513 | } | ||