HubProtocol.cs :  » Business-Application » DC » DCSharp » Backend » Protocols » Nmdc » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Business Application » DC 
DC » DCSharp » Backend » Protocols » Nmdc » HubProtocol.cs
/* 
 * Copyright (C) 2004-2005 Jonathan Bindel
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

using System;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Objects;
using DCSharp.Extras;

namespace DCSharp.Backend.Protocols.Nmdc{
  public class HubProtocol : UdpProtocol, IHubProtocol
  {
    private Incoming incoming;
    
    public HubProtocol(IHubProtocolHelper helper) :
      base(helper, helper)
    {
      if (helper == null)
      {
        throw new ArgumentNullException("helper");
      }
      this.helper = helper;
      
      incoming = new Incoming(this);

      #region Handlers

      // Handshake
      commands.Add("$lock", new StringEventHandler(incoming.Lock));
      commands.Add("$hubname", new StringEventHandler(incoming.HubName));
      commands.Add("$validatedenide", new StringEventHandler(incoming.ValidateDenide));
      commands.Add("$getpass", new BlankEventHandler(incoming.GetPass));
      commands.Add("$badpass", new BlankEventHandler(incoming.BadPass));
      commands.Add("$logedin", new StringEventHandler(incoming.LogedIn));
      commands.Add("$hello", new StringEventHandler(incoming.Hello));
      
      // General
      commands.Add("$to:", new StringEventHandler(incoming.To));
      commands.Add("$myinfo", new StringEventHandler(incoming.MyInfo));
      commands.Add("$getnetinfo", new BlankEventHandler(incoming.GetNetInfo));
      commands.Add("$quit", new StringEventHandler(incoming.Quit));
      
      // Connect
      commands.Add("$revconnecttome", new StringEventHandler(incoming.RevConnectToMe));
      commands.Add("$connecttome", new StringEventHandler(incoming.ConnectToMe));
      commands.Add("$forcemove", new StringEventHandler(incoming.ForceMove));
      
      // User
      commands.Add("$nicklist", new StringEventHandler(incoming.NickList));
      commands.Add("$oplist", new StringEventHandler(incoming.OpList));
      
      // Search
      commands.Add("$search", new StringEventHandler(incoming.Search));

      #endregion
    }

    #region Classes

    internal class Incoming
    {
      private HubProtocol protocol;

      public Incoming(HubProtocol protocol)
      {
        this.protocol = protocol;
      }

      #region Handshake

      public void Lock(string argument)
      {
        protocol.Key(CalculateKey(argument, protocol.Encoding));
        protocol.ValidateNick();
      }

      public void HubName(string argument)
      {
        protocol.Connection.Name = argument;
      }

      public void ValidateDenide(string argument)
      {
        //protocol.Connection.Display("Username taken");
        protocol.Connection.Disconnect();
      }

      public void GetPass()
      {
        protocol.MyPass();
      }

      public void BadPass()
      {
        //protocol.Connection.Display("*** Bad Password");
        protocol.Connection.Disconnect();
      }

      public void LogedIn(string nick)
      {
        // This command is sent to ops after a successful $MyPass.
        if(nick == protocol.Connection.LocalIdentity.Nick)
        {
          protocol.Connection.LocalIdentity.Op = true;
        }
      }

      public void Hello(string argument)
      {
        if(argument == protocol.Connection.LocalIdentity.Nick)
        {
          protocol.Connection.Send("$Version 1,0091|");
          protocol.GetNickList();
          protocol.Connection.Send("$MyINFO $ALL " + GetIdentity() +
            "|");
        }
      }

      #endregion

      #region General

      public void To(string argument)
      {
        //<othernick> From: <nick> $<<nick>> <message>
        int end = argument.IndexOf('$');
        if(end < 0)
        {
          return;
        }

        int beg = argument.IndexOf("From: ") + 6;
        if(beg < 6)
        {
          return;
        }

        string nick = argument.Substring(beg, end - beg).Trim();
        
        Identity user = protocol.GetUser(nick);
        if(user == null)
        {
          user = protocol.CreateUser(nick);
        }

        string message = argument.Substring(end + 1);
        message = message.Substring(message.IndexOf('>') + 1);
        message = message.Trim();
        message = message.Replace("&#36;", "$");
        message = message.Replace("&#124;", "|");

        protocol.Connection.EmitPrivateMessage(user,
          protocol.Connection.LocalIdentity, message);
      }

      public void MyInfo(string argument)
      {
        if(argument.StartsWith("$ALL") && argument.Length > 5)
        {
          argument = argument.Substring(5);
        }
        string nick = Regex.Match(argument, @"([^\s^<])*").Value;
        
        Identity user = protocol.GetUser(nick);
        if (user == null)
        {
          user = protocol.CreateUser(nick);
          ParseMyInfo(user, argument);
          
          protocol.Connection.Users.Add(user);
        }
        else
        {
          ParseMyInfo(user, argument);
          protocol.Connection.Users.EmitUserChanged(user);
        }
      }

      public void GetNetInfo()
      {
        StringBuilder sb = new StringBuilder();
        sb.Append("$NetInfo ");
        sb.Append(protocol.Helper.Slots);
        sb.Append('$');
        sb.Append("1");
        sb.Append(protocol.Helper.ConnectionSettings.SupportsIncoming ? 'A' : 'P');
        sb.Append('|');

        protocol.Connection.Send(sb.ToString());
      }

      public void Quit(string argument)
      {
        Identity user = protocol.GetUser(argument);
        if(user != null)
        {
          //user.Online = false;
          protocol.Connection.Users.Remove(user);
        }
      }

      #endregion

      #region Connect

      public void RevConnectToMe(string argument)
      {
        if (protocol.Helper.ConnectionSettings.SupportsIncoming)
        {
          string[] strings = argument.Split(' ');
          if(strings.Length > 0)
          {
            string nick = strings[0];
            protocol.ConnectToMe(nick);
          }
        }
      }

      public void ConnectToMe(string argument)
      {
        string[] strings = argument.Split(' ');
        if(strings.Length == 2)
        {
          string hostname = strings[1];
          string address = Util.AddressPart(hostname);
          int port = Util.PortPart(hostname);

          IPAddress ipAddress = IPAddress.Parse(address);
          IPEndPoint endPoint = new IPEndPoint(ipAddress, port);
          
          UserProtocol userProtocol = new UserProtocol(protocol.Helper.UserProtocolHelper);
          UserConnection userConnection = protocol.Helper.CreateUserConnection(endPoint,
            userProtocol);
          userProtocol.Connection = userConnection;
          
          userConnection.LocalIdentity = protocol.Connection.LocalIdentity;
          userConnection.Hub = protocol.Connection;
          userConnection.Connect();
        }
      }

      public void ForceMove(string argument)
      {
        protocol.Connection.Disconnect();
        
        protocol.Helper.RedirectTo(argument);
      }

      #endregion

      #region User

      public void NickList(string argument)
      {
        string[] nicks = Regex.Split(argument, @"\${2}");

        foreach(string nick in nicks)
        {
          if(nick.Length > 0)
          {
            if(protocol.GetUser(nick) == null)
            {
              Identity user = protocol.CreateUser(nick);
              //user.Online = true;

              protocol.Connection.Users.Add(user);
            }

            //TODO: (possibly) get sending encoder to work correctly with tm's.
            protocol.GetInfo(nick);
          }
        }
      }

      public void OpList(string argument)
      {
        string[] nicks = Regex.Split(argument, @"\${2}");

        foreach(string nick in nicks)
        {
          if(nick.Length > 0)
          {
            Identity user = protocol.GetUser(nick);
            if(user == null)
            {
              user = protocol.CreateUser(nick);
              user.Op = true;
              
              protocol.Connection.Users.Add(user);
            }
            else if(!user.Op)
            {
              user.Op = true;
              protocol.Connection.Users.EmitUserChanged(user);
            }
          }
        }
      }

      #endregion
      
      #region Search

      public void Search(string argument)
      {
        // $Search <ip>:<port> <searchstring>
        // $Search Hub:<requestornick> <searchstring>
        
        string[] parts = argument.Split(' ');
        if(parts.Length != 2)
        {
          return;
        }
        
        string[] from = parts[0].Split(':');
        if(from.Length != 2)
        {
          return;
        }
        
        string nick = null;
        IPEndPoint endPoint = null;
        
        if(from[0].Equals("Hub"))
        {
          nick = from[1];
          Identity identity = protocol.GetUser(nick);
          
          if (identity == null || identity == protocol.Connection.LocalIdentity ||
            (protocol.Helper.ConnectionSettings.SupportsIncoming == false &&
            identity.Active == false))
          {
            return;
          }
        }
        else
        {
          if(from[0] == protocol.Helper.ConnectionSettings.Address &&
            from[1] == protocol.Helper.ConnectionSettings.Port.ToString())
          {
            return;
          }
          
          string ip = Util.AddressPart(parts[0]);
          int port = Util.PortPart(parts[0]);
          
          endPoint = new IPEndPoint(IPAddress.Parse(ip), port);
        }
        
        if (!protocol.Helper.CanSearch(parts[0]))
          return;
        
        // <sizerestricted>?<ismaxsize>?<size>?<datatype>?<searchpattern>
        
        string[] searchString = parts[1].Split('?');
        if(searchString.Length < 5)
        {
          return;
        }
        
        long? maxSize = null;
        long? minSize = null;
        
        bool sizeRestrict = searchString[0].Equals("T");
        if(sizeRestrict)
        {
          bool isMaxSize = searchString[1].Equals("T");
          long? size = long.Parse(searchString[2]);
          
          maxSize = isMaxSize ? size : null;
          minSize = !isMaxSize ? size : null;
        }
        
        SearchFileType type = (SearchFileType)Enum.Parse(typeof(SearchFileType),
          searchString[3]);
        
        SearchInfo searchInfo;
        if ((int)type == 9)
        {
          // It's a TTH search
          string tth = searchString[4].Substring(4);
          
          searchInfo = new SearchInfo(tth);
        }
        else
        {
          string pattern = searchString[4];
          string[] keywords = pattern.Split('$');
          
          searchInfo = new SearchInfo(keywords, type, maxSize,
            minSize);
        }
        
        // Search
        int? max = endPoint != null ? 10 : 5;
        
        protocol.Helper.Search(searchInfo, max);
        
        // Respond
        foreach (SearchResult result in searchInfo.SearchResults)
        {
          string message = CreateResultString(result, nick);
          
          if (endPoint != null)
          {
            UdpServer.Send(endPoint, message, Protocol.DefaultEncoding);
          }
          else
          {
            protocol.Connection.Send(message);
          }
        }
      }

      private string CreateResultString(SearchResult result, string nick)
      {
        // $SR <source_nick> <result> <free_slots>/<total_slots><0x05><hub_name> (<hub_ip:listening_port>)[<0x05><target_nick>]|
        // <result> is one of the following: 
        //   * <file_name><0x05><file_size> for file results
        //  * <directory> for directory results
        // For files containing TTH, the <hub_name> parameter is replaced with TTH:<base32_encoded_tth_hash>
        
        System.Text.StringBuilder message = new System.Text.StringBuilder();
        message.Append("$SR ");
        message.Append(protocol.Connection.LocalIdentity.Nick);
        message.Append(" ");
        message.Append(result.Path);
        
        if(result.Type == ResultType.File)
        {
          message.Append((char)5);
          message.Append(result.Size);
        }
        
        message.Append(" ");
        message.Append(protocol.Helper.FreeSlots);
        message.Append("/");
        message.Append(protocol.Helper.Slots);
        message.Append((char)5);
        
        if(result.TTH != null)
        {
          message.Append("TTH:");
          message.Append(result.TTH);
        }
        else
        {
          message.Append(protocol.Connection.Name);
        }
        
        message.Append(" (");
        message.Append(protocol.Connection.Address);
        message.Append(")");
        
        if(nick != null)
        {
          message.Append((char)5);
          message.Append(nick);
        }
        message.Append("|");
        
        return message.ToString();
      }
      
      #endregion
      
      #region MyInfo
      
      private string GetIdentity()
      {
        Identity user = protocol.Connection.LocalIdentity;
        
        string mode = protocol.Helper.ConnectionSettings.SupportsIncoming ? "A" : "P";
        int hubsNotRegistered = 1;
        int hubsRegistered = 0;
        int hubsOp = 0;
        //int maxToOpen = 40;
        
        StringBuilder tag = new StringBuilder(40);
        tag.Append("<++ V:0.674,M:");
        tag.Append(mode);
        tag.Append(",H:");
        tag.Append(hubsNotRegistered);
        tag.Append("/");
        tag.Append(hubsRegistered);
        tag.Append("/");
        tag.Append(hubsOp);
        tag.Append(",S:");
        tag.Append(protocol.Helper.Slots);
        //tag.Append(",O:");
        //tag.Append(maxToOpen);
        tag.Append(">");
        
        StringBuilder info = new StringBuilder();
        info.Append(user.Nick);
        info.Append(" ");
        info.Append(user.Description);
        info.Append(tag.ToString());
        info.Append("$ $");
        info.Append(user.Connection);
        //info.Append(Encoding.Default.GetString(new byte[] {(byte)user.Flag}));
        info.Append((char)1);
        info.Append("$");
        info.Append(user.Email);
        info.Append("$");
        //info.Append(user.ShareSize);
        info.Append(protocol.Helper.ShareTotal);
        info.Append("$");
        
        return info.ToString();
      }

      public void ParseMyInfo(Identity user, string info)
      {
        // <nick> <description>$<active>$<connection><flag>$<e-mail>$<sharesize>$|
        string[] elements = Regex.Split(info, @"\$");
        
        //[0] = <nick> <description><tag>
        //[1] = " " or <active>
        //[2] = <connection><flag>
        //[3] = <email>
        //[4] = <sharesize>
        //[5] = " "
        if(elements.Length > 0)
        {
          string descriptionAndTag = Regex.Match(elements[0], @"\s([^\$])*").Value;
          int cutoff = descriptionAndTag.LastIndexOf("<");
          if(cutoff > 0)
          {
            user.Description = descriptionAndTag.Substring(0, cutoff).Trim();
            
            string tag = descriptionAndTag.Substring(cutoff).Trim();
            if (tag.Length > 0)
            {
              user.SetParameter("tag", tag);
              user.Active = tag.IndexOf("M:A") > 0;
            }
          }
          else
          {
            user.Description = descriptionAndTag.Trim();
            
            // TODO: Check if this is still correct.
            user.Active = elements.Length > 1 && elements[1] == "A";
          }
          
          if(elements.Length > 2)
          {
            if(elements[2].Length > 1)
            {
              user.Connection = elements[2].Substring(0, elements[2].Length - 1);
              //user.Flag = (int)Encoding.Default.GetBytes(elements[2].Substring(elements[2].Length - 1))[0];
            }

            if(elements.Length > 3)
            {
              user.Email = elements[3];

              if(elements.Length > 4 && elements[4].Length > 0 &&
                Util.WholeNumber(elements[4]))
              {
                user.ShareSize = long.Parse(elements[4]);
              }
            }
          }
        }
      }
      
      #endregion
    }

    #endregion
    
    #region Properties
    
    public HubConnection Connection
    {
      get { return connection; }
      set { connection = value; }
    }
    private HubConnection connection;
    
    public IHubProtocolHelper Helper
    {
      get { return helper; }
    }
    private IHubProtocolHelper helper;
    
    #endregion
    
    #region Methods

    public override void Handle(string message)
    {
      if (message.StartsWith("$"))
      {
        base.Handle(message);
      }
      else
      {
        Identity user = null;
        
        int beg = message.IndexOf('<');
        int end = message.IndexOf('>');
        if (beg == 0 && end > 0)
        {
          string nick = message.Substring(1, end - 1);
          
          user = GetUser(nick);
          if (user == null)
          {
            user = CreateUser(nick);
          }
          message = message.Substring(end + 2);
        }
        
        message = message.Replace("&#36;", "$");
        message = message.Replace("&#124;", "|");

        connection.EmitMessage(user, message);
      }
    }

    protected override void Invoke(string command, string argument)
    {
      try
      {
        base.Invoke(command, argument);
      }
      catch (Exception e)
      {
        Debug.WriteLine(e);
      }
    }
    
    protected override Identity GetIdentity(string nick, string hubName,
      string hubAddress)
    {
      return GetUser(nick);
    }
    
    #region Outgoing Commands

    #region Handshake
    
    protected void Key(string argument)
    {
      connection.ConnectionSend("$Key " + argument + "|");
    }

    protected void ValidateNick()
    {
      connection.Send("$ValidateNick " + connection.LocalIdentity.Nick + "|");
    }

    protected void MyPass()
    {
      connection.Send("$MyPass " + connection.Password + "|");
    }

    #endregion

    #region General

    public void GetInfo(string nick)
    {
      connection.Send("$GetINFO " + nick + " " + connection.LocalIdentity.Nick + "|");
    }

    public void GetNickList()
    {
      connection.Send("$GetNickList|");
    }

    public void Kick(Identity user, string reason)
    {
      SendPrivateMessage(user, "You are being kicked because: " + reason);
      connection.Send("$Kick " + user.Nick + "|");
    }

    public void Redirect(Identity user, string address, string reason)
    {
      SendPrivateMessage(user, "You are being redirected to " + address + " because: " + reason);
      connection.Send("$OpForceMove $Who:" + user.Nick + "$Where:" + address + "$Msg:" + reason + "|");
    }

    #endregion

    #region Connect

    public void RequestConnection(Identity user)
    {
      if (Helper.ConnectionSettings.SupportsIncoming)
      {
        ConnectToMe(user.Nick);
      }
      else
      {
        RevConnectToMe(user.Nick);
      }
    }

    protected void ConnectToMe(string nick)
    {
      int port = Helper.ConnectionSettings.Port;

      connection.Send("$ConnectToMe " + nick + " " + Helper.ConnectionSettings.Address + ":" + port.ToString() + "|");
    }

    protected void RevConnectToMe(string nick)
    {
      connection.Send("$RevConnectToMe " + connection.LocalIdentity.Nick + " " + nick + "|");
    }

    #endregion

    #region Message

    public void SendMessage(string message)
    {
      if (message == null)
      {
        throw new ArgumentNullException("message");
      }
      
      message = message.Replace("$", "&#36;");
      message = message.Replace("|", "&#124;");

      connection.Send("<" + connection.LocalIdentity.Nick + "> " + message +
        "|");
    }

    public void SendPrivateMessage(Identity user, string message)
    {
      if (user == null)
      {
        throw new ArgumentNullException("user");
      }
      if (message == null)
      {
        throw new ArgumentNullException("message");
      }
      
      message = message.Replace("$", "&#36;");
      message = message.Replace("|", "&#124;");
      
      connection.Send(String.Format("$To: {0} From: {1} $<{1}> {2}|",
        user.Nick, connection.LocalIdentity.Nick, message));
      
      // Emitting event since private messages aren't echoed back
      connection.EmitPrivateMessage(connection.LocalIdentity, user, message);
    }

    #endregion

    #region Search

    public void Search(SearchInfo searchInfo)
    {
      if (searchInfo == null)
      {
        throw new ArgumentNullException("searchInfo");
      }
      
      StringBuilder message = new StringBuilder();
      message.Append("$Search ");
      if (Helper.ConnectionSettings.SupportsIncoming)
      {
        message.Append(Helper.ConnectionSettings.Address);
        message.Append(":");
        message.Append(Helper.ConnectionSettings.Port);
      }
      else
      {
        message.Append("Hub:");
        message.Append(connection.LocalIdentity.Nick);
      }
      message.Append(" ");
      message.Append(GetSearchString(searchInfo));

      connection.Send(message.ToString());
    }

    protected string GetSearchString(SearchInfo searchInfo)
    {
      StringBuilder search = new StringBuilder();

      // <sizerestricted>?<ismaximumsize>?<size>?<datatype>?<searchpattern>

      // <sizerestricted> is 'T' if the search should be restricted to files
      //  of a minimum or maximum size, otherwise 'F'.
      // <ismaximumsize> is 'F' if <sizerestricted> is 'F' or if the size
      //  restriction places a lower limit on file size, otherwise 'T'.
      // <size> is the minimum or maximum size of the file to report
      //  (according to <ismaximumsize>) if <sizerestricted> is 'T', otherwise 0.
      // <searchpattern> is used other users to determine if any files match.
      //  Non-alphanumeric characters (including spaces and periods) are replaced by '$'.
      //  The server must forward this message unmodified to all the other users. Every other
      //  user with one or more matching files must send a UDP packet to <ip>:<port>

      long size = searchInfo.MaxSize ?? (searchInfo.MinSize ?? 0);
      bool isMaximumSize = searchInfo.MaxSize != null;

      search.Append(size > 0 ? "T" : "F");
      search.Append("?");
      search.Append(isMaximumSize ? "T" : "F");
      search.Append("?");
      search.Append(size);
      search.Append("?");

      if(searchInfo.TTH == null)
      {
        string keywords = String.Join(" ", searchInfo.Keywords);
        
        search.Append((int)searchInfo.Type);
        search.Append("?"); 
        search.Append(Regex.Replace(keywords, @"\W", "$"));
      }
      else
      {
        // DC++ uses 9 as the datatype for TTH
        search.Append(9);
        //search.Append((int)SearchFileType.TTH);
        search.Append("?");
        search.Append("TTH:");
        search.Append(searchInfo.TTH);
      }

      search.Append("|");

      return search.ToString();
    }

    #endregion

    #endregion
    
    public Identity GetUser(string nick)
    {
      return GetUser(connection, nick);
    }
    
    public Identity CreateUser(string nick)
    {
      if (connection.LocalIdentity.Nick == nick)
      {
        return connection.LocalIdentity;
      }
      
      Uid cid = MakeCid(nick, connection.Address);
      return new Identity(User.Create(cid, nick), nick);
    }
    
    #endregion
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.