Esempio n. 1
0
void Processing::processRequest(EthernetClient& client)
{
    _etClient = client;
    _html.init(client);
    
    RequestType rt;
    WebPages wp;
    GetRequestTypeAndPage(rt, wp);
    
    switch (wp)
    {
        case WP_INDEX:
        {
            ReadRequestToEnd();
            _html.index();
        }
        break;
        case WP_PRIVATE_SETTINGS:
        {
            if(!IsAuthorized())
            { return; }
            // if GET.
            if (rt == RT_GET)
            { PrivateSettingsGET(); }
            
            // if post. Fill dogList with valid data. Fill Validate data list if have not valid.
            if (rt == RT_POST)
            { PrivateSettingsPOST(); }
        }
        break;
        case WP_PRIVATE_USERANDIP:
        {
            if(!IsAuthorized())
            { return; }
            // if GET.
            if (rt == RT_GET)
            { PrivateUserAndIpGET(); }
            if (rt == RT_POST)
            { PrivateUserAndIpPOST(); }
        }
        break;
        default:
        {
            ReadRequestToEnd();
            _html.notFound();
        }
        break;
    }
}
Esempio n. 2
0
int TlenMUCRecvInvitation(TlenProtocol *proto, const char *roomId, const char *roomName, const char *from, const char *reason)
{
	int	 ignore, ask, groupChatPolicy;
	if (roomId == NULL) return 1;
	groupChatPolicy = db_get_w(NULL, proto->m_szModuleName, "GroupChatPolicy", 0);
	ask = TRUE;
	ignore = FALSE;
	if (groupChatPolicy == TLEN_MUC_ASK) {
		ignore = FALSE;
		ask = TRUE;
	} else if (groupChatPolicy == TLEN_MUC_IGNORE_ALL) {
		ignore = TRUE;
	} else if (groupChatPolicy == TLEN_MUC_IGNORE_NIR) {
		char jid[256];
		DBVARIANT dbv;
		if (!db_get(NULL, proto->m_szModuleName, "LoginServer", &dbv)) {
			mir_snprintf(jid, SIZEOF(jid), "%s@%s", from, dbv.pszVal);
			db_free(&dbv);
		} else {
			strcpy(jid, from);
		}
		ignore = !IsAuthorized(proto, jid);
		ask = TRUE;
	} else if (groupChatPolicy == TLEN_MUC_ACCEPT_IR) {
		char jid[256];
		TLEN_LIST_ITEM *item;
		DBVARIANT dbv;
		if (!db_get(NULL, proto->m_szModuleName, "LoginServer", &dbv)) {
			mir_snprintf(jid, SIZEOF(jid), "%s@%s", from, dbv.pszVal);
			db_free(&dbv);
		} else {
			strcpy(jid, from);
		}
		item = TlenListGetItemPtr(proto, LIST_ROSTER, jid);
		ask = !IsAuthorized(proto, jid);
		ignore = FALSE;
	} else if (groupChatPolicy == TLEN_MUC_ACCEPT_ALL) {
		ask = FALSE;
		ignore = FALSE;
	}
	return 0;
}
Esempio n. 3
0
status_t
Jabber::Process( BMessage * msg )
{
	switch ( msg->what )
	{
		case IM::MESSAGE:
		{
			int32 im_what = 0;
			
			msg->FindInt32("im_what", &im_what );
						
			switch ( im_what )
			{
				case IM::SET_STATUS:
				{
					const char *status = msg->FindString("status");
					LOG(kProtocolName, liMedium, "Set status to %s", status);
					
					if (strcmp(status, OFFLINE_TEXT) == 0) 
					{
							
						SetStatus(S_OFFLINE,OFFLINE_TEXT); //do the log-out?
					} 
					else 
					if (strcmp(status, AWAY_TEXT) == 0) 
					{
						if(IsAuthorized()){
						
						 //const char *away_msg;	
						 BString away_msg;					 
						 if(msg->FindString("away_msg",&away_msg) == B_OK)
						 {
						 	// quick and dirty way to use advanced away status:
						 	// add 'DND: ' for Do not Disturb
						 	// or  'XA: ' for Extended Away
						 	
						 	if(away_msg.Compare("DND: ",4) == 0)
						 		SetStatus(S_DND,away_msg); 
						 	else
						 	if(away_msg.Compare("XA: ",4) == 0)
						 		SetStatus(S_XA,away_msg); 
						 	else	
						 		SetStatus(S_AWAY,away_msg); 
						 }
						 	 else
						 		 SetStatus(S_AWAY,AWAY_TEXT); 
						 
						 			
						 SetAway(true);
						} 
					} 
					else 
					if (strcmp(status, ONLINE_TEXT) == 0) 
					{
							if(!IsAuthorized())
							{
								if(fUsername == "")
									Error("Empty Username!",NULL);
								if(fServer == "")
									Error("Empty Server!",NULL);
								if(fPassword == "")
									Error("Empty Password!",NULL);
								
								Progress("Jabber Login", "Jabber: Connecting..", 0.0);
										
							}
							
							SetStatus(S_ONLINE,ONLINE_TEXT); //do the login!
							if(IsAuthorized()) SetAway(false); 
					} 
					else
					{
						Error("Invalid",NULL);
						LOG(kProtocolName, liHigh, "Invalid status when setting status: '%s'", status);
					}
				}	break;
				
				case IM::SEND_MESSAGE:
				{
						const char * buddy=msg->FindString("id");
						const char * sms=msg->FindString("message");
						JabberMessage jm;
						jm.SetTo(buddy);
						jm.SetFrom(GetJid());
						jm.SetBody(sms);
						TimeStamp(jm);
						
						//not the right place.. see Jabber::Message
						JabberContact *contact=getContact(buddy);
						
						//tmp: new mess id!
						BString messid("imkit_");
						messid << idsms;
						idsms++;
						
						if(contact)
							jm.SetID(messid);
												
						SendMessage(jm);
						
						MessageSent(buddy,sms);
				} 
				break;
				case IM::REGISTER_CONTACTS:
					
					{
					
					//debugger("REGISTER");
					type_code garbage;
					int32 count = 0;
					msg->GetInfo("id", &garbage, &count);
					
										
					if (count > 0 ) {
						
						for ( int i=0; msg->FindString("id",i); i++ )
						{
							const char * id = msg->FindString("id",i);
							JabberContact* contact=getContact(id);
							if(contact)
								  BuddyStatusChanged(contact);
							else
							{
							 
								//Are we on-line?
								// send auth req?
								if(fFullLogged)
								{ 			
									AddContact(id,id,"");
									BuddyStatusChanged(id,OFFLINE_TEXT);
								}
								
								else
								{
								 // we add to a temp list.
								 // when logged in we will register the new buddy..
									 fLaterBuddyList->push_back(BString(id));
								}							 
							} 
						};
						
					} 
					else 
						return B_ERROR;
					} 	
					break;
				case IM::UNREGISTER_CONTACTS:
				
				{
						
					
						const char * buddy=NULL;
						
						for ( int i=0; msg->FindString("id", i, &buddy) == B_OK; i++ )
						{
							LOG(kProtocolName, liDebug, "Unregister Contact: '%s'", buddy);
							
							if(!fFullLogged)
							BuddyStatusChanged(buddy,OFFLINE_TEXT);
							else
							{
								LOG(kProtocolName, liDebug, "Unregister Contact DOING IT");
							 	JabberContact* contact=getContact(buddy);
							 	if(contact)
									RemoveContact(contact);
							}
						}
				} 
					
					break;
				case IM::USER_STARTED_TYPING: 
				{
						const char * id=NULL;
						
						if( msg->FindString("id", &id) == B_OK )
						{
						 JabberContact* contact=getContact(id);
						 if(contact)
							StartComposingMessage(contact);
						}
				} 
				break;
				case IM::USER_STOPPED_TYPING: 
				{
						const char * id=NULL;
						
						if( msg->FindString("id", &id) == B_OK )
						{
						 JabberContact* contact=getContact(id);
						 if(contact && (contact->GetLastMessageID().ICompare("")!=0)){
							StopComposingMessage(contact);
							contact->SetLastMessageID("");
							
							}
						}
				} 
				break;
				
				case IM::GET_CONTACT_INFO:
					//debugger("Get Contact Info! ;)");
					SendContactInfo(msg->FindString("id"));
				break;
								
				case IM::SEND_AUTH_ACK:
				{
					if(!IsAuthorized())
						return B_ERROR;
					
					const char * id = msg->FindString("id");
					int32 button = msg->FindInt32("which");
					
					if (button == 0) {
						
						//Authorization granted
						AcceptSubscription(id);
						BMessage im_msg(IM::MESSAGE);
						im_msg.AddInt32("im_what", IM::CONTACT_AUTHORIZED);
						im_msg.AddString("protocol", kProtocolName);
						im_msg.AddString("id", id);
						im_msg.AddString("message", "");
						fServerMsgr.SendMessage(&im_msg);
						
						//now we want to see you! ;)
						AddContact(id,id,"");
														
					} 
					else 
					{
						//Authorization rejected
						Error("Authorization rejected!",id);
					}
						
					
				}
				break;
				default:
					// we don't handle this im_what code
					msg->PrintToStream();	
					return B_ERROR;
			}
		}	break;
		
		default:
			// we don't handle this what code
			return B_ERROR;
	}
	
	return B_OK;
}
Esempio n. 4
0
void TlenProcessPic(XmlNode *node, TlenProtocol *proto) {
	TLEN_LIST_ITEM *item = NULL;
	char *crc, *crc_c, *idt, *size, *from, *fromRaw, *rt;
	from = TlenXmlGetAttrValue(node, "from");
	fromRaw = TlenLoginFromJID(from);
	idt = TlenXmlGetAttrValue(node, "idt");
	size = TlenXmlGetAttrValue(node, "size");
	crc_c = TlenXmlGetAttrValue(node, "crc_c");
	crc = TlenXmlGetAttrValue(node, "crc");
	rt = TlenXmlGetAttrValue(node, "rt");
	if (idt != NULL) {
		item = TlenListGetItemPtr(proto, LIST_PICTURE, idt);
	}
	if (item != NULL) {
		if (!strcmp(from, "ps")) {
			char *st = TlenXmlGetAttrValue(node, "st");
			if (st != NULL) {
				item->ft->iqId = mir_strdup(st);
				item->ft->id2 = mir_strdup(rt);
				if (item->ft->hFileEvent != NULL) {
					SetEvent(item->ft->hFileEvent);
					item->ft->hFileEvent = NULL;
				}
			}
		} else if (!strcmp(item->ft->jid, fromRaw)) {
			if (crc_c != NULL) {
				if (!strcmp(crc_c, "n")) {
					/* crc_c = n, picture transfer accepted */
					TlenPsPost(proto, item);
				} else if (!strcmp(crc_c, "f")) {
					/* crc_c = f, picture cached, no need to transfer again */
					LogPictureMessage(proto, item->ft->jid, item->ft->files[0], TRUE);
					TlenP2PFreeFileTransfer(item->ft);
					TlenListRemove(proto, LIST_PICTURE, idt);
				}
			} else if (rt != NULL) {
				item->ft->id2 = mir_strdup(rt);
				TlenPsGet(proto, item);
			}
		}
	} else if (crc != NULL) {
		BOOL bAccept = proto->tlenOptions.imagePolicy == TLEN_IMAGES_ACCEPT_ALL || (proto->tlenOptions.imagePolicy == TLEN_IMAGES_IGNORE_NIR && IsAuthorized(proto, from));
		if (bAccept) {
			FILE* fp;
			char fileName[MAX_PATH];
			char *ext = TlenXmlGetAttrValue(node, "ext");
			char *tmpPath = Utils_ReplaceVars( "%miranda_userdata%" );
			int tPathLen = mir_snprintf(fileName, MAX_PATH, "%s\\Images\\Tlen", tmpPath);
			long oldSize = 0, lSize = atol(size);
			DWORD dwAttributes = GetFileAttributesA( fileName );
			if ( dwAttributes == 0xffffffff || ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 )
				CreateDirectoryTree(fileName);

			mir_free(tmpPath);
			fileName[ tPathLen++ ] = '\\';
			mir_snprintf( fileName + tPathLen, MAX_PATH - tPathLen, "%s.%s", crc, ext );
			fp = fopen( fileName, "rb" );
			if (fp) {
				fseek(fp, 0, SEEK_END);
				oldSize = ftell(fp);
				fclose(fp);
			}
			if (oldSize != lSize) {
				item = TlenListAdd(proto, LIST_PICTURE, idt);
				item->ft = TlenFileCreateFT(proto, from);
				item->ft->files = (char **) mir_alloc(sizeof(char *));
				item->ft->filesSize = (long *) mir_alloc(sizeof(long));
				item->ft->files[0] = mir_strdup(fileName);
				item->ft->filesSize[0] = lSize;
				item->ft->fileTotalSize = item->ft->filesSize[0];
				TlenSend(proto, "<message type='pic' to='%s' crc_c='n' idt='%s'/>", from, idt);
			} else {
				TlenSend(proto, "<message type='pic' to='%s' crc_c='f' idt='%s'/>", from, idt);
				LogPictureMessage(proto, from, fileName, FALSE);
			}
		}
	}
	mir_free(fromRaw);
}
Esempio n. 5
0
void TClient::OnDataReceived(const TBuffer& data) {
    switch (CurrentState) {
    case CS_Registering: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            packetStr = Decompress(packetStr);
            TServerRegisterPacket packet;
            if (!packet.ParseFromString(packetStr)) {
                ForceDisconnect();
                Config.RegisterResultCallback(RR_ConnectionFailure);
                return;
            }
            string captcha = packet.captcha();
            State.set_serverpublickey(packet.publickey());
            Config.CaptchaAvailableCallback(captcha);
        }
    } break;
    case CS_RegisteringConfirmWait: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            packetStr = Decompress(DecryptAsymmetrical(State.privatekey(), packetStr));
            if (packetStr.size() != 1) {
                ForceDisconnect();
                Config.RegisterResultCallback(RR_ConnectionFailure);
                return;
            }
            ERegisterResult registerResult;
            registerResult = (ERegisterResult)packetStr[0];
            if (registerResult == RR_Success) {
                SaveState();
            }
            ForceDisconnect();
            Config.RegisterResultCallback(registerResult);
            return;
        }
    } break;
    case CS_Logining: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            packetStr = Decompress(packetStr);
            TServerLoginPacket packet;
            if (!packet.ParseFromString(packetStr)) {
                ForceDisconnect();
                Config.LoginResultCallback(LR_ConnectionFail);
                return;
            }
            string captcha = packet.captcha();
            State.set_serverpublickey(packet.publickey());
            Config.CaptchaAvailableCallback(captcha);
        }
    } break;
    case CS_LoginingConfirmWait: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            packetStr = Decompress(packetStr);
            TServerLoginConfirm packet;
            if (!packet.ParseFromString(packetStr)) {
                ForceDisconnect();
                Config.LoginResultCallback(LR_ConnectionFail);
                return;
            }
            if (packet.result() != LR_Success) {
                ForceDisconnect();
                Config.LoginResultCallback(packet.result());
            } else {
                assert(packet.has_publickey() && "no public key in packet");
                assert(packet.has_encryptedprivatekey() && "no private key in packet");
                assert(!Password.empty() && "no password");
                State.set_publickey(packet.publickey());
                State.set_privatekey(DecryptSymmetrical(GenerateKey(Password), packet.encryptedprivatekey()));
                Password.clear();
                SaveState();
                ForceDisconnect();
                Config.LoginResultCallback(packet.result());
            }
            ForceDisconnect();
        }
    } break;
    case CS_Connecting: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            packetStr = Decompress(packetStr);
            TServerAuthorizeRequest packet;
            if (!packet.ParseFromString(packetStr)) {
                ForceDisconnect();
                Config.ConnectedCallback(false);
                return;
            }
            if (!CheckSignature(State.serverpublickey(), packet.randomsequence(), packet.signature())) {
                ForceDisconnect();
                Config.ConnectedCallback(false);
                return;
            }
            TClientAuthorizeRequest request;
            assert(State.has_login() && "no login in state");
            request.set_login(State.login());
            string hash = Hash(packet.randomsequence());
            request.set_randomsequencehash(hash);
            request.set_randomsequencehashsignature(Sign(State.privatekey(), hash));
            string response = Compress(request.SerializeAsString());
            response = Serialize(EncryptAsymmetrical(State.serverpublickey(), response));
            CurrentState = CS_ConnectingConfirmWait;
            UdtClient->Send(response);
        }
    } break;
    case CS_ConnectingConfirmWait: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            assert(State.has_privatekey());
            string sessionKey = Decompress(DecryptAsymmetrical(State.privatekey(), packetStr));
            State.set_sessionkey(sessionKey);
            CurrentState = CS_Connected;
            Config.ConnectedCallback(true);


            string response;
            response.resize(1);
            response[0] = RT_SyncMessages;
            TSyncMessagesRequest syncRequest;
            if (State.has_lastsync()) {
                syncRequest.set_from(State.lastsync());
            } else {
                syncRequest.set_from(0);
            }
            syncRequest.set_to(TDuration::Max().GetValue()); // request all messages after connection
            response += syncRequest.SerializeAsString();
            response = EncryptSymmetrical(State.sessionkey(), Compress(response));
            UdtClient->Send(Serialize(response));
        }
    } break;
    case CS_Connected: {
        Buffer += data.ToString();
        string packetStr;
        if (Deserialize(Buffer, packetStr)) {
            assert(!packetStr.empty() && "empty packet");
            packetStr = DecryptSymmetrical(State.sessionkey(), packetStr);
            assert(!packetStr.empty() && "empty decrypted");
            packetStr = Decompress(packetStr);
            assert(!packetStr.empty() && "empty decompress");
            EServerPacket packetType = (EServerPacket)packetStr[0];
            packetStr = packetStr.substr(1);
            switch (packetType) {
            case SP_FriendAlreadyExists: {
                // todo: just sync friendList if required
                // send repeated authorization request
            } break;
            case SP_FriendAdded: {
                // todo: send authorization request
            } break;
               case SP_SyncMessages: {
                TClientSyncPacket packet;
                if (!packet.ParseFromString(packetStr)) {
                    throw UException("failed to parse client sync message packet");
                }
                for (size_t i = 0; i < packet.encryptedmessages_size(); ++i) {
                    const TDirectionedMessage& currMessage = packet.encryptedmessages(i);
                    auto friendIt = Friends.find(currMessage.login());
                    if (friendIt == Friends.end()) {
                        cerr << "warning: friend login not found\n";
                        continue;
                    }
                    TFriendRef& frnd = friendIt->second;
                    frnd->OnOfflineMessageReceived(currMessage.message(), currMessage.incoming());
                }
                State.set_lastsync(packet.to());
                SaveState();
            } break;
            case SP_SyncInfo: {
                TClientSyncInfoPacket packet;
                if (!packet.ParseFromString(packetStr)) {
                    throw UException("failed to parse client sync info packet");
                }
                for (TFriendIterator it = Friends.begin(); it != Friends.end(); ++it) {
                    TFriendRef& frnd = it->second;
                    frnd->ToDelete = true;
                }
                bool friendListUpdated = false;
                for (size_t i = 0; i < packet.friends_size(); ++i) {
                    const TSyncFriend& frnd = packet.friends(i);
                    auto frndIt = Friends.find(frnd.login());
                    TFriendRef currentFrnd;
                    bool friendAdded = false;
                    if (frndIt == Friends.end()) {
                        // todo: refactor syncing
                        friendListUpdated = true;
                        friendAdded = true;
                        Friends.insert(pair<string, TFriendRef>(frnd.login(), make_shared<TFriend>(this)));
                        currentFrnd = Friends[frnd.login()];
                        currentFrnd->FullLogin = frnd.login();
                        currentFrnd->PublicKey = frnd.publickey();
                        currentFrnd->ServerPublicKey = frnd.serverpublickey();
                        currentFrnd->SelfOfflineKey = DecryptSymmetrical(GenerateKey(State.privatekey()), frnd.encryptedkey());
                        if (frnd.status() == AS_WaitingAuthorization) {
                            currentFrnd->Status = FS_AddRequest;
                            Config.FriendRequestCallback(frnd.login());
                        } else if (frnd.status() == AS_UnAuthorized) {
                            currentFrnd->Status = FS_Unauthorized;
                        } else if (frnd.status() == AS_Authorized) {
                            currentFrnd->Status = FS_Offline;
                            // todo: start trying to connect
                        } else {
                            assert(!"unknown status");
                        }
                    } else {
                        currentFrnd = frndIt->second;
                        if (currentFrnd->PublicKey.empty()) {
                            currentFrnd->PublicKey = frnd.publickey();
                        } else {
                            if (currentFrnd->PublicKey != frnd.publickey()) {
                                throw UException("public keys missmatch");
                            }
                        }
                        if (currentFrnd->SelfOfflineKey.empty()) {
                            currentFrnd->SelfOfflineKey = DecryptSymmetrical(GenerateKey(State.privatekey()), frnd.encryptedkey());
                        } else {
                            if (!frnd.encryptedkey().empty() &&
                                currentFrnd->SelfOfflineKey != DecryptSymmetrical(GenerateKey(State.privatekey()), frnd.encryptedkey()))
                            {
                                throw UException("public keys missmatch");
                            }
                        }
                        if (currentFrnd->ServerPublicKey.empty()) {
                            currentFrnd->ServerPublicKey = frnd.serverpublickey();
                        } else {
                            if (currentFrnd->ServerPublicKey != frnd.serverpublickey()) {
                                throw UException("server public keys missmatch");
                            }
                        }
                        if ((currentFrnd->Status == FS_Unauthorized ||
                             currentFrnd->Status == FS_AddRequest) &&
                                frnd.status() == AS_Authorized)
                        {
                            friendListUpdated = true;
                            currentFrnd->Status = FS_Offline;
                            // todo: start trying to connect
                        } else if (frnd.status() == AS_WaitingAuthorization) {
                            currentFrnd->Status = FS_AddRequest;
                        } else if (frnd.status() == AS_UnAuthorized) {
                            currentFrnd->Status = FS_Unauthorized;
                        }
                    }

                    if (currentFrnd->FriendOfflineKey.empty() && IsAuthorized(currentFrnd->Status)) {
                        if (frnd.has_offlinekey() && frnd.has_offlinekeysignature()) {
                            assert(!frnd.offlinekey().empty() && "empty offline key");
                            string offlineSignature = DecryptAsymmetrical(State.privatekey(), frnd.offlinekey());
                            assert(!currentFrnd->PublicKey.empty() && "no public key for friend");
                            if (!CheckSignature(currentFrnd->PublicKey, offlineSignature, frnd.offlinekeysignature())) {
                                cerr << "all bad, wrong signature\n"; // todo: normal handling
                                continue;
                            }
                            currentFrnd->FriendOfflineKey = DecryptAsymmetrical(State.privatekey(), frnd.offlinekey());
                        }
                    }

                    currentFrnd->ToDelete = false;
                    if (friendAdded && Config.OnFriendAdded) {
                        Config.OnFriendAdded(currentFrnd);
                    }

                    if (frnd.has_needofflinekey() && frnd.needofflinekey()) {
                        string response;
                        response.resize(1);
                        response[0] = RT_SetFriendOfflineKey;
                        TFriendOfflineKey offlineKeyPacket;
                        assert(!currentFrnd->PublicKey.empty() && "missing friend public key");
                        assert(!currentFrnd->SelfOfflineKey.empty() && "missing self offline key for friend");
                        offlineKeyPacket.set_offlinekey(EncryptAsymmetrical(currentFrnd->PublicKey, currentFrnd->SelfOfflineKey));
                        offlineKeyPacket.set_offlinekeysignature(Sign(State.privatekey(), currentFrnd->SelfOfflineKey));
                        offlineKeyPacket.set_login(currentFrnd->GetLogin());
                        response += offlineKeyPacket.SerializeAsString();
                        response = EncryptSymmetrical(State.sessionkey(), Compress(response));
                        UdtClient->Send(Serialize(response));
                    }
                }

                for (TFriendIterator it = Friends.begin(); it != Friends.end();) {
                    TFriendRef& frnd = it->second;
                    if (frnd->ToDelete) {
                        friendListUpdated = true;
                        if (Config.OnFriendRemoved) {
                            Config.OnFriendRemoved(frnd);
                        }
                        it = Friends.erase(it);
                    } else {
                        frnd->Connect();
                        ++it;
                    }
                }
                if (friendListUpdated && Config.FriendlistChangedCallback) {
                    Config.FriendlistChangedCallback();
                }
            } break;
            case SP_ConnectToFriend: {
                auto frndIt = Friends.find(packetStr);
                if (frndIt == Friends.end()) {
                    throw UException("no friend to connect to");
                }
                TFriendRef& frnd = frndIt->second;
                frnd->ConnectAccept();
            }
            }
        }
    } break;
    default:
        assert(false && "unknown state");
        break;
    }
}