/// <summary> /// Determines if a user is known by the internal databases. If not /// it will create and authenticate the user. If so, it will simply look up the user. /// </summary> /// <param name="s">The server to check for the user on.</param> /// <param name="irch">The irc host to check for.</param> /// <returns>The new or old user.</returns> private static User UpdateUserDB(Server s, IRCHost irch) { User u; try { u = s.UserDatabase[irch.Nick]; if ( !u.CurrentHost.Equals( irch ) ) u.CurrentHost = irch; } catch ( KeyNotFoundException ) { u = new User( irch ); s.UserDatabase.AddUser( u ); s.UserDatabase.Authenticate( u ); } return u; }
/// <summary> /// Retrieves a ChannelUser from the database. /// </summary> /// <param name="s">The server to check for the ChannelUser.</param> /// <param name="channelname">The channel to check.</param> /// <param name="usernick">The nick of a definetely existing user.</param> /// <param name="uflag">If the channel user does not exist he must be created with a userflag. This is that flag.</param> /// <returns>The new or old ChannelUser.</returns> private static ChannelUser UpdateBothDB(Server s, string channelname, string usernick, Nullable<char> uflag) { ChannelUser cu; try { cu = s.ChannelDatabase[channelname][usernick]; //Check if ChannelUser exists. } catch ( KeyNotFoundException ) { //ChannelUser does not exist. cu = new ChannelUser(); User internalu; try { internalu = s.UserDatabase[usernick]; } catch ( KeyNotFoundException ) { cu.UserFlag = uflag; s.ChannelDatabase[channelname].AddUser( usernick, cu ); return cu; } cu.InternalUser = internalu; cu.UserFlag = uflag; s.ChannelDatabase[channelname].AddUser( cu ); return cu; } //Make certain that if the usernick exists in the database that we attach it to it's user counterpart. if ( cu.InternalUser == null ) { User internalu; try { internalu = s.UserDatabase[usernick]; cu.InternalUser = internalu; } catch ( KeyNotFoundException ) { //throw new ExecutionEngineException( "PLACEHOLDER FOR DESTRUCTION THIS SHOULD NEVER HAPPEN" ); } } return cu; }
/// <summary> /// Determines if a channel is known by the internal databases. If not /// it will create the channel. If so, it will simply look up the channel.. /// </summary> /// <param name="s">The server to check for the channel on.</param> /// <param name="channelname">The channel name to check for.</param> /// <returns>The new or old user.</returns> private static Channel UpdateChanDB(Server s, string channelname) { Channel c; try { c = s.ChannelDatabase[channelname]; } catch ( KeyNotFoundException ) { c = new Channel( channelname ); s.ChannelDatabase.AddChannel( c ); } return c; }
/// <summary> /// Retrieves a ChannelUser from the database. /// </summary> /// <param name="s">The server to check for the ChannelUser.</param> /// <param name="channelname">The channel to check.</param> /// <param name="usernick">The nick of a definetely existing user.</param> /// <returns>The new or old ChannelUser.</returns> private static ChannelUser UpdateBothDB(Server s, string channelname, string usernick) { return UpdateBothDB( s, channelname, usernick, null ); }
/// <summary> /// Invokes the Parsers. /// </summary> /// <param name="s">The server object.</param> /// <param name="c">The channel triggered on.</param> /// <param name="u">The user triggered from.</param> /// <param name="cu">The channeluser triggered from.</param> /// <param name="text">The offending text.</param> /// <param name="pt">The Parse Types to invoke on.</param> private static void InvokeParsers(Server s, Channel c, User u, ChannelUser cu, string text, IRCEvents.ParseTypes pt) { bool pcount = s.EventObject.Parses.Count > 0; bool wcount = s.EventObject.WildcardParses.Count > 0; if ( pcount || wcount ) { IRCEvents.ParseReturns pr = CreateParseReturns( s.ServerID, c, u, cu, text ); if ( pcount ) s.QueueData( ParseInvocation( pr.Text, s.EventObject, pr, pt, false ) ); if ( wcount ) s.QueueData( ParseInvocation( pr.Text, s.EventObject, pr, pt, true ) ); } }
/// <summary> /// This method parses all raw data for each packet of data that /// a Server object might receive. It then uses the events defined /// in IRCEvents and fires them through the appropriate server object. /// </summary> /// <param name="s">The Server which the data was received on.</param> /// <param name="raw">The raw text from the socket.</param> public static void Parse(Server s, string raw) { //TODO: IMPLEMENT THE REST :D int spacestring = 0; int lastchar = 0; int firstchar = 0; //Remove excessive spaces from everything. StringBuilder sb = new StringBuilder(); for ( int i = 0; i < raw.Length; i++ ) { if ( raw[i] == ' ' ) { if ( spacestring++ == 0 ) lastchar = i; } else if ( spacestring > 1 ) { sb.Append( raw, firstchar, lastchar - firstchar + 1 ); spacestring = 0; firstchar = i; lastchar = i; } else { spacestring = 0; lastchar = i; } } if ( sb.Length > 0 || ( lastchar != firstchar && firstchar != 0 && lastchar != raw.Length - 1 ) ) { sb.Append( raw, firstchar, lastchar - firstchar + 1 ); } if ( sb.Length > 0 ) raw = sb.ToString(); //Make sure this excessive space is handled with care. (For all instances where text is preceeded by a space) raw = raw.Replace( ": ", ":" ); //Replies to ping handler if ( raw.StartsWith( "PING :" ) ) { string[] parsed = raw.Split( ':' ); s.QueueData( s.EventObject.OnPing( s.ServerID, parsed[1] ) ); } if ( raw.StartsWith( "ERROR :" ) ) { Console.WriteLine( "[Error: whatsit]" ); } //All that work above for this? string[] spaceSplit = raw.Split( ' ' ); if ( spaceSplit.Length <= 1 ) return; //Nothing below here takes only one spaceSplit arg. switch ( spaceSplit[1] ) { case "001": { string welcome = raw.Substring( spaceSplit[0].Length + spaceSplit[1].Length + 4 + spaceSplit[2].Length ); s.QueueData( s.EventObject.OnWelcome( s.ServerID, welcome ) ); } break; case "352": { //spaceSplit[5] = spaceSplit[5].Substring( 1 ); //Get rid of the : on the first name. // // 0 1 2 3 4 5 6 7 8 9 10 //":TechConnect.NL.EU.GameSurge.net 352 P2Q #phishcave ~2q d75-155-184-58.bchsia.telus.net *.GameSurge.net P2Q H :0 2Q Beta" IRCHost ir = new IRCHost(spaceSplit[7], spaceSplit[4], spaceSplit[5]); //fix on debug. Channel c = UpdateChanDB(s, spaceSplit[3]); UpdateUserDB(s, ir); //UpdateBothDB(s, spaceSplit[3], spaceSplit[7]); //This should blank add all the users if (spaceSplit[8].Contains("@")) UpdateBothDB(s, spaceSplit[3], spaceSplit[7], '@'); //NO Simple. hah else if (spaceSplit[8].Contains("+")) UpdateBothDB(s, spaceSplit[3], spaceSplit[7], '+'); else UpdateBothDB(s, spaceSplit[3], spaceSplit[7], null); //Invoke the event. s.QueueData(s.EventObject.OnNames(s.ServerID, c)); } break; case "353": { spaceSplit[5] = spaceSplit[5].Substring( 1 ); //Get rid of the : on the first name. //if ( spaceSplit[2] == s.Config.NickName ) //Do we need to parse ourself? ~fish // break; Channel c = UpdateChanDB( s, spaceSplit[4] ); //This should blank add all the users for ( int i = 5; i < spaceSplit.Length; i++ ) { if ( spaceSplit[i][0] == '@' || spaceSplit[i][0] == '+' ) UpdateBothDB( s, spaceSplit[4], spaceSplit[i].Substring( 1 ), spaceSplit[i][0] ); else UpdateBothDB( s, spaceSplit[4], spaceSplit[i], null ); } //Invoke the event. s.QueueData( s.EventObject.OnNames( s.ServerID, c ) ); } break; case "433": { //:SERVER 433 * THENICK :Nickname is already in use. s.QueueData( s.EventObject.OnErr_NickNameInUse( s.ServerID, spaceSplit[3] ) ); } break; case "MODE": { //SERVERMODE: //:USERHOST MODE RECIPIENT :MODESTRING if ( spaceSplit[3][0] == ':' ) { IRCHost irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); User u = new User( irch ); s.QueueData( s.EventObject.OnServerModeMessage( s.ServerID, u, spaceSplit[2], spaceSplit[3].TrimStart( ':' ) ) ); } else { //:USERMODE //TODO: HANDLE THIS //:USERHOST MODE MODESTRING MODEAFFECTUSER MODEAFFECTUSER MODEAFFECTUSER } } break; case "JOIN": { IRCHost irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); Channel c = UpdateChanDB( s, spaceSplit[2] ); if ( irch.Nick.Equals( s.CurrentNickName ) ) { //We joined the channel. s.QueueData( s.EventObject.OnBotJoin( s.ServerID, c ) ); } else { //Someone joined the channel. //Update the user first. UpdateUserDB( s, irch ); //Now update the channel. ChannelUser cu = UpdateBothDB( s, spaceSplit[2], irch.Nick ); s.QueueData( s.EventObject.OnJoin( s.ServerID, c, cu ) ); } } break; case "PRIVMSG": { string privmsgtxt = raw.Substring( spaceSplit[0].Length + spaceSplit[1].Length + spaceSplit[2].Length + 4 ); //:USERHOST PRIVMSG RECIPIENT :TEXT // length length length + 4 (3 spaces & a :) IRCHost irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); User u = UpdateUserDB( s, irch ); //Replies to Channel/User CTCP Messages. if ( spaceSplit.Length > 4 && spaceSplit[3].Length > 2 && spaceSplit[3][1] == (char)1 && raw.EndsWith( ( (char)1 ).ToString() ) ) { if ( spaceSplit[2].Length >= 1 && ( spaceSplit[2][0] == '#' || spaceSplit[2][0] == '&' ) ) { Channel c = UpdateChanDB( s, spaceSplit[2] ); ChannelUser cu = UpdateBothDB( s, spaceSplit[2], irch.Nick ); s.QueueData( s.EventObject.OnChannelCTCPMessage( s.ServerID, c, cu, privmsgtxt.Trim( (char)1 ) ) ); } else s.QueueData( s.EventObject.OnUserCTCPMessage( s.ServerID, spaceSplit[2], u, privmsgtxt.Trim( (char)1 ) ) ); } //TODO: Evaluate if these can be moved up above the CTCP messages once it's converted. else if ( spaceSplit[2][0] == '#' || spaceSplit[2][0] == '&' ) { Channel c = UpdateChanDB( s, spaceSplit[2] ); ChannelUser cu = UpdateBothDB( s, spaceSplit[2], irch.Nick ); InvokeParsers( s, c, u, cu, privmsgtxt, IRCEvents.ParseTypes.ChannelMessage ); s.QueueData( s.EventObject.OnChannelMessage( s.ServerID, c, cu, privmsgtxt ) ); } else { InvokeParsers( s, null, u, null, privmsgtxt, IRCEvents.ParseTypes.PrivateMessage ); s.QueueData( s.EventObject.OnUserMessage( s.ServerID, spaceSplit[2], u, privmsgtxt ) ); } } break; case "NOTICE": { // user notice // :HOST NOTICE ME :MSG // channel notice // :HOST NOTICE CHANNEL :MSG IRCHost irch; string text = raw.Substring( spaceSplit[0].Length + spaceSplit[1].Length + spaceSplit[2].Length + 4 ); try { irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); } catch ( FormatException ) { // looks like this is a server notice. InvokeParsers( s, null, null, null, text, IRCEvents.ParseTypes.ServerNotice ); s.QueueData( s.EventObject.OnServerNotice( s.ServerID, text ) ); break; } User u = UpdateUserDB( s, irch ); if ( spaceSplit[2][0] == '#' || spaceSplit[2][0] == '&' ) { Channel c = UpdateChanDB( s, spaceSplit[2] ); InvokeParsers( s, c, u, null, text, IRCEvents.ParseTypes.ChannelNotice ); s.QueueData( s.EventObject.OnChannelNotice( s.ServerID, c, u, text ) ); } else { InvokeParsers( s, null, u, null, text, IRCEvents.ParseTypes.PrivateNotice ); s.QueueData( s.EventObject.OnPrivateNotice( s.ServerID, u, text ) ); } } break; case "QUIT": { //:USERHOST QUIT :MESSAGE IRCHost irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); User u = UpdateUserDB( s, irch ); //Premature call of event. s.QueueData( s.EventObject.OnQuit( s.ServerID, u, raw.Substring( spaceSplit[0].Length + spaceSplit[1].Length + 3 ) ) ); foreach ( Channel c in s.ChannelDatabase ) c.RemoveUser( u.Nickname ); s.UserDatabase.RemoveUser( u ); } break; case "PART": { //:USERHOST PART CHANNELNAME :MESSAGE IRCHost irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); if ( irch.Nick.Equals( s.CurrentNickName ) ) { s.QueueData( s.EventObject.OnBotPart( s.ServerID, s.ChannelDatabase[spaceSplit[2]], spaceSplit.Length > 3 ? spaceSplit[3] : null ) ); s.ChannelDatabase.RemoveChannel( spaceSplit[2] ); } else { User u = UpdateUserDB( s, irch ); ChannelUser cu = UpdateBothDB( s, spaceSplit[2], u.Nickname ); //Premature call of event. s.QueueData( s.EventObject.OnPart( s.ServerID, cu, s.ChannelDatabase[spaceSplit[2]], spaceSplit.Length > 3 ? spaceSplit[3] : null ) ); s.ChannelDatabase[spaceSplit[2]].RemoveUser( u ); bool stillOnAChannel = false; foreach ( Channel c in s.ChannelDatabase ) if ( c.HasNick( u.Nickname ) ) stillOnAChannel = true; if ( stillOnAChannel == false ) s.UserDatabase.RemoveUser( u ); } } break; case "NICK": { //:USERHOST NICK :NEWNICK IRCHost irch = new IRCHost( spaceSplit[0].Substring( 1 ) ); string newNick = spaceSplit[2].Substring( 1 ); string oldNick = irch.Nick; if ( oldNick == s.CurrentNickName ) { //We need to update. s.CurrentNickName = newNick; s.QueueData( s.EventObject.OnBotNickName( s.ServerID, newNick, oldNick ) ); } else { User oldUser = null; try { oldUser = s.UserDatabase[irch.Nick];//Get the old user. Remove him from the DB. } catch ( KeyNotFoundException ) { //see below. } if ( oldUser == null ) { //This means he wasn't in our user database.. WEIRD. Add him, add him to the db, but still //broadcast it as a nick message. oldUser = new User( irch ); s.UserDatabase.AddUser( oldUser ); s.UserDatabase.Authenticate( oldUser ); } else { s.UserDatabase.RemoveUser( oldUser ); //Removed him. oldUser.CurrentHost.Nick = newNick; //Change his nick in the local User variable. s.UserDatabase.AddUser( oldUser ); //Re-Add him to the database. s.UserDatabase.Authenticate( oldUser ); } ChannelUser cu = null; foreach ( Channel c in s.ChannelDatabase ) { //Replace nick in all channels that had the old nick identifier. try { cu = c[oldNick]; //Preserve his channel settings by retrieving this piece of info. } catch ( KeyNotFoundException ) { continue; } cu.InternalUser = oldUser; //Set the new internal user. (nick matches might change authentication) c.ReplaceUser( oldNick, cu ); //Replace the user in the database, remove oldNick, add the channeluser. } s.QueueData( s.EventObject.OnNickName( s.ServerID, oldUser, oldNick ) ); } } break; } }
public ServerThread(Server s, Thread t) { server = s; thread = t; }
/// <summary> /// Takes the configuration info, and begins the threading /// model to take care of all the Server objects created. /// </summary> private void StartServerThreads() { //Make sure the file system environment is set up for the servers: if ( !Directory.Exists( "userdb" ) ) Directory.CreateDirectory( "userdb" ); if ( !Directory.Exists( "logs" ) ) Directory.CreateDirectory( "logs" ); if ( !Directory.Exists( "modules" ) ) Directory.CreateDirectory( "modules" ); Configuration.ServerConfig[] list = config.Servers; WriteLogFunction wlf = new WriteLogFunction( WriteLog ); bool allServersReady = true; //Loads & Initializes all servers. for ( int i = 0; i < list.Length; i++ ) { Server s = new Server( list[i], wlf ); Thread t = new Thread( s.Connect ); servers[i] = new ServerThread( s, t ); servers[i].LoadModules = s.Init(); allServersReady = allServersReady && servers[i].LoadModules; } //Load all modules. WriteLog( "Loading Modules..." ); LoadModules( config.Modules ); //Activate all the modules. for ( int m = 0; m < MaxModules; m++ ) { if ( modules[m] == null ) continue; WriteLog( " Module Loaded: " + modules[m].ModuleConfig.FullName ); for ( int s = 0; s < MaxServers; s++ ) { if ( servers[s] == null || !servers[s].LoadModules || !modules[m].ModuleConfig.QueryServerList( servers[s].Server.Config.Name ) ) continue; modules[m].Activate( s ); WriteLog( " Activated Link: " + servers[s].Server.Config.Name + "." + modules[m].ModuleConfig.FullName ); } if ( allServersReady ) { WriteLog( " Activation Completed: " + modules[m].ModuleConfig.FullName ); modules[m].ActivationComplete(); } } foreach ( ServerThread st in servers ) if ( st == null ) continue; else st.Thread.Start(); }