//~~~~~~~~~~~~~~{Setter}~~~~~~~~~~~~~~ /// <summary>Merges two connections, grabbing the socket from this conneciton</summary> /// <param name="OtherConnection"></param> public void AbsorbConnection(SwitchboardConnection OtherConnection) { while (Busy) { } //Wait for *this* connection to not be busy TickThread.Abort(); //Stop the thread //Close this connection's river and socket River.Close(); TheSocket.Close(); //Move everything over River = OtherConnection.River; TheSocket = OtherConnection.TheSocket; IP = OtherConnection.IP; //Send OK Send("OK", false); //Start async again StartAsync(); //Log it HeadServer.ToLog("User from " + IP.Address.ToString() + " reconnected from " + OtherConnection.IP.Address.ToString()); //Remove the other connection from the list of connections HeadServer.Connections.Remove(OtherConnection.ID); //Look at how far back we can go HeadServer.TheForm.ServerBWorker.ReportProgress(0); //Refresh the main form's listview. //Stop the other tickthread. This will stop *this* operation, so it's the last one to go. OtherConnection.TickThread.Abort(); }
protected virtual void HandleOut(SwitchboardConnection c, Command cmd) { // remove the user from its SB session // and send BYE to the users if (c.Session != null) { c.Session.Connections.Remove(c); // HACK: This isn't thread-safe Command bye = new Command(Verb.Bye, -1, c.User.UserHandle); if (c.Session.Connections.Count == 0) { Server.EndSession(c.Session); } else { Server.BroadcastCommand(c.Session, bye, c); } c.Session = null; } // SB servers don't echo an OUT unlike NS or DS Server.CloseConnection(c); }
public override void HandleCommand(SwitchboardConnection c, Command cmd) { switch (cmd.Verb) { case Verb.Usr: HandleUsr(c, cmd); break; case Verb.Ans: HandleAns(c, cmd); break; case Verb.Cal: HandleCal(c, cmd); break; case Verb.Out: HandleOut(c, cmd); break; case Verb.Msg: HandleMsg(c, cmd); break; default: HandleUnrecognised(c, cmd); break; } }
//------------------------------[Functions]------------------------------ /// <summary> /// Ticks the server. Each tick, the server: <br></br><br></br> /// <list type="number"> /// <item>Checks if there's a pending connection, and if so allows them in.</item> /// <item>Ticks each connection, essentially processing any pending commands they have.</item> /// <item>Checks if any connection has been closed, and if so, removes it.</item> /// <item>Ticks each extension.</item> /// </list></summary> public void Tick() { //Check if we need to let in another connection. if (Ears.Pending()) { int newID = GetNewConnectionID(); SwitchboardConnection Connection = new SwitchboardConnection(this, Ears.AcceptSocket(), newID); Connections.Add(newID, Connection); //Let them in to the system. Connection.StartAsync(); TheForm.ServerBWorker.ReportProgress(0); //Refresh the main form's listview. } foreach (SwitchboardConnection Connection in GetConnections()) { if (!Connection.IsConnected) { Connections.Remove(Connection.ID); //if the connection was closed, remove it from the list TheForm.ServerBWorker.ReportProgress(0); //Refresh the main form's listview. } } //Now tick every extension foreach (SwitchboardExtension extension in Extensions) { extension.Tick(); } }
protected virtual void HandleMsg(SwitchboardConnection c, Command cmd) { // >>> MSG TrID [U | N | A] Length\r\nMessage // <<< NAK TrID // <<< ACK TrID String ackMode = cmd.Params[0]; String message = cmd.Payload; int messLen = Int32.Parse(cmd.Params[1]); if (messLen != message.Length) { throw new ProtocolException(); } SwitchboardSession session = c.Session; // TODO: I'll need to convert MSGs or do some kind of special handling if there's any protocol-specific functionality // <<< MSG UserHandle FriendlyName Length\r\nMessage Command broadcastMsg = Command.CreateWithPayload(Verb.Msg, -1, message, c.User.UserHandle, c.User.FriendlyName); bool succeeded = Server.BroadcastCommand(session, broadcastMsg, c); // TODO: I'll need to separate out ACK code because it is NOT as simple as this switch (ackMode) { case "U": // Send nothing return; case "N": // Send only if it fails if (!succeeded) { Server.Send(c, new Command(Verb.Nak, cmd.TrId)); } break; case "A": // the spec didn't say what the response was in A mode (as it isn't implemented in MSNP2) but I assume this is what the client expects // ACK if it succeeds, and NAK if it fails Command response = new Command(succeeded ? Verb.Ack : Verb.Nak, cmd.TrId); Server.Send(c, response); break; default: Command errResponse = new Command(Error.InvalidParameter, cmd.TrId); Server.Send(c, errResponse); break; } }
private void ConnectionDetailsButton_Click(object sender, EventArgs e) { //Make sure there is at least one connection selected. //Well, there will only ever be one because we disabled multi-select but shh its ok. if (ConnectionsListView.SelectedIndices.Count == 0) { MessageBox.Show("Please select a connection to see its details", "n o", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } //Get the connection index int ConnectionIndex = ConnectionsListView.SelectedIndices[0]; //Get the connection. SwitchboardConnection Connection = MainServer.GetConnections()[ConnectionIndex]; //Pass the connection to a connection details form new UserDetailsForm(ref Connection).Show(); }
protected virtual void HandleUsr(SwitchboardConnection c, Command cmd) { // >>> USR TrID UserHandle AuthResponseInfo // <<< USR TrID OK UserHandle FriendlyName // authenticate and ensure there's a matching session invite String userHandle = cmd.Params[0]; String keyToken = cmd.Params[1]; User user = User.GetUser(userHandle); SwitchboardInvitation invite = Server.GetInvitationByUserAndKey(user, keyToken); if (invite == null) { Command err = new Command(Error.AuthenticationFailed, cmd.TrId); Server.Send(c, err); } else { invite.SetRsvp(); if (c.Protocol == null) { c.Protocol = _protocols.Find(p => p.CompatibleWithProtocol(invite.Protocol)); //( p => p.Name == invite.Protocol ); } SwitchboardSession session = invite.Session; c.User = user; c.Session = session; session.Connections.Add(c); User thisUser = User.GetUser(userHandle); Command response = new Command(Verb.Usr, cmd.TrId, "OK", userHandle, thisUser.FriendlyName); Server.Send(c, response); } }
protected virtual void HandleCal(SwitchboardConnection c, Command cmd) { // >>> CAL TrID UserHandle // <<< CAL TrID Status SessionID String recipientHandle = cmd.Params[0]; User recipient = User.GetUser(recipientHandle); if (recipient.NotificationServer == null || (recipient.Status & Status.Nln) != Status.Nln) { // the user is not online (or is hidden) Command responseErr = new Command(Error.SwitchboardFailed, cmd.TrId); // I guess? Server.Send(c, responseErr); return; } // if the invited user is allowed to contact the calling user (or vice-versa) then that user's notification server sends a RNG // otherwise, SB returns an error // TODO: Check permissions first User caller = c.User; SwitchboardSession session = c.Session; SwitchboardInvitation invite = session.CreateInvitation(recipient); recipient.NotificationServer.ASNotifyRng(caller, recipient, invite); Command responseOk = new Command(Verb.Cal, cmd.TrId, "RINGING", session.Id.ToStringInvariant()); Server.Send(c, responseOk); }
internal UserDetailsForm(ref SwitchboardConnection MyConnection) { InitializeComponent(); this.MyConnection = MyConnection; RefreshDetails(); }
protected virtual void HandleAns(SwitchboardConnection c, Command cmd) { // >>> ANS TrID LocalUserHandle AuthResponseInfo SessionID // <<< IRO TrID Participant# TotalParticipants UserHandle FriendlyName // <<< ANS TrID OK // add this user to that session, assuming it authenticates String sessionKey = cmd.Params[1]; Int32 sessionId = Int32.Parse(cmd.Params[2]); User user = User.GetUser(cmd.Params[0]); if (user == null) { Command responseErr = new Command(Error.AuthenticationFailed, cmd.TrId); Server.Send(c, responseErr); return; } SwitchboardInvitation invite = Server.GetInvitationByUserKeyAndId(user, sessionKey, sessionId); if (invite == null) { Command responseErr = new Command(Error.AuthenticationFailed, cmd.TrId); Server.Send(c, responseErr); return; } invite.SetRsvp(); if (c.Protocol == null) { c.Protocol = _protocols.Find(p => p.CompatibleWithProtocol(invite.Protocol)); //( p => p.Name == invite.Protocol ); } SwitchboardSession session = invite.Session; int cnt = session.Connections.Count; for (int i = 0; i < session.Connections.Count; i++) { SwitchboardConnection sc = session.Connections[i]; if (sc != c) { Command iro = new Command(Verb.Iro, cmd.TrId, (i + 1).ToStringInvariant(), cnt.ToStringInvariant(), sc.User.UserHandle, sc.User.FriendlyName); Server.Send(c, iro); } } c.User = user; c.Session = session; session.Connections.Add(c); Command respOk = new Command(Verb.Ans, cmd.TrId, "OK"); Server.Send(c, respOk); // When a new user joins a Switchboard session, the server sends the // following command to all participating clients, including the client // joining the session: // <<< JOI CalleeUserHandle CalleeUserFriendlyName // UPDATE: Actually, I think not; don't send it to the person joining Command joi = new Command(Verb.Joi, -1, user.UserHandle, user.FriendlyName); Server.BroadcastCommand(session, joi, c); }