using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.IsolatedStorage; using System.Threading; using System.Windows.Forms; using System.Xml; using System.Xml.Serialization; using sharprd.Properties; using sharpupdater; using System.Configuration; namespace sharprd { public partial class MainWindow : Form, IObserver { private const string UPDATE_URL = "http://elvenprogrammer.themanaworld.org/updates/sharprd.xml"; private const string VERSION = "0.0.5"; private const string PRODUCT_NAME = "sharprd"; private bool closing; public MainWindow() { InitializeComponent(); try { Configuration exeConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); //MessageBox.Show(exeConfig.FilePath); Configuration localConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal); //MessageBox.Show(localConfig.FilePath); Configuration roamingConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming); //MessageBox.Show(roamingConfig.FilePath); } catch (ConfigurationErrorsException ex) { MessageBox.Show("Your configuration file is corrupted. A new one will be generated.\r\nPlease restart the application."); File.Delete(ex.Filename); Settings.Default.Reload(); } Thread thread = new Thread(new ThreadStart(CheckUpdate)); thread.Start(); if (!LoadConnections("connections.xml")) { string message = "Unable to load connections"; // Try to load backup if (LoadConnections("connections.bak")) { message += "\nBackup has been restored"; Error(message); } else { message += "\nExisting connections will be deleted.\nAre you sure?"; if (MessageBox.Show(this, message, "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No) { Application.Exit(); } } } treeConnections.Sort(); RefreshUI(); } private void CheckUpdate() { SharpUpdater updater = new SharpUpdater(UPDATE_URL); try { updater.UpdateProduct(PRODUCT_NAME, VERSION); } catch { } } private bool LoadConnections(string path) { bool result = false; IsolatedStorageFile store = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null); if (store.GetFileNames(path).Length == 1) { IsolatedStorageFileStream stream = new IsolatedStorageFileStream(path, FileMode.Open, store); try { XmlSerializer serializer = new XmlSerializer(typeof(Node[])); if (stream.Length > 0) { XmlReader reader = new XmlTextReader(stream); Node[] nodes = (Node[])serializer.Deserialize(reader); reader.Close(); foreach (Node node in nodes) { CustomTreeNode treeNode = ToCustomTreeNode(node); treeConnections.Nodes.Add(treeNode); } result = true; } } catch (Exception ex) { Error(ex.ToString()); } finally { stream.Close(); } } return result; } private bool SaveConnections(string path) { bool result = false; IsolatedStorageFile store = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null); using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(path, FileMode.Create, store)) { result = SaveConnections(stream); } return result; } private bool SaveConnections(Stream stream) { bool result = false; List nodes = new List(); foreach (CustomTreeNode node in treeConnections.Nodes) { nodes.Add(ToInfoNode(node)); } try { XmlSerializer serializer = new XmlSerializer(typeof(Node[])); serializer.Serialize(stream, nodes.ToArray()); stream.Flush(); result = true; } catch (Exception ex) { Error(ex.ToString()); } return result; } private bool SaveConnection(Stream stream) { bool result = false; if (treeConnections.SelectedNode != null && treeConnections.SelectedNode is ConnectionTreeNode) { List nodes = new List(); nodes.Add(ToInfoNode((CustomTreeNode)treeConnections.SelectedNode)); try { XmlSerializer serializer = new XmlSerializer(typeof(Node[])); serializer.Serialize(stream, nodes.ToArray()); stream.Flush(); result = true; } catch (Exception ex) { Error(ex.ToString()); } } return result; } private static string CleanFileName(string fileName) { foreach (char c in Path.GetInvalidFileNameChars()) { fileName = fileName.Replace(c.ToString(), string.Empty); } return fileName; } public Node ToInfoNode(CustomTreeNode node) { string name = node.Text; bool folder = node is FolderTreeNode; bool collapsed = !node.IsExpanded; Node infoNode = new Node(); infoNode.Name = name; infoNode.Folder = folder; infoNode.Collapsed = collapsed; if (!folder) { infoNode.Connection = ((ConnectionTreeNode)node).Connection; } if (node.Nodes != null && node.Nodes.Count > 0) { List infoNodes = new List(); foreach (CustomTreeNode childNode in node.Nodes) { infoNodes.Add(ToInfoNode(childNode)); } infoNode.Nodes = infoNodes.ToArray(); } else { // NOOP } return infoNode; } public CustomTreeNode ToCustomTreeNode(Node node) { CustomTreeNode treeNode; if (node.Folder) { treeNode = new FolderTreeNode(node.Name); } else { treeNode = new ConnectionTreeNode(node.Connection); } if (node.Nodes != null && node.Nodes.Length > 0) { List nodes = new List(); foreach (Node childNode in node.Nodes) { nodes.Add(ToCustomTreeNode(childNode)); } treeNode.Nodes.AddRange(nodes.ToArray()); } if (!node.Collapsed) { treeNode.Expand(); } return treeNode; } private void NewEdit(object sender, EventArgs e) { if ((sender == btnEdit || sender == editMenuItem) && treeConnections.SelectedNode == null) return; if (treeConnections.SelectedNode == null || treeConnections.SelectedNode is ConnectionTreeNode || (treeConnections.SelectedNode is FolderTreeNode && (sender != btnEdit || sender != editMenuItem))) { NewRDPConnection dialog = new NewRDPConnection(); if (sender == btnEdit || sender == editMenuItem) { dialog.Connection = ((ConnectionTreeNode)treeConnections.SelectedNode).Connection; } if (dialog.ShowDialog(this) == DialogResult.OK && (sender != btnEdit && sender != editMenuItem)) { if (treeConnections.SelectedNode is FolderTreeNode) { AddConnection(dialog.Connection, treeConnections.SelectedNode); } else { AddConnection(dialog.Connection, treeConnections.SelectedNode.Parent); } } else if (sender == btnEdit || sender == editMenuItem) { treeConnections.SelectedNode.Text = dialog.Connection.Name; } } else if (treeConnections.SelectedNode is FolderTreeNode) { TextInputDialog dialog = new TextInputDialog("Folder name", "Folder name"); if (sender == btnEdit || sender == editMenuItem) { dialog.Value = treeConnections.SelectedNode.Text; } if (dialog.ShowDialog(this) == DialogResult.OK) { treeConnections.SelectedNode.Text = dialog.Value; } } RefreshUI(); } private void exitToolStripMenuItem_Click(object sender, EventArgs e) { Close(); } private void btnBrowser_Click(object sender, EventArgs e) { if (sender != btnBrowser) btnBrowser.Checked = !btnBrowser.Checked; splitContainer1.Panel1Collapsed = !btnBrowser.Checked; if (splitContainer1.Panel1Collapsed) splitContainer1.Panel1.Hide(); } private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) { closing = true; // Force disconnection foreach (TreeNode node in treeConnections.Nodes) { if (node is ConnectionTreeNode) { ConnectionTreeNode connectionNode = (ConnectionTreeNode)node; if (connectionNode.Connection.Connected) { connectionNode.Connection.Page.Disconnect(); } } } if (!Save()) { e.Cancel = true; } Settings.Default.SplitterDistance = splitContainer1.SplitterDistance; Settings.Default.HideWhenMinimized = hideMenuItem.Checked; Settings.Default.Save(); } public bool Save() { // Ensure a full path //string path = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "connections.xml"); //string backupPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "connections.bak"); string path = "connections.xml"; string backupPath = "connections.bak"; if (!SaveConnections(path)) { Error("Unable to save connections"); return false; } else { // Create a backup if saving was successful SaveConnections(backupPath); } return true; } private void Connect(object sender, EventArgs e) { if (treeConnections.SelectedNode != null && treeConnections.SelectedNode is ConnectionTreeNode) { RDConnection connection = ((ConnectionTreeNode)treeConnections.SelectedNode).Connection; if (connection.Connected) { connection.Page.Disconnect(); } else if (!connection.Connecting) { RDTabPage page = null; switch (connection.Type) { case RDConnection.Types.RDP: page = new RDPTabPage(connection); break; case RDConnection.Types.VNC: page = new VNCTabPage(connection); break; } page.Attach(this); tabContainer.TabPages.Add(page); connection.Page = page; page.Connect(); treeConnections.Invalidate(); } } RefreshUI(); } private void Reconnect(object sender, EventArgs e) { if (treeConnections.SelectedNode != null) { RDConnection connection = ((ConnectionTreeNode)treeConnections.SelectedNode).Connection; if (connection.Connected) { connection.Page.Reconnect(); } else { Connect(sender, e); } } RefreshUI(); } private void Error(string text) { MessageBox.Show(text, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void treeConnections_AfterSelect(object sender, TreeViewEventArgs e) { RefreshUI(); } public void RefreshUI() { if (closing) return; btnEdit.Enabled = false; btnConnect.Enabled = false; btnConnect.Text = "Connect"; btnConnect.Image = sharprd.Properties.Resources.play; btnEmail.Enabled = false; btnClone.Enabled = false; if (treeConnections.SelectedNode != null) { btnEdit.Enabled = true; if (treeConnections.SelectedNode is ConnectionTreeNode) { RDConnection connection = ((ConnectionTreeNode)treeConnections.SelectedNode).Connection; btnConnect.Enabled = true; btnReconnect.Enabled = connection.Connected; btnEmail.Enabled = true; btnClone.Enabled = true; if (connection.Connected) { btnConnect.Text = "Disconnect"; btnConnect.Image = sharprd.Properties.Resources.stop; } } } foreach (TreeNode node in treeConnections.Nodes) { UpdateNode(node); } treeConnections.Invalidate(); } private void UpdateNode(TreeNode node) { if (node.Nodes != null && node.Nodes.Count > 0) { foreach (TreeNode childNode in node.Nodes) { UpdateNode(childNode); } } else { if (node is ConnectionTreeNode) { ConnectionTreeNode connectionNode = (ConnectionTreeNode)node; if (connectionNode.Connection != null) { connectionNode.ImageIndex = connectionNode.Connection.ImageIndex; } } node.SelectedImageIndex = node.ImageIndex; } } private void AddConnection(RDConnection connection) { AddConnection(connection, null); } private void AddConnection(RDConnection connection, TreeNode parent) { if (Exists(connection.Name)) { if (MessageBox.Show( string.Format("A connection named {0} already exists.\nDo you still want to add a new {0} connection?", connection.Name), "Duplicate detected", MessageBoxButtons.YesNo) == DialogResult.No) { return; } } ConnectionTreeNode connectionNode = new ConnectionTreeNode(connection); if (parent != null) { parent.Nodes.Add(connectionNode); } else { treeConnections.Nodes.Add(connectionNode); } treeConnections.Sort(); } private bool CheckDuplicates(string name, TreeNode node) { if (node.Nodes == null || node.Nodes.Count == 0) { return name == node.Text; } foreach (TreeNode childNode in node.Nodes) { if (CheckDuplicates(name, childNode)) { return true; } } return false; } #region IObserver Members void IObserver.Update() { RefreshUI(); } #endregion private void Delete(object sender, EventArgs e) { if (treeConnections.SelectedNode != null) { if (treeConnections.SelectedNode is FolderTreeNode) { if (treeConnections.SelectedNode.Nodes != null && treeConnections.SelectedNode.Nodes.Count > 0) { MessageBox.Show("Folder is not empty. Delete its children first", "Info", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } } if (MessageBox.Show("Are you sure?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { treeConnections.Nodes.Remove(treeConnections.SelectedNode); } } RefreshUI(); } private void Clone(object sender, EventArgs e) { if (treeConnections.SelectedNode != null && treeConnections.SelectedNode is ConnectionTreeNode) { RDConnection connection = ((ConnectionTreeNode)treeConnections.SelectedNode).Connection; RDConnection copy = (RDConnection)connection.Clone(); int i = 1; while (Exists(string.Format("{0} ({1})", connection.Name, i))) { i++; } copy.Name = string.Format("{0} ({1})", connection.Name, i); AddConnection(copy, treeConnections.SelectedNode.Parent); } } private void MainWindow_Resize(object sender, EventArgs e) { if (hideMenuItem.Checked && FormWindowState.Minimized == WindowState) { Hide(); notifyIcon.Visible = true; } } private void notifyIcon_DoubleClick(object sender, EventArgs e) { Restore(); } private void MainWindow_Shown(object sender, EventArgs e) { splitContainer1.SplitterDistance = Settings.Default.SplitterDistance; hideMenuItem.Checked = Settings.Default.HideWhenMinimized; } private void importToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog openDialog = new OpenFileDialog(); openDialog.Filter = "SharpRD Connections file (*.xml)|*.xml|Remote Desktop Protocol (*.rdp)|*.rdp|All Files (*.*)|*.*"; openDialog.Multiselect = false; if (openDialog.ShowDialog(this) == DialogResult.OK) { switch (openDialog.FilterIndex) { // *.xml case 1: // *.* default: if (MessageBox.Show(this, "Existing connections will be deleted. Are you sure?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No) { return; } try { XmlSerializer serializer = new XmlSerializer(typeof(Node[])); XmlReader reader = new XmlTextReader(openDialog.FileName); Node[] nodes = (Node[])serializer.Deserialize(reader); reader.Close(); treeConnections.Nodes.Clear(); foreach (Node node in nodes) { CustomTreeNode treeNode = ToCustomTreeNode(node); treeConnections.Nodes.Add(treeNode); } } catch (Exception ex) { Error(ex.ToString()); } finally { } break; case 2: RDConnection connection = RDPFileFormat.Load(openDialog.FileName); NewRDPConnection dialog = new NewRDPConnection(); dialog.Connection = connection; if (dialog.ShowDialog(this) == DialogResult.OK) { AddConnection(dialog.Connection); } break; } RefreshUI(); } } private void exportToolStripMenu_Click(object sender, EventArgs e) { SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.Filter = "SharpRD Connections file (*.xml)|*.xml|Remote Desktop Protocol (*.rdp)|*.rdp|All Files (*.*)|*.*"; if (saveDialog.ShowDialog(this) == DialogResult.OK) { switch (saveDialog.FilterIndex) { // *.xml case 1: // *.* default: using (StreamWriter writer = new StreamWriter(saveDialog.FileName)) { SaveConnections(writer.BaseStream); } break; case 2: throw new NotImplementedException("Feature not available yet"); } } } private void restoreMenuItem_Click(object sender, EventArgs e) { Restore(); } private void Restore() { Show(); WindowState = FormWindowState.Maximized; notifyIcon.Visible = false; } private void exitMenuItem_Click(object sender, EventArgs e) { Close(); } private void editMenuItem_Click(object sender, EventArgs e) { NewEdit(sender, e); } private void treeConnections_MouseDown(object sender, MouseEventArgs e) { TreeNode node = treeConnections.HitTest(e.Location).Node; if (node == null) { treeConnections.SelectedNode = null; } else { treeConnections.SelectedNode = node; treeConnections.Refresh(); } } private void connectionsContextMenuStrip_Opening(object sender, CancelEventArgs e) { if (treeConnections.SelectedNode == null) { e.Cancel = true; return; } // Reset defaults connectMenuItem.Visible = reconnectMenuItem.Visible = firstSeparator.Visible = true; connectMenuItem.Enabled = reconnectMenuItem.Enabled = true; cloneToolStripMenuItem.Enabled = true; if (treeConnections.SelectedNode is ConnectionTreeNode) { RDConnection connection = ((ConnectionTreeNode)treeConnections.SelectedNode).Connection; connectMenuItem.Text = connection.Connected ? "Disconnect" : "Connect"; connectMenuItem.Image = connection.Connecting ? null : connection.Connected ? sharprd.Properties.Resources.stop : sharprd.Properties.Resources.play; connectMenuItem.Enabled = !connection.Connecting; reconnectMenuItem.Enabled = connection.Connected; saveToolStripMenuItem.Enabled = true; cloneToolStripMenuItem.Enabled = true; } else { connectMenuItem.Visible = reconnectMenuItem.Visible = firstSeparator.Visible = false; connectMenuItem.Enabled = reconnectMenuItem.Enabled = false; saveToolStripMenuItem.Enabled = false; cloneToolStripMenuItem.Enabled = false; } } private void deleteMenuItem_Click(object sender, EventArgs e) { Delete(sender, e); } private void connectMenuItem_Click(object sender, EventArgs e) { Connect(sender, e); } private void reconnectMenuItem_Click(object sender, EventArgs e) { Reconnect(sender, e); } private void btnReconnect_Click(object sender, EventArgs e) { Reconnect(sender, e); } private void btnAddFolder_Click(object sender, EventArgs e) { // Save selected node before TreeView loses control and changes SelectedNode TreeNode selectedNode = treeConnections.SelectedNode; if (selectedNode != null) { if (treeConnections.SelectedNode is ConnectionTreeNode) { MessageBox.Show("Cannot add a folder here", "Info", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } } TextInputDialog dialog = new TextInputDialog("Folder name", "New folder name"); if (dialog.ShowDialog(this) == DialogResult.OK) { string folderName = string.IsNullOrEmpty(dialog.Value) ? string.Empty : dialog.Value; FolderTreeNode folder = new FolderTreeNode(folderName); if (selectedNode != null) { selectedNode.Nodes.Add(folder); selectedNode.Expand(); } else { treeConnections.Nodes.Add(folder); } treeConnections.SelectedNode = folder; treeConnections.Refresh(); } } private TreeNode sourceNode; private void treeConnections_ItemDrag(object sender, ItemDragEventArgs e) { sourceNode = (TreeNode)e.Item; DoDragDrop(e.Item.ToString(), DragDropEffects.Move | DragDropEffects.Copy); } private void treeConnections_DragDrop(object sender, DragEventArgs e) { Point position = treeConnections.PointToClient(new Point(e.X, e.Y)); if (treeConnections.ClientRectangle.Contains(position)) { TreeNode targetNode = treeConnections.GetNodeAt(position); if (targetNode != null) { if (targetNode is FolderTreeNode) { if (sourceNode.Parent != null) { sourceNode.Parent.Nodes.Remove(sourceNode); } else { treeConnections.Nodes.Remove(sourceNode); } targetNode.Nodes.Add(sourceNode); treeConnections.Invalidate(); } else { MessageBox.Show("You can only drop over a folder", "Info", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } else { if (sourceNode.Parent != null) { sourceNode.Parent.Nodes.Remove(sourceNode); } else { treeConnections.Nodes.Remove(sourceNode); } treeConnections.Nodes.Add(sourceNode); treeConnections.Invalidate(); } } } private void treeConnections_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text)) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } private void btnEmail_Click(object sender, EventArgs e) { string to = string.Empty; string subject = "SharpRD connection"; string body = "Sending SharpRD connection"; string attachment = Path.Combine(Path.GetTempPath(), string.Format("{0}.xml", CleanFileName(treeConnections.SelectedNode.Text))); using (StreamWriter writer = new StreamWriter(attachment)) { SaveConnection(writer.BaseStream); } Mapi.Send(subject, body, attachment); } private void btnSave_Click(object sender, EventArgs e) { if (!Save()) { Error("Unable to save connections"); } } private void treeConnections_MouseMove(object sender, MouseEventArgs e) { TreeNode node = this.treeConnections.GetNodeAt(e.X, e.Y); if ((node != null)) { if (node.Tag != null) { if (node.Tag.ToString() != tooltip.GetToolTip(treeConnections)) { this.tooltip.SetToolTip(treeConnections, node.Tag.ToString()); } } else { this.tooltip.SetToolTip(treeConnections, string.Empty); } } else { this.tooltip.SetToolTip(treeConnections, string.Empty); } } private void saveToolStripMenuItem_Click(object sender, EventArgs e) { if (treeConnections.SelectedNode != null && treeConnections.SelectedNode is ConnectionTreeNode) { SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.Filter = "SharpRD Connections file (*.xml)|*.xml|Remote Desktop Protocol (*.rdp)|*.rdp|All Files (*.*)|*.*"; if (saveDialog.ShowDialog(this) == DialogResult.OK) { switch (saveDialog.FilterIndex) { // *.xml case 1: // *.* default: using (StreamWriter writer = new StreamWriter(saveDialog.FileName)) { SaveConnection(writer.BaseStream); } break; case 2: throw new NotImplementedException("Feature not available yet"); } } } } private void openToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog openDialog = new OpenFileDialog(); openDialog.Filter = "SharpRD Connections file (*.xml)|*.xml|Remote Desktop Protocol (*.rdp)|*.rdp|All Files (*.*)|*.*"; openDialog.Multiselect = false; if (openDialog.ShowDialog(this) == DialogResult.OK) { switch (openDialog.FilterIndex) { // *.xml case 1: // *.* default: try { XmlSerializer serializer = new XmlSerializer(typeof(Node[])); XmlReader reader = new XmlTextReader(openDialog.FileName); Node[] nodes = (Node[])serializer.Deserialize(reader); reader.Close(); foreach (Node node in nodes) { CustomTreeNode treeNode = ToCustomTreeNode(node); treeConnections.Nodes.Add(treeNode); } } catch (Exception ex) { Error(ex.ToString()); } finally { } break; case 2: RDConnection connection = RDPFileFormat.Load(openDialog.FileName); NewRDPConnection dialog = new NewRDPConnection(); dialog.Connection = connection; if (dialog.ShowDialog(this) == DialogResult.OK) { AddConnection(dialog.Connection); } break; } RefreshUI(); } } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show(this, string.Format("SharpRD v.{0}", VERSION), "About", MessageBoxButtons.OK, MessageBoxIcon.Information); } private bool Exists(string name) { bool duplicate = false; foreach (TreeNode node in treeConnections.Nodes) { if (CheckDuplicates(name, node)) { duplicate = true; break; } } return duplicate; } private void cloneToolStripMenuItem_Click(object sender, EventArgs e) { Clone(sender, e); } } }