corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * Copyright (c) 2006-2014, openmetaverse.org |
||
3 | * All rights reserved. |
||
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 | * |
||
8 | * - Redistributions of source code must retain the above copyright notice, this |
||
9 | * list of conditions and the following disclaimer. |
||
10 | * - Neither the name of the openmetaverse.org nor the names |
||
11 | * of its contributors may be used to endorse or promote products derived from |
||
12 | * this software without specific prior written permission. |
||
13 | * |
||
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||
18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||
24 | * POSSIBILITY OF SUCH DAMAGE. |
||
25 | */ |
||
26 | |||
27 | using System; |
||
28 | using System.IO; |
||
29 | using System.Net; |
||
30 | using System.Linq; |
||
31 | using System.Collections; |
||
32 | using System.Collections.Generic; |
||
33 | using System.ComponentModel; |
||
34 | using System.Text.RegularExpressions; |
||
35 | using System.Drawing; |
||
36 | using System.Text; |
||
37 | using System.Windows.Forms; |
||
38 | using System.Reflection; |
||
39 | using System.Threading; |
||
40 | using GridProxy; |
||
41 | using OpenMetaverse; |
||
42 | using OpenMetaverse.Packets; |
||
43 | using OpenMetaverse.StructuredData; |
||
44 | using OpenMetaverse.Interfaces; |
||
45 | using System.Xml; |
||
46 | using Nwc.XmlRpc; |
||
47 | using Logger = OpenMetaverse.Logger; |
||
48 | |||
49 | namespace WinGridProxy |
||
50 | { |
||
51 | public partial class FormWinGridProxy : Form |
||
52 | { |
||
53 | // only allow one thread at a time to do file I/O operations |
||
54 | private object m_FileIOLockerObject = new object(); |
||
55 | |||
56 | // Class for saving and restoring settings |
||
57 | private static SettingsStore Store = new SettingsStore(); |
||
58 | |||
59 | private static bool m_ProxyRunning; |
||
60 | |||
61 | private Assembly m_CurrentAssembly = Assembly.GetExecutingAssembly(); |
||
62 | |||
63 | ProxyManager proxy; |
||
64 | |||
65 | private FormPluginManager pluginManager; |
||
66 | |||
67 | private int PacketCounter; |
||
68 | |||
69 | // stats tracking |
||
70 | private int CapsInCounter; |
||
71 | private int CapsInBytes; |
||
72 | private int CapsOutCounter; |
||
73 | private int CapsOutBytes; |
||
74 | private int PacketsInCounter; |
||
75 | private int PacketsInBytes; |
||
76 | private int PacketsOutCounter; |
||
77 | private int PacketsOutBytes; |
||
78 | |||
79 | private List<ListViewItem> QueuedSessions; |
||
80 | private bool monoRuntime; |
||
81 | |||
82 | private const string PROTO_CAPABILITIES = "Cap"; |
||
83 | private const string PROTO_EVENTMESSAGE = "Event"; |
||
84 | private const string PROTO_PACKETSTRING = "UDP"; |
||
85 | private const string PROTO_AUTHENTICATE = "https"; |
||
86 | |||
87 | // some default colors for session items |
||
88 | private readonly Color Color_Login = Color.OldLace; |
||
89 | private readonly Color Color_Packet = Color.LightYellow; |
||
90 | private readonly Color Color_Cap = Color.Honeydew; |
||
91 | private readonly Color Color_Event = Color.AliceBlue; |
||
92 | |||
93 | public FormWinGridProxy() |
||
94 | { |
||
95 | InitializeComponent(); |
||
96 | |||
97 | Logger.Log("WinGridProxy ready", Helpers.LogLevel.Info); |
||
98 | |||
99 | if (FireEventAppender.Instance != null) |
||
100 | { |
||
101 | FireEventAppender.Instance.MessageLoggedEvent += new MessageLoggedEventHandler(Instance_MessageLoggedEvent); |
||
102 | } |
||
103 | |||
104 | // Attempt to work around some mono bugs |
||
105 | monoRuntime = Type.GetType("Mono.Runtime") != null; // Officially supported way of detecting mono |
||
106 | if (monoRuntime) |
||
107 | { |
||
108 | Font fixedFont = new Font(FontFamily.GenericMonospace, 9f, FontStyle.Regular, GraphicsUnit.Point); |
||
109 | richTextBoxDecodedRequest.Font = |
||
110 | richTextBoxDecodedResponse.Font = |
||
111 | richTextBoxNotationRequest.Font = |
||
112 | richTextBoxNotationResponse.Font = |
||
113 | richTextBoxRawRequest.Font = |
||
114 | richTextBoxRawResponse.Font = fixedFont; |
||
115 | } |
||
116 | |||
117 | QueuedSessions = new List<ListViewItem>(); |
||
118 | |||
119 | // populate the listen box with the known IP Addresses of this host |
||
120 | IPHostEntry iphostentry = Dns.GetHostByName(Dns.GetHostName()); |
||
121 | foreach (IPAddress address in iphostentry.AddressList) |
||
122 | comboBoxListenAddress.Items.Add(address.ToString()); |
||
123 | |||
124 | ProxyManager.OnPacketLog += ProxyManager_OnPacketLog; |
||
125 | ProxyManager.OnMessageLog += ProxyManager_OnMessageLog; |
||
126 | ProxyManager.OnLoginResponse += ProxyManager_OnLoginResponse; |
||
127 | ProxyManager.OnCapabilityAdded += ProxyManager_OnCapabilityAdded; |
||
128 | ProxyManager.OnEventMessageLog += ProxyManager_OnEventMessageLog; |
||
129 | } |
||
130 | |||
131 | #region Event Handlers for Messages/Packets |
||
132 | |||
133 | /// <summary> |
||
134 | /// Adds a new EventQueue message to the Message Filters listview. |
||
135 | /// </summary> |
||
136 | /// <param name="req"></param> |
||
137 | /// <param name="stage"></param> |
||
138 | void ProxyManager_OnEventMessageLog(CapsRequest req, CapsStage stage) |
||
139 | { |
||
140 | if (this.InvokeRequired) |
||
141 | { |
||
142 | this.BeginInvoke(new MethodInvoker(() => ProxyManager_OnEventMessageLog(req, stage))); |
||
143 | } |
||
144 | else |
||
145 | { |
||
146 | ListViewItem foundCap = FindListViewItem(listViewMessageFilters, req.Info.CapType, false); |
||
147 | |||
148 | if (foundCap == null) |
||
149 | { |
||
150 | ListViewItem addedItem = listViewMessageFilters.Items.Add(new ListViewItem(req.Info.CapType, |
||
151 | listViewMessageFilters.Groups["EventQueueMessages"])); |
||
152 | |||
153 | addedItem.SubItems.Add(PROTO_EVENTMESSAGE); |
||
154 | addedItem.BackColor = Color_Event; |
||
155 | |||
156 | if (autoAddNewDiscoveredMessagesToolStripMenuItem.Checked) |
||
157 | addedItem.Checked = true; |
||
158 | } |
||
159 | else |
||
160 | { |
||
161 | ProxyManager_OnMessageLog(req, CapsStage.Response); |
||
162 | } |
||
163 | } |
||
164 | } |
||
165 | |||
166 | /// <summary> |
||
167 | /// Adds a new Capability message to the message filters listview |
||
168 | /// </summary> |
||
169 | private void ProxyManager_OnCapabilityAdded(CapInfo cap) |
||
170 | { |
||
171 | if (this.InvokeRequired) |
||
172 | { |
||
173 | this.BeginInvoke(new MethodInvoker(() => ProxyManager_OnCapabilityAdded(cap))); |
||
174 | } |
||
175 | else |
||
176 | { |
||
177 | ListViewItem foundCap = FindListViewItem(listViewMessageFilters, cap.CapType, false); |
||
178 | if (foundCap == null) |
||
179 | { |
||
180 | ListViewItem addedItem = listViewMessageFilters.Items.Add(new ListViewItem(cap.CapType, |
||
181 | listViewMessageFilters.Groups["Capabilities"])); |
||
182 | addedItem.SubItems.Add(PROTO_CAPABILITIES); |
||
183 | addedItem.BackColor = Color_Cap; |
||
184 | |||
185 | if (autoAddNewDiscoveredMessagesToolStripMenuItem.Checked) |
||
186 | addedItem.Checked = true; |
||
187 | } |
||
188 | } |
||
189 | } |
||
190 | |||
191 | /// <summary> |
||
192 | /// Handle Login Requests/Responses |
||
193 | /// </summary> |
||
194 | private void ProxyManager_OnLoginResponse(object request, Direction direction) |
||
195 | { |
||
196 | if (this.InvokeRequired) |
||
197 | { |
||
198 | this.BeginInvoke(new MethodInvoker(() => ProxyManager_OnLoginResponse(request, direction))); |
||
199 | } |
||
200 | else |
||
201 | { |
||
202 | string loginType; |
||
203 | |||
204 | if (request is XmlRpcRequest) |
||
205 | { |
||
206 | loginType = "Login Request"; |
||
207 | } |
||
208 | else |
||
209 | { |
||
210 | loginType = "Login Response"; |
||
211 | } |
||
212 | |||
213 | ListViewItem foundItem = FindListViewItem(listViewPacketFilters, loginType, false); |
||
214 | |||
215 | if (foundItem != null && foundItem.Checked == true) |
||
216 | { |
||
217 | PacketCounter++; |
||
218 | |||
219 | SessionLogin sessionLogin = new SessionLogin(request, direction, comboBoxLoginURL.Text, request.GetType().Name + " " + loginType); |
||
220 | |||
221 | ListViewItem sessionEntry = new ListViewItem(new string[] { PacketCounter.ToString(), sessionLogin.TimeStamp.ToString("HH:mm:ss.fff"), |
||
222 | sessionLogin.Protocol, sessionLogin.Name, sessionLogin.Length.ToString(), sessionLogin.Host, sessionLogin.ContentType }); |
||
223 | |||
224 | sessionEntry.Tag = sessionLogin; |
||
225 | sessionEntry.ImageIndex = (int)sessionLogin.Direction; |
||
226 | |||
227 | AddSession(sessionEntry); |
||
228 | } |
||
229 | } |
||
230 | } |
||
231 | |||
232 | // Only raised when we've told GridProxy we want a specific packet type |
||
233 | private void ProxyManager_OnPacketLog(Packet packet, Direction direction, IPEndPoint endpoint) |
||
234 | { |
||
235 | PacketCounter++; |
||
236 | |||
237 | if (direction == Direction.Incoming) |
||
238 | { |
||
239 | PacketsInCounter++; |
||
240 | PacketsInBytes += packet.Length; |
||
241 | } |
||
242 | else |
||
243 | { |
||
244 | PacketsOutCounter++; |
||
245 | PacketsOutBytes += packet.Length; |
||
246 | } |
||
247 | |||
248 | SessionPacket sessionPacket = new SessionPacket(packet, direction, endpoint, |
||
249 | PacketDecoder.InterpretOptions(packet.Header) + " Seq: " + packet.Header.Sequence.ToString() + " Freq:" + packet.Header.Frequency.ToString()); |
||
250 | |||
251 | ListViewItem sessionItem = new ListViewItem(new string[] { PacketCounter.ToString(), sessionPacket.TimeStamp.ToString("HH:mm:ss.fff"), sessionPacket.Protocol, sessionPacket.Name, sessionPacket.Length.ToString(), sessionPacket.Host, sessionPacket.ContentType }); |
||
252 | sessionItem.Tag = sessionPacket; |
||
253 | sessionItem.ImageIndex = (int)sessionPacket.Direction; |
||
254 | |||
255 | AddSession(sessionItem); |
||
256 | } |
||
257 | |||
258 | /// <summary> |
||
259 | /// Handle Capabilities |
||
260 | /// </summary> |
||
261 | private void ProxyManager_OnMessageLog(CapsRequest req, CapsStage stage) |
||
262 | { |
||
263 | if (this.InvokeRequired) |
||
264 | { |
||
265 | this.BeginInvoke(new MethodInvoker(() => ProxyManager_OnMessageLog(req, stage))); |
||
266 | } |
||
267 | else |
||
268 | { |
||
269 | ListViewItem found = FindListViewItem(listViewMessageFilters, req.Info.CapType, false); |
||
270 | |||
271 | if (found != null && found.Checked) |
||
272 | { |
||
273 | PacketCounter++; |
||
274 | |||
275 | int size = 0; |
||
276 | string contentType = String.Empty; |
||
277 | if (req.RawRequest != null) |
||
278 | { |
||
279 | size += req.RawRequest.Length; |
||
280 | contentType = req.RequestHeaders.Get("Content-Type"); //req.RequestHeaders["Content-Type"]; |
||
281 | } |
||
282 | if (req.RawResponse != null) |
||
283 | { |
||
284 | size += req.RawResponse.Length; |
||
285 | contentType = req.ResponseHeaders.Get("Content-Type"); |
||
286 | } |
||
287 | |||
288 | Direction direction; |
||
289 | if (stage == CapsStage.Request) |
||
290 | { |
||
291 | CapsOutCounter++; |
||
292 | CapsOutBytes += req.Request.ToString().Length; |
||
293 | direction = Direction.Outgoing; |
||
294 | } |
||
295 | else |
||
296 | { |
||
297 | CapsInCounter++; |
||
298 | CapsInBytes += req.Response.ToString().Length; |
||
299 | direction = Direction.Incoming; |
||
300 | } |
||
301 | |||
302 | string proto = found.SubItems[1].Text; |
||
303 | |||
304 | Session capsSession = null; |
||
305 | if (found.Group.Header.Equals("Capabilities")) |
||
306 | { |
||
307 | capsSession = new SessionCaps(req.RawRequest, req.RawResponse, req.RequestHeaders, |
||
308 | req.ResponseHeaders, direction, req.Info.URI, req.Info.CapType, proto, req.FullUri); |
||
309 | } |
||
310 | else |
||
311 | { |
||
312 | capsSession = new SessionEvent(req.RawResponse, req.ResponseHeaders, req.Info.URI, req.Info.CapType, proto); |
||
313 | } |
||
314 | |||
315 | string[] s = { PacketCounter.ToString(), capsSession.TimeStamp.ToString("HH:mm:ss.fff"), capsSession.Protocol, capsSession.Name, capsSession.Length.ToString(), capsSession.Host, capsSession.ContentType }; |
||
316 | ListViewItem session = new ListViewItem(s); |
||
317 | |||
318 | session.ImageIndex = (int)direction; |
||
319 | session.Tag = capsSession; |
||
320 | |||
321 | session.BackColor = found.BackColor; |
||
322 | |||
323 | AddSession(session); |
||
324 | } |
||
325 | else |
||
326 | { |
||
327 | if (found == null) |
||
328 | { |
||
329 | // must be a new event not in KnownCaps, lets add it to the listview |
||
330 | ListViewItem addedItem = listViewMessageFilters.Items.Add(new ListViewItem(req.Info.CapType)); |
||
331 | addedItem.BackColor = Color_Cap; |
||
332 | |||
333 | if (autoAddNewDiscoveredMessagesToolStripMenuItem.Checked) |
||
334 | addedItem.Checked = true; |
||
335 | } |
||
336 | } |
||
337 | } |
||
338 | } |
||
339 | |||
340 | #endregion |
||
341 | |||
342 | #region Helpers |
||
343 | |||
344 | private ListViewItem FindListViewItem(ListView listView, string key, bool searchAll) |
||
345 | { |
||
346 | foreach (ListViewItem item in listView.Items) |
||
347 | { |
||
348 | if (item.Text.Equals(key) |
||
349 | || (searchAll && item.SubItems.ContainsKey(key))) |
||
350 | return item; |
||
351 | } |
||
352 | return null; |
||
353 | } |
||
354 | |||
355 | |||
356 | private static string FormatBytes(long bytes) |
||
357 | { |
||
358 | const int scale = 1024; |
||
359 | string[] orders = new string[] { "GB", "MB", "KB", "Bytes" }; |
||
360 | long max = (long)Math.Pow(scale, orders.Length - 1); |
||
361 | |||
362 | foreach (string order in orders) |
||
363 | { |
||
364 | if (bytes > max) |
||
365 | return string.Format("{0:##.##} {1}", decimal.Divide(bytes, max), order); |
||
366 | |||
367 | max /= scale; |
||
368 | } |
||
369 | return "0"; |
||
370 | } |
||
371 | #endregion |
||
372 | |||
373 | #region GUI Event Handlers |
||
374 | |||
375 | private void buttonStartProxy_Click(object sender, EventArgs e) |
||
376 | { |
||
377 | if (buttonStartProxy.Text.StartsWith("Start") && m_ProxyRunning.Equals(false)) |
||
378 | { |
||
379 | proxy = new ProxyManager(textBoxProxyPort.Text, comboBoxListenAddress.Text, comboBoxLoginURL.Text); |
||
380 | // disable any gui elements |
||
381 | comboBoxListenAddress.Enabled = textBoxProxyPort.Enabled = comboBoxLoginURL.Enabled = false; |
||
382 | |||
383 | InitProxyFilters(); |
||
384 | |||
385 | proxy.Start(); |
||
386 | |||
387 | loadFilterSelectionsToolStripMenuItem.Enabled = saveFilterSelectionsToolStripMenuItem.Enabled = true; |
||
388 | |||
389 | // enable any gui elements |
||
390 | toolStripSplitButton1.Enabled = |
||
391 | toolStripMenuItemPlugins.Enabled = grpUDPFilters.Enabled = grpCapsFilters.Enabled = m_ProxyRunning = true; |
||
392 | buttonStartProxy.Text = "Stop Proxy"; |
||
393 | buttonStartProxy.Checked = true; |
||
394 | if (enableStatisticsToolStripMenuItem.Checked && !timer1.Enabled) |
||
395 | timer1.Enabled = true; |
||
396 | } |
||
397 | else if (buttonStartProxy.Text.StartsWith("Stop") && m_ProxyRunning.Equals(true)) |
||
398 | { |
||
399 | loadFilterSelectionsToolStripMenuItem.Enabled = saveFilterSelectionsToolStripMenuItem.Enabled = false; |
||
400 | // stop the proxy |
||
401 | proxy.Stop(); |
||
402 | toolStripMenuItemPlugins.Enabled = grpUDPFilters.Enabled = grpCapsFilters.Enabled = m_ProxyRunning = false; |
||
403 | buttonStartProxy.Text = "Start Proxy"; |
||
404 | buttonStartProxy.Checked = false; |
||
405 | comboBoxListenAddress.Enabled = textBoxProxyPort.Enabled = comboBoxLoginURL.Enabled = true; |
||
406 | |||
407 | if (!enableStatisticsToolStripMenuItem.Checked && timer1.Enabled) |
||
408 | timer1.Enabled = false; |
||
409 | } |
||
410 | } |
||
411 | |||
412 | private void checkBoxCheckAllPackets_CheckedChanged(object sender, EventArgs e) |
||
413 | { |
||
414 | foreach (ListViewItem item in listViewPacketFilters.Items) |
||
415 | { |
||
416 | item.Checked = checkBoxCheckAllPackets.Checked; |
||
417 | } |
||
418 | } |
||
419 | |||
420 | private void listViewSessions_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) |
||
421 | { |
||
422 | |||
423 | if (e.IsSelected && listViewSessions.SelectedItems.Count == 1) |
||
424 | { |
||
425 | tabControlMain.SelectTab("tabPageInspect"); |
||
426 | |||
427 | object tag = e.Item.Tag; |
||
428 | |||
429 | if (tag is Session) |
||
430 | { |
||
431 | Session session = (Session)tag; |
||
432 | |||
433 | treeViewXmlResponse.Nodes.Clear(); |
||
434 | treeViewXMLRequest.Nodes.Clear(); |
||
435 | |||
436 | Be.Windows.Forms.DynamicByteProvider responseBytes = new Be.Windows.Forms.DynamicByteProvider(session.ToBytes(Direction.Incoming)); |
||
437 | richTextBoxDecodedResponse.Text = session.ToPrettyString(Direction.Incoming); |
||
438 | richTextBoxRawResponse.Text = session.ToRawString(Direction.Incoming); |
||
439 | richTextBoxNotationResponse.Text = session.ToStringNotation(Direction.Incoming); |
||
440 | hexBoxResponse.ByteProvider = responseBytes; |
||
441 | updateTreeView(session.ToXml(Direction.Incoming), treeViewXmlResponse); |
||
442 | |||
443 | Be.Windows.Forms.DynamicByteProvider requestBytes = new Be.Windows.Forms.DynamicByteProvider(session.ToBytes(Direction.Outgoing)); |
||
444 | richTextBoxDecodedRequest.Text = session.ToPrettyString(Direction.Outgoing); |
||
445 | richTextBoxRawRequest.Text = session.ToRawString(Direction.Outgoing); |
||
446 | richTextBoxNotationRequest.Text = session.ToStringNotation(Direction.Outgoing); |
||
447 | hexBoxRequest.ByteProvider = requestBytes; |
||
448 | updateTreeView(session.ToXml(Direction.Outgoing), treeViewXMLRequest); |
||
449 | |||
450 | RequestPosition_Changed(this, EventArgs.Empty); |
||
451 | ReplyPosition_Changed(this, EventArgs.Empty); |
||
452 | } |
||
453 | else |
||
454 | { |
||
455 | richTextBoxDecodedResponse.Text = "Unknown data object encountered: " + tag.GetType().ToString(); |
||
456 | } |
||
457 | |||
458 | } |
||
459 | } |
||
460 | |||
461 | private void Form1_FormClosing(object sender, FormClosingEventArgs e) |
||
462 | { |
||
463 | if (m_ProxyRunning) |
||
464 | proxy.Stop(); |
||
465 | |||
466 | if (saveOptionsOnExitToolStripMenuItem.Checked) |
||
467 | SaveAllSettings("settings.osd"); |
||
468 | } |
||
469 | |||
470 | // select all items in session list |
||
471 | private void sessionSelectAll_Click(object sender, EventArgs e) |
||
472 | { |
||
473 | foreach (ListViewItem item in listViewSessions.Items) |
||
474 | { |
||
475 | item.Selected = true; |
||
476 | } |
||
477 | } |
||
478 | |||
479 | // unselect all items in session list |
||
480 | private void sessionSelectNone_Click(object sender, EventArgs e) |
||
481 | { |
||
482 | foreach (ListViewItem item in listViewSessions.Items) |
||
483 | { |
||
484 | item.Selected = false; |
||
485 | } |
||
486 | } |
||
487 | |||
488 | // invert selection |
||
489 | private void sessionInvertSelection_Click(object sender, EventArgs e) |
||
490 | { |
||
491 | foreach (ListViewItem item in listViewSessions.Items) |
||
492 | { |
||
493 | item.Selected = !item.Selected; |
||
494 | } |
||
495 | } |
||
496 | |||
497 | // remove all sessions |
||
498 | private void sessionRemoveAll_Click(object sender, EventArgs e) |
||
499 | { |
||
500 | listViewSessions.Items.Clear(); |
||
501 | } |
||
502 | |||
503 | // remove sessions that are currently selected |
||
504 | private void sessionRemoveSelected_Click(object sender, EventArgs e) |
||
505 | { |
||
506 | foreach (ListViewItem item in listViewSessions.Items) |
||
507 | { |
||
508 | if (item.Selected) |
||
509 | listViewSessions.Items.Remove(item); |
||
510 | } |
||
511 | } |
||
512 | |||
513 | // remove sessions that are not currently selected |
||
514 | private void sessionRemoveUnselected_Click(object sender, EventArgs e) |
||
515 | { |
||
516 | foreach (ListViewItem item in listViewSessions.Items) |
||
517 | { |
||
518 | if (!item.Selected) |
||
519 | listViewSessions.Items.Remove(item); |
||
520 | } |
||
521 | } |
||
522 | |||
523 | // Colorize selected sessions |
||
524 | private void sessionMarkSelected_Click(object sender, EventArgs e) |
||
525 | { |
||
526 | ToolStripMenuItem menu = (ToolStripMenuItem)sender; |
||
527 | |||
528 | foreach (ListViewItem item in listViewSessions.Items) |
||
529 | { |
||
530 | if (item.Selected) |
||
531 | item.BackColor = Color.FromName(menu.Text); |
||
532 | } |
||
533 | sessionSelectNone_Click(sender, e); |
||
534 | } |
||
535 | |||
536 | // Unmark selected sessions |
||
537 | private void sessionUnmarkSelected_Click(object sender, EventArgs e) |
||
538 | { |
||
539 | foreach (ListViewItem item in listViewSessions.Items) |
||
540 | { |
||
541 | if (item.Selected) |
||
542 | item.BackColor = Color.White; |
||
543 | } |
||
544 | sessionSelectNone_Click(sender, e); |
||
545 | } |
||
546 | |||
547 | private void aboutWinGridProxyToolStripMenuItem_Click(object sender, EventArgs e) |
||
548 | { |
||
549 | AboutBox1 about = new AboutBox1(); |
||
550 | about.ShowDialog(); |
||
551 | } |
||
552 | |||
553 | // Update Request Hexbox status bar with current cursor location |
||
554 | private void RequestPosition_Changed(object sender, EventArgs e) |
||
555 | { |
||
556 | if (hexBoxRequest.ByteProvider != null) |
||
557 | { |
||
558 | labelHexRequestStatus.Text = String.Format("Ln {0} Col {1} bytes {2}", |
||
559 | hexBoxRequest.CurrentLine, hexBoxRequest.CurrentPositionInLine, hexBoxRequest.ByteProvider.Length); |
||
560 | buttonSaveRequestHex.Visible = (hexBoxRequest.ByteProvider.Length > 0); |
||
561 | } |
||
562 | buttonSaveRequestHex.Visible = (hexBoxRequest.ByteProvider != null && hexBoxRequest.ByteProvider.Length > 0); |
||
563 | } |
||
564 | |||
565 | // Update Response Hexbox status bar with current cursor location |
||
566 | void ReplyPosition_Changed(object sender, EventArgs e) |
||
567 | { |
||
568 | if (hexBoxResponse.ByteProvider != null) |
||
569 | { |
||
570 | labelHexBoxResponseStatus.Text = String.Format("Ln {0} Col {1} bytes {2}", |
||
571 | hexBoxResponse.CurrentLine, hexBoxResponse.CurrentPositionInLine, hexBoxResponse.ByteProvider.Length); |
||
572 | } |
||
573 | buttonExportRawHex.Visible = (hexBoxResponse.ByteProvider != null && hexBoxResponse.ByteProvider.Length > 0); |
||
574 | } |
||
575 | |||
576 | // select all specified sessions by packet name |
||
577 | private void sessionSelectAllPacketType_Click(object sender, EventArgs e) |
||
578 | { |
||
579 | foreach (ListViewItem item in listViewSessions.Items) |
||
580 | { |
||
581 | if (item.SubItems[3].Text.Equals(toolStripMenuItemSelectPacketName.Tag) && !item.Selected) |
||
582 | item.Selected = true; |
||
583 | } |
||
584 | } |
||
585 | |||
586 | // stop capturing selected filters |
||
587 | private void filterDisableByPacketName_CheckedChanged(object sender, EventArgs e) |
||
588 | { |
||
589 | if (enableDisableFilterByNameToolStripMenuItem.Tag != null) |
||
590 | { |
||
591 | ListViewItem found = FindListViewItem(listViewMessageFilters, enableDisableFilterByNameToolStripMenuItem.Tag.ToString(), false); |
||
592 | |||
593 | if (found != null) |
||
594 | { |
||
595 | listViewMessageFilters.Items[found.Index].Checked = enableDisableFilterByNameToolStripMenuItem.Checked; |
||
596 | } |
||
597 | else |
||
598 | { |
||
599 | found = FindListViewItem(listViewPacketFilters, enableDisableFilterByNameToolStripMenuItem.Tag.ToString(), false); |
||
600 | |||
601 | if (found != null) |
||
602 | listViewPacketFilters.Items[found.Index].Checked = enableDisableFilterByNameToolStripMenuItem.Checked; |
||
603 | } |
||
604 | } |
||
605 | } |
||
606 | |||
607 | /// <summary> |
||
608 | /// Setup the context menu prior to it being displayed with specific entries for filtering packets/messages |
||
609 | /// </summary> |
||
610 | /// <param name="sender"></param> |
||
611 | /// <param name="e"></param> |
||
612 | private void contextMenuStripSessions_Opening(object sender, CancelEventArgs e) |
||
613 | { |
||
614 | if (listViewSessions.SelectedItems.Count == 1) |
||
615 | { |
||
616 | ListViewItem item = listViewSessions.SelectedItems[0]; |
||
617 | string strPacketOrMessage = (item.SubItems[2].Text.Equals(PROTO_PACKETSTRING)) ? "Packets" : "Messages"; |
||
618 | |||
619 | enableDisableFilterByNameToolStripMenuItem.Text = String.Format("Capture {0} {1}", item.SubItems[3].Text, strPacketOrMessage); |
||
620 | toolStripMenuItemSelectPacketName.Tag = enableDisableFilterByNameToolStripMenuItem.Tag = item.SubItems[3].Text; |
||
621 | |||
622 | toolStripMenuItemSelectPacketName.Text = String.Format("All {0} {1}", item.SubItems[3].Text, strPacketOrMessage); |
||
623 | |||
624 | enableDisableFilterByNameToolStripMenuItem.Visible = |
||
625 | toolStripSeparatorSelectPacketProto.Visible = |
||
626 | toolStripSeparatorFilterPacketByName.Visible = |
||
627 | toolStripMenuItemSelectPacketName.Visible = true; |
||
628 | |||
629 | // find checkstate of selected menuitem in packets or messages filters checkedListBoxes |
||
630 | bool ctxChecked = false; |
||
631 | |||
632 | if (strPacketOrMessage.Equals("Packets")) |
||
633 | { |
||
634 | ListViewItem found = FindListViewItem(listViewPacketFilters, toolStripMenuItemSelectPacketName.Tag.ToString(), false); |
||
635 | if (found != null) |
||
636 | ctxChecked = found.Checked; |
||
637 | } |
||
638 | else if (strPacketOrMessage.Equals("Messages")) |
||
639 | { |
||
640 | ListViewItem found = FindListViewItem(listViewMessageFilters, toolStripMenuItemSelectPacketName.Tag.ToString(), false); |
||
641 | if (found != null) |
||
642 | ctxChecked = found.Checked; |
||
643 | } |
||
644 | enableDisableFilterByNameToolStripMenuItem.Checked = ctxChecked; |
||
645 | } |
||
646 | else |
||
647 | { |
||
648 | // Hide specific selection options on context menu |
||
649 | enableDisableFilterByNameToolStripMenuItem.Visible = |
||
650 | toolStripSeparatorSelectPacketProto.Visible = |
||
651 | toolStripSeparatorFilterPacketByName.Visible = |
||
652 | toolStripMenuItemSelectPacketName.Visible = false; |
||
653 | } |
||
654 | |||
655 | if (listViewSessions.Items.Count > 0) |
||
656 | { |
||
657 | markToolStripMenuItem2.Enabled = |
||
658 | findToolStripMenuItem1.Enabled = |
||
659 | toolStripMenuSessionsRemove.Enabled = |
||
660 | selectToolStripMenuItem2.Enabled = true; |
||
661 | } |
||
662 | else |
||
663 | { |
||
664 | markToolStripMenuItem2.Enabled = |
||
665 | findToolStripMenuItem1.Enabled = |
||
666 | toolStripMenuSessionsRemove.Enabled = |
||
667 | selectToolStripMenuItem2.Enabled = false; |
||
668 | } |
||
669 | } |
||
670 | |||
671 | private void findSessions_Click(object sender, EventArgs e) |
||
672 | { |
||
673 | FilterOptions opts = new FilterOptions((listViewSessions.SelectedItems.Count > 0)); |
||
674 | FormSessionSearch search = new FormSessionSearch(ref opts); |
||
675 | search.ShowDialog(); |
||
676 | |||
677 | if (!String.IsNullOrEmpty(opts.SearchText)) |
||
678 | { |
||
679 | Thread sThread = new Thread(delegate() |
||
680 | { |
||
681 | SearchSessions(opts); |
||
682 | }); |
||
683 | sThread.Name = "Search"; |
||
684 | sThread.Start(); |
||
685 | } |
||
686 | } |
||
687 | |||
688 | // Enable Inject button if box contains text |
||
689 | private void richTextBoxInject_TextChanged(object sender, EventArgs e) |
||
690 | { |
||
691 | buttonInjectPacket.Enabled = (richTextBoxInject.TextLength > 0); |
||
692 | } |
||
693 | |||
694 | private void buttonInjectPacket_Click(object sender, EventArgs e) |
||
695 | { |
||
696 | proxy.InjectPacket(richTextBoxInject.Text, true); |
||
697 | } |
||
698 | |||
699 | private void saveFilterSelectionsToolStripMenuItem_Click(object sender, EventArgs e) |
||
700 | { |
||
701 | if (saveFileDialog2.ShowDialog() == DialogResult.OK) |
||
702 | { |
||
703 | SaveAllSettings(saveFileDialog2.FileName); |
||
704 | } |
||
705 | } |
||
706 | |||
707 | private void loadFilterSelectionsToolStripMenuItem_Click(object sender, EventArgs e) |
||
708 | { |
||
709 | if (openFileDialog2.ShowDialog() == DialogResult.OK) |
||
710 | { |
||
711 | RestoreSavedSettings(openFileDialog2.FileName); |
||
712 | if (listViewSessions.Items.Count > 0) |
||
713 | { |
||
714 | if (MessageBox.Show("Would you like to apply these settings to the current session list?", |
||
715 | "Apply Filter", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes) |
||
716 | { |
||
717 | listViewSessions.BeginUpdate(); |
||
718 | foreach (ListViewItem item in listViewSessions.Items) |
||
719 | { |
||
720 | ListViewItem found = FindListViewItem(listViewPacketFilters, item.SubItems[3].Text, false); |
||
721 | if (found == null) |
||
722 | found = FindListViewItem(listViewMessageFilters, item.SubItems[3].Text, false); |
||
723 | |||
724 | if (found != null && !found.Checked) |
||
725 | listViewSessions.Items.Remove(item); |
||
726 | } |
||
727 | listViewSessions.EndUpdate(); |
||
728 | } |
||
729 | } |
||
730 | } |
||
731 | } |
||
732 | |||
733 | private void listViewMessageFilters_ItemChecked(object sender, ItemCheckedEventArgs e) |
||
734 | { |
||
735 | proxy.AddCapsDelegate(e.Item.Text, e.Item.Checked); |
||
736 | } |
||
737 | |||
738 | private void listViewPacketFilters_ItemChecked(object sender, ItemCheckedEventArgs e) |
||
739 | { |
||
740 | if (e.Item.Group.Name.Equals("Packets")) |
||
741 | proxy.AddUDPDelegate(PacketTypeFromName(e.Item.Text), e.Item.Checked); |
||
742 | } |
||
743 | |||
744 | private void checkBoxCheckallCaps_CheckedChanged(object sender, EventArgs e) |
||
745 | { |
||
746 | foreach (ListViewItem item in listViewMessageFilters.Items) |
||
747 | { |
||
748 | item.Checked = checkBoxCheckAllMessages.Checked; |
||
749 | } |
||
750 | } |
||
751 | |||
752 | /// <summary> |
||
753 | /// Start/Stop the statistics gathering timer |
||
754 | /// </summary> |
||
755 | /// <param name="sender"></param> |
||
756 | /// <param name="e"></param> |
||
757 | private void enableStatisticsToolStripMenuItem_CheckedChanged(object sender, EventArgs e) |
||
758 | { |
||
759 | if (timer1.Enabled && !enableStatisticsToolStripMenuItem.Checked) |
||
760 | timer1.Enabled = false; |
||
761 | |||
762 | if (!timer1.Enabled && enableStatisticsToolStripMenuItem.Checked) |
||
763 | timer1.Enabled = true; |
||
764 | } |
||
765 | |||
766 | private void saveSessionArchiveToolStripMenuItem_Click(object sender, EventArgs e) |
||
767 | { |
||
768 | if (saveFileDialog1.ShowDialog() == DialogResult.OK) |
||
769 | { |
||
770 | OSDMap map = new OSDMap(1); |
||
771 | OSDArray sessionArray = new OSDArray(); |
||
772 | foreach (ListViewItem item in listViewSessions.Items) |
||
773 | { |
||
774 | if (item.Tag is Session) |
||
775 | { |
||
776 | Session data = null; |
||
777 | if (item.Tag is SessionCaps) |
||
778 | { |
||
779 | data = (SessionCaps)item.Tag; |
||
780 | } |
||
781 | else if (item.Tag is SessionEvent) |
||
782 | { |
||
783 | data = (SessionEvent)item.Tag; |
||
784 | } |
||
785 | else if (item.Tag is SessionLogin) |
||
786 | { |
||
787 | data = (SessionLogin)item.Tag; |
||
788 | } |
||
789 | else if (item.Tag is SessionPacket) |
||
790 | { |
||
791 | data = (SessionPacket)item.Tag; |
||
792 | } |
||
793 | else |
||
794 | { |
||
795 | Console.WriteLine("Not a valid session type?"); |
||
796 | continue; |
||
797 | } |
||
798 | //Type t = item.Tag.GetType(); |
||
799 | |||
800 | //Session data = (SessionCaps)item.Tag; |
||
801 | OSDMap session = new OSDMap(); |
||
802 | //session["name"] = OSD.FromString(item.Name); |
||
803 | //session["image_index"] = OSD.FromInteger(item.ImageIndex); |
||
804 | session["id"] = OSD.FromString(item.SubItems[0].Text); |
||
805 | //session["protocol"] = OSD.FromString(item.SubItems[1].Text); |
||
806 | //session["packet"] = OSD.FromString(item.SubItems[2].Text); |
||
807 | //session["size"] = OSD.FromString(item.SubItems[3].Text); |
||
808 | //session["host"] = OSD.FromString(item.SubItems[4].Text); |
||
809 | session["type"] = OSD.FromString(data.GetType().ToString()); |
||
810 | session["tag"] = OSD.FromBinary(data.Serialize()); |
||
811 | sessionArray.Add(session); |
||
812 | } |
||
813 | } |
||
814 | |||
815 | map["sessions"] = sessionArray; |
||
816 | |||
817 | try |
||
818 | { |
||
819 | File.WriteAllText(saveFileDialog1.FileName, map.ToString()); |
||
820 | } |
||
821 | catch (Exception ex) |
||
822 | { |
||
823 | MessageBox.Show("Exception occurred trying to save session archive: " + ex); |
||
824 | } |
||
825 | } |
||
826 | } |
||
827 | |||
828 | private void loadSessionArchiveToolStripMenuItem_Click(object sender, EventArgs e) |
||
829 | { |
||
830 | if (openFileDialog1.ShowDialog() == DialogResult.OK) |
||
831 | { |
||
832 | OSDMap map = (OSDMap)OSDParser.DeserializeLLSDNotation(File.ReadAllText(openFileDialog1.FileName)); |
||
833 | |||
834 | OSDArray sessionsArray = (OSDArray)map["sessions"]; |
||
835 | |||
836 | listViewSessions.Items.Clear(); |
||
837 | listViewSessions.BeginUpdate(); |
||
838 | for (int i = 0; i < sessionsArray.Count; i++) |
||
839 | { |
||
840 | OSDMap session = (OSDMap)sessionsArray[i]; |
||
841 | |||
842 | Session importedSession = (Session)m_CurrentAssembly.CreateInstance(session["type"].AsString()); |
||
843 | importedSession.Deserialize(session["tag"].AsBinary()); |
||
844 | |||
845 | ListViewItem addedItem = new ListViewItem(new string[] { |
||
846 | session["id"].AsString(), |
||
847 | importedSession.TimeStamp.ToString("HH:mm:ss.fff"), |
||
848 | importedSession.Protocol, |
||
849 | importedSession.Name, |
||
850 | importedSession.Length.ToString(), |
||
851 | importedSession.Host, |
||
852 | importedSession.ContentType}); |
||
853 | AddSession(addedItem); |
||
854 | //addedItem.ImageIndex = session["image_index"].AsInteger(); |
||
855 | addedItem.ImageIndex = (int)importedSession.Direction; |
||
856 | addedItem.BackColor = Color.GhostWhite; // give imported items a different color |
||
857 | addedItem.Tag = importedSession; |
||
858 | } |
||
859 | |||
860 | listViewSessions.EndUpdate(); |
||
861 | } |
||
862 | } |
||
863 | |||
864 | //Generic ListView sort event used by filter listviews only |
||
865 | private void listViewFilterSorter_ColumnClick(object sender, ColumnClickEventArgs e) |
||
866 | { |
||
867 | ListView lv = (ListView)sender; |
||
868 | ListViewItemComparer columnSorter = new ListViewItemComparer(); |
||
869 | columnSorter.column = e.Column; |
||
870 | |||
871 | if ((columnSorter.bAscending = (lv.Sorting == SortOrder.Ascending))) |
||
872 | lv.Sorting = SortOrder.Descending; |
||
873 | else |
||
874 | lv.Sorting = SortOrder.Ascending; |
||
875 | |||
876 | lv.ListViewItemSorter = columnSorter as IComparer; |
||
877 | } |
||
878 | |||
879 | private void exitToolStripMenuItem1_Click(object sender, EventArgs e) |
||
880 | { |
||
881 | // TODO: warn if client is connected! |
||
882 | |||
883 | this.Close(); |
||
884 | } |
||
885 | #endregion GUI Event Handlers |
||
886 | |||
887 | #region Helpers |
||
888 | |||
889 | private void SaveAllSettings(string fileName) |
||
890 | { |
||
891 | Store.MessageSessions.Clear(); |
||
892 | Store.PacketSessions.Clear(); |
||
893 | |||
894 | foreach (ListViewItem item in listViewPacketFilters.Items) |
||
895 | { |
||
896 | FilterEntryOptions entry = new FilterEntryOptions(); |
||
897 | entry.Checked = item.Checked; |
||
898 | entry.Type = item.SubItems[1].Text; |
||
899 | entry.Group = item.Group.Name; |
||
900 | |||
901 | if (!Store.PacketSessions.ContainsKey(item.Text)) |
||
902 | Store.PacketSessions.Add(item.Text, entry); |
||
903 | } |
||
904 | |||
905 | foreach (ListViewItem item in listViewMessageFilters.Items) |
||
906 | { |
||
907 | FilterEntryOptions entry = new FilterEntryOptions(); |
||
908 | entry.Checked = item.Checked; |
||
909 | entry.Type = item.SubItems[1].Text; |
||
910 | entry.Group = item.Group.Name; |
||
911 | |||
912 | if (!Store.MessageSessions.ContainsKey(item.Text)) |
||
913 | Store.MessageSessions.Add(item.Text, entry); |
||
914 | } |
||
915 | |||
916 | Store.StatisticsEnabled = enableStatisticsToolStripMenuItem.Checked; |
||
917 | Store.SaveSessionOnExit = saveOptionsOnExitToolStripMenuItem.Checked; |
||
918 | Store.AutoCheckNewCaps = autoAddNewDiscoveredMessagesToolStripMenuItem.Checked; |
||
919 | |||
920 | Store.SerializeToFile(fileName); |
||
921 | } |
||
922 | |||
923 | private void RestoreSavedSettings(string fileName) |
||
924 | { |
||
925 | // load saved settings from OSD Formatted file |
||
926 | |||
927 | if (Store.DeserializeFromFile(fileName)) |
||
928 | { |
||
929 | enableStatisticsToolStripMenuItem.Checked = Store.StatisticsEnabled; |
||
930 | saveOptionsOnExitToolStripMenuItem.Checked = Store.SaveSessionOnExit; |
||
931 | autoAddNewDiscoveredMessagesToolStripMenuItem.Checked = Store.AutoCheckNewCaps; |
||
932 | |||
933 | // Update message filter listview |
||
934 | listViewMessageFilters.BeginUpdate(); |
||
935 | foreach (KeyValuePair<string, FilterEntryOptions> kvp in Store.MessageSessions) |
||
936 | { |
||
937 | ListViewItem foundMessage = FindListViewItem(listViewPacketFilters, kvp.Key, false); |
||
938 | if (foundMessage == null) |
||
939 | { |
||
940 | ListViewItem addedItem = listViewMessageFilters.Items.Add( |
||
941 | new ListViewItem(kvp.Key, listViewMessageFilters.Groups[kvp.Value.Group])); |
||
942 | addedItem.Name = kvp.Key; |
||
943 | addedItem.Checked = kvp.Value.Checked; |
||
944 | addedItem.SubItems.Add(kvp.Value.Type); |
||
945 | //addedItem.Group = listViewMessageFilters.Groups[kvp.Value.Group]; |
||
946 | |||
947 | addedItem.BackColor = (kvp.Value.Type.Equals(PROTO_CAPABILITIES)) ? Color_Cap : Color_Event; |
||
948 | } |
||
949 | else |
||
950 | { |
||
951 | foundMessage.Checked = kvp.Value.Checked; |
||
952 | } |
||
953 | if (kvp.Value.Type.Equals(PROTO_CAPABILITIES)) |
||
954 | { |
||
955 | proxy.AddCapsDelegate(kvp.Key, kvp.Value.Checked); |
||
956 | } |
||
957 | } |
||
958 | listViewMessageFilters.EndUpdate(); |
||
959 | |||
960 | // updateTreeView packet filter listview |
||
961 | listViewPacketFilters.BeginUpdate(); |
||
962 | foreach (KeyValuePair<string, FilterEntryOptions> kvp in Store.PacketSessions) |
||
963 | { |
||
964 | ListViewItem foundPacket = FindListViewItem(listViewPacketFilters, kvp.Key, false); |
||
965 | if (foundPacket == null) |
||
966 | { |
||
967 | ListViewItem addedItem = listViewPacketFilters.Items.Add( |
||
968 | new ListViewItem(kvp.Key, listViewPacketFilters.Groups[kvp.Value.Group])); |
||
969 | |||
970 | addedItem.Name = kvp.Key; |
||
971 | addedItem.Checked = kvp.Value.Checked; |
||
972 | addedItem.SubItems.Add(kvp.Value.Type); |
||
973 | |||
974 | addedItem.BackColor = (kvp.Value.Type.Equals(PROTO_AUTHENTICATE)) ? Color_Login : Color_Packet; |
||
975 | } |
||
976 | else |
||
977 | { |
||
978 | foundPacket.Checked = kvp.Value.Checked; |
||
979 | } |
||
980 | if (kvp.Value.Type.Equals(PROTO_PACKETSTRING)) |
||
981 | { |
||
982 | proxy.AddUDPDelegate(PacketTypeFromName(kvp.Key), kvp.Value.Checked); |
||
983 | } |
||
984 | } |
||
985 | listViewPacketFilters.EndUpdate(); |
||
986 | } |
||
987 | } |
||
988 | |||
989 | private void InitProxyFilters() |
||
990 | { |
||
991 | RestoreSavedSettings("settings.osd"); |
||
992 | |||
993 | listViewPacketFilters.BeginUpdate(); |
||
994 | foreach (string name in Enum.GetNames(typeof(PacketType))) |
||
995 | { |
||
996 | ListViewItem found = FindListViewItem(listViewPacketFilters, name, false); |
||
997 | if (!String.IsNullOrEmpty(name) && found == null) |
||
998 | { |
||
999 | ListViewItem addedItem = listViewPacketFilters.Items.Add(new ListViewItem(name, listViewPacketFilters.Groups["Packets"])); |
||
1000 | addedItem.Name = name; |
||
1001 | addedItem.SubItems.Add(PROTO_PACKETSTRING); |
||
1002 | } |
||
1003 | |||
1004 | } |
||
1005 | |||
1006 | ListViewItem tmp; |
||
1007 | if (!listViewPacketFilters.Items.ContainsKey("Login Request")) |
||
1008 | { |
||
1009 | tmp = listViewPacketFilters.Items.Add(new ListViewItem("Login Request", listViewPacketFilters.Groups["Login"])); |
||
1010 | tmp.Name = "Login Request"; |
||
1011 | tmp.BackColor = Color_Login; |
||
1012 | tmp.SubItems.Add("Login"); |
||
1013 | } |
||
1014 | |||
1015 | if (!listViewPacketFilters.Items.ContainsKey("Login Response")) |
||
1016 | { |
||
1017 | tmp = listViewPacketFilters.Items.Add(new ListViewItem("Login Response", listViewPacketFilters.Groups["Login"])); |
||
1018 | tmp.Name = "Login Response"; |
||
1019 | tmp.BackColor = Color_Login; |
||
1020 | tmp.SubItems.Add("Login"); |
||
1021 | } |
||
1022 | |||
1023 | listViewPacketFilters.EndUpdate(); |
||
1024 | |||
1025 | } |
||
1026 | |||
1027 | private static PacketType PacketTypeFromName(string name) |
||
1028 | { |
||
1029 | Type packetTypeType = typeof(PacketType); |
||
1030 | System.Reflection.FieldInfo f = packetTypeType.GetField(name); |
||
1031 | if (f == null) |
||
1032 | {//throw new ArgumentException("Bad packet type"); |
||
1033 | return PacketType.Error; |
||
1034 | } |
||
1035 | |||
1036 | return (PacketType)Enum.ToObject(packetTypeType, (int)f.GetValue(packetTypeType)); |
||
1037 | } |
||
1038 | |||
1039 | private void SearchSessions(FilterOptions opts) |
||
1040 | { |
||
1041 | if (this.InvokeRequired) |
||
1042 | { |
||
1043 | this.BeginInvoke(new MethodInvoker(() => SearchSessions(opts))); |
||
1044 | } |
||
1045 | else |
||
1046 | { |
||
1047 | int resultCount = 0; |
||
1048 | |||
1049 | foreach (ListViewItem item in listViewSessions.Items) |
||
1050 | { |
||
1051 | if (opts.UnMarkPrevious) |
||
1052 | item.BackColor = Color.White; |
||
1053 | |||
1054 | if (opts.SearchSelected && !item.Selected) |
||
1055 | { |
||
1056 | continue; |
||
1057 | } |
||
1058 | |||
1059 | if ( |
||
1060 | (opts.MatchCase |
||
1061 | && (item.SubItems[3].Text.Contains(opts.SearchText) |
||
1062 | /*|| TagToString(item.Tag, item.SubItems[2].Text).Contains(opts.SearchText)*/) |
||
1063 | ) // no case matching |
||
1064 | || ((item.SubItems[3].Text.ToLower().Contains(opts.SearchText.ToLower()) |
||
1065 | /*|| TagToString(item.Tag, item.SubItems[2].Text).ToLower().Contains(opts.SearchText.ToLower())*/ |
||
1066 | )) |
||
1067 | ) |
||
1068 | { |
||
1069 | resultCount++; |
||
1070 | |||
1071 | if (opts.MarkMatches) |
||
1072 | item.BackColor = opts.HighlightMatchColor; |
||
1073 | |||
1074 | if (opts.SelectResults) |
||
1075 | item.Selected = true; |
||
1076 | else |
||
1077 | item.Selected = false; |
||
1078 | } |
||
1079 | } |
||
1080 | |||
1081 | //toolStripMainLabel.Text = String.Format("Search found {0} Matches", resultCount); |
||
1082 | } |
||
1083 | } |
||
1084 | |||
1085 | #endregion |
||
1086 | |||
1087 | #region XML Tree |
||
1088 | |||
1089 | private void updateTreeView(string xml, TreeView treeView) |
||
1090 | { |
||
1091 | try |
||
1092 | { |
||
1093 | treeView.Nodes.Clear(); |
||
1094 | |||
1095 | XmlDocument tmpxmldoc = new XmlDocument(); |
||
1096 | tmpxmldoc.LoadXml(xml); |
||
1097 | FillTree(tmpxmldoc.DocumentElement, treeView.Nodes); |
||
1098 | treeView.ExpandAll(); |
||
1099 | } |
||
1100 | catch (Exception ex) |
||
1101 | { |
||
1102 | Console.WriteLine("Error during xml conversion:" + ex.Message); |
||
1103 | } |
||
1104 | } |
||
1105 | |||
1106 | private void FillTree(XmlNode node, TreeNodeCollection parentnode) |
||
1107 | { |
||
1108 | // End recursion if the node is a text type |
||
1109 | if (node == null || node.NodeType == XmlNodeType.Text || node.NodeType == XmlNodeType.CDATA) |
||
1110 | return; |
||
1111 | |||
1112 | TreeNodeCollection tmptreenodecollection = AddNodeToTree(node, parentnode); |
||
1113 | |||
1114 | // Add all the children of the current node to the treeview |
||
1115 | foreach (XmlNode tmpchildnode in node.ChildNodes) |
||
1116 | { |
||
1117 | FillTree(tmpchildnode, tmptreenodecollection); |
||
1118 | } |
||
1119 | } |
||
1120 | |||
1121 | private TreeNodeCollection AddNodeToTree(XmlNode node, TreeNodeCollection parentnode) |
||
1122 | { |
||
1123 | TreeNode newchildnode = CreateTreeNodeFromXmlNode(node); |
||
1124 | |||
1125 | // if nothing to add, return the parent item |
||
1126 | if (newchildnode == null) return parentnode; |
||
1127 | |||
1128 | // add the newly created tree node to its parent |
||
1129 | if (parentnode != null) parentnode.Add(newchildnode); |
||
1130 | |||
1131 | return newchildnode.Nodes; |
||
1132 | } |
||
1133 | |||
1134 | private TreeNode CreateTreeNodeFromXmlNode(XmlNode node) |
||
1135 | { |
||
1136 | TreeNode tmptreenode = new TreeNode(); |
||
1137 | |||
1138 | if ((node.HasChildNodes) && (node.FirstChild.Value != null)) |
||
1139 | { |
||
1140 | tmptreenode = new TreeNode(node.Name); |
||
1141 | TreeNode tmptreenode2 = new TreeNode(node.FirstChild.Value); |
||
1142 | tmptreenode.Nodes.Add(tmptreenode2); |
||
1143 | } |
||
1144 | else if (node.NodeType != XmlNodeType.CDATA) |
||
1145 | { |
||
1146 | tmptreenode = new TreeNode(node.Name); |
||
1147 | } |
||
1148 | |||
1149 | return tmptreenode; |
||
1150 | } |
||
1151 | |||
1152 | #endregion |
||
1153 | |||
1154 | #region Timers |
||
1155 | |||
1156 | private void timer1_Tick(object sender, EventArgs e) |
||
1157 | { |
||
1158 | if (this.InvokeRequired) |
||
1159 | { |
||
1160 | this.BeginInvoke(new MethodInvoker(() => timer1_Tick(sender, e))); |
||
1161 | } |
||
1162 | else |
||
1163 | { |
||
1164 | label1PacketsOut.Text = String.Format("{0} ({1} bytes)", PacketsOutCounter, PacketsOutBytes); |
||
1165 | labelPacketsIn.Text = String.Format("{0} ({1} bytes)", PacketsInCounter, PacketsInBytes); |
||
1166 | labelPacketsTotal.Text = String.Format("{0} ({1} bytes)", PacketsOutCounter + PacketsInCounter, PacketsOutBytes + PacketsInBytes); |
||
1167 | |||
1168 | labelCapsIn.Text = String.Format("{0} ({1} bytes)", CapsInCounter, CapsInBytes); |
||
1169 | labelCapsOut.Text = String.Format("{0} ({1} bytes)", CapsOutCounter, CapsOutBytes); |
||
1170 | labelCapsTotal.Text = String.Format("{0} ({1} bytes)", CapsInCounter + CapsOutCounter, CapsOutBytes + CapsInBytes); |
||
1171 | } |
||
1172 | } |
||
1173 | |||
1174 | private void timerSessionQueue_Tick(object sender, EventArgs e) |
||
1175 | { |
||
1176 | lock (QueuedSessions) |
||
1177 | { |
||
1178 | if (QueuedSessions.Count > 0) |
||
1179 | { |
||
1180 | listViewSessions.BeginUpdate(); |
||
1181 | listViewSessions.Items.AddRange(QueuedSessions.ToArray()); |
||
1182 | if (listViewSessions.Items.Count > 0 && autoscrollToolStripMenuItem.Checked) |
||
1183 | { |
||
1184 | listViewSessions.Items[listViewSessions.Items.Count - 1].EnsureVisible(); |
||
1185 | } |
||
1186 | listViewSessions.EndUpdate(); |
||
1187 | QueuedSessions.Clear(); |
||
1188 | } |
||
1189 | } |
||
1190 | } |
||
1191 | |||
1192 | #endregion |
||
1193 | |||
1194 | private void EditToolStripButton_DropDownOpening(object sender, EventArgs e) |
||
1195 | { |
||
1196 | if (listViewSessions.Items.Count > 0) |
||
1197 | { |
||
1198 | toolStripMenuSessionsRemove.Enabled = |
||
1199 | removeToolStripMenuItem2.Enabled = |
||
1200 | selectToolStripMenuItem1.Enabled = |
||
1201 | saveSessionArchiveToolStripMenuItem.Enabled = |
||
1202 | toolStripMenuItemRemoveAll.Enabled = true; |
||
1203 | |||
1204 | if (listViewSessions.SelectedItems.Count < listViewSessions.Items.Count) |
||
1205 | { |
||
1206 | toolStripMenuItemRemoveUnselected.Enabled = true; |
||
1207 | } |
||
1208 | else |
||
1209 | { |
||
1210 | toolStripMenuItemRemoveUnselected.Enabled = false; |
||
1211 | } |
||
1212 | |||
1213 | if (listViewSessions.SelectedItems.Count > 0) |
||
1214 | { |
||
1215 | markToolStripMenuItem1.Enabled = |
||
1216 | toolStripSeparatorSelectPacketProto.Visible = |
||
1217 | toolStripMenuItemSelectPacketName.Visible = |
||
1218 | noneToolStripMenuItem2.Enabled = |
||
1219 | copyToolStripMenuItem1.Enabled = |
||
1220 | toolStripMenuItemRemoveSelected.Enabled = true; |
||
1221 | } |
||
1222 | else |
||
1223 | { |
||
1224 | markToolStripMenuItem1.Enabled = |
||
1225 | toolStripSeparatorSelectPacketProto.Visible = |
||
1226 | toolStripMenuItemSelectPacketName.Visible = |
||
1227 | noneToolStripMenuItem2.Enabled = |
||
1228 | noneToolStripMenuItem2.Enabled = |
||
1229 | copyToolStripMenuItem1.Enabled = |
||
1230 | toolStripMenuItemRemoveSelected.Enabled = false; |
||
1231 | } |
||
1232 | |||
1233 | if (listViewSessions.SelectedItems.Count > 0 |
||
1234 | && listViewSessions.SelectedItems.Count != listViewSessions.Items.Count) |
||
1235 | { |
||
1236 | toolStripMenuItemRemoveUnselected.Enabled = |
||
1237 | invertToolStripMenuItem1.Enabled = |
||
1238 | noneToolStripMenuItem2.Enabled = true; |
||
1239 | } |
||
1240 | else |
||
1241 | { |
||
1242 | toolStripMenuItemRemoveUnselected.Enabled = |
||
1243 | invertToolStripMenuItem1.Enabled = |
||
1244 | noneToolStripMenuItem2.Enabled = false; |
||
1245 | } |
||
1246 | |||
1247 | } |
||
1248 | else |
||
1249 | { |
||
1250 | toolStripMenuSessionsRemove.Enabled = |
||
1251 | toolStripSeparatorSelectPacketProto.Visible = |
||
1252 | // toolStripMenuItemSelectProtocol.Visible = |
||
1253 | toolStripMenuItemSelectPacketName.Visible = |
||
1254 | findToolStripMenuItem.Enabled = |
||
1255 | selectToolStripMenuItem1.Enabled = |
||
1256 | removeToolStripMenuItem2.Enabled = |
||
1257 | toolStripMenuItemRemoveUnselected.Enabled = |
||
1258 | copyToolStripMenuItem1.Enabled = |
||
1259 | markToolStripMenuItem1.Enabled = |
||
1260 | saveSessionArchiveToolStripMenuItem.Enabled = |
||
1261 | toolStripMenuItemRemoveAll.Enabled = false; |
||
1262 | } |
||
1263 | |||
1264 | if (listViewPacketFilters.Items.Count + listViewSessions.Items.Count > 0) |
||
1265 | { |
||
1266 | saveFilterSelectionsToolStripMenuItem.Enabled = true; |
||
1267 | } |
||
1268 | else |
||
1269 | { |
||
1270 | saveFilterSelectionsToolStripMenuItem.Enabled = false; |
||
1271 | } |
||
1272 | |||
1273 | } |
||
1274 | |||
1275 | private void autoColorizeToolStripMenuItem_Click(object sender, EventArgs e) |
||
1276 | { |
||
1277 | if (colorDialog1.ShowDialog() == DialogResult.OK) |
||
1278 | { |
||
1279 | //listview.BackColor = colorDialog1.Color; |
||
1280 | } |
||
1281 | } |
||
1282 | |||
1283 | private void toolStripMenuItem1_Click(object sender, EventArgs e) |
||
1284 | { |
||
1285 | if (pluginManager == null) |
||
1286 | pluginManager = new FormPluginManager(proxy.Proxy); |
||
1287 | |||
1288 | pluginManager.ShowDialog(); |
||
1289 | } |
||
1290 | |||
1291 | void Instance_MessageLoggedEvent(object sender, MessageLoggedEventArgs e) |
||
1292 | { |
||
1293 | if (this.IsDisposed || this.Disposing) |
||
1294 | return; |
||
1295 | |||
1296 | if (InvokeRequired) |
||
1297 | { |
||
1298 | BeginInvoke(new MethodInvoker(() => Instance_MessageLoggedEvent(sender, e))); |
||
1299 | } |
||
1300 | else |
||
1301 | { |
||
1302 | string s = String.Format("{0} [{1}] {2} {3}", e.LoggingEvent.TimeStamp, e.LoggingEvent.Level, |
||
1303 | e.LoggingEvent.RenderedMessage, e.LoggingEvent.ExceptionObject); |
||
1304 | richTextBoxDebugLog.AppendText(s + "\n"); |
||
1305 | } |
||
1306 | } |
||
1307 | |||
1308 | private void richTextBoxDecodedRequest_TextChanged(object sender, EventArgs e) |
||
1309 | { |
||
1310 | RichTextBox m_rtb = (RichTextBox)sender; |
||
1311 | |||
1312 | // don't colorize xml! |
||
1313 | if (m_rtb.Lines.Length <= 0 || m_rtb.Lines[0].StartsWith("<?xml")) |
||
1314 | return; |
||
1315 | |||
1316 | Regex typesRegex = new Regex(@"\[(?<Type>\w+|\w+\[\])\]|\((?<Enum>.*)\)|\s-- (?<Header>\w+|\w+ \[\]) --\s|(?<BlockSep>\s\*\*\*\s)|(?<Tag>\s<\w+>\s|\s<\/\w+>\s)|(?<BlockCounter>\s\w+\[\d+\]\s)", RegexOptions.ExplicitCapture); |
||
1317 | |||
1318 | MatchCollection matches = typesRegex.Matches(m_rtb.Text); |
||
1319 | foreach (Match match in matches) |
||
1320 | { |
||
1321 | m_rtb.SelectionStart = match.Index + 1; |
||
1322 | m_rtb.SelectionLength = match.Length - 2; |
||
1323 | m_rtb.SelectionFont = new Font(m_rtb.Font.FontFamily, m_rtb.Font.Size, FontStyle.Bold); |
||
1324 | |||
1325 | if (!String.IsNullOrEmpty(match.Groups["Type"].Value)) |
||
1326 | m_rtb.SelectionColor = Color.FromArgb(43, 145, 175); |
||
1327 | else if (!String.IsNullOrEmpty(match.Groups["Enum"].Value)) |
||
1328 | m_rtb.SelectionColor = Color.FromArgb(43, 145, 175); |
||
1329 | else if (!String.IsNullOrEmpty(match.Groups["Header"].Value)) |
||
1330 | { |
||
1331 | m_rtb.SelectionColor = Color.Green; |
||
1332 | m_rtb.SelectionBackColor = Color.LightSteelBlue; |
||
1333 | } |
||
1334 | else if (!String.IsNullOrEmpty(match.Groups["BlockSep"].Value)) |
||
1335 | m_rtb.SelectionColor = Color.Gold; |
||
1336 | else if (!String.IsNullOrEmpty(match.Groups["Tag"].Value)) |
||
1337 | { |
||
1338 | m_rtb.SelectionColor = Color.White; |
||
1339 | m_rtb.SelectionBackColor = Color.Black; |
||
1340 | } |
||
1341 | else if (!String.IsNullOrEmpty(match.Groups["BlockCounter"].Value)) |
||
1342 | m_rtb.SelectionColor = Color.Green; |
||
1343 | |||
1344 | } |
||
1345 | } |
||
1346 | |||
1347 | private void AddSession(ListViewItem item) |
||
1348 | { |
||
1349 | lock (QueuedSessions) |
||
1350 | { |
||
1351 | QueuedSessions.Add(item); |
||
1352 | } |
||
1353 | } |
||
1354 | |||
1355 | private void asDecodedTextToolStripMenuItem_Click(object sender, EventArgs e) |
||
1356 | { |
||
1357 | if (saveFileDialog1.ShowDialog() == DialogResult.OK) |
||
1358 | { |
||
1359 | StringBuilder outString = new StringBuilder(); |
||
1360 | foreach (ListViewItem item in listViewSessions.Items) |
||
1361 | { |
||
1362 | if (item.Tag is Packet) |
||
1363 | { |
||
1364 | //outString.AppendLine(DecodePacket.PacketToString((Packet)item.Tag)); |
||
1365 | outString.AppendLine(PacketDecoder.PacketToString((Packet)item.Tag)); |
||
1366 | } |
||
1367 | |||
1368 | if (item.Tag is IMessage) |
||
1369 | { |
||
1370 | IMessage msg = (IMessage)item.Tag; |
||
1371 | outString.AppendLine(msg.Serialize().ToString()); |
||
1372 | } |
||
1373 | |||
1374 | try |
||
1375 | { |
||
1376 | File.WriteAllText(saveFileDialog1.FileName, outString.ToString()); |
||
1377 | } |
||
1378 | catch (Exception ex) |
||
1379 | { |
||
1380 | MessageBox.Show("Exception occurred trying to save session archive: " + ex); |
||
1381 | } |
||
1382 | } |
||
1383 | } |
||
1384 | } |
||
1385 | |||
1386 | private void richTextBoxDecodedRequest_LinkClicked(object sender, LinkClickedEventArgs e) |
||
1387 | { |
||
1388 | System.Diagnostics.Process.Start(e.LinkText); |
||
1389 | } |
||
1390 | |||
1391 | private void saveSettingsToolStripMenuItem_Click(object sender, EventArgs e) |
||
1392 | { |
||
1393 | SaveAllSettings("settings.osd"); |
||
1394 | } |
||
1395 | // Column sorter |
||
1396 | private void listViewSessions_ColumnClick(object sender, ColumnClickEventArgs e) |
||
1397 | { |
||
1398 | ListView listView1 = (ListView)sender; |
||
1399 | |||
1400 | ListViewSorter Sorter = new ListViewSorter(); |
||
1401 | listView1.ListViewItemSorter = Sorter; |
||
1402 | if (!(listView1.ListViewItemSorter is ListViewSorter)) |
||
1403 | return; |
||
1404 | |||
1405 | Sorter = (ListViewSorter)listView1.ListViewItemSorter; |
||
1406 | |||
1407 | if (Sorter.LastSort == e.Column) |
||
1408 | { |
||
1409 | if (listView1.Sorting == SortOrder.Ascending) |
||
1410 | listView1.Sorting = SortOrder.Descending; |
||
1411 | else |
||
1412 | listView1.Sorting = SortOrder.Ascending; |
||
1413 | } |
||
1414 | else |
||
1415 | { |
||
1416 | listView1.Sorting = SortOrder.Descending; |
||
1417 | } |
||
1418 | Sorter.ByColumn = e.Column; |
||
1419 | |||
1420 | listView1.Sort(); |
||
1421 | |||
1422 | listView1.Columns[e.Column].Width = -2;// = listView1.Columns[e.Column].Text + " " + '\u23BC'; |
||
1423 | } |
||
1424 | |||
1425 | private void buttonSaveRequestHex_Click(object sender, EventArgs e) |
||
1426 | { |
||
1427 | if (hexBoxRequest.ByteProvider != null && hexBoxRequest.ByteProvider.Length > 0) |
||
1428 | { |
||
1429 | saveFileDialog3.FileName = listViewSessions.SelectedItems[0].Name; |
||
1430 | if (saveFileDialog3.ShowDialog() == DialogResult.OK) |
||
1431 | { |
||
1432 | byte[] bytes = new byte[hexBoxRequest.ByteProvider.Length]; |
||
1433 | for (int i = 0; i < hexBoxRequest.ByteProvider.Length; i++) |
||
1434 | { |
||
1435 | bytes[i] = hexBoxRequest.ByteProvider.ReadByte(i); |
||
1436 | } |
||
1437 | File.WriteAllBytes(saveFileDialog3.FileName, bytes); |
||
1438 | } |
||
1439 | } |
||
1440 | } |
||
1441 | |||
1442 | private void buttonExportRawHex_Click(object sender, EventArgs e) |
||
1443 | { |
||
1444 | if (hexBoxResponse.ByteProvider != null && hexBoxResponse.ByteProvider.Length > 0) |
||
1445 | { |
||
1446 | saveFileDialog3.FileName = listViewSessions.SelectedItems[0].Name; |
||
1447 | if (saveFileDialog3.ShowDialog() == DialogResult.OK) |
||
1448 | { |
||
1449 | byte[] bytes = new byte[hexBoxResponse.ByteProvider.Length]; |
||
1450 | for (int i = 0; i < hexBoxResponse.ByteProvider.Length; i++) |
||
1451 | { |
||
1452 | bytes[i] = hexBoxResponse.ByteProvider.ReadByte(i); |
||
1453 | } |
||
1454 | File.WriteAllBytes(saveFileDialog3.FileName, bytes); |
||
1455 | } |
||
1456 | } |
||
1457 | else |
||
1458 | { |
||
1459 | // no bytes to read! |
||
1460 | } |
||
1461 | } |
||
1462 | } |
||
1463 | |||
1464 | public class ListViewSorter : System.Collections.IComparer |
||
1465 | { |
||
1466 | public int Compare(object o1, object o2) |
||
1467 | { |
||
1468 | if (!(o1 is ListViewItem)) |
||
1469 | return 0; |
||
1470 | if (!(o2 is ListViewItem)) |
||
1471 | return 0; |
||
1472 | |||
1473 | int result; |
||
1474 | |||
1475 | ListViewItem lvi1 = (ListViewItem)o2; |
||
1476 | ListViewItem lvi2 = (ListViewItem)o1; |
||
1477 | |||
1478 | if (lvi1.ListView.Columns[ByColumn].Tag == null |
||
1479 | || lvi1.ListView.Columns[ByColumn].Tag == null) |
||
1480 | { |
||
1481 | return 0; |
||
1482 | } |
||
1483 | |||
1484 | if (lvi1.ListView.Columns[ByColumn].Tag.ToString().ToLower().Equals("number")) |
||
1485 | { |
||
1486 | float fl1 = float.Parse(lvi1.SubItems[ByColumn].Text); |
||
1487 | float fl2 = float.Parse(lvi2.SubItems[ByColumn].Text); |
||
1488 | |||
1489 | if (lvi1.ListView.Sorting == SortOrder.Ascending) |
||
1490 | result = fl1.CompareTo(fl2); |
||
1491 | else |
||
1492 | result = fl2.CompareTo(fl1); |
||
1493 | } |
||
1494 | else if (lvi1.ListView.Columns[ByColumn].Tag.ToString().ToLower().Equals("string")) |
||
1495 | { |
||
1496 | string str1 = lvi1.SubItems[ByColumn].Text; |
||
1497 | string str2 = lvi2.SubItems[ByColumn].Text; |
||
1498 | |||
1499 | if (lvi1.ListView.Sorting == SortOrder.Ascending) |
||
1500 | result = String.Compare(str1, str2); |
||
1501 | else |
||
1502 | result = String.Compare(str2, str1); |
||
1503 | } |
||
1504 | else |
||
1505 | { |
||
1506 | return 0; |
||
1507 | } |
||
1508 | |||
1509 | LastSort = ByColumn; |
||
1510 | |||
1511 | return (result); |
||
1512 | } |
||
1513 | |||
1514 | |||
1515 | public int ByColumn |
||
1516 | { |
||
1517 | get { return Column; } |
||
1518 | set { Column = value; } |
||
1519 | } |
||
1520 | int Column = 0; |
||
1521 | |||
1522 | public int LastSort |
||
1523 | { |
||
1524 | get { return LastColumn; } |
||
1525 | set { LastColumn = value; } |
||
1526 | } |
||
1527 | int LastColumn = 0; |
||
1528 | } |
||
1529 | } |