diff --git a/src/chat/chat-room/server-group-chat-room-p.h b/src/chat/chat-room/server-group-chat-room-p.h
index 7ea1737d25134867ee939058457a9c2d993a2cbe..37ce2277b1b20cd32dab8a3aa084791f39f77ea7 100644
--- a/src/chat/chat-room/server-group-chat-room-p.h
+++ b/src/chat/chat-room/server-group-chat-room-p.h
@@ -69,7 +69,6 @@ public:
 	std::shared_ptr<Participant> findAuthorizedParticipant (const std::shared_ptr<const CallSession> &session) const;
 	std::shared_ptr<Participant> findAuthorizedParticipant (const IdentityAddress &participantAddress) const;
 
-	ParticipantDevice::State getParticipantDeviceState (const std::shared_ptr<const ParticipantDevice> &device) const;
 	void setParticipantDeviceState (const std::shared_ptr<ParticipantDevice> &device, ParticipantDevice::State state);
 
 	void acceptSession (const std::shared_ptr<CallSession> &session);
@@ -127,10 +126,10 @@ private:
 	};
 
 	static void copyMessageHeaders (const std::shared_ptr<Message> &fromMessage, const std::shared_ptr<ChatMessage> &toMessage);
-
+	static bool allDevicesLeft(const std::shared_ptr<Participant> &participant);
 	void addParticipantDevice (const std::shared_ptr<Participant> &participant, const ParticipantDeviceIdentity &deviceInfo);
 	void designateAdmin ();
-	void dispatchMessage (const std::shared_ptr<Message> &message, const std::string &uri);
+	void sendMessage (const std::shared_ptr<Message> &message, const IdentityAddress &deviceAddr);
 	void finalizeCreation ();
 	std::shared_ptr<CallSession> makeSession(const std::shared_ptr<ParticipantDevice> &device);
 	void inviteDevice (const std::shared_ptr<ParticipantDevice> &device);
diff --git a/src/chat/chat-room/server-group-chat-room.cpp b/src/chat/chat-room/server-group-chat-room.cpp
index 2cf488f99971574f49ebb36f56ffd511aef2dffb..f87c8cf2b86c36ac8b2281f913fcc85aefe6eb2f 100644
--- a/src/chat/chat-room/server-group-chat-room.cpp
+++ b/src/chat/chat-room/server-group-chat-room.cpp
@@ -98,7 +98,7 @@ void ServerGroupChatRoomPrivate::setState (ChatRoom::State state) {
 			bool atLeastOneDeviceJoining = false;
 			bool atLeastOneDevicePresent = false;
 			for (const auto &device : participant->getPrivate()->getDevices()) {
-				switch (getParticipantDeviceState(device)) {
+				switch (device->getState()) {
 					case ParticipantDevice::State::ScheduledForLeaving:
 					case ParticipantDevice::State::Leaving:
 						atLeastOneDeviceLeaving = true;
@@ -165,10 +165,6 @@ void ServerGroupChatRoomPrivate::resumeParticipant(const std::shared_ptr<Partici
 	}
 }
 
-ParticipantDevice::State ServerGroupChatRoomPrivate::getParticipantDeviceState (const shared_ptr<const ParticipantDevice> &device) const {
-	return device->getState();
-}
-
 void ServerGroupChatRoomPrivate::setParticipantDeviceState (const shared_ptr<ParticipantDevice> &device, ParticipantDevice::State state) {
 	L_Q();
 	string address(device->getAddress().asString());
@@ -260,17 +256,22 @@ void ServerGroupChatRoomPrivate::confirmJoining (SalCallOp *op) {
 		if (!participant) {
 			lError() << q << ": Declining INVITE coming from someone that is not a participant";
 			op->decline(SalReasonDeclined, "");
-			joiningPendingAfterCreation = false;
 			return;
 		}
-		if (op->getRemoteBody().getContentType() == ContentType::ResourceLists){
+		// One to one chatroom can be resurected by a participant, but the participant actually never leaves from server's standpoint.
+		if (!(capabilities & ServerGroupChatRoom::Capabilities::OneToOne) && op->getRemoteBody().getContentType() == ContentType::ResourceLists){
 			lError() << q << "Receiving ressource list body while not in creation step.";
 			op->decline(SalReasonNotAcceptable);
 			return;
 		}
-		if (capabilities & ServerGroupChatRoom::Capabilities::OneToOne)
-			participant->getPrivate()->setAdmin(true);
 		device = participant->getPrivate()->addDevice(gruu);
+		if (capabilities & ServerGroupChatRoom::Capabilities::OneToOne){
+			if (device->getState() == ParticipantDevice::State::Left){
+				lInfo() << q << " " << gruu << " is reconnected to the one to one chatroom.";
+				setParticipantDeviceState(device, ParticipantDevice::State::Joining);
+			}
+			participant->getPrivate()->setAdmin(true);
+		}
 		session = device->getSession();
 	}
 
@@ -304,11 +305,22 @@ void ServerGroupChatRoomPrivate::confirmJoining (SalCallOp *op) {
  	}
 }
 
+/* This is called only when a participant attempts to create a one to one chatroom that already exists.
+ * We just redirect it to the existing chatroom uri. */
 void ServerGroupChatRoomPrivate::confirmRecreation (SalCallOp *op) {
 	L_Q();
 	L_Q_T(LocalConference, qConference);
+	
+	auto participant = q->findParticipant(Address(op->getFrom()));
+	if (!participant){
+		lError() << q << " bug - " << op->getFrom() << " is not a participant.";
+		op->decline(SalReasonInternalError, nullptr);
+		return;
+	}
+	
 	IdentityAddress confAddr(qConference->getPrivate()->conferenceAddress);
-	lInfo() << q << " recreated";
+	
+	lInfo() << q << " is re-joined by " << participant->getAddress();
 	Address addr(confAddr);
 	addr.setParam("isfocus");
 	shared_ptr<Participant> me = q->getMe();
@@ -316,7 +328,6 @@ void ServerGroupChatRoomPrivate::confirmRecreation (SalCallOp *op) {
 	session->configure(LinphoneCallIncoming, nullptr, op, Address(op->getFrom()), Address(op->getTo()));
 	session->startIncomingNotification(false);
 	session->redirect(addr);
-	joiningPendingAfterCreation = true;
 }
 
 void ServerGroupChatRoomPrivate::declineSession (const shared_ptr<CallSession> &session, LinphoneReason reason) {
@@ -326,23 +337,18 @@ void ServerGroupChatRoomPrivate::declineSession (const shared_ptr<CallSession> &
 void ServerGroupChatRoomPrivate::dispatchQueuedMessages () {
 	L_Q();
 	for (const auto &participant : q->getParticipants()) {
-		// Check if some messages has been queued for the participant address
-		// This can happen when a prior participant of a one-to-one chatroom if
-		// re-invited by the receiving of a chat message. At the moment of the receiving of this
-		// chat message, we do not know yet the devices of the re-invited participant.
-		auto devices = participant->getPrivate()->getDevices();
-		if (!devices.empty()) {
-			string participantUri(participant->getAddress().asString());
-			while (!queuedMessages[participantUri].empty()) {
-				for (const auto &device : participant->getPrivate()->getDevices()) {
-					queuedMessages[device->getAddress().asString()].push(queuedMessages[participantUri].front());
-				}
-				queuedMessages[participantUri].pop();
-			}
-		}
-		// Dispatch messages for each device
+		/*
+		 * Dispatch messages for each device in Present state. In a one to one chatroom, if a device
+		 * is found is Left state, it must be invited first.
+		 */
+		
 		for (const auto &device : participant->getPrivate()->getDevices()) {
-			if (getParticipantDeviceState(device) != ParticipantDevice::State::Present)
+			if ( (capabilities & ServerGroupChatRoom::Capabilities::OneToOne) && device->getState() == ParticipantDevice::State::Left){
+				inviteDevice(device);
+				continue;
+			}
+			
+			if (device->getState() != ParticipantDevice::State::Present)
 				continue;
 			string uri(device->getAddress().asString());
 			size_t nbMessages = queuedMessages[uri].size();
@@ -350,7 +356,7 @@ void ServerGroupChatRoomPrivate::dispatchQueuedMessages () {
 				lInfo() << q << ": Dispatching " << nbMessages << " queued message(s) for '" << uri << "'";
 			while (!queuedMessages[uri].empty()) {
 				shared_ptr<Message> msg = queuedMessages[uri].front();
-				dispatchMessage(msg, uri);
+				sendMessage(msg, device->getAddress());
 				queuedMessages[uri].pop();
 			}
 		}
@@ -362,8 +368,8 @@ void ServerGroupChatRoomPrivate::removeParticipant (const shared_ptr<const Parti
 	L_Q_T(LocalConference, qConference);
 
 	for (const auto &device : participant->getPrivate()->getDevices()) {
-		if ((getParticipantDeviceState(device) == ParticipantDevice::State::Leaving)
-			|| (getParticipantDeviceState(device) == ParticipantDevice::State::Left)
+		if ((device->getState() == ParticipantDevice::State::Leaving)
+			|| (device->getState() == ParticipantDevice::State::Left)
 		)
 			continue;
 		setParticipantDeviceState(device, ParticipantDevice::State::ScheduledForLeaving);
@@ -379,11 +385,9 @@ void ServerGroupChatRoomPrivate::removeParticipant (const shared_ptr<const Parti
 
 	queuedMessages.erase(participant->getAddress().asString());
 
-	// Do not notify participant removal for one-to-one chat rooms
-	if (!(capabilities & ServerGroupChatRoom::Capabilities::OneToOne)) {
-		shared_ptr<ConferenceParticipantEvent> event = qConference->getPrivate()->eventHandler->notifyParticipantRemoved(participant->getAddress());
-		q->getCore()->getPrivate()->mainDb->addEvent(event);
-	}
+	shared_ptr<ConferenceParticipantEvent> event = qConference->getPrivate()->eventHandler->notifyParticipantRemoved(participant->getAddress());
+	q->getCore()->getPrivate()->mainDb->addEvent(event);
+	
 	if (!isAdminLeft())
 		designateAdmin();
 }
@@ -488,10 +492,6 @@ bool ServerGroupChatRoomPrivate::initializeParticipants (const shared_ptr<Partic
 		}
 	}
 	identAddresses.push_back(initiator->getAddress());
-	if (initiator->getPrivate()->getDevices().size() > 1){
-		/* This happens only for one to one chatrooms, when a participant has left and then resumes the chatroom.*/
-		resumeParticipant(initiator);
-	}
 	if (!subscribeRegistrationForParticipants(identAddresses)){
 		/* If we are not waiting for any registration information, then we can conclude immediately. */
 		conclude();
@@ -502,11 +502,10 @@ bool ServerGroupChatRoomPrivate::initializeParticipants (const shared_ptr<Partic
 // -----------------------------------------------------------------------------
 
 LinphoneReason ServerGroupChatRoomPrivate::onSipMessageReceived (SalOp *op, const SalMessage *message) {
-	L_Q();
 	// Check that the message is coming from a participant of the chat room
 	IdentityAddress fromAddr(op->getFrom());
 	if (!findAuthorizedParticipant(fromAddr)) {
-		return LinphoneReasonNotAcceptable;
+		return LinphoneReasonForbidden;
 	}
 
 	// Do not check that we received a CPIM message because ciphered messages are not
@@ -517,21 +516,6 @@ LinphoneReason ServerGroupChatRoomPrivate::onSipMessageReceived (SalOp *op, cons
 		op->getRecvCustomHeaders()
 	);
 
-	// Handle reinvite of potentially missing participant in one-to-one chat room
-	if (capabilities & ServerGroupChatRoom::Capabilities::OneToOne) {
-		if (q->getParticipantCount() != 2) {
-			IdentityAddress missingAddr = q->getCore()->getPrivate()->mainDb->findMissingOneToOneConferenceChatRoomParticipantAddress(
-				q->getSharedFromThis(),
-				q->getParticipants().front()->getAddress()
-			);
-			if (queuedMessages[missingAddr.asString()].size() == 0) {
-				q->addParticipant(missingAddr, nullptr, false);
-			}
-			queueMessage(msg, missingAddr);
-			return LinphoneReasonNone;
-		}
-	}
-
 	queueMessage(msg);
 	dispatchQueuedMessages();
 	return LinphoneReasonNone;
@@ -600,12 +584,14 @@ void ServerGroupChatRoomPrivate::updateParticipantDevices(const IdentityAddress
 				devicesToRemove.push_back(device);
 			}
 		}
-		for (auto &device : devicesToRemove)
-			removeParticipantDevice(participant, device->getAddress());
-
 		// Add all the devices in the given list, if already present they will be ignored
 		for (const auto &device : devices)
 			addParticipantDevice(participant, device);
+		
+		// Remove all devices that are no longer existing.
+		for (auto &device : devicesToRemove)
+			removeParticipantDevice(participant, device->getAddress());
+
 	}else{
 		if (newParticipantReginfo){
 			lInfo() << q << participantAddress << " has no compatible devices.";
@@ -736,22 +722,17 @@ void ServerGroupChatRoomPrivate::designateAdmin () {
 	}
 }
 
-void ServerGroupChatRoomPrivate::dispatchMessage (const shared_ptr<Message> &message, const string &uri) {
+void ServerGroupChatRoomPrivate::sendMessage (const shared_ptr<Message> &message, const IdentityAddress &deviceAddr){
 	L_Q();
-	IdentityAddress deviceAddr(uri);
-	for (const auto &p : q->getParticipants()) {
-		shared_ptr<ParticipantDevice> device = p->getPrivate()->findDevice(deviceAddr);
-		if (device) {
-			shared_ptr<ChatMessage> msg = q->createChatMessage();
-			copyMessageHeaders(message, msg);
-			msg->setInternalContent(message->content);
-			msg->getPrivate()->forceFromAddress(q->getConferenceAddress());
-			msg->getPrivate()->forceToAddress(device->getAddress());
-			msg->getPrivate()->setApplyModifiers(false);
-			msg->send();
-			return;
-		}
-	}
+	
+	shared_ptr<ChatMessage> msg = q->createChatMessage();
+	copyMessageHeaders(message, msg);
+	msg->getPrivate()->addSalCustomHeader("Session-mode", "true"); // Special custom header to identify MESSAGE that belong to server group chatroom
+	msg->setInternalContent(message->content);
+	msg->getPrivate()->forceFromAddress(q->getConferenceAddress());
+	msg->getPrivate()->forceToAddress(deviceAddr);
+	msg->getPrivate()->setApplyModifiers(false);
+	msg->send();
 }
 
 void ServerGroupChatRoomPrivate::finalizeCreation () {
@@ -890,7 +871,10 @@ void ServerGroupChatRoomPrivate::queueMessage (const shared_ptr<Message> &msg) {
 	L_Q();
 	for (const auto &participant : q->getParticipants()) {
 		for (const auto &device : participant->getPrivate()->getDevices()) {
-			queueMessage(msg, device->getAddress());
+			// Queue the message for all devices except the one that sent it
+			if (msg->fromAddr != device->getAddress()){
+				queueMessage(msg, device->getAddress());
+			}
 		}
 	}
 }
@@ -907,50 +891,82 @@ void ServerGroupChatRoomPrivate::queueMessage (const shared_ptr<Message> &msg, c
 			break;
 		queuedMessages[uri].pop();
 	}
-	// Queue the message for all devices except the one that sent it
-	if (msg->fromAddr != deviceAddress)
-		queuedMessages[uri].push(msg);
+	queuedMessages[uri].push(msg);
 }
 
+/* The removal of participant device is done only when such device disapears from registration database, ie when a device unregisters explicitely
+ * or removed by an administrator.
+ */
 void ServerGroupChatRoomPrivate::removeParticipantDevice (const shared_ptr<Participant> &participant, const IdentityAddress &deviceAddress) {
 	L_Q();
 	L_Q_T(LocalConference, qConference);
-	participant->getPrivate()->removeDevice(deviceAddress);
+	shared_ptr<Participant> participantCopy = participant; // make a copy of the shared_ptr because the participant may be removed by setParticipantDeviceState().
+	lInfo() << q << " device " << deviceAddress << " is removed because it is has unregistered.";
+	auto participantDevice = participant->getPrivate()->findDevice(deviceAddress);
+	if (!participantDevice){
+		lError() << q << " device " << deviceAddress << " is removed, but we can't find it in this chatroom.";
+		return;
+	}
+	// Notify to everyone the retirement of this device.
 	auto deviceEvent = qConference->getPrivate()->eventHandler->notifyParticipantDeviceRemoved(participant->getAddress(), deviceAddress);
 	q->getCore()->getPrivate()->mainDb->addEvent(deviceEvent);
+	// First set it as left, so that it may eventually trigger the destruction of the chatroom if no device are present for any participant.
+	setParticipantDeviceState(participantDevice, ParticipantDevice::State::Left);
+	participantCopy->getPrivate()->removeDevice(deviceAddress);
 }
 
 // -----------------------------------------------------------------------------
 
-void ServerGroupChatRoomPrivate::onParticipantDeviceLeft (const std::shared_ptr<ParticipantDevice> &device) {
-	L_Q();
-	L_Q_T(LocalConference, qConference);
-
-	lInfo() << q << ": Participant device '" << device->getAddress().asString() << "' left";
-	shared_ptr<Participant> participant = const_pointer_cast<Participant>(device->getParticipant()->getSharedFromThis());
-
+bool ServerGroupChatRoomPrivate::allDevicesLeft(const std::shared_ptr<Participant> &participant){
 	bool allDevicesLeft = true;
+	
 	for (const auto &device : participant->getPrivate()->getDevices()) {
-		if (getParticipantDeviceState(device) != ParticipantDevice::State::Left) {
+		if (device->getState() != ParticipantDevice::State::Left) {
 			allDevicesLeft = false;
 			break;
 		}
 	}
-	if (allDevicesLeft) {
-		/* Handle the case where a participant is removed forcibly because all its devices have been retired. */
-		if (findAuthorizedParticipant(participant->getAddress()) != nullptr) {
-			lInfo() << q << ": Removing participant '" << participant->getAddress().asString() << "' since it has no device left";
-			q->removeParticipant(participant);
+	return allDevicesLeft;
+}
+
+void ServerGroupChatRoomPrivate::onParticipantDeviceLeft (const std::shared_ptr<ParticipantDevice> &device) {
+	L_Q();
+	L_Q_T(LocalConference, qConference);
+
+	lInfo() << q << ": Participant device '" << device->getAddress().asString() << "' left";
+	
+	if (! (capabilities & ServerGroupChatRoom::Capabilities::OneToOne) ){
+		shared_ptr<Participant> participant = const_pointer_cast<Participant>(device->getParticipant()->getSharedFromThis());
+
+		if (allDevicesLeft(participant)) {
+			/* Handle the case where a participant is removed forcibly because all its devices have been retired. */
+			if (findAuthorizedParticipant(participant->getAddress()) != nullptr) {
+				lInfo() << q << ": Removing participant '" << participant->getAddress().asString() << "' since it has no device left";
+				q->removeParticipant(participant);
+			}
+			/* When all devices have left, we can get rid of our reginfo subscription. */
+			unSubscribeRegistrationForParticipant(participant->getAddress());
+			/* And remove the existence of this participant in the chatroom*/
+			q->LocalConference::removeParticipant(participant);
 		}
-		/* When all devices have left, we can get rid of our reginfo subscription. */
-		unSubscribeRegistrationForParticipant(participant->getAddress());
-		/* And remove the existence of this participant in the chatroom*/
-		q->LocalConference::removeParticipant(participant);
-	}
 
-	if (qConference->getPrivate()->participants.size() == 0) {
-		lInfo() << q << ": No participant left, deleting the chat room";
-		requestDeletion();
+		if (qConference->getPrivate()->participants.size() == 0) {
+			lInfo() << q << ": No participant left, deleting the chat room";
+			requestDeletion();
+		}
+	}else{
+		/* For 1 to 1 chatrooms, if all devices of both participants are left we'll delete the chatroom*/
+		bool allLeft = true;
+		for (const auto &participant : q->getParticipants()){
+			if (!allDevicesLeft(participant)){
+				allLeft = false;
+				break;
+			}
+		}
+		if (allLeft){
+			lInfo() << q << ": No participant left, deleting the chat room";
+			requestDeletion();
+		}
 	}
 }
 
@@ -977,7 +993,7 @@ void ServerGroupChatRoomPrivate::onCallSessionStateChanged (const shared_ptr<Cal
 	L_Q();
 	auto device = q->findParticipantDevice(session);
 	if (!device) {
-		lError() << q << "onCallSessionStateChanged on unknown device.";
+		lInfo() << q << "onCallSessionStateChanged on unknown device (maybe not yet).";
 		return;
 	}
 	switch(newState){
@@ -988,11 +1004,18 @@ void ServerGroupChatRoomPrivate::onCallSessionStateChanged (const shared_ptr<Cal
 		case CallSession::State::End:
 			if (device->getState() == ParticipantDevice::State::Present){
 				lInfo() << q << device->getParticipant()->getAddress().asString() << " is leaving the chatroom.";
-				// Participant leaves the chat room on its own by sending a BYE.
-				// Set it the Leaving state first, so that q->removeParticipant() does nothing with it.
-				setParticipantDeviceState(device, ParticipantDevice::State::Leaving);
-				q->removeParticipant(device->getParticipant()->getSharedFromThis());
-				// But since we received its BYE, it is actually already left.
+				/* Unlike normal group chatrooms, a participant can never leave a one to one chatroom.
+				 * In real life you cannot prevent someone to send you a message.
+				 * The receiving of BYE is then interpreted as a termination of the SIP session specific for the device.
+				 */
+				if (! (capabilities & ServerGroupChatRoom::Capabilities::OneToOne) ){
+					// Participant leaves the chat room on its own by sending a BYE.
+					// Set it the Leaving state first, so that q->removeParticipant() does nothing with it.
+					setParticipantDeviceState(device, ParticipantDevice::State::Leaving);
+					// Remove participant will change other devices to Leaving state.
+					q->removeParticipant(device->getParticipant()->getSharedFromThis());
+					// But since we received its BYE, it is actually already left.
+				}
 				setParticipantDeviceState(device, ParticipantDevice::State::Left);
 			}
 		break;