void OnClientMessage(WebSocketConnection sender, DataReceivedEventArgs e)
        {
            OnlineUser user = Users.Single(a => a.Connection == sender);
            int index = e.Data.IndexOf(">>");
            string[] action = e.Data.Substring(0, index).Split(':');
            string[] args = action[1].Split(',');
            string data = e.Data.Remove(0, index + 2);
            Dictionary<string, string[]> actions = GetAllArgs(args);
            Func<string> combine = delegate()
            {
                List<string> newargs = new List<string>();
                foreach(KeyValuePair<string, string[]> a in actions)
                    newargs.Add(string.Format("{0}({1})", a.Key, String.Join("|", a.Value)));
                return string.Format("{0}:{1}>>{2}", action[0], string.Join(",", newargs.ToArray()), data);
            };
            switch(action[0])
            {
                case "REGISTER":
                    //auth here
                    int newuserid = int.Parse(args[0]);
                    if (Users.Exists(c => c.UserID == newuserid))
                        Users.Single(c => c.UserID == newuserid).Connection.Dispose();

                    user.UserID = newuserid;
                    user.Contacts = new List<int>();
                    ContactInfo.GetContacts(user.UserID).ForEach(delegate(ContactInfo i) { user.Contacts.Add(i.ContactID); });
                    user.Contacts.ForEach(delegate(int cid){
                        if (Users.Exists(c => c.UserID == cid))
                        {
                            SendToContact(cid, String.Format("ONLINE:{0}>>", user.UserID));
                            SendToContact(user.UserID, String.Format("ONLINE:{0}>>", cid));
                        }
                    });
                    foreach (MessageInfo undmessage in MessageInfo.GetAllUndelivered(user.UserID))
                    {
                        SendToContact(user.UserID, undmessage.MessageText);
                        UndeliveredMessageInfo.Create(undmessage.MessageID, user.UserID).Remove();
                    }
                    break;
                case "MESSAGE":
                    MessageInfo message = new MessageInfo();
                    message.UserID = user.UserID;
                    message.MessageText = e.Data;
                    message.CreatedDate = DateTime.Now;
                    if (actions["TYPE"][0] == "chat") message.MessageType = MessageTypes.Chat;
                    else if (actions["TYPE"][0] == "topic") message.MessageType = MessageTypes.Comment;
                    message.Update();
                    if (message.MessageID > 0 && actions["TYPE"][0] == "topic")
                    {
                        int tid = int.Parse(actions["ID"][0]);
                        MessageInTopicInfo.Create(message.MessageID, tid).Add();
                    }

                    int iid;
                    foreach (string id in actions["TO"])
                    {
                        if (int.TryParse(id, out iid) && user.Contacts.Contains(iid))
                        {
                            bool isOnline = Users.Exists(c => c.UserID == iid);
                            //save as undelivered
                            if (message.MessageID > 0 && !isOnline)
                                UndeliveredMessageInfo.Create(message.MessageID, iid).Add();
                            //dispatch message
                            if (isOnline)
                            {
                                OnlineUser contact = Users.Single(c => c.UserID == iid);
                                contact.Connection.Send(e.Data);
                            }
                        }
                    }
                    break;
                case "TOPICGET":
                    TopicInfo topicinfo = TopicInfo.Get(int.Parse(actions["ID"][0]));
                    List<UserInTopicInfo> usersintopic = UserInTopicInfo.GetByTopic(topicinfo.TopicID);
                    List<string> ids = new List<string>(),
                        mentors = new List<string>();
                    usersintopic.ForEach(delegate(UserInTopicInfo uit) { ids.Add(uit.UserID.ToString()); if (uit.UserTopicRole == UserTopicRoles.Mentor) mentors.Add(uit.UserID.ToString()); });
                    user.Connection.Send(string.Format("TOPIC:TO({0}),MENTORS({1}),TOPICID({2})>>{3}", string.Join("|", ids.ToArray()), string.Join("|", mentors.ToArray()), topicinfo.TopicID, topicinfo.TopicText));
                    foreach(MessageInfo topicmessageinfo in MessageInfo.GetByTopic(topicinfo.TopicID))
                    {
                        user.Connection.Send(topicmessageinfo.MessageText);
                    }
                    break;
                case "TOPIC":
                    TopicInfo info = new TopicInfo();
                    info.TopicText = data;
                    info.Update();
                    for (int i = 0; i < actions["TO"].Length; i++)
                    {
                        UserInTopicInfo usertopic = new UserInTopicInfo();
                        usertopic.UserID = int.Parse(actions["TO"][i]);
                        usertopic.TopicID = info.TopicID;
                        usertopic.UserTopicRole = (actions["MENTORS"].Contains(actions["TO"][i]) ? UserTopicRoles.Mentor : UserTopicRoles.User);
                        usertopic.Add();
                    }
                    actions.Add("TOPICID", new string[] { info.TopicID.ToString() });
                    string newdata = combine();
                    foreach (string id in actions["TO"])
                    {
                        if (int.TryParse(id, out iid) && iid != user.UserID && Users.Exists(c => c.UserID == iid))
                        {
                            OnlineUser contact = Users.Single(c => c.UserID == iid);
                            contact.Connection.Send(newdata);
                        }
                    }
                    user.Connection.Send(string.Format("TOPICREF:ID({0}),REF({1})>>{2}", info.TopicID, actions["REF"][0], info.GetTitle()));
                    break;
                case "TOPICEDIT":
                    topicinfo = TopicInfo.Get(int.Parse(actions["ID"][0]));
                    topicinfo.TopicText = data;
                    topicinfo.Update();
                    UserInfo.GetByTopic(topicinfo.TopicID).ForEach(delegate(UserInfo u) { SendToContact(u.UserID, e.Data); });
                    break;
                case "SETUSERS":
                    int setusersid = int.Parse(actions["ID"][0]);
                    string setusersdata = "";
                    if (actions["TYPE"][0] == "topic")
                    {
                        setusersdata = TopicInfo.Get(setusersid).GetTitle();
                    }
                    foreach (string setusersrej in actions["OLDUSERS"])
                    {
                        if (string.IsNullOrEmpty(setusersrej)) continue;
                        if (actions["TYPE"][0] == "topic")
                        {
                            UserInTopicInfo uintop = new UserInTopicInfo();
                            uintop.TopicID = setusersid;
                            uintop.UserID = int.Parse(setusersrej);
                            uintop.Remove();
                        }
                        SendToContact(int.Parse(setusersrej), string.Format("LEAVE:ID({0}),TYPE({1})>>", setusersid, actions["TYPE"][0]));
                    }
                    foreach (string setusersnew in actions["NEWUSERS"])
                    {
                        if (string.IsNullOrEmpty(setusersnew)) continue;
                        if (actions["TYPE"][0] == "topic")
                        {
                            UserInTopicInfo uintop = new UserInTopicInfo();
                            uintop.TopicID = setusersid;
                            uintop.UserID = int.Parse(setusersnew);
                            uintop.UserTopicRole = (actions["MENTORS"].Contains(setusersnew) ? UserTopicRoles.Mentor : UserTopicRoles.User);
                            uintop.Add();
                        }
                        SendToContact(int.Parse(setusersnew), string.Format("INVITED:ID({0}),TYPE({1})>>{2}", setusersid, actions["TYPE"][0], setusersdata));
                    }
                    TopicInfo.CheckForEmptyTopicByID(setusersid);
                    break;
                case "ATTACHMENT":
                    int attachmenttotopicid = int.Parse(actions["ID"][0]);
                    AttachmentInfo attach = new AttachmentInfo();
                    attach.AttachmentData = data;;
                    attach.AttachmentType = AttachmentTypes.Link;
                    attach.Update();
                    AttachmentInTopicInfo.Create(attachmenttotopicid, attach.AttachmentID).Add();
                    actions.Add("AID", new string[] { attach.AttachmentID.ToString() });
                    var newattachdata = combine();
                    UserInfo.GetByTopic(attachmenttotopicid).ForEach(delegate(UserInfo u) { SendToContact(u.UserID, newattachdata); });
                    break;
                case "ATTACHMENTGET":
                    AttachmentInfo.GetByTopic(int.Parse(actions["ID"][0])).ForEach(delegate(AttachmentInfo a) { user.Connection.Send("ATTACHMENT:ID(" + actions["ID"][0] + "),AID("+a.AttachmentID.ToString()+"),TYPE(link)>>" + a.AttachmentData); });
                    break;
                case "ATTACHMENTREMOVE":
                    attachmenttotopicid = int.Parse(actions["ID"][0]);
                    iid = int.Parse(actions["AID"][0]);
                    AttachmentInfo attmoveinfo = new AttachmentInfo();
                    attmoveinfo.AttachmentID = iid;
                    attmoveinfo.Remove();
                    UserInfo.GetByTopic(attachmenttotopicid).ForEach(delegate(UserInfo u) { SendToContact(u.UserID, e.Data); });
                    break;
            }
        }