Setting Up Private Internet Access on CentOS 6.4

Illustration

I am a big fan of Private Internet Access. It gives you anonymous and secure connection to Internet. I personally value my privacy and thus I find such VPN service very valuable.

Under Windows and huge variety of alternate platforms (Android, iOS, Ubuntu, …) installation is very simple and it hardly ever fails. But some platforms don’t come with instructions. Unfortunately one of them is CentOS. Fortunately, setting it all up is not that hard.

First we can do the easy stuff. Download PIA’s OpenVPN configuration files and extract it to directory of your choice. I kept them in /home/MyUserName/pia.

Next easy step is setting up DNS resolving. For that we go to System, Preferences, Network Connections. Just click edit on connection you are using and go to IPv4 Settings tab. Change Method to Automatic (DHCP addresses only). Under DNS servers enter 209.222.18.222 209.222.18.218 (Private Internet Access DNS).

All other commands are to be executed in terminal and most of them require root privileges. It might be best if you just become root for a while:

su - root

CentOS repositories are not known for their extensive software collection. But we can always add a repository of our choice:

wget http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
rpm -Uvh epel-release-6-8.noarch.rpm

This repository has OpenVPN package that we need:

yum install openvpn

Next step is getting configuration in place (replace username and password with yours):

cp /home/MyUserName/pia/ca.crt /etc/openvpn/ca.crt
cp /home/MyUserName/pia/US\ Midwest.ovpn /etc/openvpn/client.conf
echo "auth-user-pass /etc/openvpn/login.pia" >> /etc/openvpn/client.conf
echo "username" > /etc/openvpn/login.pia
echo "password" >> /etc/openvpn/login.pia

Now we can test our connection (after we restart network in order to activate DNS changes):

service network restart
openvpn --config /etc/openvpn/client.conf

Assuming that this last step ended with Initialization Sequence Completed, we just need to verify whether this connection is actually used. I found whatismyipaddress.com quite helpful here. If you see some mid-west town on map, you are golden (assuming that you don’t actually live in US mid-west).

Now you can stop test connection via Ctrl+C in order to properly start it. In addition, you can specify it should start on each system startup:

service openvpn start
chkconfig openvpn on

And that is all.

CentOS 6.4 and VirtualBox Additions

When you install CentOS 6.4 in VirtualBox, quite quickly you might be annoyed by a lack of a mouse integration. Usual cure in form of VM guest additions simply fails with

Building the main Guest Additions module   [FAILED]

Fortunately this message comes with some additional information which points to lack of compiler and kernel headers. Easiest way to install them is in terminal:

su - root
yum install gcc
yum install kernel-devel-`uname -r`

After this you can retry guest additions installation and you should see better results.

PS: This method probably works for RedHat also.

ShortcutKeyDisplayString in Tray Context-menu

Illustration

It all started when I wanted to show custom shortcut text next to a menu item on a tray icon’s context menu. Usually this is as easy as setting ShortcutKeyDisplayString property. So I did, and it worked. Sort-of.

For some reason ContextMenuStrip on TrayIcon is not shown on first right-click but only on second. While it is not a major issue, I found it really annoying. That meant that I had to stick to good old ContextMenu and its MenuItem. Unfortunately that also meant that there was no ShortcutKeyDisplayString to help me.

And then I remembered trick from my VB 6 days: anything could be displayed in shortcut position if you would separate it by tab character. So I tried this:

someMenuItem.Text = "Show application" + "\t" + "Ctrl+Alt+P";

Surprisingly, this trick still works.

Editing Labels in a Sorted TreeView

In my previous post we’ve been dealing with TreeView drag&drop. One other functionality that is almost mandatory for TreeView is renaming a node. While basic code is quite straight forward, there are few tricks in order to get better-than-default behavior.

First order of business is BeforeLabelEdit event. There we define which nodes will have fixed name. In our case, we will not allow editing of folder names:

e.CancelEdit = (e.Node.ImageIndex == 0); //don't allow editing folders

In AfterLabel event we handle everything else. We want new text without spaces on either end and no duplicates are allowed. It complicates code a bit but not by much. Probably only non obvious thing is actual sorting. Here we just “schedule” it after event handler is done with processing:

if (e.Label == null) { return; } //no change was made
e.CancelEdit = true; //we will handle changes manually
string newText = e.Label.Trim(); //no spaces

var nodes = (e.Node.Parent == null) ? tree.Nodes : e.Node.Parent.Nodes;
foreach (TreeNode node in nodes) {
    if ((node != e.Node) && string.Equals(newText, node.Text, StringComparison.Ordinal)) {
        return; //duplicate name
    }
}

e.Node.Text = newText; //rename manually

tree.BeginInvoke(new Action<TreeNode>(delegate(TreeNode node) { //sort again
    tree.Sort();
    tree.SelectedNode = node;
}), e.Node);

Full sample can be downloaded here.

PS: In sample code you will see that I use ImageIndex==0 to determine whether node is of folder type. In real program you would probably go with sub-classing TreeNode.

Drag&drop in a Sorted TreeView

Illustration

If you have a TreeView, chances are that you want it sorted and with a drag&drop functionality. And that is not too hard.

In order to sort items, don’t forget to assign TreeViewNodeSorter property. This requires simple IComparer, e.g.:

internal class NodeSorter : IComparer {
    public int Compare(object item1, object item2) {
        var node1 = item1 as TreeNode;
        var node2 = item2 as TreeNode;

        if (node1.ImageIndex == node2.ImageIndex) { //both are of same type
            return string.Compare(node1.Text, node2.Text, StringComparison.CurrentCultureIgnoreCase);
        } else {
            return (node1.ImageIndex == 0) ? -1 : +1;
        }
    }
}

This will ensure that “folders” (with ImageIndex==0) are sorted before files (any other value of ImageIndex). All that is left is to call Sort method when needed.

In order to support drag&drop, a bit more work is needed. Before we even start doing anything, we need to set AllowDrop=true on our TreeView. Only then we can setup events. To initiate drag we just work with ItemDrag event:

this.DoDragDrop(e.Item, DragDropEffects.Move);

In DragOver we need to check for “droppability” of each item. Rules are simple; We allow only tree nodes in; if we drop file on file, it will actually drop it in file’s folder; and don’t allow parent to be dropped into its child. This class will then either allow movement (DragDropEffects.Move) or it will deny it (DragDropEffects.None).

var fromNode = e.Data.GetData("System.Windows.Forms.TreeNode") as TreeNode;
if (fromNode == null) { return; } //not our stuff

var dropNode = tree.GetNodeAt(tree.PointToClient(new Point(e.X, e.Y)));
while ((dropNode != null) && (dropNode.ImageIndex != 0)) { //search for suitable folder
    dropNode = dropNode.Parent;
}

var noCommonParent = (fromNode.Parent != dropNode);
while (noCommonParent && (dropNode != null)) {
    if (fromNode == dropNode) { noCommonParent = false; } //to stop parent becoming a child
    dropNode = dropNode.Parent;
}

e.Effect = noCommonParent ? DragDropEffects.Move : DragDropEffects.None;

Final movement happens in DragDrop event. First part is same node discovery process we had in DragOver. After that we simply move nodes from one parent to another and we wrap all up by performing a sort.

var fromNode = e.Data.GetData("System.Windows.Forms.TreeNode") as TreeNode;
var dropNode = tree.GetNodeAt(tree.PointToClient(new Point(e.X, e.Y)));
while ((dropNode != null) && (dropNode.ImageIndex != 0)) { //search for suitable folder
    dropNode = dropNode.Parent;
}

var fromParentNodes = (fromNode.Parent != null) ? fromNode.Parent.Nodes : tree.Nodes;
fromParentNodes.Remove(fromNode);
if (dropNode == null) {
    tree.Nodes.Add(fromNode);
} else {
    dropNode.Nodes.Add(fromNode);
}

tree.Sort();
tree.SelectedNode = fromNode;

Full sample can be downloaded here.

PS: In sample code you will see that I use ImageIndex==0 to determine whether node is of folder type. In real program you would probably go with sub-classing TreeNode.