aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Console
diff options
context:
space:
mode:
authorUbitUmarov2015-09-01 11:43:07 +0100
committerUbitUmarov2015-09-01 11:43:07 +0100
commitfb78b182520fc9bb0f971afd0322029c70278ea6 (patch)
treeb4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Framework/Console
parentlixo (diff)
parentMantis #7713: fixed bug introduced by 1st MOSES patch. (diff)
downloadopensim-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 '')
-rw-r--r--OpenSim/Framework/Console/AssemblyInfo.cs58
-rw-r--r--OpenSim/Framework/Console/CommandConsole.cs793
-rwxr-xr-xOpenSim/Framework/Console/ConsoleBase.cs180
-rw-r--r--OpenSim/Framework/Console/ConsoleDisplayList.cs112
-rw-r--r--OpenSim/Framework/Console/ConsoleDisplayTable.cs155
-rw-r--r--OpenSim/Framework/Console/ConsoleDisplayUtil.cs48
-rwxr-xr-xOpenSim/Framework/Console/ConsolePluginCommand.cs139
-rw-r--r--OpenSim/Framework/Console/ConsoleUtil.cs387
-rw-r--r--OpenSim/Framework/Console/LocalConsole.cs585
-rw-r--r--OpenSim/Framework/Console/MockConsole.cs89
-rw-r--r--OpenSim/Framework/Console/OpenSimAppender.cs86
-rw-r--r--OpenSim/Framework/Console/RemoteConsole.cs513
12 files changed, 3145 insertions, 0 deletions
diff --git a/OpenSim/Framework/Console/AssemblyInfo.cs b/OpenSim/Framework/Console/AssemblyInfo.cs
new file mode 100644
index 0000000..67af471
--- /dev/null
+++ b/OpenSim/Framework/Console/AssemblyInfo.cs
@@ -0,0 +1,58 @@
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
28using System.Reflection;
29using System.Runtime.InteropServices;
30
31// Information about this assembly is defined by the following
32// attributes.
33//
34// change them to the information which is associated with the assembly
35// you compile.
36
37[assembly : AssemblyTitle("ServerConsole")]
38[assembly : AssemblyDescription("")]
39[assembly: AssemblyConfiguration("")]
40[assembly: AssemblyCompany("http://opensimulator.org")]
41[assembly : AssemblyProduct("ServerConsole")]
42[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")]
43[assembly: AssemblyTrademark("")]
44[assembly: AssemblyCulture("")]
45
46// This sets the default COM visibility of types in the assembly to invisible.
47// If you need to expose a type to COM, use [ComVisible(true)] on that type.
48
49[assembly : ComVisible(false)]
50
51// The assembly version has following format :
52//
53// Major.Minor.Build.Revision
54//
55// You can specify all values by your own or you can build default build and revision
56// numbers with the '*' character (the default):
57
58[assembly : AssemblyVersion("0.8.2.*")]
diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs
new file mode 100644
index 0000000..0f68afe
--- /dev/null
+++ b/OpenSim/Framework/Console/CommandConsole.cs
@@ -0,0 +1,793 @@
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
28using System;
29using System.Xml;
30using System.Collections.Generic;
31using System.Diagnostics;
32using System.Linq;
33using System.Reflection;
34using System.Text;
35using System.Text.RegularExpressions;
36using System.Threading;
37using log4net;
38using OpenSim.Framework;
39
40namespace OpenSim.Framework.Console
41{
42 public class Commands : ICommands
43 {
44// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45
46 /// <summary>
47 /// Encapsulates a command that can be invoked from the console
48 /// </summary>
49 private class CommandInfo
50 {
51 /// <value>
52 /// The module from which this command comes
53 /// </value>
54 public string module;
55
56 /// <value>
57 /// Whether the module is shared
58 /// </value>
59 public bool shared;
60
61 /// <value>
62 /// Very short BNF description
63 /// </value>
64 public string help_text;
65
66 /// <value>
67 /// Longer one line help text
68 /// </value>
69 public string long_help;
70
71 /// <value>
72 /// Full descriptive help for this command
73 /// </value>
74 public string descriptive_help;
75
76 /// <value>
77 /// The method to invoke for this command
78 /// </value>
79 public List<CommandDelegate> fn;
80 }
81
82 public const string GeneralHelpText
83 = "To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n";
84
85 public const string ItemHelpText
86= @"For more information, type 'help all' to get a list of all commands,
87 or type help <item>' where <item> is one of the following:";
88
89 /// <value>
90 /// Commands organized by keyword in a tree
91 /// </value>
92 private Dictionary<string, object> tree =
93 new Dictionary<string, object>();
94
95 /// <summary>
96 /// Commands organized by module
97 /// </summary>
98 private Dictionary<string, List<CommandInfo>> m_modulesCommands = new Dictionary<string, List<CommandInfo>>();
99
100 /// <summary>
101 /// Get help for the given help string
102 /// </summary>
103 /// <param name="helpParts">Parsed parts of the help string. If empty then general help is returned.</param>
104 /// <returns></returns>
105 public List<string> GetHelp(string[] cmd)
106 {
107 List<string> help = new List<string>();
108 List<string> helpParts = new List<string>(cmd);
109
110 // Remove initial help keyword
111 helpParts.RemoveAt(0);
112
113 help.Add(""); // Will become a newline.
114
115 // General help
116 if (helpParts.Count == 0)
117 {
118 help.Add(GeneralHelpText);
119 help.Add(ItemHelpText);
120 help.AddRange(CollectModulesHelp(tree));
121 }
122 else if (helpParts.Count == 1 && helpParts[0] == "all")
123 {
124 help.AddRange(CollectAllCommandsHelp());
125 }
126 else
127 {
128 help.AddRange(CollectHelp(helpParts));
129 }
130
131 help.Add(""); // Will become a newline.
132
133 return help;
134 }
135
136 /// <summary>
137 /// Collects the help from all commands and return in alphabetical order.
138 /// </summary>
139 /// <returns></returns>
140 private List<string> CollectAllCommandsHelp()
141 {
142 List<string> help = new List<string>();
143
144 lock (m_modulesCommands)
145 {
146 foreach (List<CommandInfo> commands in m_modulesCommands.Values)
147 {
148 var ourHelpText = commands.ConvertAll(c => string.Format("{0} - {1}", c.help_text, c.long_help));
149 help.AddRange(ourHelpText);
150 }
151 }
152
153 help.Sort();
154
155 return help;
156 }
157
158 /// <summary>
159 /// See if we can find the requested command in order to display longer help
160 /// </summary>
161 /// <param name="helpParts"></param>
162 /// <returns></returns>
163 private List<string> CollectHelp(List<string> helpParts)
164 {
165 string originalHelpRequest = string.Join(" ", helpParts.ToArray());
166 List<string> help = new List<string>();
167
168 // Check modules first to see if we just need to display a list of those commands
169 if (TryCollectModuleHelp(originalHelpRequest, help))
170 {
171 help.Insert(0, ItemHelpText);
172 return help;
173 }
174
175 Dictionary<string, object> dict = tree;
176 while (helpParts.Count > 0)
177 {
178 string helpPart = helpParts[0];
179
180 if (!dict.ContainsKey(helpPart))
181 break;
182
183 //m_log.Debug("Found {0}", helpParts[0]);
184
185 if (dict[helpPart] is Dictionary<string, Object>)
186 dict = (Dictionary<string, object>)dict[helpPart];
187
188 helpParts.RemoveAt(0);
189 }
190
191 // There was a command for the given help string
192 if (dict.ContainsKey(String.Empty))
193 {
194 CommandInfo commandInfo = (CommandInfo)dict[String.Empty];
195 help.Add(commandInfo.help_text);
196 help.Add(commandInfo.long_help);
197
198 string descriptiveHelp = commandInfo.descriptive_help;
199
200 // If we do have some descriptive help then insert a spacing line before for readability.
201 if (descriptiveHelp != string.Empty)
202 help.Add(string.Empty);
203
204 help.Add(commandInfo.descriptive_help);
205 }
206 else
207 {
208 help.Add(string.Format("No help is available for {0}", originalHelpRequest));
209 }
210
211 return help;
212 }
213
214 /// <summary>
215 /// Try to collect help for the given module if that module exists.
216 /// </summary>
217 /// <param name="moduleName"></param>
218 /// <param name="helpText">/param>
219 /// <returns>true if there was the module existed, false otherwise.</returns>
220 private bool TryCollectModuleHelp(string moduleName, List<string> helpText)
221 {
222 lock (m_modulesCommands)
223 {
224 foreach (string key in m_modulesCommands.Keys)
225 {
226 // Allow topic help requests to succeed whether they are upper or lowercase.
227 if (moduleName.ToLower() == key.ToLower())
228 {
229 List<CommandInfo> commands = m_modulesCommands[key];
230 var ourHelpText = commands.ConvertAll(c => string.Format("{0} - {1}", c.help_text, c.long_help));
231 ourHelpText.Sort();
232 helpText.AddRange(ourHelpText);
233
234 return true;
235 }
236 }
237
238 return false;
239 }
240 }
241
242 private List<string> CollectModulesHelp(Dictionary<string, object> dict)
243 {
244 lock (m_modulesCommands)
245 {
246 List<string> helpText = new List<string>(m_modulesCommands.Keys);
247 helpText.Sort();
248 return helpText;
249 }
250 }
251
252// private List<string> CollectHelp(Dictionary<string, object> dict)
253// {
254// List<string> result = new List<string>();
255//
256// foreach (KeyValuePair<string, object> kvp in dict)
257// {
258// if (kvp.Value is Dictionary<string, Object>)
259// {
260// result.AddRange(CollectHelp((Dictionary<string, Object>)kvp.Value));
261// }
262// else
263// {
264// if (((CommandInfo)kvp.Value).long_help != String.Empty)
265// result.Add(((CommandInfo)kvp.Value).help_text+" - "+
266// ((CommandInfo)kvp.Value).long_help);
267// }
268// }
269// return result;
270// }
271
272 /// <summary>
273 /// Add a command to those which can be invoked from the console.
274 /// </summary>
275 /// <param name="module"></param>
276 /// <param name="command"></param>
277 /// <param name="help"></param>
278 /// <param name="longhelp"></param>
279 /// <param name="fn"></param>
280 public void AddCommand(string module, bool shared, string command,
281 string help, string longhelp, CommandDelegate fn)
282 {
283 AddCommand(module, shared, command, help, longhelp, String.Empty, fn);
284 }
285
286 /// <summary>
287 /// Add a command to those which can be invoked from the console.
288 /// </summary>
289 /// <param name="module"></param>
290 /// <param name="command"></param>
291 /// <param name="help"></param>
292 /// <param name="longhelp"></param>
293 /// <param name="descriptivehelp"></param>
294 /// <param name="fn"></param>
295 public void AddCommand(string module, bool shared, string command,
296 string help, string longhelp, string descriptivehelp,
297 CommandDelegate fn)
298 {
299 string[] parts = Parser.Parse(command);
300
301 Dictionary<string, Object> current = tree;
302
303 foreach (string part in parts)
304 {
305 if (current.ContainsKey(part))
306 {
307 if (current[part] is Dictionary<string, Object>)
308 current = (Dictionary<string, Object>)current[part];
309 else
310 return;
311 }
312 else
313 {
314 current[part] = new Dictionary<string, Object>();
315 current = (Dictionary<string, Object>)current[part];
316 }
317 }
318
319 CommandInfo info;
320
321 if (current.ContainsKey(String.Empty))
322 {
323 info = (CommandInfo)current[String.Empty];
324 if (!info.shared && !info.fn.Contains(fn))
325 info.fn.Add(fn);
326
327 return;
328 }
329
330 info = new CommandInfo();
331 info.module = module;
332 info.shared = shared;
333 info.help_text = help;
334 info.long_help = longhelp;
335 info.descriptive_help = descriptivehelp;
336 info.fn = new List<CommandDelegate>();
337 info.fn.Add(fn);
338 current[String.Empty] = info;
339
340 // Now add command to modules dictionary
341 lock (m_modulesCommands)
342 {
343 List<CommandInfo> commands;
344 if (m_modulesCommands.ContainsKey(module))
345 {
346 commands = m_modulesCommands[module];
347 }
348 else
349 {
350 commands = new List<CommandInfo>();
351 m_modulesCommands[module] = commands;
352 }
353
354// m_log.DebugFormat("[COMMAND CONSOLE]: Adding to category {0} command {1}", module, command);
355 commands.Add(info);
356 }
357 }
358
359 public string[] FindNextOption(string[] cmd, bool term)
360 {
361 Dictionary<string, object> current = tree;
362
363 int remaining = cmd.Length;
364
365 foreach (string s in cmd)
366 {
367 remaining--;
368
369 List<string> found = new List<string>();
370
371 foreach (string opt in current.Keys)
372 {
373 if (remaining > 0 && opt == s)
374 {
375 found.Clear();
376 found.Add(opt);
377 break;
378 }
379 if (opt.StartsWith(s))
380 {
381 found.Add(opt);
382 }
383 }
384
385 if (found.Count == 1 && (remaining != 0 || term))
386 {
387 current = (Dictionary<string, object>)current[found[0]];
388 }
389 else if (found.Count > 0)
390 {
391 return found.ToArray();
392 }
393 else
394 {
395 break;
396// return new string[] {"<cr>"};
397 }
398 }
399
400 if (current.Count > 1)
401 {
402 List<string> choices = new List<string>();
403
404 bool addcr = false;
405 foreach (string s in current.Keys)
406 {
407 if (s == String.Empty)
408 {
409 CommandInfo ci = (CommandInfo)current[String.Empty];
410 if (ci.fn.Count != 0)
411 addcr = true;
412 }
413 else
414 choices.Add(s);
415 }
416 if (addcr)
417 choices.Add("<cr>");
418 return choices.ToArray();
419 }
420
421 if (current.ContainsKey(String.Empty))
422 return new string[] { "Command help: "+((CommandInfo)current[String.Empty]).help_text};
423
424 return new string[] { new List<string>(current.Keys)[0] };
425 }
426
427 private CommandInfo ResolveCommand(string[] cmd, out string[] result)
428 {
429 result = cmd;
430 int index = -1;
431
432 Dictionary<string, object> current = tree;
433
434 foreach (string s in cmd)
435 {
436 index++;
437
438 List<string> found = new List<string>();
439
440 foreach (string opt in current.Keys)
441 {
442 if (opt == s)
443 {
444 found.Clear();
445 found.Add(opt);
446 break;
447 }
448 if (opt.StartsWith(s))
449 {
450 found.Add(opt);
451 }
452 }
453
454 if (found.Count == 1)
455 {
456 result[index] = found[0];
457 current = (Dictionary<string, object>)current[found[0]];
458 }
459 else if (found.Count > 0)
460 {
461 return null;
462 }
463 else
464 {
465 break;
466 }
467 }
468
469 if (current.ContainsKey(String.Empty))
470 return (CommandInfo)current[String.Empty];
471
472 return null;
473 }
474
475 public bool HasCommand(string command)
476 {
477 string[] result;
478 return ResolveCommand(Parser.Parse(command), out result) != null;
479 }
480
481 public string[] Resolve(string[] cmd)
482 {
483 string[] result;
484 CommandInfo ci = ResolveCommand(cmd, out result);
485
486 if (ci == null)
487 return new string[0];
488
489 if (ci.fn.Count == 0)
490 return new string[0];
491
492 foreach (CommandDelegate fn in ci.fn)
493 {
494 if (fn != null)
495 fn(ci.module, result);
496 else
497 return new string[0];
498 }
499
500 return result;
501 }
502
503 public XmlElement GetXml(XmlDocument doc)
504 {
505 CommandInfo help = (CommandInfo)((Dictionary<string, object>)tree["help"])[String.Empty];
506 ((Dictionary<string, object>)tree["help"]).Remove(string.Empty);
507 if (((Dictionary<string, object>)tree["help"]).Count == 0)
508 tree.Remove("help");
509
510 CommandInfo quit = (CommandInfo)((Dictionary<string, object>)tree["quit"])[String.Empty];
511 ((Dictionary<string, object>)tree["quit"]).Remove(string.Empty);
512 if (((Dictionary<string, object>)tree["quit"]).Count == 0)
513 tree.Remove("quit");
514
515 XmlElement root = doc.CreateElement("", "HelpTree", "");
516
517 ProcessTreeLevel(tree, root, doc);
518
519 if (!tree.ContainsKey("help"))
520 tree["help"] = (object) new Dictionary<string, object>();
521 ((Dictionary<string, object>)tree["help"])[String.Empty] = help;
522
523 if (!tree.ContainsKey("quit"))
524 tree["quit"] = (object) new Dictionary<string, object>();
525 ((Dictionary<string, object>)tree["quit"])[String.Empty] = quit;
526
527 return root;
528 }
529
530 private void ProcessTreeLevel(Dictionary<string, object> level, XmlElement xml, XmlDocument doc)
531 {
532 foreach (KeyValuePair<string, object> kvp in level)
533 {
534 if (kvp.Value is Dictionary<string, Object>)
535 {
536 XmlElement next = doc.CreateElement("", "Level", "");
537 next.SetAttribute("Name", kvp.Key);
538
539 xml.AppendChild(next);
540
541 ProcessTreeLevel((Dictionary<string, object>)kvp.Value, next, doc);
542 }
543 else
544 {
545 CommandInfo c = (CommandInfo)kvp.Value;
546
547 XmlElement cmd = doc.CreateElement("", "Command", "");
548
549 XmlElement e;
550
551 e = doc.CreateElement("", "Module", "");
552 cmd.AppendChild(e);
553 e.AppendChild(doc.CreateTextNode(c.module));
554
555 e = doc.CreateElement("", "Shared", "");
556 cmd.AppendChild(e);
557 e.AppendChild(doc.CreateTextNode(c.shared.ToString()));
558
559 e = doc.CreateElement("", "HelpText", "");
560 cmd.AppendChild(e);
561 e.AppendChild(doc.CreateTextNode(c.help_text));
562
563 e = doc.CreateElement("", "LongHelp", "");
564 cmd.AppendChild(e);
565 e.AppendChild(doc.CreateTextNode(c.long_help));
566
567 e = doc.CreateElement("", "Description", "");
568 cmd.AppendChild(e);
569 e.AppendChild(doc.CreateTextNode(c.descriptive_help));
570
571 xml.AppendChild(cmd);
572 }
573 }
574 }
575
576 public void FromXml(XmlElement root, CommandDelegate fn)
577 {
578 CommandInfo help = (CommandInfo)((Dictionary<string, object>)tree["help"])[String.Empty];
579 ((Dictionary<string, object>)tree["help"]).Remove(string.Empty);
580 if (((Dictionary<string, object>)tree["help"]).Count == 0)
581 tree.Remove("help");
582
583 CommandInfo quit = (CommandInfo)((Dictionary<string, object>)tree["quit"])[String.Empty];
584 ((Dictionary<string, object>)tree["quit"]).Remove(string.Empty);
585 if (((Dictionary<string, object>)tree["quit"]).Count == 0)
586 tree.Remove("quit");
587
588 tree.Clear();
589
590 ReadTreeLevel(tree, root, fn);
591
592 if (!tree.ContainsKey("help"))
593 tree["help"] = (object) new Dictionary<string, object>();
594 ((Dictionary<string, object>)tree["help"])[String.Empty] = help;
595
596 if (!tree.ContainsKey("quit"))
597 tree["quit"] = (object) new Dictionary<string, object>();
598 ((Dictionary<string, object>)tree["quit"])[String.Empty] = quit;
599 }
600
601 private void ReadTreeLevel(Dictionary<string, object> level, XmlNode node, CommandDelegate fn)
602 {
603 Dictionary<string, object> next;
604 string name;
605
606 XmlNodeList nodeL = node.ChildNodes;
607 XmlNodeList cmdL;
608 CommandInfo c;
609
610 foreach (XmlNode part in nodeL)
611 {
612 switch (part.Name)
613 {
614 case "Level":
615 name = ((XmlElement)part).GetAttribute("Name");
616 next = new Dictionary<string, object>();
617 level[name] = next;
618 ReadTreeLevel(next, part, fn);
619 break;
620 case "Command":
621 cmdL = part.ChildNodes;
622 c = new CommandInfo();
623 foreach (XmlNode cmdPart in cmdL)
624 {
625 switch (cmdPart.Name)
626 {
627 case "Module":
628 c.module = cmdPart.InnerText;
629 break;
630 case "Shared":
631 c.shared = Convert.ToBoolean(cmdPart.InnerText);
632 break;
633 case "HelpText":
634 c.help_text = cmdPart.InnerText;
635 break;
636 case "LongHelp":
637 c.long_help = cmdPart.InnerText;
638 break;
639 case "Description":
640 c.descriptive_help = cmdPart.InnerText;
641 break;
642 }
643 }
644 c.fn = new List<CommandDelegate>();
645 c.fn.Add(fn);
646 level[String.Empty] = c;
647 break;
648 }
649 }
650 }
651 }
652
653 public class Parser
654 {
655 // If an unquoted portion ends with an element matching this regex
656 // and the next element contains a space, then we have stripped
657 // embedded quotes that should not have been stripped
658 private static Regex optionRegex = new Regex("^--[a-zA-Z0-9-]+=$");
659
660 public static string[] Parse(string text)
661 {
662 List<string> result = new List<string>();
663
664 int index;
665
666 string[] unquoted = text.Split(new char[] {'"'});
667
668 for (index = 0 ; index < unquoted.Length ; index++)
669 {
670 if (index % 2 == 0)
671 {
672 string[] words = unquoted[index].Split(new char[] {' '});
673
674 bool option = false;
675 foreach (string w in words)
676 {
677 if (w != String.Empty)
678 {
679 if (optionRegex.Match(w) == Match.Empty)
680 option = false;
681 else
682 option = true;
683 result.Add(w);
684 }
685 }
686 // The last item matched the regex, put the quotes back
687 if (option)
688 {
689 // If the line ended with it, don't do anything
690 if (index < (unquoted.Length - 1))
691 {
692 // Get and remove the option name
693 string optionText = result[result.Count - 1];
694 result.RemoveAt(result.Count - 1);
695
696 // Add the quoted value back
697 optionText += "\"" + unquoted[index + 1] + "\"";
698
699 // Push the result into our return array
700 result.Add(optionText);
701
702 // Skip the already used value
703 index++;
704 }
705 }
706 }
707 else
708 {
709 result.Add(unquoted[index]);
710 }
711 }
712
713 return result.ToArray();
714 }
715 }
716
717 /// <summary>
718 /// A console that processes commands internally
719 /// </summary>
720 public class CommandConsole : ConsoleBase, ICommandConsole
721 {
722// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
723
724 public event OnOutputDelegate OnOutput;
725
726 public ICommands Commands { get; private set; }
727
728 public CommandConsole(string defaultPrompt) : base(defaultPrompt)
729 {
730 Commands = new Commands();
731
732 Commands.AddCommand(
733 "Help", false, "help", "help [<item>]",
734 "Display help on a particular command or on a list of commands in a category", Help);
735 }
736
737 private void Help(string module, string[] cmd)
738 {
739 List<string> help = Commands.GetHelp(cmd);
740
741 foreach (string s in help)
742 Output(s);
743 }
744
745 protected void FireOnOutput(string text)
746 {
747 OnOutputDelegate onOutput = OnOutput;
748 if (onOutput != null)
749 onOutput(text);
750 }
751
752 /// <summary>
753 /// Display a command prompt on the console and wait for user input
754 /// </summary>
755 public void Prompt()
756 {
757 string line = ReadLine(DefaultPrompt + "# ", true, true);
758
759 if (line != String.Empty)
760 Output("Invalid command");
761 }
762
763 public void RunCommand(string cmd)
764 {
765 string[] parts = Parser.Parse(cmd);
766 Commands.Resolve(parts);
767 }
768
769 public override string ReadLine(string p, bool isCommand, bool e)
770 {
771 System.Console.Write("{0}", p);
772 string cmdinput = System.Console.ReadLine();
773
774 if (isCommand)
775 {
776 string[] cmd = Commands.Resolve(Parser.Parse(cmdinput));
777
778 if (cmd.Length != 0)
779 {
780 int i;
781
782 for (i=0 ; i < cmd.Length ; i++)
783 {
784 if (cmd[i].Contains(" "))
785 cmd[i] = "\"" + cmd[i] + "\"";
786 }
787 return String.Empty;
788 }
789 }
790 return cmdinput;
791 }
792 }
793}
diff --git a/OpenSim/Framework/Console/ConsoleBase.cs b/OpenSim/Framework/Console/ConsoleBase.cs
new file mode 100755
index 0000000..2d8e723
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsoleBase.cs
@@ -0,0 +1,180 @@
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
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Reflection;
32using System.Text;
33using System.Threading;
34using log4net;
35
36namespace OpenSim.Framework.Console
37{
38 public class ConsoleBase
39 {
40// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41
42 protected string prompt = "# ";
43
44 public object ConsoleScene { get; set; }
45
46 public string DefaultPrompt { get; set; }
47
48 public ConsoleBase(string defaultPrompt)
49 {
50 DefaultPrompt = defaultPrompt;
51 }
52
53 public virtual void LockOutput()
54 {
55 }
56
57 public virtual void UnlockOutput()
58 {
59 }
60
61 public virtual void Output(string text, string level)
62 {
63 Output(text);
64 }
65
66 public virtual void Output(string text)
67 {
68 System.Console.WriteLine(text);
69 }
70
71 public virtual void OutputFormat(string format, params object[] components)
72 {
73 Output(string.Format(format, components));
74 }
75
76 public string CmdPrompt(string p)
77 {
78 return ReadLine(String.Format("{0}: ", p), false, true);
79 }
80
81 public string CmdPrompt(string p, string def)
82 {
83 string ret = ReadLine(String.Format("{0} [{1}]: ", p, def), false, true);
84 if (ret == String.Empty)
85 ret = def;
86
87 return ret;
88 }
89
90 public string CmdPrompt(string p, List<char> excludedCharacters)
91 {
92 bool itisdone = false;
93 string ret = String.Empty;
94 while (!itisdone)
95 {
96 itisdone = true;
97 ret = CmdPrompt(p);
98
99 foreach (char c in excludedCharacters)
100 {
101 if (ret.Contains(c.ToString()))
102 {
103 System.Console.WriteLine("The character \"" + c.ToString() + "\" is not permitted.");
104 itisdone = false;
105 }
106 }
107 }
108
109 return ret;
110 }
111
112 public string CmdPrompt(string p, string def, List<char> excludedCharacters)
113 {
114 bool itisdone = false;
115 string ret = String.Empty;
116 while (!itisdone)
117 {
118 itisdone = true;
119 ret = CmdPrompt(p, def);
120
121 if (ret == String.Empty)
122 {
123 ret = def;
124 }
125 else
126 {
127 foreach (char c in excludedCharacters)
128 {
129 if (ret.Contains(c.ToString()))
130 {
131 System.Console.WriteLine("The character \"" + c.ToString() + "\" is not permitted.");
132 itisdone = false;
133 }
134 }
135 }
136 }
137
138 return ret;
139 }
140
141 // Displays a command prompt and returns a default value, user may only enter 1 of 2 options
142 public string CmdPrompt(string prompt, string defaultresponse, List<string> options)
143 {
144 bool itisdone = false;
145 string optstr = String.Empty;
146 foreach (string s in options)
147 optstr += " " + s;
148
149 string temp = CmdPrompt(prompt, defaultresponse);
150 while (itisdone == false)
151 {
152 if (options.Contains(temp))
153 {
154 itisdone = true;
155 }
156 else
157 {
158 System.Console.WriteLine("Valid options are" + optstr);
159 temp = CmdPrompt(prompt, defaultresponse);
160 }
161 }
162 return temp;
163 }
164
165 // Displays a prompt and waits for the user to enter a string, then returns that string
166 // (Done with no echo and suitable for passwords)
167 public string PasswdPrompt(string p)
168 {
169 return ReadLine(String.Format("{0}: ", p), false, false);
170 }
171
172 public virtual string ReadLine(string p, bool isCommand, bool e)
173 {
174 System.Console.Write("{0}", p);
175 string cmdinput = System.Console.ReadLine();
176
177 return cmdinput;
178 }
179 }
180}
diff --git a/OpenSim/Framework/Console/ConsoleDisplayList.cs b/OpenSim/Framework/Console/ConsoleDisplayList.cs
new file mode 100644
index 0000000..6885509
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsoleDisplayList.cs
@@ -0,0 +1,112 @@
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
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32
33namespace OpenSim.Framework.Console
34{
35 /// <summary>
36 /// Used to generated a formatted table for the console.
37 /// </summary>
38 /// <remarks>
39 /// Currently subject to change. If you use this, be prepared to change your code when this class changes.
40 /// </remarks>
41 public class ConsoleDisplayList
42 {
43 /// <summary>
44 /// The default divider between key and value for a list item.
45 /// </summary>
46 public const string DefaultKeyValueDivider = " : ";
47
48 /// <summary>
49 /// The divider used between key and value for a list item.
50 /// </summary>
51 public string KeyValueDivider { get; set; }
52
53 /// <summary>
54 /// Table rows
55 /// </summary>
56 public List<KeyValuePair<string, string>> Rows { get; private set; }
57
58 /// <summary>
59 /// Number of spaces to indent the list.
60 /// </summary>
61 public int Indent { get; set; }
62
63 public ConsoleDisplayList()
64 {
65 Rows = new List<KeyValuePair<string, string>>();
66 KeyValueDivider = DefaultKeyValueDivider;
67 }
68
69 public override string ToString()
70 {
71 StringBuilder sb = new StringBuilder();
72 AddToStringBuilder(sb);
73 return sb.ToString();
74 }
75
76 public void AddToStringBuilder(StringBuilder sb)
77 {
78 string formatString = GetFormatString();
79// System.Console.WriteLine("FORMAT STRING [{0}]", formatString);
80
81 // rows
82 foreach (KeyValuePair<string, string> row in Rows)
83 sb.AppendFormat(formatString, row.Key, row.Value);
84 }
85
86 /// <summary>
87 /// Gets the format string for the table.
88 /// </summary>
89 private string GetFormatString()
90 {
91 StringBuilder formatSb = new StringBuilder();
92
93 int longestKey = -1;
94
95 foreach (KeyValuePair<string, string> row in Rows)
96 if (row.Key.Length > longestKey)
97 longestKey = row.Key.Length;
98
99 formatSb.Append(' ', Indent);
100
101 // Can only do left formatting for now
102 formatSb.AppendFormat("{{0,-{0}}}{1}{{1}}\n", longestKey, KeyValueDivider);
103
104 return formatSb.ToString();
105 }
106
107 public void AddRow(object key, object value)
108 {
109 Rows.Add(new KeyValuePair<string, string>(key.ToString(), value.ToString()));
110 }
111 }
112} \ No newline at end of file
diff --git a/OpenSim/Framework/Console/ConsoleDisplayTable.cs b/OpenSim/Framework/Console/ConsoleDisplayTable.cs
new file mode 100644
index 0000000..711a337
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsoleDisplayTable.cs
@@ -0,0 +1,155 @@
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
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32
33namespace OpenSim.Framework.Console
34{
35 /// <summary>
36 /// Used to generated a formatted table for the console.
37 /// </summary>
38 /// <remarks>
39 /// Currently subject to change. If you use this, be prepared to change your code when this class changes.
40 /// </remarks>
41 public class ConsoleDisplayTable
42 {
43 /// <summary>
44 /// Default number of spaces between table columns.
45 /// </summary>
46 public const int DefaultTableSpacing = 2;
47
48 /// <summary>
49 /// Table columns.
50 /// </summary>
51 public List<ConsoleDisplayTableColumn> Columns { get; private set; }
52
53 /// <summary>
54 /// Table rows
55 /// </summary>
56 public List<ConsoleDisplayTableRow> Rows { get; private set; }
57
58 /// <summary>
59 /// Number of spaces to indent the whole table.
60 /// </summary>
61 public int Indent { get; set; }
62
63 /// <summary>
64 /// Spacing between table columns
65 /// </summary>
66 public int TableSpacing { get; set; }
67
68 public ConsoleDisplayTable()
69 {
70 TableSpacing = DefaultTableSpacing;
71 Columns = new List<ConsoleDisplayTableColumn>();
72 Rows = new List<ConsoleDisplayTableRow>();
73 }
74
75 public override string ToString()
76 {
77 StringBuilder sb = new StringBuilder();
78 AddToStringBuilder(sb);
79 return sb.ToString();
80 }
81
82 public void AddColumn(string name, int width)
83 {
84 Columns.Add(new ConsoleDisplayTableColumn(name, width));
85 }
86
87 public void AddRow(params object[] cells)
88 {
89 Rows.Add(new ConsoleDisplayTableRow(cells));
90 }
91
92 public void AddToStringBuilder(StringBuilder sb)
93 {
94 string formatString = GetFormatString();
95// System.Console.WriteLine("FORMAT STRING [{0}]", formatString);
96
97 // columns
98 sb.AppendFormat(formatString, Columns.ConvertAll(c => c.Header).ToArray());
99
100 // rows
101 foreach (ConsoleDisplayTableRow row in Rows)
102 sb.AppendFormat(formatString, row.Cells.ToArray());
103 }
104
105 /// <summary>
106 /// Gets the format string for the table.
107 /// </summary>
108 private string GetFormatString()
109 {
110 StringBuilder formatSb = new StringBuilder();
111
112 formatSb.Append(' ', Indent);
113
114 for (int i = 0; i < Columns.Count; i++)
115 {
116 if (i != 0)
117 formatSb.Append(' ', TableSpacing);
118
119 // Can only do left formatting for now
120 formatSb.AppendFormat("{{{0},-{1}}}", i, Columns[i].Width);
121 }
122
123 formatSb.Append('\n');
124
125 return formatSb.ToString();
126 }
127 }
128
129 public struct ConsoleDisplayTableColumn
130 {
131 public string Header { get; set; }
132 public int Width { get; set; }
133
134 public ConsoleDisplayTableColumn(string header, int width) : this()
135 {
136 Header = header;
137 Width = width;
138 }
139 }
140
141 public struct ConsoleDisplayTableRow
142 {
143 public List<object> Cells { get; private set; }
144
145 public ConsoleDisplayTableRow(List<object> cells) : this()
146 {
147 Cells = cells;
148 }
149
150 public ConsoleDisplayTableRow(params object[] cells) : this()
151 {
152 Cells = new List<object>(cells);
153 }
154 }
155} \ No newline at end of file
diff --git a/OpenSim/Framework/Console/ConsoleDisplayUtil.cs b/OpenSim/Framework/Console/ConsoleDisplayUtil.cs
new file mode 100644
index 0000000..6417663
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsoleDisplayUtil.cs
@@ -0,0 +1,48 @@
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
28using System;
29
30namespace OpenSim.Framework.Console
31{
32 /// <summary>
33 /// This will be a set of typical column sizes to allow greater consistency between console commands.
34 /// </summary>
35 public static class ConsoleDisplayUtil
36 {
37 public const int CoordTupleSize = 11;
38 public const int PortSize = 5;
39
40 public const int EstateNameSize = 20;
41 public const int ParcelNameSize = 40;
42 public const int RegionNameSize = 20;
43 public const int UserNameSize = 35;
44
45 public const int UuidSize = 36;
46 public const int VectorSize = 15;
47 }
48} \ No newline at end of file
diff --git a/OpenSim/Framework/Console/ConsolePluginCommand.cs b/OpenSim/Framework/Console/ConsolePluginCommand.cs
new file mode 100755
index 0000000..f4d3687
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsolePluginCommand.cs
@@ -0,0 +1,139 @@
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
28using System;
29
30namespace OpenSim.Framework.Console
31{
32 public delegate void ConsoleCommand(string[] comParams);
33
34 /// <summary>
35 /// Holder object for a new console plugin command
36 ///
37 /// Override the methods like Run and IsHelpfull (but the defaults might work ok.)
38 /// </summary>
39 public class ConsolePluginCommand
40 {
41 /// <summary>
42 /// command delegate used in running
43 /// </summary>
44 private ConsoleCommand m_commandDelegate;
45 /// <summary>
46 /// help text displayed
47 /// </summary>
48 private string m_helpText;
49 /// <summary>
50 /// command in the form of "showme new commands"
51 /// </summary>
52 private string[] m_cmdText;
53
54 /// <summary>
55 /// Construct a new ConsolePluginCommand
56 ///
57 /// for use with OpenSim.RegisterConsolePluginCommand(myCmd);
58 ///
59 /// </summary>
60 /// <param name="command">in the form of "showme new commands"</param>
61 /// <param name="dlg">ommand delegate used in running</param>
62 /// <param name="help">the text displayed in "help showme new commands"</param>
63 public ConsolePluginCommand(string command, ConsoleCommand dlg, string help)
64 {
65 m_cmdText = command.Split(new char[] { ' ' });
66 m_commandDelegate = dlg;
67 m_helpText = help;
68 }
69
70 /// <summary>
71 /// Returns the match length this command has upon the 'cmdWithParams'
72 /// At least a higher number for "show plugin status" then "show" would return
73 /// This is used to have multi length command verbs
74 ///
75 /// @see OopenSim.RunPluginCommands
76 /// It will only run the one with the highest number
77 ///
78 /// </summary>
79 public int matchLength(string cmdWithParams)
80 {
81 // QUESTION: have a case insensitive flag?
82 cmdWithParams = cmdWithParams.ToLower().Trim();
83 string matchText = String.Join(" ",m_cmdText).ToLower().Trim();
84 if (cmdWithParams.StartsWith(matchText))
85 {
86 // QUESTION Instead return cmdText.Length; ?
87 return matchText.Length;
88 }
89 return 0;
90 }
91
92 /// <summary>
93 /// Run the delegate the incomming string may contain the command, if so, it is chopped off the cmdParams[]
94 /// </summary>
95 public void Run(string cmd, string[] cmdParams)
96 {
97 int skipParams = 0;
98 if (m_cmdText.Length > 1)
99 {
100 int currentParam = 1;
101 while (currentParam < m_cmdText.Length)
102 {
103 if (cmdParams[skipParams].ToLower().Equals(m_cmdText[currentParam].ToLower()))
104 {
105 skipParams++;
106 }
107 currentParam++;
108 }
109
110 }
111 string[] sendCmdParams = cmdParams;
112 if (skipParams > 0)
113 {
114 sendCmdParams = new string[cmdParams.Length-skipParams];
115 for (int i=0;i<sendCmdParams.Length;i++) {
116 sendCmdParams[i] = cmdParams[skipParams++];
117 }
118 }
119 m_commandDelegate(sendCmdParams);//.Trim().Split(new char[] { ' ' }));
120 }
121
122 /// <summary>
123 /// Shows help information on the console's Notice method
124 /// </summary>
125 public void ShowHelp(ConsoleBase console)
126 {
127 console.Output(String.Join(" ", m_cmdText) + " - " + m_helpText + "\n");
128 }
129
130 /// <summary>
131 /// return true if the ShowHelp(..) method might be helpfull
132 /// </summary>
133 public bool IsHelpfull(string cmdWithParams)
134 {
135 cmdWithParams = cmdWithParams.ToLower();
136 return cmdWithParams.Contains(String.Join(" ", m_cmdText).ToLower()) || m_helpText.ToLower().Contains(cmdWithParams);
137 }
138 }
139}
diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs
new file mode 100644
index 0000000..44f6dc1
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsoleUtil.cs
@@ -0,0 +1,387 @@
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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Linq;
32using System.Reflection;
33using log4net;
34using OpenMetaverse;
35
36namespace OpenSim.Framework.Console
37{
38 public class ConsoleUtil
39 {
40 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41
42 public const int LocalIdNotFound = 0;
43
44 /// <summary>
45 /// Used by modules to display stock co-ordinate help, though possibly this should be under some general section
46 /// rather than in each help summary.
47 /// </summary>
48 public const string CoordHelp
49 = @"Each component of the coord is comma separated. There must be no spaces between the commas.
50 If you don't care about the z component you can simply omit it.
51 If you don't care about the x or y components then you can leave them blank (though a comma is still required)
52 If you want to specify the maximum value of a component then you can use ~ instead of a number
53 If you want to specify the minimum value of a component then you can use -~ instead of a number
54 e.g.
55 show object pos 20,20,20 to 40,40,40
56 delete object pos 20,20 to 40,40
57 show object pos ,20,20 to ,40,40
58 delete object pos ,,30 to ,,~
59 show object pos ,,-~ to ,,30";
60
61 public const string MinRawConsoleVectorValue = "-~";
62 public const string MaxRawConsoleVectorValue = "~";
63
64 public const string VectorSeparator = ",";
65 public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray();
66
67 /// <summary>
68 /// Check if the given file path exists.
69 /// </summary>
70 /// <remarks>If not, warning is printed to the given console.</remarks>
71 /// <returns>true if the file does not exist, false otherwise.</returns>
72 /// <param name='console'></param>
73 /// <param name='path'></param>
74 public static bool CheckFileDoesNotExist(ICommandConsole console, string path)
75 {
76 if (File.Exists(path))
77 {
78 console.OutputFormat("File {0} already exists. Please move or remove it.", path);
79 return false;
80 }
81
82 return true;
83 }
84
85 /// <summary>
86 /// Try to parse a console UUID from the console.
87 /// </summary>
88 /// <remarks>
89 /// Will complain to the console if parsing fails.
90 /// </remarks>
91 /// <returns></returns>
92 /// <param name='console'>If null then no complaint is printed.</param>
93 /// <param name='rawUuid'></param>
94 /// <param name='uuid'></param>
95 public static bool TryParseConsoleUuid(ICommandConsole console, string rawUuid, out UUID uuid)
96 {
97 if (!UUID.TryParse(rawUuid, out uuid))
98 {
99 if (console != null)
100 console.OutputFormat("ERROR: {0} is not a valid uuid", rawUuid);
101
102 return false;
103 }
104
105 return true;
106 }
107
108 public static bool TryParseConsoleLocalId(ICommandConsole console, string rawLocalId, out uint localId)
109 {
110 if (!uint.TryParse(rawLocalId, out localId))
111 {
112 if (console != null)
113 console.OutputFormat("ERROR: {0} is not a valid local id", localId);
114
115 return false;
116 }
117
118 if (localId == 0)
119 {
120 if (console != null)
121 console.OutputFormat("ERROR: {0} is not a valid local id - it must be greater than 0", localId);
122
123 return false;
124 }
125
126 return true;
127 }
128
129 /// <summary>
130 /// Tries to parse the input as either a UUID or a local ID.
131 /// </summary>
132 /// <returns>true if parsing succeeded, false otherwise.</returns>
133 /// <param name='console'></param>
134 /// <param name='rawId'></param>
135 /// <param name='uuid'></param>
136 /// <param name='localId'>
137 /// Will be set to ConsoleUtil.LocalIdNotFound if parsing result was a UUID or no parse succeeded.
138 /// </param>
139 public static bool TryParseConsoleId(ICommandConsole console, string rawId, out UUID uuid, out uint localId)
140 {
141 if (TryParseConsoleUuid(null, rawId, out uuid))
142 {
143 localId = LocalIdNotFound;
144 return true;
145 }
146
147 if (TryParseConsoleLocalId(null, rawId, out localId))
148 {
149 return true;
150 }
151
152 if (console != null)
153 console.OutputFormat("ERROR: {0} is not a valid UUID or local id", rawId);
154
155 return false;
156 }
157
158 /// <summary>
159 /// Convert a console input to a bool, automatically complaining if a console is given.
160 /// </summary>
161 /// <param name='console'>Can be null if no console is available.</param>
162 /// <param name='rawConsoleVector'>/param>
163 /// <param name='vector'></param>
164 /// <returns></returns>
165 public static bool TryParseConsoleBool(ICommandConsole console, string rawConsoleString, out bool b)
166 {
167 if (!bool.TryParse(rawConsoleString, out b))
168 {
169 if (console != null)
170 console.OutputFormat("ERROR: {0} is not a true or false value", rawConsoleString);
171
172 return false;
173 }
174
175 return true;
176 }
177
178 /// <summary>
179 /// Convert a console input to an int, automatically complaining if a console is given.
180 /// </summary>
181 /// <param name='console'>Can be null if no console is available.</param>
182 /// <param name='rawConsoleInt'>/param>
183 /// <param name='i'></param>
184 /// <returns></returns>
185 public static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i)
186 {
187 if (!int.TryParse(rawConsoleInt, out i))
188 {
189 if (console != null)
190 console.OutputFormat("ERROR: {0} is not a valid integer", rawConsoleInt);
191
192 return false;
193 }
194
195 return true;
196 }
197
198 /// <summary>
199 /// Convert a console input to a float, automatically complaining if a console is given.
200 /// </summary>
201 /// <param name='console'>Can be null if no console is available.</param>
202 /// <param name='rawConsoleInput'>/param>
203 /// <param name='i'></param>
204 /// <returns></returns>
205 public static bool TryParseConsoleFloat(ICommandConsole console, string rawConsoleInput, out float i)
206 {
207 if (!float.TryParse(rawConsoleInput, out i))
208 {
209 if (console != null)
210 console.OutputFormat("ERROR: {0} is not a valid float", rawConsoleInput);
211
212 return false;
213 }
214
215 return true;
216 }
217
218 /// <summary>
219 /// Convert a console input to a double, automatically complaining if a console is given.
220 /// </summary>
221 /// <param name='console'>Can be null if no console is available.</param>
222 /// <param name='rawConsoleInput'>/param>
223 /// <param name='i'></param>
224 /// <returns></returns>
225 public static bool TryParseConsoleDouble(ICommandConsole console, string rawConsoleInput, out double i)
226 {
227 if (!double.TryParse(rawConsoleInput, out i))
228 {
229 if (console != null)
230 console.OutputFormat("ERROR: {0} is not a valid double", rawConsoleInput);
231
232 return false;
233 }
234
235 return true;
236 }
237
238 /// <summary>
239 /// Convert a console integer to a natural int, automatically complaining if a console is given.
240 /// </summary>
241 /// <param name='console'>Can be null if no console is available.</param>
242 /// <param name='rawConsoleInt'>/param>
243 /// <param name='i'></param>
244 /// <returns></returns>
245 public static bool TryParseConsoleNaturalInt(ICommandConsole console, string rawConsoleInt, out int i)
246 {
247 if (TryParseConsoleInt(console, rawConsoleInt, out i))
248 {
249 if (i < 0)
250 {
251 if (console != null)
252 console.OutputFormat("ERROR: {0} is not a positive integer", rawConsoleInt);
253
254 return false;
255 }
256
257 return true;
258 }
259
260 return false;
261 }
262
263 /// <summary>
264 /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
265 /// </summary>
266 /// <param name='rawConsoleVector'>/param>
267 /// <param name='vector'></param>
268 /// <returns></returns>
269 public static bool TryParseConsoleMinVector(string rawConsoleVector, out Vector3 vector)
270 {
271 return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector);
272 }
273
274 /// <summary>
275 /// Convert a maximum vector input from the console to an OpenMetaverse.Vector3
276 /// </summary>
277 /// <param name='rawConsoleVector'>/param>
278 /// <param name='vector'></param>
279 /// <returns></returns>
280 public static bool TryParseConsoleMaxVector(string rawConsoleVector, out Vector3 vector)
281 {
282 return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector);
283 }
284
285 /// <summary>
286 /// Convert a vector input from the console to an OpenMetaverse.Vector3
287 /// </summary>
288 /// <param name='rawConsoleVector'>
289 /// A string in the form <x>,<y>,<z> where there is no space between values.
290 /// Any component can be missing (e.g. ,,40). blankComponentFunc is invoked to replace the blank with a suitable value
291 /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40,30 or 40)
292 /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue
293 /// Other than that, component values must be numeric.
294 /// </param>
295 /// <param name='blankComponentFunc'>
296 /// Behaviour if component is blank. If null then conversion fails on a blank component.
297 /// </param>
298 /// <param name='vector'></param>
299 /// <returns></returns>
300 public static bool TryParseConsoleVector(
301 string rawConsoleVector, Func<string, string> blankComponentFunc, out Vector3 vector)
302 {
303 return Vector3.TryParse(CookVector(rawConsoleVector, 3, blankComponentFunc), out vector);
304 }
305
306 /// <summary>
307 /// Convert a vector input from the console to an OpenMetaverse.Vector2
308 /// </summary>
309 /// <param name='rawConsoleVector'>
310 /// A string in the form <x>,<y> where there is no space between values.
311 /// Any component can be missing (e.g. ,40). blankComponentFunc is invoked to replace the blank with a suitable value
312 /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40)
313 /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue
314 /// Other than that, component values must be numeric.
315 /// </param>
316 /// <param name='blankComponentFunc'>
317 /// Behaviour if component is blank. If null then conversion fails on a blank component.
318 /// </param>
319 /// <param name='vector'></param>
320 /// <returns></returns>
321 public static bool TryParseConsole2DVector(
322 string rawConsoleVector, Func<string, string> blankComponentFunc, out Vector2 vector)
323 {
324 // We don't use Vector2.TryParse() for now because for some reason it expects an input with 3 components
325 // rather than 2.
326 string cookedVector = CookVector(rawConsoleVector, 2, blankComponentFunc);
327
328 if (cookedVector == null)
329 {
330 vector = Vector2.Zero;
331
332 return false;
333 }
334 else
335 {
336 string[] cookedComponents = cookedVector.Split(VectorSeparatorChars);
337
338 vector = new Vector2(float.Parse(cookedComponents[0]), float.Parse(cookedComponents[1]));
339
340 return true;
341 }
342
343 //return Vector2.TryParse(CookVector(rawConsoleVector, 2, blankComponentFunc), out vector);
344 }
345
346 /// <summary>
347 /// Convert a raw console vector into a vector that can be be parsed by the relevant OpenMetaverse.TryParse()
348 /// </summary>
349 /// <param name='rawConsoleVector'></param>
350 /// <param name='dimensions'></param>
351 /// <param name='blankComponentFunc'></param>
352 /// <returns>null if conversion was not possible</returns>
353 private static string CookVector(
354 string rawConsoleVector, int dimensions, Func<string, string> blankComponentFunc)
355 {
356 List<string> components = rawConsoleVector.Split(VectorSeparatorChars).ToList();
357
358 if (components.Count < 1 || components.Count > dimensions)
359 return null;
360
361 if (components.Count < dimensions)
362 {
363 if (blankComponentFunc == null)
364 return null;
365 else
366 for (int i = components.Count; i < dimensions; i++)
367 components.Add("");
368 }
369
370 List<string> cookedComponents
371 = components.ConvertAll<string>(
372 c =>
373 {
374 if (c == "")
375 return blankComponentFunc.Invoke(c);
376 else if (c == MaxRawConsoleVectorValue)
377 return float.MaxValue.ToString();
378 else if (c == MinRawConsoleVectorValue)
379 return float.MinValue.ToString();
380 else
381 return c;
382 });
383
384 return string.Join(VectorSeparator, cookedComponents.ToArray());
385 }
386 }
387} \ No newline at end of file
diff --git a/OpenSim/Framework/Console/LocalConsole.cs b/OpenSim/Framework/Console/LocalConsole.cs
new file mode 100644
index 0000000..28293c0
--- /dev/null
+++ b/OpenSim/Framework/Console/LocalConsole.cs
@@ -0,0 +1,585 @@
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
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Reflection;
32using System.Text;
33using System.Text.RegularExpressions;
34using System.Threading;
35using System.IO;
36using Nini.Config;
37using log4net;
38
39namespace OpenSim.Framework.Console
40{
41 /// <summary>
42 /// A console that uses cursor control and color
43 /// </summary>
44 public class LocalConsole : CommandConsole
45 {
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47 private string m_historyPath;
48 private bool m_historyEnable;
49
50 // private readonly object m_syncRoot = new object();
51 private const string LOGLEVEL_NONE = "(none)";
52
53 // Used to extract categories for colourization.
54 private Regex m_categoryRegex
55 = new Regex(
56 @"^(?<Front>.*?)\[(?<Category>[^\]]+)\]:?(?<End>.*)", RegexOptions.Singleline | RegexOptions.Compiled);
57
58 private int m_cursorYPosition = -1;
59 private int m_cursorXPosition = 0;
60 private StringBuilder m_commandLine = new StringBuilder();
61 private bool m_echo = true;
62 private List<string> m_history = new List<string>();
63
64 private static readonly ConsoleColor[] Colors = {
65 // the dark colors don't seem to be visible on some black background terminals like putty :(
66 //ConsoleColor.DarkBlue,
67 //ConsoleColor.DarkGreen,
68 //ConsoleColor.DarkCyan,
69 //ConsoleColor.DarkMagenta,
70 //ConsoleColor.DarkYellow,
71 ConsoleColor.Gray,
72 //ConsoleColor.DarkGray,
73 ConsoleColor.Blue,
74 ConsoleColor.Green,
75 ConsoleColor.Cyan,
76 ConsoleColor.Magenta,
77 ConsoleColor.Yellow
78 };
79
80 private static ConsoleColor DeriveColor(string input)
81 {
82 // it is important to do Abs, hash values can be negative
83 return Colors[(Math.Abs(input.ToUpper().GetHashCode()) % Colors.Length)];
84 }
85
86 public LocalConsole(string defaultPrompt, IConfig startupConfig = null) : base(defaultPrompt)
87 {
88
89 if (startupConfig == null) return;
90
91 m_historyEnable = startupConfig.GetBoolean("ConsoleHistoryFileEnabled", false);
92 if (!m_historyEnable)
93 {
94 m_log.Info("[LOCAL CONSOLE]: Persistent command line history from file is Disabled");
95 return;
96 }
97
98 string m_historyFile = startupConfig.GetString("ConsoleHistoryFile", "OpenSimConsoleHistory.txt");
99 int m_historySize = startupConfig.GetInt("ConsoleHistoryFileLines", 100);
100 m_historyPath = Path.GetFullPath(Path.Combine(Util.configDir(), m_historyFile));
101 m_log.InfoFormat("[LOCAL CONSOLE]: Persistent command line history is Enabled, up to {0} lines from file {1}", m_historySize, m_historyPath);
102
103 if (File.Exists(m_historyPath))
104 {
105 using (StreamReader history_file = new StreamReader(m_historyPath))
106 {
107 string line;
108 while ((line = history_file.ReadLine()) != null)
109 {
110 m_history.Add(line);
111 }
112 }
113
114 if (m_history.Count > m_historySize)
115 {
116 while (m_history.Count > m_historySize)
117 m_history.RemoveAt(0);
118
119 using (StreamWriter history_file = new StreamWriter(m_historyPath))
120 {
121 foreach (string line in m_history)
122 {
123 history_file.WriteLine(line);
124 }
125 }
126 }
127 m_log.InfoFormat("[LOCAL CONSOLE]: Read {0} lines of command line history from file {1}", m_history.Count, m_historyPath);
128 }
129 else
130 {
131 m_log.InfoFormat("[LOCAL CONSOLE]: Creating new empty command line history file {0}", m_historyPath);
132 File.Create(m_historyPath).Dispose();
133 }
134 }
135
136 private void AddToHistory(string text)
137 {
138 while (m_history.Count >= 100)
139 m_history.RemoveAt(0);
140
141 m_history.Add(text);
142 if (m_historyEnable)
143 {
144 File.AppendAllText(m_historyPath, text + Environment.NewLine);
145 }
146 }
147
148 /// <summary>
149 /// Set the cursor row.
150 /// </summary>
151 ///
152 /// <param name="top">
153 /// Row to set. If this is below 0, then the row is set to 0. If it is equal to the buffer height or greater
154 /// then it is set to one less than the height.
155 /// </param>
156 /// <returns>
157 /// The new cursor row.
158 /// </returns>
159 private int SetCursorTop(int top)
160 {
161 // From at least mono 2.4.2.3, window resizing can give mono an invalid row and column values. If we try
162 // to set a cursor row position with a currently invalid column, mono will throw an exception.
163 // Therefore, we need to make sure that the column position is valid first.
164 int left = System.Console.CursorLeft;
165
166 if (left < 0)
167 {
168 System.Console.CursorLeft = 0;
169 }
170 else
171 {
172 int bufferWidth = System.Console.BufferWidth;
173
174 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
175 if (bufferWidth > 0 && left >= bufferWidth)
176 System.Console.CursorLeft = bufferWidth - 1;
177 }
178
179 if (top < 0)
180 {
181 top = 0;
182 }
183 else
184 {
185 int bufferHeight = System.Console.BufferHeight;
186
187 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
188 if (bufferHeight > 0 && top >= bufferHeight)
189 top = bufferHeight - 1;
190 }
191
192 System.Console.CursorTop = top;
193
194 return top;
195 }
196
197 /// <summary>
198 /// Set the cursor column.
199 /// </summary>
200 ///
201 /// <param name="left">
202 /// Column to set. If this is below 0, then the column is set to 0. If it is equal to the buffer width or greater
203 /// then it is set to one less than the width.
204 /// </param>
205 /// <returns>
206 /// The new cursor column.
207 /// </returns>
208 private int SetCursorLeft(int left)
209 {
210 // From at least mono 2.4.2.3, window resizing can give mono an invalid row and column values. If we try
211 // to set a cursor column position with a currently invalid row, mono will throw an exception.
212 // Therefore, we need to make sure that the row position is valid first.
213 int top = System.Console.CursorTop;
214
215 if (top < 0)
216 {
217 System.Console.CursorTop = 0;
218 }
219 else
220 {
221 int bufferHeight = System.Console.BufferHeight;
222 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
223 if (bufferHeight > 0 && top >= bufferHeight)
224 System.Console.CursorTop = bufferHeight - 1;
225 }
226
227 if (left < 0)
228 {
229 left = 0;
230 }
231 else
232 {
233 int bufferWidth = System.Console.BufferWidth;
234
235 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
236 if (bufferWidth > 0 && left >= bufferWidth)
237 left = bufferWidth - 1;
238 }
239
240 System.Console.CursorLeft = left;
241
242 return left;
243 }
244
245 private void Show()
246 {
247 lock (m_commandLine)
248 {
249 if (m_cursorYPosition == -1 || System.Console.BufferWidth == 0)
250 return;
251
252 int xc = prompt.Length + m_cursorXPosition;
253 int new_x = xc % System.Console.BufferWidth;
254 int new_y = m_cursorYPosition + xc / System.Console.BufferWidth;
255 int end_y = m_cursorYPosition + (m_commandLine.Length + prompt.Length) / System.Console.BufferWidth;
256
257 if (end_y >= System.Console.BufferHeight) // wrap
258 {
259 m_cursorYPosition--;
260 new_y--;
261 SetCursorLeft(0);
262 SetCursorTop(System.Console.BufferHeight - 1);
263 System.Console.WriteLine(" ");
264 }
265
266 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
267 SetCursorLeft(0);
268
269 if (m_echo)
270 System.Console.Write("{0}{1}", prompt, m_commandLine);
271 else
272 System.Console.Write("{0}", prompt);
273
274 SetCursorTop(new_y);
275 SetCursorLeft(new_x);
276 }
277 }
278
279 public override void LockOutput()
280 {
281 Monitor.Enter(m_commandLine);
282 try
283 {
284 if (m_cursorYPosition != -1)
285 {
286 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
287 System.Console.CursorLeft = 0;
288
289 int count = m_commandLine.Length + prompt.Length;
290
291 while (count-- > 0)
292 System.Console.Write(" ");
293
294 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
295 SetCursorLeft(0);
296 }
297 }
298 catch (Exception)
299 {
300 }
301 }
302
303 public override void UnlockOutput()
304 {
305 if (m_cursorYPosition != -1)
306 {
307 m_cursorYPosition = System.Console.CursorTop;
308 Show();
309 }
310 Monitor.Exit(m_commandLine);
311 }
312
313 private void WriteColorText(ConsoleColor color, string sender)
314 {
315 try
316 {
317 lock (this)
318 {
319 try
320 {
321 System.Console.ForegroundColor = color;
322 System.Console.Write(sender);
323 System.Console.ResetColor();
324 }
325 catch (ArgumentNullException)
326 {
327 // Some older systems dont support coloured text.
328 System.Console.WriteLine(sender);
329 }
330 }
331 }
332 catch (ObjectDisposedException)
333 {
334 }
335 }
336
337 private void WriteLocalText(string text, string level)
338 {
339 string outText = text;
340
341 if (level != LOGLEVEL_NONE)
342 {
343 MatchCollection matches = m_categoryRegex.Matches(text);
344
345 if (matches.Count == 1)
346 {
347 outText = matches[0].Groups["End"].Value;
348 System.Console.Write(matches[0].Groups["Front"].Value);
349
350 System.Console.Write("[");
351 WriteColorText(DeriveColor(matches[0].Groups["Category"].Value),
352 matches[0].Groups["Category"].Value);
353 System.Console.Write("]:");
354 }
355 else
356 {
357 outText = outText.Trim();
358 }
359 }
360
361 if (level == "error")
362 WriteColorText(ConsoleColor.Red, outText);
363 else if (level == "warn")
364 WriteColorText(ConsoleColor.Yellow, outText);
365 else
366 System.Console.Write(outText);
367
368 System.Console.WriteLine();
369 }
370
371 public override void Output(string text)
372 {
373 Output(text, LOGLEVEL_NONE);
374 }
375
376 public override void Output(string text, string level)
377 {
378 FireOnOutput(text);
379
380 lock (m_commandLine)
381 {
382 if (m_cursorYPosition == -1)
383 {
384 WriteLocalText(text, level);
385
386 return;
387 }
388
389 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
390 SetCursorLeft(0);
391
392 int count = m_commandLine.Length + prompt.Length;
393
394 while (count-- > 0)
395 System.Console.Write(" ");
396
397 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
398 SetCursorLeft(0);
399
400 WriteLocalText(text, level);
401
402 m_cursorYPosition = System.Console.CursorTop;
403
404 Show();
405 }
406 }
407
408 private bool ContextHelp()
409 {
410 string[] words = Parser.Parse(m_commandLine.ToString());
411
412 bool trailingSpace = m_commandLine.ToString().EndsWith(" ");
413
414 // Allow ? through while typing a URI
415 //
416 if (words.Length > 0 && words[words.Length-1].StartsWith("http") && !trailingSpace)
417 return false;
418
419 string[] opts = Commands.FindNextOption(words, trailingSpace);
420
421 if (opts[0].StartsWith("Command help:"))
422 Output(opts[0]);
423 else
424 Output(String.Format("Options: {0}", String.Join(" ", opts)));
425
426 return true;
427 }
428
429 public override string ReadLine(string p, bool isCommand, bool e)
430 {
431 m_cursorXPosition = 0;
432 prompt = p;
433 m_echo = e;
434 int historyLine = m_history.Count;
435
436 SetCursorLeft(0); // Needed for mono
437 System.Console.Write(" "); // Needed for mono
438
439 lock (m_commandLine)
440 {
441 m_cursorYPosition = System.Console.CursorTop;
442 m_commandLine.Remove(0, m_commandLine.Length);
443 }
444
445 while (true)
446 {
447 Show();
448
449 ConsoleKeyInfo key = System.Console.ReadKey(true);
450 char enteredChar = key.KeyChar;
451
452 if (!Char.IsControl(enteredChar))
453 {
454 if (m_cursorXPosition >= 318)
455 continue;
456
457 if (enteredChar == '?' && isCommand)
458 {
459 if (ContextHelp())
460 continue;
461 }
462
463 m_commandLine.Insert(m_cursorXPosition, enteredChar);
464 m_cursorXPosition++;
465 }
466 else
467 {
468 switch (key.Key)
469 {
470 case ConsoleKey.Backspace:
471 if (m_cursorXPosition == 0)
472 break;
473 m_commandLine.Remove(m_cursorXPosition-1, 1);
474 m_cursorXPosition--;
475
476 SetCursorLeft(0);
477 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
478
479 if (m_echo)
480 System.Console.Write("{0}{1} ", prompt, m_commandLine);
481 else
482 System.Console.Write("{0}", prompt);
483
484 break;
485 case ConsoleKey.Delete:
486 if (m_cursorXPosition == m_commandLine.Length)
487 break;
488
489 m_commandLine.Remove(m_cursorXPosition, 1);
490
491 SetCursorLeft(0);
492 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
493
494 if (m_echo)
495 System.Console.Write("{0}{1} ", prompt, m_commandLine);
496 else
497 System.Console.Write("{0}", prompt);
498
499 break;
500 case ConsoleKey.End:
501 m_cursorXPosition = m_commandLine.Length;
502 break;
503 case ConsoleKey.Home:
504 m_cursorXPosition = 0;
505 break;
506 case ConsoleKey.UpArrow:
507 if (historyLine < 1)
508 break;
509 historyLine--;
510 LockOutput();
511 m_commandLine.Remove(0, m_commandLine.Length);
512 m_commandLine.Append(m_history[historyLine]);
513 m_cursorXPosition = m_commandLine.Length;
514 UnlockOutput();
515 break;
516 case ConsoleKey.DownArrow:
517 if (historyLine >= m_history.Count)
518 break;
519 historyLine++;
520 LockOutput();
521 if (historyLine == m_history.Count)
522 {
523 m_commandLine.Remove(0, m_commandLine.Length);
524 }
525 else
526 {
527 m_commandLine.Remove(0, m_commandLine.Length);
528 m_commandLine.Append(m_history[historyLine]);
529 }
530 m_cursorXPosition = m_commandLine.Length;
531 UnlockOutput();
532 break;
533 case ConsoleKey.LeftArrow:
534 if (m_cursorXPosition > 0)
535 m_cursorXPosition--;
536 break;
537 case ConsoleKey.RightArrow:
538 if (m_cursorXPosition < m_commandLine.Length)
539 m_cursorXPosition++;
540 break;
541 case ConsoleKey.Enter:
542 SetCursorLeft(0);
543 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
544
545 System.Console.WriteLine();
546 //Show();
547
548 lock (m_commandLine)
549 {
550 m_cursorYPosition = -1;
551 }
552
553 string commandLine = m_commandLine.ToString();
554
555 if (isCommand)
556 {
557 string[] cmd = Commands.Resolve(Parser.Parse(commandLine));
558
559 if (cmd.Length != 0)
560 {
561 int index;
562
563 for (index=0 ; index < cmd.Length ; index++)
564 {
565 if (cmd[index].Contains(" "))
566 cmd[index] = "\"" + cmd[index] + "\"";
567 }
568 AddToHistory(String.Join(" ", cmd));
569 return String.Empty;
570 }
571 }
572
573 // If we're not echoing to screen (e.g. a password) then we probably don't want it in history
574 if (m_echo && commandLine != "")
575 AddToHistory(commandLine);
576
577 return commandLine;
578 default:
579 break;
580 }
581 }
582 }
583 }
584 }
585}
diff --git a/OpenSim/Framework/Console/MockConsole.cs b/OpenSim/Framework/Console/MockConsole.cs
new file mode 100644
index 0000000..1a142df
--- /dev/null
+++ b/OpenSim/Framework/Console/MockConsole.cs
@@ -0,0 +1,89 @@
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
28using System;
29using System.Threading;
30using System.Collections.Generic;
31using System.Text;
32using System.Xml;
33
34namespace OpenSim.Framework.Console
35{
36 /// <summary>
37 /// This is a Fake console that's used when setting up the Scene in Unit Tests
38 /// Don't use this except for Unit Testing or you're in for a world of hurt when the
39 /// sim gets to ReadLine
40 /// </summary>
41 public class MockConsole : ICommandConsole
42 {
43#pragma warning disable 0067
44 public event OnOutputDelegate OnOutput;
45#pragma warning restore 0067
46
47 private MockCommands m_commands = new MockCommands();
48
49 public ICommands Commands { get { return m_commands; } }
50
51 public string DefaultPrompt { get; set; }
52
53 public void Prompt() {}
54
55 public void RunCommand(string cmd) {}
56
57 public string ReadLine(string p, bool isCommand, bool e) { return ""; }
58
59 public object ConsoleScene {
60 get { return null; }
61 set {}
62 }
63
64 public void Output(string text, string level) {}
65 public void Output(string text) {}
66 public void OutputFormat(string format, params object[] components) {}
67
68 public string CmdPrompt(string p) { return ""; }
69 public string CmdPrompt(string p, string def) { return ""; }
70 public string CmdPrompt(string p, List<char> excludedCharacters) { return ""; }
71 public string CmdPrompt(string p, string def, List<char> excludedCharacters) { return ""; }
72
73 public string CmdPrompt(string prompt, string defaultresponse, List<string> options) { return ""; }
74
75 public string PasswdPrompt(string p) { return ""; }
76 }
77
78 public class MockCommands : ICommands
79 {
80 public void FromXml(XmlElement root, CommandDelegate fn) {}
81 public List<string> GetHelp(string[] cmd) { return null; }
82 public void AddCommand(string module, bool shared, string command, string help, string longhelp, CommandDelegate fn) {}
83 public void AddCommand(string module, bool shared, string command, string help, string longhelp, string descriptivehelp, CommandDelegate fn) {}
84 public string[] FindNextOption(string[] cmd, bool term) { return null; }
85 public bool HasCommand(string cmd) { return false; }
86 public string[] Resolve(string[] cmd) { return null; }
87 public XmlElement GetXml(XmlDocument doc) { return null; }
88 }
89}
diff --git a/OpenSim/Framework/Console/OpenSimAppender.cs b/OpenSim/Framework/Console/OpenSimAppender.cs
new file mode 100644
index 0000000..72a251e
--- /dev/null
+++ b/OpenSim/Framework/Console/OpenSimAppender.cs
@@ -0,0 +1,86 @@
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
28using System;
29using log4net.Appender;
30using log4net.Core;
31
32namespace OpenSim.Framework.Console
33{
34 /// <summary>
35 /// Writes log information out onto the console
36 /// </summary>
37 public class OpenSimAppender : AnsiColorTerminalAppender
38 {
39 private ConsoleBase m_console = null;
40
41 public ConsoleBase Console
42 {
43 get { return m_console; }
44 set { m_console = value; }
45 }
46
47 override protected void Append(LoggingEvent le)
48 {
49 if (m_console != null)
50 m_console.LockOutput();
51
52 string loggingMessage = RenderLoggingEvent(le);
53
54 try
55 {
56 if (m_console != null)
57 {
58 string level = "normal";
59
60 if (le.Level == Level.Error)
61 level = "error";
62 else if (le.Level == Level.Warn)
63 level = "warn";
64
65 m_console.Output(loggingMessage, level);
66 }
67 else
68 {
69 if (!loggingMessage.EndsWith("\n"))
70 System.Console.WriteLine(loggingMessage);
71 else
72 System.Console.Write(loggingMessage);
73 }
74 }
75 catch (Exception e)
76 {
77 System.Console.WriteLine("Couldn't write out log message: {0}", e.ToString());
78 }
79 finally
80 {
81 if (m_console != null)
82 m_console.UnlockOutput();
83 }
84 }
85 }
86}
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
28using System;
29using System.Xml;
30using System.Collections;
31using System.Collections.Generic;
32using System.Diagnostics;
33using System.Reflection;
34using System.Text;
35using System.Text.RegularExpressions;
36using System.Threading;
37using OpenMetaverse;
38using Nini.Config;
39using OpenSim.Framework.Servers.HttpServer;
40using log4net;
41
42namespace 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}