aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Console
diff options
context:
space:
mode:
authoronefang2019-05-19 21:24:15 +1000
committeronefang2019-05-19 21:24:15 +1000
commit5e4d6cab00cb29cd088ab7b62ab13aff103b64cb (patch)
treea9fbc62df9eb2d1d9ba2698d8552eae71eca20d8 /OpenSim/Framework/Console
parentAdd a build script. (diff)
downloadopensim-SC_OLD-5e4d6cab00cb29cd088ab7b62ab13aff103b64cb.zip
opensim-SC_OLD-5e4d6cab00cb29cd088ab7b62ab13aff103b64cb.tar.gz
opensim-SC_OLD-5e4d6cab00cb29cd088ab7b62ab13aff103b64cb.tar.bz2
opensim-SC_OLD-5e4d6cab00cb29cd088ab7b62ab13aff103b64cb.tar.xz
Dump OpenSim 0.9.0.1 into it's own branch.
Diffstat (limited to 'OpenSim/Framework/Console')
-rw-r--r--OpenSim/Framework/Console/AssemblyInfo.cs2
-rw-r--r--OpenSim/Framework/Console/CommandConsole.cs44
-rwxr-xr-x[-rw-r--r--]OpenSim/Framework/Console/ConsoleBase.cs8
-rwxr-xr-x[-rw-r--r--]OpenSim/Framework/Console/ConsolePluginCommand.cs0
-rw-r--r--OpenSim/Framework/Console/ConsoleUtil.cs22
-rw-r--r--OpenSim/Framework/Console/LocalConsole.cs24
-rw-r--r--OpenSim/Framework/Console/MockConsole.cs4
-rw-r--r--OpenSim/Framework/Console/RemoteConsole.cs317
8 files changed, 315 insertions, 106 deletions
diff --git a/OpenSim/Framework/Console/AssemblyInfo.cs b/OpenSim/Framework/Console/AssemblyInfo.cs
index 67af471..c53d92b 100644
--- a/OpenSim/Framework/Console/AssemblyInfo.cs
+++ b/OpenSim/Framework/Console/AssemblyInfo.cs
@@ -55,4 +55,4 @@ using System.Runtime.InteropServices;
55// You can specify all values by your own or you can build default build and revision 55// You can specify all values by your own or you can build default build and revision
56// numbers with the '*' character (the default): 56// numbers with the '*' character (the default):
57 57
58[assembly : AssemblyVersion("0.8.2.*")] 58[assembly : AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs
index 0f68afe..52360b4 100644
--- a/OpenSim/Framework/Console/CommandConsole.cs
+++ b/OpenSim/Framework/Console/CommandConsole.cs
@@ -52,27 +52,27 @@ namespace OpenSim.Framework.Console
52 /// The module from which this command comes 52 /// The module from which this command comes
53 /// </value> 53 /// </value>
54 public string module; 54 public string module;
55 55
56 /// <value> 56 /// <value>
57 /// Whether the module is shared 57 /// Whether the module is shared
58 /// </value> 58 /// </value>
59 public bool shared; 59 public bool shared;
60 60
61 /// <value> 61 /// <value>
62 /// Very short BNF description 62 /// Very short BNF description
63 /// </value> 63 /// </value>
64 public string help_text; 64 public string help_text;
65 65
66 /// <value> 66 /// <value>
67 /// Longer one line help text 67 /// Longer one line help text
68 /// </value> 68 /// </value>
69 public string long_help; 69 public string long_help;
70 70
71 /// <value> 71 /// <value>
72 /// Full descriptive help for this command 72 /// Full descriptive help for this command
73 /// </value> 73 /// </value>
74 public string descriptive_help; 74 public string descriptive_help;
75 75
76 /// <value> 76 /// <value>
77 /// The method to invoke for this command 77 /// The method to invoke for this command
78 /// </value> 78 /// </value>
@@ -83,8 +83,8 @@ namespace OpenSim.Framework.Console
83 = "To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n"; 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 84
85 public const string ItemHelpText 85 public const string ItemHelpText
86= @"For more information, type 'help all' to get a list of all commands, 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:"; 87 or type help <item>' where <item> is one of the following:";
88 88
89 /// <value> 89 /// <value>
90 /// Commands organized by keyword in a tree 90 /// Commands organized by keyword in a tree
@@ -106,7 +106,7 @@ namespace OpenSim.Framework.Console
106 { 106 {
107 List<string> help = new List<string>(); 107 List<string> help = new List<string>();
108 List<string> helpParts = new List<string>(cmd); 108 List<string> helpParts = new List<string>(cmd);
109 109
110 // Remove initial help keyword 110 // Remove initial help keyword
111 helpParts.RemoveAt(0); 111 helpParts.RemoveAt(0);
112 112
@@ -154,7 +154,7 @@ namespace OpenSim.Framework.Console
154 154
155 return help; 155 return help;
156 } 156 }
157 157
158 /// <summary> 158 /// <summary>
159 /// See if we can find the requested command in order to display longer help 159 /// See if we can find the requested command in order to display longer help
160 /// </summary> 160 /// </summary>
@@ -171,23 +171,23 @@ namespace OpenSim.Framework.Console
171 help.Insert(0, ItemHelpText); 171 help.Insert(0, ItemHelpText);
172 return help; 172 return help;
173 } 173 }
174 174
175 Dictionary<string, object> dict = tree; 175 Dictionary<string, object> dict = tree;
176 while (helpParts.Count > 0) 176 while (helpParts.Count > 0)
177 { 177 {
178 string helpPart = helpParts[0]; 178 string helpPart = helpParts[0];
179 179
180 if (!dict.ContainsKey(helpPart)) 180 if (!dict.ContainsKey(helpPart))
181 break; 181 break;
182 182
183 //m_log.Debug("Found {0}", helpParts[0]); 183 //m_log.Debug("Found {0}", helpParts[0]);
184 184
185 if (dict[helpPart] is Dictionary<string, Object>) 185 if (dict[helpPart] is Dictionary<string, Object>)
186 dict = (Dictionary<string, object>)dict[helpPart]; 186 dict = (Dictionary<string, object>)dict[helpPart];
187 187
188 helpParts.RemoveAt(0); 188 helpParts.RemoveAt(0);
189 } 189 }
190 190
191 // There was a command for the given help string 191 // There was a command for the given help string
192 if (dict.ContainsKey(String.Empty)) 192 if (dict.ContainsKey(String.Empty))
193 { 193 {
@@ -200,14 +200,14 @@ namespace OpenSim.Framework.Console
200 // If we do have some descriptive help then insert a spacing line before for readability. 200 // If we do have some descriptive help then insert a spacing line before for readability.
201 if (descriptiveHelp != string.Empty) 201 if (descriptiveHelp != string.Empty)
202 help.Add(string.Empty); 202 help.Add(string.Empty);
203 203
204 help.Add(commandInfo.descriptive_help); 204 help.Add(commandInfo.descriptive_help);
205 } 205 }
206 else 206 else
207 { 207 {
208 help.Add(string.Format("No help is available for {0}", originalHelpRequest)); 208 help.Add(string.Format("No help is available for {0}", originalHelpRequest));
209 } 209 }
210 210
211 return help; 211 return help;
212 } 212 }
213 213
@@ -268,7 +268,7 @@ namespace OpenSim.Framework.Console
268// } 268// }
269// return result; 269// return result;
270// } 270// }
271 271
272 /// <summary> 272 /// <summary>
273 /// Add a command to those which can be invoked from the console. 273 /// Add a command to those which can be invoked from the console.
274 /// </summary> 274 /// </summary>
@@ -299,7 +299,7 @@ namespace OpenSim.Framework.Console
299 string[] parts = Parser.Parse(command); 299 string[] parts = Parser.Parse(command);
300 300
301 Dictionary<string, Object> current = tree; 301 Dictionary<string, Object> current = tree;
302 302
303 foreach (string part in parts) 303 foreach (string part in parts)
304 { 304 {
305 if (current.ContainsKey(part)) 305 if (current.ContainsKey(part))
@@ -326,7 +326,7 @@ namespace OpenSim.Framework.Console
326 326
327 return; 327 return;
328 } 328 }
329 329
330 info = new CommandInfo(); 330 info = new CommandInfo();
331 info.module = module; 331 info.module = module;
332 info.shared = shared; 332 info.shared = shared;
@@ -471,7 +471,7 @@ namespace OpenSim.Framework.Console
471 471
472 return null; 472 return null;
473 } 473 }
474 474
475 public bool HasCommand(string command) 475 public bool HasCommand(string command)
476 { 476 {
477 string[] result; 477 string[] result;
diff --git a/OpenSim/Framework/Console/ConsoleBase.cs b/OpenSim/Framework/Console/ConsoleBase.cs
index 2d8e723..64cddea 100644..100755
--- a/OpenSim/Framework/Console/ConsoleBase.cs
+++ b/OpenSim/Framework/Console/ConsoleBase.cs
@@ -67,7 +67,7 @@ namespace OpenSim.Framework.Console
67 { 67 {
68 System.Console.WriteLine(text); 68 System.Console.WriteLine(text);
69 } 69 }
70 70
71 public virtual void OutputFormat(string format, params object[] components) 71 public virtual void OutputFormat(string format, params object[] components)
72 { 72 {
73 Output(string.Format(format, components)); 73 Output(string.Format(format, components));
@@ -86,7 +86,7 @@ namespace OpenSim.Framework.Console
86 86
87 return ret; 87 return ret;
88 } 88 }
89 89
90 public string CmdPrompt(string p, List<char> excludedCharacters) 90 public string CmdPrompt(string p, List<char> excludedCharacters)
91 { 91 {
92 bool itisdone = false; 92 bool itisdone = false;
@@ -95,7 +95,7 @@ namespace OpenSim.Framework.Console
95 { 95 {
96 itisdone = true; 96 itisdone = true;
97 ret = CmdPrompt(p); 97 ret = CmdPrompt(p);
98 98
99 foreach (char c in excludedCharacters) 99 foreach (char c in excludedCharacters)
100 { 100 {
101 if (ret.Contains(c.ToString())) 101 if (ret.Contains(c.ToString()))
@@ -117,7 +117,7 @@ namespace OpenSim.Framework.Console
117 { 117 {
118 itisdone = true; 118 itisdone = true;
119 ret = CmdPrompt(p, def); 119 ret = CmdPrompt(p, def);
120 120
121 if (ret == String.Empty) 121 if (ret == String.Empty)
122 { 122 {
123 ret = def; 123 ret = def;
diff --git a/OpenSim/Framework/Console/ConsolePluginCommand.cs b/OpenSim/Framework/Console/ConsolePluginCommand.cs
index f4d3687..f4d3687 100644..100755
--- a/OpenSim/Framework/Console/ConsolePluginCommand.cs
+++ b/OpenSim/Framework/Console/ConsolePluginCommand.cs
diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs
index 44f6dc1..bfa05a2 100644
--- a/OpenSim/Framework/Console/ConsoleUtil.cs
+++ b/OpenSim/Framework/Console/ConsoleUtil.cs
@@ -40,7 +40,7 @@ namespace OpenSim.Framework.Console
40 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 40 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41 41
42 public const int LocalIdNotFound = 0; 42 public const int LocalIdNotFound = 0;
43 43
44 /// <summary> 44 /// <summary>
45 /// Used by modules to display stock co-ordinate help, though possibly this should be under some general section 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. 46 /// rather than in each help summary.
@@ -57,10 +57,10 @@ namespace OpenSim.Framework.Console
57 show object pos ,20,20 to ,40,40 57 show object pos ,20,20 to ,40,40
58 delete object pos ,,30 to ,,~ 58 delete object pos ,,30 to ,,~
59 show object pos ,,-~ to ,,30"; 59 show object pos ,,-~ to ,,30";
60 60
61 public const string MinRawConsoleVectorValue = "-~"; 61 public const string MinRawConsoleVectorValue = "-~";
62 public const string MaxRawConsoleVectorValue = "~"; 62 public const string MaxRawConsoleVectorValue = "~";
63 63
64 public const string VectorSeparator = ","; 64 public const string VectorSeparator = ",";
65 public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray(); 65 public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray();
66 66
@@ -81,7 +81,7 @@ namespace OpenSim.Framework.Console
81 81
82 return true; 82 return true;
83 } 83 }
84 84
85 /// <summary> 85 /// <summary>
86 /// Try to parse a console UUID from the console. 86 /// Try to parse a console UUID from the console.
87 /// </summary> 87 /// </summary>
@@ -101,7 +101,7 @@ namespace OpenSim.Framework.Console
101 101
102 return false; 102 return false;
103 } 103 }
104 104
105 return true; 105 return true;
106 } 106 }
107 107
@@ -259,7 +259,7 @@ namespace OpenSim.Framework.Console
259 259
260 return false; 260 return false;
261 } 261 }
262 262
263 /// <summary> 263 /// <summary>
264 /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 264 /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
265 /// </summary> 265 /// </summary>
@@ -270,7 +270,7 @@ namespace OpenSim.Framework.Console
270 { 270 {
271 return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector); 271 return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector);
272 } 272 }
273 273
274 /// <summary> 274 /// <summary>
275 /// Convert a maximum vector input from the console to an OpenMetaverse.Vector3 275 /// Convert a maximum vector input from the console to an OpenMetaverse.Vector3
276 /// </summary> 276 /// </summary>
@@ -281,7 +281,7 @@ namespace OpenSim.Framework.Console
281 { 281 {
282 return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector); 282 return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector);
283 } 283 }
284 284
285 /// <summary> 285 /// <summary>
286 /// Convert a vector input from the console to an OpenMetaverse.Vector3 286 /// Convert a vector input from the console to an OpenMetaverse.Vector3
287 /// </summary> 287 /// </summary>
@@ -354,10 +354,10 @@ namespace OpenSim.Framework.Console
354 string rawConsoleVector, int dimensions, Func<string, string> blankComponentFunc) 354 string rawConsoleVector, int dimensions, Func<string, string> blankComponentFunc)
355 { 355 {
356 List<string> components = rawConsoleVector.Split(VectorSeparatorChars).ToList(); 356 List<string> components = rawConsoleVector.Split(VectorSeparatorChars).ToList();
357 357
358 if (components.Count < 1 || components.Count > dimensions) 358 if (components.Count < 1 || components.Count > dimensions)
359 return null; 359 return null;
360 360
361 if (components.Count < dimensions) 361 if (components.Count < dimensions)
362 { 362 {
363 if (blankComponentFunc == null) 363 if (blankComponentFunc == null)
@@ -380,7 +380,7 @@ namespace OpenSim.Framework.Console
380 else 380 else
381 return c; 381 return c;
382 }); 382 });
383 383
384 return string.Join(VectorSeparator, cookedComponents.ToArray()); 384 return string.Join(VectorSeparator, cookedComponents.ToArray());
385 } 385 }
386 } 386 }
diff --git a/OpenSim/Framework/Console/LocalConsole.cs b/OpenSim/Framework/Console/LocalConsole.cs
index d8f8ecc..73f0323 100644
--- a/OpenSim/Framework/Console/LocalConsole.cs
+++ b/OpenSim/Framework/Console/LocalConsole.cs
@@ -51,7 +51,7 @@ namespace OpenSim.Framework.Console
51 private const string LOGLEVEL_NONE = "(none)"; 51 private const string LOGLEVEL_NONE = "(none)";
52 52
53 // Used to extract categories for colourization. 53 // Used to extract categories for colourization.
54 private Regex m_categoryRegex 54 private Regex m_categoryRegex
55 = new Regex( 55 = new Regex(
56 @"^(?<Front>.*?)\[(?<Category>[^\]]+)\]:?(?<End>.*)", RegexOptions.Singleline | RegexOptions.Compiled); 56 @"^(?<Front>.*?)\[(?<Category>[^\]]+)\]:?(?<End>.*)", RegexOptions.Singleline | RegexOptions.Compiled);
57 57
@@ -97,7 +97,7 @@ namespace OpenSim.Framework.Console
97 97
98 string m_historyFile = startupConfig.GetString("ConsoleHistoryFile", "OpenSimConsoleHistory.txt"); 98 string m_historyFile = startupConfig.GetString("ConsoleHistoryFile", "OpenSimConsoleHistory.txt");
99 int m_historySize = startupConfig.GetInt("ConsoleHistoryFileLines", 100); 99 int m_historySize = startupConfig.GetInt("ConsoleHistoryFileLines", 100);
100 m_historyPath = Path.GetFullPath(Path.Combine("../caches", m_historyFile)); 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); 101 m_log.InfoFormat("[LOCAL CONSOLE]: Persistent command line history is Enabled, up to {0} lines from file {1}", m_historySize, m_historyPath);
102 102
103 if (File.Exists(m_historyPath)) 103 if (File.Exists(m_historyPath))
@@ -167,15 +167,15 @@ namespace OpenSim.Framework.Console
167 { 167 {
168 System.Console.CursorLeft = 0; 168 System.Console.CursorLeft = 0;
169 } 169 }
170 else 170 else
171 { 171 {
172 int bufferWidth = System.Console.BufferWidth; 172 int bufferWidth = System.Console.BufferWidth;
173 173
174 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657) 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) 175 if (bufferWidth > 0 && left >= bufferWidth)
176 System.Console.CursorLeft = bufferWidth - 1; 176 System.Console.CursorLeft = bufferWidth - 1;
177 } 177 }
178 178
179 if (top < 0) 179 if (top < 0)
180 { 180 {
181 top = 0; 181 top = 0;
@@ -183,7 +183,7 @@ namespace OpenSim.Framework.Console
183 else 183 else
184 { 184 {
185 int bufferHeight = System.Console.BufferHeight; 185 int bufferHeight = System.Console.BufferHeight;
186 186
187 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657) 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) 188 if (bufferHeight > 0 && top >= bufferHeight)
189 top = bufferHeight - 1; 189 top = bufferHeight - 1;
@@ -216,14 +216,14 @@ namespace OpenSim.Framework.Console
216 { 216 {
217 System.Console.CursorTop = 0; 217 System.Console.CursorTop = 0;
218 } 218 }
219 else 219 else
220 { 220 {
221 int bufferHeight = System.Console.BufferHeight; 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) 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) 223 if (bufferHeight > 0 && top >= bufferHeight)
224 System.Console.CursorTop = bufferHeight - 1; 224 System.Console.CursorTop = bufferHeight - 1;
225 } 225 }
226 226
227 if (left < 0) 227 if (left < 0)
228 { 228 {
229 left = 0; 229 left = 0;
@@ -339,7 +339,7 @@ namespace OpenSim.Framework.Console
339 string outText = text; 339 string outText = text;
340 340
341 if (level != LOGLEVEL_NONE) 341 if (level != LOGLEVEL_NONE)
342 { 342 {
343 MatchCollection matches = m_categoryRegex.Matches(text); 343 MatchCollection matches = m_categoryRegex.Matches(text);
344 344
345 if (matches.Count == 1) 345 if (matches.Count == 1)
@@ -364,7 +364,7 @@ namespace OpenSim.Framework.Console
364 WriteColorText(ConsoleColor.Yellow, outText); 364 WriteColorText(ConsoleColor.Yellow, outText);
365 else 365 else
366 System.Console.Write(outText); 366 System.Console.Write(outText);
367 367
368 System.Console.WriteLine(); 368 System.Console.WriteLine();
369 } 369 }
370 370
@@ -551,7 +551,7 @@ namespace OpenSim.Framework.Console
551 } 551 }
552 552
553 string commandLine = m_commandLine.ToString(); 553 string commandLine = m_commandLine.ToString();
554 554
555 if (isCommand) 555 if (isCommand)
556 { 556 {
557 string[] cmd = Commands.Resolve(Parser.Parse(commandLine)); 557 string[] cmd = Commands.Resolve(Parser.Parse(commandLine));
@@ -573,7 +573,7 @@ namespace OpenSim.Framework.Console
573 // If we're not echoing to screen (e.g. a password) then we probably don't want it in history 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 != "") 574 if (m_echo && commandLine != "")
575 AddToHistory(commandLine); 575 AddToHistory(commandLine);
576 576
577 return commandLine; 577 return commandLine;
578 default: 578 default:
579 break; 579 break;
diff --git a/OpenSim/Framework/Console/MockConsole.cs b/OpenSim/Framework/Console/MockConsole.cs
index 1a142df..e1ff720 100644
--- a/OpenSim/Framework/Console/MockConsole.cs
+++ b/OpenSim/Framework/Console/MockConsole.cs
@@ -35,7 +35,7 @@ namespace OpenSim.Framework.Console
35{ 35{
36 /// <summary> 36 /// <summary>
37 /// This is a Fake console that's used when setting up the Scene in Unit Tests 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 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 39 /// sim gets to ReadLine
40 /// </summary> 40 /// </summary>
41 public class MockConsole : ICommandConsole 41 public class MockConsole : ICommandConsole
@@ -56,7 +56,7 @@ namespace OpenSim.Framework.Console
56 56
57 public string ReadLine(string p, bool isCommand, bool e) { return ""; } 57 public string ReadLine(string p, bool isCommand, bool e) { return ""; }
58 58
59 public object ConsoleScene { 59 public object ConsoleScene {
60 get { return null; } 60 get { return null; }
61 set {} 61 set {}
62 } 62 }
diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs
index 8ad7b0d..f59c902 100644
--- a/OpenSim/Framework/Console/RemoteConsole.cs
+++ b/OpenSim/Framework/Console/RemoteConsole.cs
@@ -34,6 +34,7 @@ using System.Reflection;
34using System.Text; 34using System.Text;
35using System.Text.RegularExpressions; 35using System.Text.RegularExpressions;
36using System.Threading; 36using System.Threading;
37using System.Timers;
37using OpenMetaverse; 38using OpenMetaverse;
38using Nini.Config; 39using Nini.Config;
39using OpenSim.Framework.Servers.HttpServer; 40using OpenSim.Framework.Servers.HttpServer;
@@ -41,51 +42,147 @@ using log4net;
41 42
42namespace OpenSim.Framework.Console 43namespace OpenSim.Framework.Console
43{ 44{
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 45 // A console that uses REST interfaces
52 // 46 //
53 public class RemoteConsole : CommandConsole 47 public class RemoteConsole : CommandConsole
54 { 48 {
55 private IHttpServer m_Server = null; 49 // Connection specific data, indexed by a session ID
56 private IConfigSource m_Config = null; 50 // we create when a client connects.
57 51 protected class ConsoleConnection
58 private List<string> m_Scrollback = new List<string>(); 52 {
59 private ManualResetEvent m_DataEvent = new ManualResetEvent(false); 53 // Last activity from the client
60 private List<string> m_InputData = new List<string>(); 54 public int last;
61 private long m_LineNumber = 0; 55
62 private Dictionary<UUID, ConsoleConnection> m_Connections = 56 // Last line of scrollback posted to this client
57 public long lastLineSeen;
58
59 // True if this is a new connection, e.g. has never
60 // displayed a prompt to the user.
61 public bool newConnection = true;
62 }
63
64 // A line in the scrollback buffer.
65 protected class ScrollbackEntry
66 {
67 // The line number of this entry
68 public long lineNumber;
69
70 // The text to send to the client
71 public string text;
72
73 // The level this should be logged as. Omitted for
74 // prompts and input echo.
75 public string level;
76
77 // True if the text above is a prompt, e.g. the
78 // client should turn on the cursor / accept input
79 public bool isPrompt;
80
81 // True if the requested input is a command. A
82 // client may offer help or validate input if
83 // this is set. If false, input should be sent
84 // as typed.
85 public bool isCommand;
86
87 // True if this text represents a line of text that
88 // was input in response to a prompt. A client should
89 // turn off the cursor and refrain from sending commands
90 // until a new prompt is received.
91 public bool isInput;
92 }
93
94 // Data that is relevant to all connections
95
96 // The scrollback buffer
97 protected List<ScrollbackEntry> m_Scrollback = new List<ScrollbackEntry>();
98
99 // Monotonously incrementing line number. This may eventually
100 // wrap. No provision is made for that case because 64 bits
101 // is a long, long time.
102 protected long m_lineNumber = 0;
103
104 // These two variables allow us to send the correct
105 // information about the prompt status to the client,
106 // irrespective of what may have run off the top of the
107 // scrollback buffer;
108 protected bool m_expectingInput = false;
109 protected bool m_expectingCommand = true;
110 protected string m_lastPromptUsed;
111
112 // This is the list of things received from clients.
113 // Note: Race conditions can happen. If a client sends
114 // something while nothing is expected, it will be
115 // intepreted as input to the next prompt. For
116 // commands this is largely correct. For other prompts,
117 // YMMV.
118 // TODO: Find a better way to fix this
119 protected List<string> m_InputData = new List<string>();
120
121 // Event to allow ReadLine to wait synchronously even though
122 // everthing else is asynchronous here.
123 protected ManualResetEvent m_DataEvent = new ManualResetEvent(false);
124
125 // The list of sessions we maintain. Unlike other console types,
126 // multiple users on the same console are explicitly allowed.
127 protected Dictionary<UUID, ConsoleConnection> m_Connections =
63 new Dictionary<UUID, ConsoleConnection>(); 128 new Dictionary<UUID, ConsoleConnection>();
64 private string m_UserName = String.Empty; 129
65 private string m_Password = String.Empty; 130 // Timer to control expiration of sessions that have been
66 private string m_AllowedOrigin = String.Empty; 131 // disconnected.
132 protected System.Timers.Timer m_expireTimer = new System.Timers.Timer(5000);
133
134 // The less interesting stuff that makes the actual server
135 // work.
136 protected IHttpServer m_Server = null;
137 protected IConfigSource m_Config = null;
138
139 protected string m_UserName = String.Empty;
140 protected string m_Password = String.Empty;
141 protected string m_AllowedOrigin = String.Empty;
142
67 143
68 public RemoteConsole(string defaultPrompt) : base(defaultPrompt) 144 public RemoteConsole(string defaultPrompt) : base(defaultPrompt)
69 { 145 {
146 // There is something wrong with this architecture.
147 // A prompt is sent on every single input, so why have this?
148 // TODO: Investigate and fix.
149 m_lastPromptUsed = defaultPrompt;
150
151 // Start expiration of sesssions.
152 m_expireTimer.Elapsed += DoExpire;
153 m_expireTimer.Start();
70 } 154 }
71 155
72 public void ReadConfig(IConfigSource config) 156 public void ReadConfig(IConfigSource config)
73 { 157 {
74 m_Config = config; 158 m_Config = config;
75 159
160 // We're pulling this from the 'Network' section for legacy
161 // compatibility. However, this is so essentially insecure
162 // that TLS and client certs should be used instead of
163 // a username / password.
76 IConfig netConfig = m_Config.Configs["Network"]; 164 IConfig netConfig = m_Config.Configs["Network"];
165
77 if (netConfig == null) 166 if (netConfig == null)
78 return; 167 return;
79 168
169 // Get the username and password.
80 m_UserName = netConfig.GetString("ConsoleUser", String.Empty); 170 m_UserName = netConfig.GetString("ConsoleUser", String.Empty);
81 m_Password = netConfig.GetString("ConsolePass", String.Empty); 171 m_Password = netConfig.GetString("ConsolePass", String.Empty);
172
173 // Woefully underdocumented, this is what makes javascript
174 // console clients work. Set to "*" for anywhere or (better)
175 // to specific addresses.
82 m_AllowedOrigin = netConfig.GetString("ConsoleAllowedOrigin", String.Empty); 176 m_AllowedOrigin = netConfig.GetString("ConsoleAllowedOrigin", String.Empty);
83 } 177 }
84 178
85 public void SetServer(IHttpServer server) 179 public void SetServer(IHttpServer server)
86 { 180 {
181 // This is called by the framework to give us the server
182 // instance (means: port) to work with.
87 m_Server = server; 183 m_Server = server;
88 184
185 // Add our handlers
89 m_Server.AddHTTPHandler("/StartSession/", HandleHttpStartSession); 186 m_Server.AddHTTPHandler("/StartSession/", HandleHttpStartSession);
90 m_Server.AddHTTPHandler("/CloseSession/", HandleHttpCloseSession); 187 m_Server.AddHTTPHandler("/CloseSession/", HandleHttpCloseSession);
91 m_Server.AddHTTPHandler("/SessionCommand/", HandleHttpSessionCommand); 188 m_Server.AddHTTPHandler("/SessionCommand/", HandleHttpSessionCommand);
@@ -93,38 +190,84 @@ namespace OpenSim.Framework.Console
93 190
94 public override void Output(string text, string level) 191 public override void Output(string text, string level)
95 { 192 {
193 Output(text, level, false, false, false);
194 }
195
196 protected void Output(string text, string level, bool isPrompt, bool isCommand, bool isInput)
197 {
198 // Increment the line number. It was 0 and they start at 1
199 // so we need to pre-increment.
200 m_lineNumber++;
201
202 // Create and populate the new entry.
203 ScrollbackEntry newEntry = new ScrollbackEntry();
204
205 newEntry.lineNumber = m_lineNumber;
206 newEntry.text = text;
207 newEntry.level = level;
208 newEntry.isPrompt = isPrompt;
209 newEntry.isCommand = isCommand;
210 newEntry.isInput = isInput;
211
212 // Add a line to the scrollback. In some cases, that may not
213 // actually be a line of text.
96 lock (m_Scrollback) 214 lock (m_Scrollback)
97 { 215 {
216 // Prune the scrollback to the length se send as connect
217 // burst to give the user some context.
98 while (m_Scrollback.Count >= 1000) 218 while (m_Scrollback.Count >= 1000)
99 m_Scrollback.RemoveAt(0); 219 m_Scrollback.RemoveAt(0);
100 m_LineNumber++; 220
101 m_Scrollback.Add(String.Format("{0}", m_LineNumber)+":"+level+":"+text); 221 m_Scrollback.Add(newEntry);
102 } 222 }
223
224 // Let the rest of the system know we have output something.
103 FireOnOutput(text.Trim()); 225 FireOnOutput(text.Trim());
226
227 // Also display it for debugging.
104 System.Console.WriteLine(text.Trim()); 228 System.Console.WriteLine(text.Trim());
105 } 229 }
106 230
107 public override void Output(string text) 231 public override void Output(string text)
108 { 232 {
109 Output(text, "normal"); 233 // Output plain (non-logging style) text.
234 Output(text, String.Empty, false, false, false);
110 } 235 }
111 236
112 public override string ReadLine(string p, bool isCommand, bool e) 237 public override string ReadLine(string p, bool isCommand, bool e)
113 { 238 {
239 // Output the prompt an prepare to wait. This
240 // is called on a dedicated console thread and
241 // needs to be synchronous. Old architecture but
242 // not worth upgrading.
114 if (isCommand) 243 if (isCommand)
115 Output("+++"+p); 244 {
245 m_expectingInput = true;
246 m_expectingCommand = true;
247 Output(p, String.Empty, true, true, false);
248 m_lastPromptUsed = p;
249 }
116 else 250 else
117 Output("-++"+p); 251 {
252 m_expectingInput = true;
253 Output(p, String.Empty, true, false, false);
254 }
118 255
256
257 // Here is where we wait for the user to input something.
119 m_DataEvent.WaitOne(); 258 m_DataEvent.WaitOne();
120 259
121 string cmdinput; 260 string cmdinput;
122 261
262 // Check for empty input. Read input if not empty.
123 lock (m_InputData) 263 lock (m_InputData)
124 { 264 {
125 if (m_InputData.Count == 0) 265 if (m_InputData.Count == 0)
126 { 266 {
127 m_DataEvent.Reset(); 267 m_DataEvent.Reset();
268 m_expectingInput = false;
269 m_expectingCommand = false;
270
128 return ""; 271 return "";
129 } 272 }
130 273
@@ -135,8 +278,19 @@ namespace OpenSim.Framework.Console
135 278
136 } 279 }
137 280
281 m_expectingInput = false;
282 m_expectingCommand = false;
283
284 // Echo to all the other users what we have done. This
285 // will also go to ourselves.
286 Output (cmdinput, String.Empty, false, false, true);
287
288 // If this is a command, we need to resolve and execute it.
138 if (isCommand) 289 if (isCommand)
139 { 290 {
291 // This call will actually execute the command and create
292 // any output associated with it. The core just gets an
293 // empty string so it will call again immediately.
140 string[] cmd = Commands.Resolve(Parser.Parse(cmdinput)); 294 string[] cmd = Commands.Resolve(Parser.Parse(cmdinput));
141 295
142 if (cmd.Length != 0) 296 if (cmd.Length != 0)
@@ -151,18 +305,23 @@ namespace OpenSim.Framework.Console
151 return String.Empty; 305 return String.Empty;
152 } 306 }
153 } 307 }
308
309 // Return the raw input string if not a command.
154 return cmdinput; 310 return cmdinput;
155 } 311 }
156 312
157 private Hashtable CheckOrigin(Hashtable result) 313 // Very simplistic static access control header.
314 protected Hashtable CheckOrigin(Hashtable result)
158 { 315 {
159 if (!string.IsNullOrEmpty(m_AllowedOrigin)) 316 if (!string.IsNullOrEmpty(m_AllowedOrigin))
160 result["access_control_allow_origin"] = m_AllowedOrigin; 317 result["access_control_allow_origin"] = m_AllowedOrigin;
318
161 return result; 319 return result;
162 } 320 }
321
163 /* TODO: Figure out how PollServiceHTTPHandler can access the request headers 322 /* TODO: Figure out how PollServiceHTTPHandler can access the request headers
164 * in order to use m_AllowedOrigin as a regular expression 323 * in order to use m_AllowedOrigin as a regular expression
165 private Hashtable CheckOrigin(Hashtable headers, Hashtable result) 324 protected Hashtable CheckOrigin(Hashtable headers, Hashtable result)
166 { 325 {
167 if (!string.IsNullOrEmpty(m_AllowedOrigin)) 326 if (!string.IsNullOrEmpty(m_AllowedOrigin))
168 { 327 {
@@ -177,18 +336,23 @@ namespace OpenSim.Framework.Console
177 } 336 }
178 */ 337 */
179 338
180 private void DoExpire() 339 protected void DoExpire(Object sender, ElapsedEventArgs e)
181 { 340 {
341 // Iterate the list of console connections and find those we
342 // haven't heard from for longer then the longpoll interval.
343 // Remove them.
182 List<UUID> expired = new List<UUID>(); 344 List<UUID> expired = new List<UUID>();
183 345
184 lock (m_Connections) 346 lock (m_Connections)
185 { 347 {
348 // Mark the expired ones
186 foreach (KeyValuePair<UUID, ConsoleConnection> kvp in m_Connections) 349 foreach (KeyValuePair<UUID, ConsoleConnection> kvp in m_Connections)
187 { 350 {
188 if (System.Environment.TickCount - kvp.Value.last > 500000) 351 if (System.Environment.TickCount - kvp.Value.last > 500000)
189 expired.Add(kvp.Key); 352 expired.Add(kvp.Key);
190 } 353 }
191 354
355 // Delete them
192 foreach (UUID id in expired) 356 foreach (UUID id in expired)
193 { 357 {
194 m_Connections.Remove(id); 358 m_Connections.Remove(id);
@@ -197,10 +361,10 @@ namespace OpenSim.Framework.Console
197 } 361 }
198 } 362 }
199 363
200 private Hashtable HandleHttpStartSession(Hashtable request) 364 // Start a new session.
365 protected Hashtable HandleHttpStartSession(Hashtable request)
201 { 366 {
202 DoExpire(); 367 // The login is in the form of a http form post
203
204 Hashtable post = DecodePostString(request["body"].ToString()); 368 Hashtable post = DecodePostString(request["body"].ToString());
205 Hashtable reply = new Hashtable(); 369 Hashtable reply = new Hashtable();
206 370
@@ -208,6 +372,7 @@ namespace OpenSim.Framework.Console
208 reply["int_response_code"] = 401; 372 reply["int_response_code"] = 401;
209 reply["content_type"] = "text/plain"; 373 reply["content_type"] = "text/plain";
210 374
375 // Check user name and password
211 if (m_UserName == String.Empty) 376 if (m_UserName == String.Empty)
212 return reply; 377 return reply;
213 378
@@ -220,22 +385,28 @@ namespace OpenSim.Framework.Console
220 return reply; 385 return reply;
221 } 386 }
222 387
388 // Set up the new console connection record
223 ConsoleConnection c = new ConsoleConnection(); 389 ConsoleConnection c = new ConsoleConnection();
224 c.last = System.Environment.TickCount; 390 c.last = System.Environment.TickCount;
225 c.lastLineSeen = 0; 391 c.lastLineSeen = 0;
226 392
393 // Assign session ID
227 UUID sessionID = UUID.Random(); 394 UUID sessionID = UUID.Random();
228 395
396 // Add connection to list.
229 lock (m_Connections) 397 lock (m_Connections)
230 { 398 {
231 m_Connections[sessionID] = c; 399 m_Connections[sessionID] = c;
232 } 400 }
233 401
402 // This call is a CAP. The URL is the authentication.
234 string uri = "/ReadResponses/" + sessionID.ToString() + "/"; 403 string uri = "/ReadResponses/" + sessionID.ToString() + "/";
235 404
236 m_Server.AddPollServiceHTTPHandler( 405 m_Server.AddPollServiceHTTPHandler(
237 uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout 406 uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout
238 407
408 // Our reply is an XML document.
409 // TODO: Change this to Linq.Xml
239 XmlDocument xmldoc = new XmlDocument(); 410 XmlDocument xmldoc = new XmlDocument();
240 XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, 411 XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,
241 "", ""); 412 "", "");
@@ -252,12 +423,13 @@ namespace OpenSim.Framework.Console
252 rootElement.AppendChild(id); 423 rootElement.AppendChild(id);
253 424
254 XmlElement prompt = xmldoc.CreateElement("", "Prompt", ""); 425 XmlElement prompt = xmldoc.CreateElement("", "Prompt", "");
255 prompt.AppendChild(xmldoc.CreateTextNode(DefaultPrompt)); 426 prompt.AppendChild(xmldoc.CreateTextNode(m_lastPromptUsed));
256 427
257 rootElement.AppendChild(prompt); 428 rootElement.AppendChild(prompt);
258 429
259 rootElement.AppendChild(MainConsole.Instance.Commands.GetXml(xmldoc)); 430 rootElement.AppendChild(MainConsole.Instance.Commands.GetXml(xmldoc));
260 431
432 // Set up the response and check origin
261 reply["str_response_string"] = xmldoc.InnerXml; 433 reply["str_response_string"] = xmldoc.InnerXml;
262 reply["int_response_code"] = 200; 434 reply["int_response_code"] = 200;
263 reply["content_type"] = "text/xml"; 435 reply["content_type"] = "text/xml";
@@ -266,10 +438,9 @@ namespace OpenSim.Framework.Console
266 return reply; 438 return reply;
267 } 439 }
268 440
269 private Hashtable HandleHttpCloseSession(Hashtable request) 441 // Client closes session. Clean up.
442 protected Hashtable HandleHttpCloseSession(Hashtable request)
270 { 443 {
271 DoExpire();
272
273 Hashtable post = DecodePostString(request["body"].ToString()); 444 Hashtable post = DecodePostString(request["body"].ToString());
274 Hashtable reply = new Hashtable(); 445 Hashtable reply = new Hashtable();
275 446
@@ -316,10 +487,9 @@ namespace OpenSim.Framework.Console
316 return reply; 487 return reply;
317 } 488 }
318 489
319 private Hashtable HandleHttpSessionCommand(Hashtable request) 490 // Command received from the client.
491 protected Hashtable HandleHttpSessionCommand(Hashtable request)
320 { 492 {
321 DoExpire();
322
323 Hashtable post = DecodePostString(request["body"].ToString()); 493 Hashtable post = DecodePostString(request["body"].ToString());
324 Hashtable reply = new Hashtable(); 494 Hashtable reply = new Hashtable();
325 495
@@ -327,6 +497,7 @@ namespace OpenSim.Framework.Console
327 reply["int_response_code"] = 404; 497 reply["int_response_code"] = 404;
328 reply["content_type"] = "text/plain"; 498 reply["content_type"] = "text/plain";
329 499
500 // Check the ID
330 if (post["ID"] == null) 501 if (post["ID"] == null)
331 return reply; 502 return reply;
332 503
@@ -334,21 +505,25 @@ namespace OpenSim.Framework.Console
334 if (!UUID.TryParse(post["ID"].ToString(), out id)) 505 if (!UUID.TryParse(post["ID"].ToString(), out id))
335 return reply; 506 return reply;
336 507
508 // Find the connection for that ID.
337 lock (m_Connections) 509 lock (m_Connections)
338 { 510 {
339 if (!m_Connections.ContainsKey(id)) 511 if (!m_Connections.ContainsKey(id))
340 return reply; 512 return reply;
341 } 513 }
342 514
515 // Empty post. Just error out.
343 if (post["COMMAND"] == null) 516 if (post["COMMAND"] == null)
344 return reply; 517 return reply;
345 518
519 // Place the input data in the buffer.
346 lock (m_InputData) 520 lock (m_InputData)
347 { 521 {
348 m_DataEvent.Set(); 522 m_DataEvent.Set();
349 m_InputData.Add(post["COMMAND"].ToString()); 523 m_InputData.Add(post["COMMAND"].ToString());
350 } 524 }
351 525
526 // Create the XML reply document.
352 XmlDocument xmldoc = new XmlDocument(); 527 XmlDocument xmldoc = new XmlDocument();
353 XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, 528 XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,
354 "", ""); 529 "", "");
@@ -372,7 +547,8 @@ namespace OpenSim.Framework.Console
372 return reply; 547 return reply;
373 } 548 }
374 549
375 private Hashtable DecodePostString(string data) 550 // Decode a HTTP form post to a Hashtable
551 protected Hashtable DecodePostString(string data)
376 { 552 {
377 Hashtable result = new Hashtable(); 553 Hashtable result = new Hashtable();
378 554
@@ -389,13 +565,14 @@ namespace OpenSim.Framework.Console
389 565
390 if (elems.Length > 1) 566 if (elems.Length > 1)
391 value = System.Web.HttpUtility.UrlDecode(elems[1]); 567 value = System.Web.HttpUtility.UrlDecode(elems[1]);
392 568
393 result[name] = value; 569 result[name] = value;
394 } 570 }
395 571
396 return result; 572 return result;
397 } 573 }
398 574
575 // Close the CAP receiver for the responses for a given client.
399 public void CloseConnection(UUID id) 576 public void CloseConnection(UUID id)
400 { 577 {
401 try 578 try
@@ -409,7 +586,9 @@ namespace OpenSim.Framework.Console
409 } 586 }
410 } 587 }
411 588
412 private bool HasEvents(UUID RequestID, UUID sessionID) 589 // Check if there is anything to send. Return true if this client has
590 // lines pending.
591 protected bool HasEvents(UUID RequestID, UUID sessionID)
413 { 592 {
414 ConsoleConnection c = null; 593 ConsoleConnection c = null;
415 594
@@ -420,13 +599,15 @@ namespace OpenSim.Framework.Console
420 c = m_Connections[sessionID]; 599 c = m_Connections[sessionID];
421 } 600 }
422 c.last = System.Environment.TickCount; 601 c.last = System.Environment.TickCount;
423 if (c.lastLineSeen < m_LineNumber) 602 if (c.lastLineSeen < m_lineNumber)
424 return true; 603 return true;
425 return false; 604 return false;
426 } 605 }
427 606
428 private Hashtable GetEvents(UUID RequestID, UUID sessionID) 607 // Send all pending output to the client.
608 protected Hashtable GetEvents(UUID RequestID, UUID sessionID)
429 { 609 {
610 // Find the connection that goes with this client.
430 ConsoleConnection c = null; 611 ConsoleConnection c = null;
431 612
432 lock (m_Connections) 613 lock (m_Connections)
@@ -435,12 +616,15 @@ namespace OpenSim.Framework.Console
435 return NoEvents(RequestID, UUID.Zero); 616 return NoEvents(RequestID, UUID.Zero);
436 c = m_Connections[sessionID]; 617 c = m_Connections[sessionID];
437 } 618 }
619
620 // If we have nothing to send, send the no events response.
438 c.last = System.Environment.TickCount; 621 c.last = System.Environment.TickCount;
439 if (c.lastLineSeen >= m_LineNumber) 622 if (c.lastLineSeen >= m_lineNumber)
440 return NoEvents(RequestID, UUID.Zero); 623 return NoEvents(RequestID, UUID.Zero);
441 624
442 Hashtable result = new Hashtable(); 625 Hashtable result = new Hashtable();
443 626
627 // Create the response document.
444 XmlDocument xmldoc = new XmlDocument(); 628 XmlDocument xmldoc = new XmlDocument();
445 XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, 629 XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,
446 "", ""); 630 "", "");
@@ -449,30 +633,53 @@ namespace OpenSim.Framework.Console
449 XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", 633 XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession",
450 ""); 634 "");
451 635
452 if (c.newConnection) 636 //if (c.newConnection)
453 { 637 //{
454 c.newConnection = false; 638 // c.newConnection = false;
455 Output("+++" + DefaultPrompt); 639 // Output("+++" + DefaultPrompt);
456 } 640 //}
457 641
458 lock (m_Scrollback) 642 lock (m_Scrollback)
459 { 643 {
460 long startLine = m_LineNumber - m_Scrollback.Count; 644 long startLine = m_lineNumber - m_Scrollback.Count;
461 long sendStart = startLine; 645 long sendStart = startLine;
462 if (sendStart < c.lastLineSeen) 646 if (sendStart < c.lastLineSeen)
463 sendStart = c.lastLineSeen; 647 sendStart = c.lastLineSeen;
464 648
465 for (long i = sendStart ; i < m_LineNumber ; i++) 649 for (long i = sendStart ; i < m_lineNumber ; i++)
466 { 650 {
651 ScrollbackEntry e = m_Scrollback[(int)(i - startLine)];
652
467 XmlElement res = xmldoc.CreateElement("", "Line", ""); 653 XmlElement res = xmldoc.CreateElement("", "Line", "");
468 long line = i + 1; 654 res.SetAttribute("Number", e.lineNumber.ToString());
469 res.SetAttribute("Number", line.ToString()); 655 res.SetAttribute("Level", e.level);
470 res.AppendChild(xmldoc.CreateTextNode(m_Scrollback[(int)(i - startLine)])); 656 // Don't include these for the scrollback, we'll send the
657 // real state later.
658 if (!c.newConnection)
659 {
660 res.SetAttribute("Prompt", e.isPrompt ? "true" : "false");
661 res.SetAttribute("Command", e.isCommand ? "true" : "false");
662 res.SetAttribute("Input", e.isInput ? "true" : "false");
663 }
664 else if (i == m_lineNumber - 1) // Last line for a new connection
665 {
666 res.SetAttribute("Prompt", m_expectingInput ? "true" : "false");
667 res.SetAttribute("Command", m_expectingCommand ? "true" : "false");
668 res.SetAttribute("Input", (!m_expectingInput) ? "true" : "false");
669 }
670 else
671 {
672 res.SetAttribute("Input", e.isInput ? "true" : "false");
673 }
674
675 res.AppendChild(xmldoc.CreateTextNode(e.text));
471 676
472 rootElement.AppendChild(res); 677 rootElement.AppendChild(res);
473 } 678 }
474 } 679 }
475 c.lastLineSeen = m_LineNumber; 680
681 c.lastLineSeen = m_lineNumber;
682 c.newConnection = false;
476 683
477 xmldoc.AppendChild(rootElement); 684 xmldoc.AppendChild(rootElement);
478 685
@@ -486,7 +693,9 @@ namespace OpenSim.Framework.Console
486 return result; 693 return result;
487 } 694 }
488 695
489 private Hashtable NoEvents(UUID RequestID, UUID id) 696 // This is really just a no-op. It generates what is sent
697 // to the client if the poll times out without any events.
698 protected Hashtable NoEvents(UUID RequestID, UUID id)
490 { 699 {
491 Hashtable result = new Hashtable(); 700 Hashtable result = new Hashtable();
492 701