From 1aa7fb19562f9462311e55dc0b9a7fac18c31180 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Robert=20P=C3=B6sel?= <robyer@seznam.cz>
Date: Fri, 25 May 2012 18:11:53 +0000
Subject: Updated Facebook RM

git-svn-id: http://svn.miranda-ng.org/main/trunk@176 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
---
 protocols/FacebookRM/communication.cpp           |  82 ++++-------
 protocols/FacebookRM/connection.cpp              |   5 +-
 protocols/FacebookRM/constants.h                 |   6 +
 protocols/FacebookRM/contacts.cpp                | 120 ++++++---------
 protocols/FacebookRM/db.h                        |  56 ++++---
 protocols/FacebookRM/facebook.rc                 |   5 +-
 protocols/FacebookRM/facebook_10.vcxproj         |   5 +-
 protocols/FacebookRM/facebook_10.vcxproj.filters |   7 +-
 protocols/FacebookRM/icons/addFriend.ico         | Bin 2550 -> 0 bytes
 protocols/FacebookRM/icons/removeFriend.ico      | Bin 2550 -> 0 bytes
 protocols/FacebookRM/json.cpp                    |   2 +-
 protocols/FacebookRM/messages.cpp                |   5 +-
 protocols/FacebookRM/process.cpp                 | 104 +++++++------
 protocols/FacebookRM/proto.cpp                   | 179 ++++++++---------------
 protocols/FacebookRM/proto.h                     |   9 +-
 protocols/FacebookRM/resource.h                  |   7 +-
 protocols/FacebookRM/stubs.cpp                   |  10 --
 protocols/FacebookRM/theme.cpp                   | 177 ++++++++++++++++------
 protocols/FacebookRM/theme.h                     |  10 +-
 19 files changed, 391 insertions(+), 398 deletions(-)
 delete mode 100644 protocols/FacebookRM/icons/addFriend.ico
 delete mode 100644 protocols/FacebookRM/icons/removeFriend.ico

(limited to 'protocols')

diff --git a/protocols/FacebookRM/communication.cpp b/protocols/FacebookRM/communication.cpp
index 5b2cb60ace..2e4dfc81f0 100644
--- a/protocols/FacebookRM/communication.cpp
+++ b/protocols/FacebookRM/communication.cpp
@@ -653,11 +653,9 @@ bool facebook_client::login(const std::string &username,const std::string &passw
 	flap( FACEBOOK_REQUEST_HOME, NULL );
 
 	// Prepare login data
-	std::string data = "charset_test=%e2%82%ac%2c%c2%b4%2c%e2%82%ac%2c%c2%b4%2c%e6%b0%b4%2c%d0%94%2c%d0%84&locale=en&email=";
-	data += utils::url::encode( username );
-	data += "&pass=";
-	data += utils::url::encode( password );
-	data += "&pass_placeHolder=Password&login=Login&persistent=1";
+	std::string data = "charset_test=%e2%82%ac%2c%c2%b4%2c%e2%82%ac%2c%c2%b4%2c%e6%b0%b4%2c%d0%94%2c%d0%84&locale=en&pass_placeHolder=Password&login=Login&persistent=1";
+	data += "&email=" + utils::url::encode( username );
+	data += "&pass=" + utils::url::encode( password );	
 
 	// Send validation
 	http::response resp = flap( FACEBOOK_REQUEST_LOGIN, &data );
@@ -681,14 +679,9 @@ bool facebook_client::login(const std::string &username,const std::string &passw
 			resp = flap( FACEBOOK_REQUEST_SETUP_MACHINE );
 			
 			std::string inner_data = "machine_name=MirandaIM&submit[Save%20Device]=Save%20Device";
-			inner_data += "&post_form_id=";
-			inner_data += utils::text::source_get_value(&resp.data, 3, "name=\"post_form_id\"", "value=\"", "\"" );
-			
-			inner_data += "&lsd=";
-			inner_data += utils::text::source_get_value(&resp.data, 3, "name=\"lsd\"", "value=\"", "\"" );
-			
-			inner_data += "&nh=";
-			inner_data += utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"" );
+			inner_data += "&post_form_id=" + utils::text::source_get_value(&resp.data, 3, "name=\"post_form_id\"", "value=\"", "\"" );
+			inner_data += "&lsd=" + utils::text::source_get_value(&resp.data, 3, "name=\"lsd\"", "value=\"", "\"" );
+			inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"" );
 
 			resp = flap( FACEBOOK_REQUEST_SETUP_MACHINE, &inner_data );
 			validate_response(&resp);
@@ -778,12 +771,9 @@ bool facebook_client::logout( )
 
 	handle_entry( "logout" );
 
-	std::string data = "post_form_id=";
-	data += ( this->post_form_id_.length( ) ) ? this->post_form_id_ : "0";
-	data += "&fb_dtsg=";
-	data += ( this->dtsg_.length( ) ) ? this->dtsg_ : "0";
-	data += "&ref=mb&h=";
-	data += this->logout_hash_;
+	std::string data = "post_form_id=" + (this->post_form_id_.length() ? this->post_form_id_ : "0");
+	data += "&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+	data += "&ref=mb&h=" + this->logout_hash_;
 
 	http::response resp = flap( FACEBOOK_REQUEST_LOGOUT, &data );
 
@@ -918,13 +908,9 @@ bool facebook_client::chat_state( bool online )
   
 	std::string data = "visibility=";
 	data += ( online ) ? "1" : "0";
-	data += "&window_id=0";
-	data += "&post_form_id=";
-	data += ( post_form_id_.length( ) ) ? post_form_id_ : "0";
-	data += "&post_form_id_source=AsyncRequest";
-	data += "&fb_dtsg=" + this->dtsg_;
-	data += "&lsd=&phstamp=0&__user=";
-	data += self_.user_id;
+	data += "&window_id=0&post_form_id=" + (post_form_id_.length() ? post_form_id_ : "0");
+	data += "&post_form_id_source=AsyncRequest&fb_dtsg=" + this->dtsg_;
+	data += "&lsd=&phstamp=0&__user=" + self_.user_id;
 	http::response resp = flap( FACEBOOK_REQUEST_VISIBILITY, &data );
   
 	return handle_success( "chat_state" );
@@ -1262,16 +1248,10 @@ void facebook_client::close_chat( std::string message_recipient )
 		can't close it so soon. But maybe this didnt help also. */
 	Sleep(300); 
 
-	std::string data = "close_chat=";
-	data += message_recipient;
-	data += "&window_id=0";
-	data += "&post_form_id=";
-	data += ( post_form_id_.length( ) ) ? post_form_id_ : "0";
-	data += "&post_form_id_source=AsyncRequest";
-	data += "&fb_dtsg=";
-	data += ( this->dtsg_.length( ) ) ? this->dtsg_ : "0";
-	data += "&__user=";
-	data += self_.user_id;
+	std::string data = "close_chat=" + message_recipient;
+	data += "&window_id=0&post_form_id=" + (post_form_id_.length() ? post_form_id_ : "0");
+	data += "&post_form_id_source=AsyncRequest&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+	data += "&__user=" + self_.user_id;
 	
 	http::response resp = flap( FACEBOOK_REQUEST_TABS, &data );
 }
@@ -1280,14 +1260,11 @@ void facebook_client::chat_mark_read( std::string message_recipient )
 {
 	// TODO RM: optimalization?
 
-	std::string data = "action=chatMarkRead&other_user=";
-	data += message_recipient;
-	data += "&post_form_id=";
-	data += ( post_form_id_.length( ) ) ? post_form_id_ : "0";
-	data += "&fb_dtsg=";
-	data += ( this->dtsg_.length( ) ) ? this->dtsg_ : "0";
-	data += "&post_form_id_source=AsyncRequest&lsd=&__user=";
-	data += self_.user_id;
+	std::string data = "action=chatMarkRead";
+	data += "&other_user=" + message_recipient;
+	data += "&post_form_id=" + (post_form_id_.length() ? post_form_id_ : "0");
+	data += "&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+	data += "&post_form_id_source=AsyncRequest&lsd=&__user=" + self_.user_id;
 	
 	http::response resp = flap( FACEBOOK_REQUEST_ASYNC, &data );
 }
@@ -1296,19 +1273,16 @@ bool facebook_client::set_status(const std::string &status_text)
 {
 	handle_entry( "set_status" );
 
-	std::string data = "post_form_id_source=AsyncRequest&post_form_id=";
-	data += ( this->post_form_id_.length( ) ) ? this->post_form_id_ : "0";
-	data += "&fb_dtsg=";
-	data += ( this->dtsg_.length( ) ) ? this->dtsg_ : "0";
-	data += "&target_id=";
-	data += this->self_.user_id;
+	std::string data = "post_form_id_source=AsyncRequest";
+	data += "&post_form_id=" + (this->post_form_id_.length() ? this->post_form_id_ : "0");
+	data += "&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+	data += "&target_id=" + this->self_.user_id;
 
 	if ( status_text.length( ) )
 	{
-		data += "&action=PROFILE_UPDATE&app_id=&hey_kid_im_a_composer=true&display_context=profile&_log_display_context=profile&ajax_log=1&status=";
-		data += utils::url::encode( status_text );
-		data += "&profile_id=";
-		data += this->self_.user_id;
+		data += "&action=PROFILE_UPDATE&app_id=&hey_kid_im_a_composer=true&display_context=profile&_log_display_context=profile&ajax_log=1";
+		data += "&status=" + utils::url::encode( status_text );
+		data += "&profile_id=" + this->self_.user_id;
 	}
 
 	http::response resp = flap( FACEBOOK_REQUEST_STATUS_SET, &data );
diff --git a/protocols/FacebookRM/connection.cpp b/protocols/FacebookRM/connection.cpp
index 14b5c181c1..5d955d917f 100644
--- a/protocols/FacebookRM/connection.cpp
+++ b/protocols/FacebookRM/connection.cpp
@@ -191,7 +191,7 @@ void FacebookProto::UpdateLoop(void *)
 	time_t tim = ::time(NULL);
 	LOG( ">>>>> Entering Facebook::UpdateLoop[%d]", tim );
 
-	for ( int i = -1; !isOffline(); i = ++i % 100 )
+	for ( int i = -1; !isOffline(); i = ++i % 50 )
 	{
 		if ( i != -1 ) {
 			if ( !facy.invisible_ )
@@ -202,7 +202,7 @@ void FacebookProto::UpdateLoop(void *)
 			if ( !facy.feeds( ) )
 				break;
 
-		if ( i == 99 )
+		if ( i == 49 )
 			ForkThread( &FacebookProto::ProcessFriendRequests, this, NULL );
 
 		LOG( "***** FacebookProto::UpdateLoop[%d] going to sleep...", tim );
@@ -217,7 +217,6 @@ void FacebookProto::UpdateLoop(void *)
 
 void FacebookProto::MessageLoop(void *)
 {
-	//ScopedLock s(message_loop_lock_); // TODO: Required?
 	time_t tim = ::time(NULL);
 	LOG( ">>>>> Entering Facebook::MessageLoop[%d]", tim );
 
diff --git a/protocols/FacebookRM/constants.h b/protocols/FacebookRM/constants.h
index 93ae6022ee..e647cabf62 100644
--- a/protocols/FacebookRM/constants.h
+++ b/protocols/FacebookRM/constants.h
@@ -111,6 +111,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define FACEBOOK_RECV_MESSAGE	1
 #define FACEBOOK_SEND_MESSAGE	2
 
+// Contact types
+#define FACEBOOK_CONTACT_FRIEND		1 // contact that IS on our server list
+#define FACEBOOK_CONTACT_NONE		2 // contact that ISN'T on our server list
+#define FACEBOOK_CONTACT_REQUEST	3 // contact that we asked for friendship
+#define FACEBOOK_CONTACT_APPROVE	4 // contact that is asking us for approval of friendship
+
 // News Feed types
 static const struct
 {
diff --git a/protocols/FacebookRM/contacts.cpp b/protocols/FacebookRM/contacts.cpp
index 0f1759e628..23a6ca4455 100644
--- a/protocols/FacebookRM/contacts.cpp
+++ b/protocols/FacebookRM/contacts.cpp
@@ -63,7 +63,7 @@ HANDLE FacebookProto::ContactIDToHContact(std::string user_id)
 	return 0;
 }
 
-HANDLE FacebookProto::AddToContactList(facebook_user* fbu, bool dont_check, const char *new_name)
+HANDLE FacebookProto::AddToContactList(facebook_user* fbu, BYTE type, bool dont_check, const char *new_name)
 {
 	HANDLE hContact;
 
@@ -96,11 +96,13 @@ HANDLE FacebookProto::AddToContactList(facebook_user* fbu, bool dont_check, cons
 				DBFreeVariant(&dbv);
 			}
 
-			if (strlen(new_name) > 0) {
+			if (strlen(new_name) > 0)
+			{
 				DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, new_name);
 				DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NICK, new_name);
-				DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 1); // We suppose he is not on server list
-			}						
+			}
+			
+			DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, type);
 
 			if (getByte(FACEBOOK_KEY_DISABLE_STATUS_NOTIFY, 0))
 				CallService(MS_IGNORE_IGNORE, (WPARAM)hContact, (LPARAM)IGNOREEVENT_USERONLINE);
@@ -155,20 +157,23 @@ void FacebookProto::DeleteContactFromServer(void *data)
 	// Process result data
 	facy.validate_response(&resp);
 
-	if (resp.data.find("\"success\":true", 0) != std::string::npos) {
-		
-		// TODO: do only when operation is successful
+	if (resp.data.find("\"payload\":null", 0) != std::string::npos)
+	{		
 		facebook_user* fbu = facy.buddies.find( id );
-		if (fbu != NULL) {
-			fbu->deleted = true;		
-			// TODO: change type of contact in database...
-			DBWriteContactSettingWord(fbu->handle, m_szModuleName, "Status", ID_STATUS_OFFLINE); // set offline status
-			
-			// TODO: if not in actual buddies list, search in database...
-			DBWriteContactSettingDword(fbu->handle, m_szModuleName, FACEBOOK_KEY_DELETED, ::time(NULL)); // set deleted time
+		if (fbu != NULL)
+			fbu->deleted = true;
+
+		HANDLE hContact = ContactIDToHContact(id);		
+		
+		// If contact wasn't deleted from database
+		if (hContact != NULL)
+		{
+			DBWriteContactSettingWord(hContact, m_szModuleName, "Status", ID_STATUS_OFFLINE);
+			DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_NONE);
+			DBWriteContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, ::time(NULL));
 		}
 		
-		NotifyEvent(TranslateT("Deleting contact"), TranslateT("Contact was sucessfully removed from your server list."), NULL, FACEBOOK_EVENT_OTHER, NULL);		
+		NotifyEvent(TranslateT("Deleting contact"), TranslateT("Contact was sucessfully removed from your server list."), NULL, FACEBOOK_EVENT_OTHER, NULL);
 	} else {
 		facy.client_notify( TranslateT("Error occured when removing contact from server.") );
 	}
@@ -184,32 +189,34 @@ void FacebookProto::AddContactToServer(void *data)
 	if ( data == NULL )
 		return;
 
-	std::string *id = (std::string*)data;
+	std::string id = (*(std::string*)data);
+	delete data;
 	
 	std::string query = "action=add_friend&how_found=profile_button&ref_param=ts&outgoing_id=&unwanted=&logging_location=&no_flyout_on_click=false&ego_log_data=&post_form_id_source=AsyncRequest&lsd=&fb_dtsg=";
 	query += facy.dtsg_;
 	query += "&post_form_id=";
 	query += facy.post_form_id_;	
 	query += "&to_friend=";
-	query += *id;
+	query += id;
 	query += "&__user=";
 	query += facy.self_.user_id;
 
-	delete data;
-
 	// Get unread inbox threads
 	http::response resp = facy.flap( FACEBOOK_REQUEST_REQUEST_FRIEND, &query );
 
 	// Process result data
 	facy.validate_response(&resp);
 
-	if (resp.data.find("\"success\":true", 0) != std::string::npos) {
-		/*facebook_user* fbu = facy.buddies.find( id );
-		if (fbu != NULL) {
-			// TODO: change type of contact in database...
-			// TODO: if not in actual buddies list, search in database...
-		}*/			
+	if (resp.data.find("\"success\":true", 0) != std::string::npos)
+	{
+		HANDLE hContact = ContactIDToHContact(id);		
 		
+		// If contact wasn't deleted from database
+		if (hContact != NULL)
+		{
+			DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_REQUEST);
+		}
+				
 		NotifyEvent(TranslateT("Adding contact"), TranslateT("Request for friendship was sent successfully."), NULL, FACEBOOK_EVENT_OTHER, NULL);
 	} else {
 		facy.client_notify( TranslateT("Error occured when requesting friendship.") );
@@ -227,38 +234,27 @@ void FacebookProto::ApproveContactToServer(void *data)
 	if ( data == NULL )
 		return;
 
-	std::string *id = (std::string*)data;
-		
+	HANDLE hContact = (*(HANDLE*)data);
+	delete data;
+
 	std::string post_data = "fb_dtsg=" + facy.dtsg_;
 	post_data += "&charset_test=%e2%82%ac%2c%c2%b4%2c%e2%82%ac%2c%c2%b4%2c%e6%b0%b4%2c%d0%94%2c%d0%84&confirm_button=";
 
-	std::string get_data;
-
-	HANDLE *hContact = (HANDLE*)data;
+	std::string get_data = "id=";
 
 	DBVARIANT dbv;
-	if (!DBGetContactSettingString(*hContact, m_szModuleName, FACEBOOK_KEY_APPROVE, &dbv))
+	if (!DBGetContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_ID, &dbv))
 	{
-		get_data = dbv.pszVal;
+		get_data += dbv.pszVal;
 		DBFreeVariant(&dbv);
-	}	
-
-	// replace absolute link to params only
-	utils::text::replace_first(&get_data, "/a/notifications.php?", "&");
+	}
 
 	http::response resp = facy.flap( FACEBOOK_REQUEST_APPROVE_FRIEND, &post_data, &get_data );
 
 	// Process result data
 	facy.validate_response(&resp);
 
-	if (resp.code != HTTP_CODE_OK)
-		facy.handle_error( "ApproveContactToServer" );
-	else {
-		NotifyEvent(TranslateT("Adding contact"), TranslateT("Contact was added to your server list."), NULL, FACEBOOK_EVENT_OTHER, NULL);
-		DBDeleteContactSetting(*hContact, m_szModuleName, FACEBOOK_KEY_APPROVE);
-	}
-
-	delete data;
+	DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_FRIEND);
 }
 
 HANDLE FacebookProto::GetAwayMsg(HANDLE hContact)
@@ -266,41 +262,9 @@ HANDLE FacebookProto::GetAwayMsg(HANDLE hContact)
 	return 0; // Status messages are disabled
 }
 
-int FacebookProto::OnContactDeleted(WPARAM wparam,LPARAM)
+int FacebookProto::OnContactDeleted(WPARAM wParam,LPARAM)
 {
-	HANDLE hContact = (HANDLE)wparam;
-
-	DBVARIANT dbv;
-	char str[256];
-
-	if ( !DBGetContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv) ) {
-		mir_snprintf(str,SIZEOF(str),Translate("Do you want to delete contact '%s' from server list?"), dbv.pszVal);
-		DBFreeVariant(&dbv);
-	} else if( !DBGetContactSettingUTF8String(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv) ) {
-		mir_snprintf(str,SIZEOF(str),Translate("Do you want to delete contact '%s' from server list?"), dbv.pszVal);
-		DBFreeVariant(&dbv);
-	}	
-
-	TCHAR *text = mir_a2t_cp(str, CP_UTF8);
-	if (MessageBox( 0, text, m_tszUserName, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 ) == IDYES) {
-		
-		if( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv) )
-		{
-			if (!isOffline()) { // TODO: is this needed?
-				std::string* id = new std::string(dbv.pszVal);
-
-				facebook_user* fbu = facy.buddies.find( (*id) );
-				if (fbu != NULL) {
-					fbu->handle = NULL;
-				}
-
-				ForkThread( &FacebookProto::DeleteContactFromServer, this, ( void* )id );
-				DBFreeVariant(&dbv);
-			}
-		}
-				
-	}
-	mir_free(text);
+	CancelFriendship(wParam, 1);
 
 	return 0;
 }
diff --git a/protocols/FacebookRM/db.h b/protocols/FacebookRM/db.h
index cadddef8ca..c397522f3c 100644
--- a/protocols/FacebookRM/db.h
+++ b/protocols/FacebookRM/db.h
@@ -38,37 +38,35 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define deleteSetting( setting )            DBDeleteContactSetting( NULL, m_szModuleName, setting )
 
 // DB keys
-#define FACEBOOK_KEY_LOGIN          "Email"
-#define FACEBOOK_KEY_ID             "ID"
-#define FACEBOOK_KEY_NAME           "RealName"
-#define FACEBOOK_KEY_NICK			"Nick"
-#define FACEBOOK_KEY_PASS           "Password"
-#define FACEBOOK_KEY_UPD_NAMES      "UpdateNames"
-#define FACEBOOK_KEY_DEVICE_ID      "DeviceID"
-#define FACEBOOK_KEY_AV_URL         "AvatarURL"
-#define FACEBOOK_KEY_DELETED		"Deleted"
-#define FACEBOOK_KEY_CONTACT_TYPE	"ContactType"
-#define FACEBOOK_KEY_APPROVE		"ApproveAction"
-
-#define FACEBOOK_KEY_DEF_GROUP      "DefaultGroup"
-#define FACEBOOK_KEY_FORCE_HTTPS    "ForceHTTPS"
+#define FACEBOOK_KEY_LOGIN					"Email"
+#define FACEBOOK_KEY_ID						"ID"
+#define FACEBOOK_KEY_NAME					"RealName"
+#define FACEBOOK_KEY_NICK					"Nick"
+#define FACEBOOK_KEY_PASS					"Password"
+#define FACEBOOK_KEY_UPD_NAMES				"UpdateNames"
+#define FACEBOOK_KEY_DEVICE_ID				"DeviceID"
+#define FACEBOOK_KEY_AV_URL					"AvatarURL"
+#define FACEBOOK_KEY_DELETED				"Deleted"
+#define FACEBOOK_KEY_CONTACT_TYPE			"ContactType"
+#define FACEBOOK_KEY_DEF_GROUP				"DefaultGroup"
+#define FACEBOOK_KEY_FORCE_HTTPS			"ForceHTTPS"
 #define FACEBOOK_KEY_FORCE_HTTPS_CHANNEL    "ForceHTTPSChannel"
 #define FACEBOOK_KEY_CLOSE_WINDOWS_ENABLE   "CloseChatEnable"
-#define FACEBOOK_KEY_SET_MIRANDA_STATUS "SetMirandaStatus"
-#define FACEBOOK_KEY_LOGGING_ENABLE "LoggingEnable"
-#define FACEBOOK_KEY_SYSTRAY_NOTIFY "UseSystrayNotify"
-#define FACEBOOK_KEY_DISABLE_STATUS_NOTIFY "DisableStatusNotify"
-#define FACEBOOK_KEY_PARSE_MESSAGES	"ParseUnreadMessages"
-#define FACEBOOK_KEY_BIG_AVATARS	"UseBigAvatars"
-#define FACEBOOK_KEY_DISCONNECT_CHAT "DisconnectChatEnable"
-#define FACEBOOK_KEY_MAP_STATUSES	"MapStatuses"
-#define FACEBOOK_KEY_LOAD_MOBILE	"LoadMobile"
-#define FACEBOOK_KEY_ENABLE_GROUPCHATS "GroupchatsEnable"
-
-#define FACEBOOK_KEY_POLL_RATE      "PollRate" // [HIDDEN]
-#define FACEBOOK_KEY_TIMEOUTS_LIMIT "TimeoutsLimit" // [HIDDEN]
-#define FACEBOOK_KEY_DISABLE_LOGOUT "DisableLogout" // [HIDDEN]
-#define FACEBOOK_KEY_VALIDATE_RESPONSE "ValidateResponse" // [HIDDEN] - 0 = standard, 1 = always, 2 = never
+#define FACEBOOK_KEY_SET_MIRANDA_STATUS		"SetMirandaStatus"
+#define FACEBOOK_KEY_LOGGING_ENABLE			"LoggingEnable"
+#define FACEBOOK_KEY_SYSTRAY_NOTIFY			"UseSystrayNotify"
+#define FACEBOOK_KEY_DISABLE_STATUS_NOTIFY	"DisableStatusNotify"
+#define FACEBOOK_KEY_PARSE_MESSAGES			"ParseUnreadMessages"
+#define FACEBOOK_KEY_BIG_AVATARS			"UseBigAvatars"
+#define FACEBOOK_KEY_DISCONNECT_CHAT		"DisconnectChatEnable"
+#define FACEBOOK_KEY_MAP_STATUSES			"MapStatuses"
+#define FACEBOOK_KEY_LOAD_MOBILE			"LoadMobile"
+#define FACEBOOK_KEY_ENABLE_GROUPCHATS		"GroupchatsEnable"
+
+#define FACEBOOK_KEY_POLL_RATE				"PollRate" // [HIDDEN]
+#define FACEBOOK_KEY_TIMEOUTS_LIMIT			"TimeoutsLimit" // [HIDDEN]
+#define FACEBOOK_KEY_DISABLE_LOGOUT			"DisableLogout" // [HIDDEN]
+#define FACEBOOK_KEY_VALIDATE_RESPONSE		"ValidateResponse" // [HIDDEN] - 0 = standard, 1 = always, 2 = never
 
 #define FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE     "EventNotificationsEnable"
 #define FACEBOOK_KEY_EVENT_FEEDS_ENABLE             "EventFeedsEnable"
diff --git a/protocols/FacebookRM/facebook.rc b/protocols/FacebookRM/facebook.rc
index 7f66333cbc..ae7489daa4 100644
--- a/protocols/FacebookRM/facebook.rc
+++ b/protocols/FacebookRM/facebook.rc
@@ -53,8 +53,9 @@ END
 // remains consistent on all systems.
 IDI_FACEBOOK            ICON                    "icons/facebook.ico"
 IDI_MIND                ICON                    "icons/mind.ico"
-IDI_ADDFRIEND           ICON                    "icons/addFriend.ico"
-IDI_REMOVEFRIEND        ICON                    "icons/removeFriend.ico"
+IDI_AUTH_GRANT          ICON                    "icons/auth_grant.ico"
+IDI_AUTH_ASK            ICON                    "icons/auth_ask.ico"
+IDI_AUTH_REVOKE         ICON                    "icons/auth_revoke.ico"
 
 /////////////////////////////////////////////////////////////////////////////
 //
diff --git a/protocols/FacebookRM/facebook_10.vcxproj b/protocols/FacebookRM/facebook_10.vcxproj
index 0fea434eeb..198c8e647b 100644
--- a/protocols/FacebookRM/facebook_10.vcxproj
+++ b/protocols/FacebookRM/facebook_10.vcxproj
@@ -310,8 +310,9 @@
     <ClInclude Include="resource.h" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="icons\addFriend.ico" />
-    <None Include="icons\removeFriend.ico" />
+    <None Include="icons\auth_ask.ico" />
+    <None Include="icons\auth_grant.ico" />
+    <None Include="icons\auth_revoke.ico" />
     <None Include="JSON_CAJUN\elements.inl" />
     <None Include="JSON_CAJUN\reader.inl" />
     <None Include="JSON_CAJUN\writer.inl" />
diff --git a/protocols/FacebookRM/facebook_10.vcxproj.filters b/protocols/FacebookRM/facebook_10.vcxproj.filters
index fc92cf1075..5ddb449d60 100644
--- a/protocols/FacebookRM/facebook_10.vcxproj.filters
+++ b/protocols/FacebookRM/facebook_10.vcxproj.filters
@@ -142,10 +142,13 @@
     <None Include="icons\mind.ico">
       <Filter>Resource Files</Filter>
     </None>
-    <None Include="icons\addFriend.ico">
+    <None Include="icons\auth_ask.ico">
       <Filter>Resource Files</Filter>
     </None>
-    <None Include="icons\removeFriend.ico">
+    <None Include="icons\auth_grant.ico">
+      <Filter>Resource Files</Filter>
+    </None>
+    <None Include="icons\auth_revoke.ico">
       <Filter>Resource Files</Filter>
     </None>
   </ItemGroup>
diff --git a/protocols/FacebookRM/icons/addFriend.ico b/protocols/FacebookRM/icons/addFriend.ico
deleted file mode 100644
index d8879d15bb..0000000000
Binary files a/protocols/FacebookRM/icons/addFriend.ico and /dev/null differ
diff --git a/protocols/FacebookRM/icons/removeFriend.ico b/protocols/FacebookRM/icons/removeFriend.ico
deleted file mode 100644
index 9639a9c728..0000000000
Binary files a/protocols/FacebookRM/icons/removeFriend.ico and /dev/null differ
diff --git a/protocols/FacebookRM/json.cpp b/protocols/FacebookRM/json.cpp
index 2231596882..bb92cb4434 100644
--- a/protocols/FacebookRM/json.cpp
+++ b/protocols/FacebookRM/json.cpp
@@ -503,7 +503,7 @@ int facebook_json_parser::parse_messages( void* data, std::vector< facebook_mess
 				facebook_user fbu;
 				fbu.user_id = user_id;
 
-				HANDLE hContact = proto->AddToContactList(&fbu);
+				HANDLE hContact = proto->AddToContactList(&fbu, FACEBOOK_CONTACT_FRIEND);
 				
 				if ( DBGetContactSettingWord(hContact,proto->m_szModuleName,"Status", 0) == ID_STATUS_OFFLINE )
 					DBWriteContactSettingWord(hContact,proto->m_szModuleName,"Status",ID_STATUS_ONLINE);
diff --git a/protocols/FacebookRM/messages.cpp b/protocols/FacebookRM/messages.cpp
index d8fd7e6d58..072ae88b4f 100644
--- a/protocols/FacebookRM/messages.cpp
+++ b/protocols/FacebookRM/messages.cpp
@@ -114,8 +114,9 @@ void FacebookProto::SendTypingWorker(void *p)
 
 	send_typing *typing = static_cast<send_typing*>(p);
 
-	// Dont send typing notifications to contacts, that are offline
-	if ( DBGetContactSettingWord(typing->hContact,m_szModuleName,"Status", 0) == ID_STATUS_OFFLINE )
+	// Dont send typing notifications to contacts, that are offline or not friends
+	if ( DBGetContactSettingWord(typing->hContact,m_szModuleName,"Status", 0) == ID_STATUS_OFFLINE
+		|| DBGetContactSettingByte(typing->hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND)
 		return;
 
 	// TODO RM: maybe better send typing optimalization
diff --git a/protocols/FacebookRM/process.cpp b/protocols/FacebookRM/process.cpp
index 4dd98c5678..4c51141cb1 100644
--- a/protocols/FacebookRM/process.cpp
+++ b/protocols/FacebookRM/process.cpp
@@ -52,7 +52,7 @@ void FacebookProto::ProcessBuddyList( void* data )
 		{
 			fbu = i->data;
 
-			if (fbu->handle && !fbu->deleted)
+			if (fbu->handle)
 				DBWriteContactSettingWord(fbu->handle, m_szModuleName, "Status", ID_STATUS_OFFLINE);
 
 			std::string to_delete( i->key );
@@ -63,7 +63,7 @@ void FacebookProto::ProcessBuddyList( void* data )
 			i = i->next;
 
 			if (!fbu->handle) { // just been added
-				fbu->handle = AddToContactList(fbu);
+				fbu->handle = AddToContactList(fbu, FACEBOOK_CONTACT_FRIEND);				
 
 				if (!fbu->real_name.empty()) {
 					DBWriteContactSettingUTF8String(fbu->handle,m_szModuleName,FACEBOOK_KEY_NAME,fbu->real_name.c_str());
@@ -75,8 +75,9 @@ void FacebookProto::ProcessBuddyList( void* data )
 				DBWriteContactSettingWord(fbu->handle,m_szModuleName,"Status", fbu->status_id );
 			}
 
-			if (DBGetContactSettingByte(fbu->handle,m_szModuleName,FACEBOOK_KEY_CONTACT_TYPE, 0)) {
-				DBDeleteContactSetting(fbu->handle,m_szModuleName,FACEBOOK_KEY_CONTACT_TYPE); // Set type "on server-list" contact
+			if (DBGetContactSettingByte(fbu->handle, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND) {
+				DBWriteContactSettingByte(fbu->handle, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_FRIEND);
+				// TODO: remove that popup and use "Contact added you" event?
 			}
 
 			// Wasn't contact removed from "server-list" someday?
@@ -156,15 +157,6 @@ void FacebookProto::ProcessFriendList( void* data )
 				if ( DBGetContactSettingByte(hContact, m_szModuleName, "Gender", 0) != fbu->gender )
 					DBWriteContactSettingByte(hContact, m_szModuleName, "Gender", fbu->gender);
 
-				// TODO: Remove in next version
-				if( !DBGetContactSettingString(hContact, m_szModuleName, "MirVer", &dbv) ) {
-					update_required = strcmp( dbv.pszVal, FACEBOOK_NAME ) != 0;
-					DBFreeVariant(&dbv);
-				}
-				if (update_required) {
-					DBWriteContactSettingString(hContact, m_szModuleName, "MirVer", FACEBOOK_NAME);
-				}
-
 				// Update real name
 				if ( !DBGetContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv) )
 				{
@@ -177,8 +169,9 @@ void FacebookProto::ProcessFriendList( void* data )
 					DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NICK, fbu->real_name.c_str());
 				}
 
-				if (DBGetContactSettingByte(fbu->handle,m_szModuleName,FACEBOOK_KEY_CONTACT_TYPE, 0)) {
-					DBDeleteContactSetting(fbu->handle,m_szModuleName,FACEBOOK_KEY_CONTACT_TYPE); // Has type "on server-list" contact
+				if (DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND) {
+					DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_FRIEND);
+					// TODO: remove that popup and use "Contact added you" event?
 				}
 
 				// Wasn't contact removed from "server-list" someday?
@@ -202,11 +195,13 @@ void FacebookProto::ProcessFriendList( void* data )
 			} else {
 				// Contact was removed from "server-list", notify it
 
-				// Wasnt we already been notified about this contact?
-				if ( !DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, 0) 
-					&& !DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0)	) { // And is this contact "on-server" contact?
+				// Wasnt we already been notified about this contact? And was this real friend?
+				if (!DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, 0) 
+					&& DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) == FACEBOOK_CONTACT_FRIEND)
+				{
 
 					DBWriteContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, ::time(NULL));
+					DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_NONE);
 
 					std::string contactname = id;
 					if ( !DBGetContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv) ) {
@@ -231,7 +226,7 @@ void FacebookProto::ProcessFriendList( void* data )
 	{
 		facebook_user *fbu = iter->second;
 		
-		HANDLE hContact = AddToContactList(fbu, true); // This contact is surely new
+		HANDLE hContact = AddToContactList(fbu, FACEBOOK_CONTACT_FRIEND, true); // This contact is surely new
 
 		DBWriteContactSettingByte(hContact, m_szModuleName, "Gender", fbu->gender );
 		DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, fbu->real_name.c_str());
@@ -313,7 +308,7 @@ void FacebookProto::ProcessUnreadMessages( void* )
 		facebook_user fbu;
 		fbu.user_id = user_id;
 
-		HANDLE hContact = AddToContactList(&fbu);
+		HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE);
 		// TODO: if contact is newly added, get his user info
 		// TODO: maybe create new "receiveMsg" function and use it for offline and channel messages?
 
@@ -431,7 +426,7 @@ void FacebookProto::ProcessMessages( void* data )
 			facebook_user fbu;
 			fbu.user_id = messages[i]->user_id;
 
-			HANDLE hContact = AddToContactList(&fbu, false, messages[i]->sender_name.c_str());
+			HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE, false, messages[i]->sender_name.c_str());
 
 			// TODO: if contact is newly added, get his user info
 			// TODO: maybe create new "receiveMsg" function and use it for offline and channel messages?
@@ -568,7 +563,8 @@ void FacebookProto::ProcessFriendRequests( void* )
 			last = true;
 		}
 				
-		std::string get = utils::text::source_get_value(&req, 3, "<form", "action=\"", "\">");		
+		std::string get = utils::text::source_get_value(&req, 3, "<form", "action=\"", "\">");
+		std::string time = utils::text::source_get_value2(&get, "seenrequesttime=", "&\"");
 
 		facebook_user *fbu = new facebook_user();
 		fbu->real_name = utils::text::source_get_value(&req, 2, "class=\"actor\">", "</");
@@ -576,37 +572,49 @@ void FacebookProto::ProcessFriendRequests( void* )
 
 		if (fbu->user_id.length() && fbu->real_name.length())
 		{
-			HANDLE hContact = AddToContactList(fbu, false, fbu->real_name.c_str());
-			DBWriteContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_APPROVE, get.c_str());
+			HANDLE hContact = AddToContactList(fbu, FACEBOOK_CONTACT_APPROVE, false, fbu->real_name.c_str());
+			DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_APPROVE);
 
-			LOG("      Friendship request from: %s (%s)", fbu->real_name.c_str(), fbu->user_id.c_str());
+			bool seen = false;
 
-			/* TODO: How to write event for auth requests etc.? */
-/*			CCSDATA ccs;
-			PROTORECVEVENT pre;
+			DBVARIANT dbv;
+			if (!DBGetContactSettingString(hContact, m_szModuleName, "RequestTime", &dbv)) {
+				seen = !strcmp(dbv.pszVal, time.c_str());
+				DBFreeVariant(&dbv);
+			}
 
-			ccs.szProtoService = PSR_AUTH;
-			ccs.hContact = hContact;
-			ccs.wParam = 0;
-			ccs.lParam = (LPARAM)&pre;
-			pre.flags |= PREF_UTF;
-			pre.timestamp = ::time(NULL);
-			pre.lParam = sizeof(DWORD) + sizeof(HANDLE) + fbu->real_name.length() + 5;
-
-			char* pCurBlob = (char*)alloca(pre.lParam);
-			pre.szMessage = pCurBlob;
-
-			*(PDWORD)pCurBlob = 0; pCurBlob += sizeof(DWORD);									// UID
-			*(PHANDLE)pCurBlob = hContact; pCurBlob += sizeof(HANDLE);							// Contact Handle
-			strcpy(pCurBlob, fbu->real_name.c_str()); pCurBlob += fbu->real_name.length() + 1;	// Nickname
-			*pCurBlob = '\0'; pCurBlob++;														// First Name
-			*pCurBlob = '\0'; pCurBlob++;														// Last Name
-			*pCurBlob = '\0'; pCurBlob++;														// E-mail
-			*pCurBlob = '\0';																	// Reason
-
-			CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); */
+			if (!seen) {
+				// This is new request
+				DBWriteContactSettingString(hContact, m_szModuleName, "RequestTime", time.c_str());
+
+				//blob is: uin( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), first( ASCIIZ ), last( ASCIIZ ), email( ASCIIZ ), reason( ASCIIZ )
+				//blob is: 0( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ )
+				DBEVENTINFO dbei = {0};
+				dbei.cbSize = sizeof( DBEVENTINFO );
+				dbei.szModule = m_szModuleName;
+				dbei.timestamp = ::time( NULL );
+				dbei.flags = 0; //DBEF_UTF;
+				dbei.eventType = EVENTTYPE_AUTHREQUEST;
+				dbei.cbBlob = (DWORD)(sizeof( DWORD )+ sizeof( HANDLE ) + fbu->real_name.length() + 5);
+					
+				PBYTE pCurBlob = dbei.pBlob = ( PBYTE ) mir_alloc( dbei.cbBlob );					
+				*(PDWORD)pCurBlob = 0; pCurBlob += sizeof(DWORD);									// UID
+				*(PHANDLE)pCurBlob = hContact; pCurBlob += sizeof(HANDLE);							// Contact Handle
+				strcpy((char*)pCurBlob, fbu->real_name.c_str()); pCurBlob += fbu->real_name.length()+1;	// Nickname
+				*pCurBlob = '\0'; pCurBlob++;														// First Name
+				*pCurBlob = '\0'; pCurBlob++;														// Last Name
+				*pCurBlob = '\0'; pCurBlob++;														// E-mail
+				*pCurBlob = '\0';																	// Reason
+
+				CallService(MS_DB_EVENT_ADD, (WPARAM)NULL, (LPARAM)&dbei);				
+
+				LOG("      (New) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
+			} else {
+				LOG("      (Old) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
+			}
 		} else {
 			LOG(" !!!  Wrong friendship request");
+			LOG(req.c_str());
 		}
 	}
 
diff --git a/protocols/FacebookRM/proto.cpp b/protocols/FacebookRM/proto.cpp
index 14a9dcfedc..fd203dffb2 100644
--- a/protocols/FacebookRM/proto.cpp
+++ b/protocols/FacebookRM/proto.cpp
@@ -96,7 +96,6 @@ FacebookProto::~FacebookProto( )
 	CloseHandle( avatar_lock_ );
 	CloseHandle( log_lock_ );
 	CloseHandle( update_loop_lock_ );
-	//CloseHandle( this->message_loop_lock_ );
 	CloseHandle( facy.buddies_lock_ );
 	CloseHandle( facy.send_message_lock_ );
 	CloseHandle( facy.fcb_conn_lock_ );
@@ -114,7 +113,7 @@ DWORD_PTR FacebookProto::GetCaps( int type, HANDLE hContact )
 	{
 	case PFLAGNUM_1: // TODO: Other caps available: PF1_BASICSEARCH, PF1_SEARCHBYEMAIL
 	{
-		DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ | PF1_ADDED | PF1_BASICSEARCH | PF1_USERIDISEMAIL | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_ADDSEARCHRES; // | PF1_VISLIST | PF1_INVISLIST;
+		DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ | /*PF1_ADDED |*/ PF1_BASICSEARCH | PF1_USERIDISEMAIL | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_ADDSEARCHRES; // | PF1_VISLIST | PF1_INVISLIST;
 		
 		if ( getByte( FACEBOOK_KEY_SET_MIRANDA_STATUS, 0 ) )
 			return flags |= PF1_MODEMSG;
@@ -262,7 +261,7 @@ HANDLE FacebookProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
 	fbu.real_name += " ";
 	fbu.real_name += surname;
 
-	HANDLE hContact = AddToContactList(&fbu, false, fbu.real_name.c_str());
+	HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE, false, fbu.real_name.c_str());
 	if (hContact) {
 		if (flags & PALF_TEMPORARY)
 		{
@@ -285,7 +284,18 @@ HANDLE FacebookProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
 
 int FacebookProto::AuthRequest(HANDLE hContact,const PROTOCHAR *message)
 {
-	return AddFriend((WPARAM)hContact, NULL);
+	return RequestFriendship((WPARAM)hContact, NULL);
+}
+
+int FacebookProto::Authorize(HANDLE hContact)
+{
+	return ApproveFriendship((WPARAM)hContact, NULL);
+}
+
+int FacebookProto::AuthDeny(HANDLE hContact,const PROTOCHAR *reason)
+{
+	// TODO: hide from facebook requests list
+	return 0;
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -378,18 +388,6 @@ int FacebookProto::OnPreShutdown(WPARAM wParam,LPARAM lParam)
 	return 0;
 }
 
-int FacebookProto::OnPrebuildContactMenu(WPARAM wParam,LPARAM lParam)
-{
-	HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
-	if(IsMyContact(hContact/*, true*/)) {
-		bool hide = (DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, 0)
-			|| DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) );
-		ShowContactMenus(true, hide);
-	}
-
-	return 0;
-}
-
 int FacebookProto::OnOptionsInit(WPARAM wParam,LPARAM lParam)
 {
 	OPTIONSDIALOGPAGE odp = {sizeof(odp)};
@@ -422,55 +420,6 @@ int FacebookProto::OnOptionsInit(WPARAM wParam,LPARAM lParam)
 	return 0;
 }
 
-int FacebookProto::OnBuildStatusMenu(WPARAM wParam,LPARAM lParam)
-{
-	char text[200];
-	strcpy(text,m_szModuleName);
-	char *tDest = text+strlen(text);
-
-	HGENMENU hRoot;
-	CLISTMENUITEM mi = {sizeof(mi)};
-	mi.pszService = text;
-
-	hRoot = MO_GetProtoRootMenu(m_szModuleName);
-	if (hRoot == NULL)
-	{
-		mi.popupPosition = 500085000;
-		mi.hParentMenu = HGENMENU_ROOT;
-		mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED | ( this->isOnline() ? 0 : CMIF_GRAYED );
-		mi.icolibItem = GetIconHandle( "facebook" );
-		mi.ptszName = m_tszUserName;
-		hRoot = m_hMenuRoot = reinterpret_cast<HGENMENU>( CallService(
-			MS_CLIST_ADDPROTOMENUITEM,0,reinterpret_cast<LPARAM>(&mi)) );
-	} else {
-		if ( m_hMenuRoot )
-			CallService( MS_CLIST_REMOVEMAINMENUITEM, ( WPARAM )m_hMenuRoot, 0 );
-		m_hMenuRoot = NULL;
-	}
-
-	mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP | ( this->isOnline() ? 0 : CMIF_GRAYED );
-	mi.position = 201001;
-
-	CreateProtoService(m_szModuleName,"/Mind",&FacebookProto::OnMind,this);
-	strcpy(tDest,"/Mind");
-	mi.hParentMenu = hRoot;
-	mi.pszName = LPGEN("Mind...");
-	mi.icolibItem = GetIconHandle("mind");
-	m_hStatusMind = reinterpret_cast<HGENMENU>( CallService(
-		MS_CLIST_ADDPROTOMENUITEM,0,reinterpret_cast<LPARAM>(&mi)) );
-
-	CreateProtoService(m_szModuleName,"/VisitProfile",&FacebookProto::VisitProfile,this);
-	strcpy(tDest,"/VisitProfile");
-	mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP;
-	mi.pszName = LPGEN("Visit Profile");
-	mi.icolibItem = GetIconHandle("homepage");
-	// TODO RM: remember and properly free in destructor?
-	/*m_hStatusMind = */reinterpret_cast<HGENMENU>( CallService(
-		MS_CLIST_ADDPROTOMENUITEM,0,reinterpret_cast<LPARAM>(&mi)) );
-
-	return 0;
-}
-
 int FacebookProto::OnMind(WPARAM,LPARAM)
 {
 	HWND hDlg = CreateDialogParam( g_hInstance, MAKEINTRESOURCE( IDD_MIND ),
@@ -488,57 +437,75 @@ int FacebookProto::VisitProfile(WPARAM wParam,LPARAM lParam)
 	{
 		CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(dbv.pszVal));
 		DBFreeVariant(&dbv);
+	}
+	else if (DBGetContactSettingByte(hContact,m_szModuleName,"ChatRoom",0))
+	{
+		std::string url = FACEBOOK_URL_GROUP;
+		if (!DBGetContactSettingString(hContact,m_szModuleName,"ChatRoomID",&dbv)) {
+			url += dbv.pszVal;
+			DBFreeVariant(&dbv);
+		}
+		CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(url.c_str()));
 	} else {
-		// self contact, probably
 		// TODO: why isn't wParam == 0 when is status menu moved to main menu?
 		CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(FACEBOOK_URL_PROFILE));
-		return 0;
 	}
 
 	return 0;
 }
 
-int FacebookProto::RemoveFriend(WPARAM wParam,LPARAM lParam)
+int FacebookProto::CancelFriendship(WPARAM wParam,LPARAM lParam)
 {
-	if (wParam == NULL)
-	{ // self contact
-	//	CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(FACEBOOK_URL_PROFILE));
-		return 0;
-	}
-	
-	if (isOffline())
+	if (wParam == NULL || isOffline())
 		return 0;
 
-	if (MessageBox( 0, TranslateT("Are you sure?"), TranslateT("Delete contact from server list"), MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 ) != IDYES)
-		return 0;
+	bool deleting = (lParam == 1);
 
 	HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
 
-	DBVARIANT dbv;			
-	if( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv) )
-	{
-		if (!isOffline()) {
+	// Ignore groupchats and, if deleting, also not-friends
+	if (DBGetContactSettingByte(hContact, m_szModuleName, "ChatRoom", 0)
+		|| (deleting && DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND))
+		return 0;
+
+	DBVARIANT dbv;
+	char str[256];
+
+	if ( !DBGetContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv) ) {
+		mir_snprintf(str,SIZEOF(str),Translate("Do you want to cancel your friendship with '%s'?"), dbv.pszVal);
+		DBFreeVariant(&dbv);
+	} else if( !DBGetContactSettingUTF8String(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv) ) {
+		mir_snprintf(str,SIZEOF(str),Translate("Do you want to cancel your friendship with '%s'?"), dbv.pszVal);
+		DBFreeVariant(&dbv);
+	}
+
+	TCHAR *text = mir_a2t_cp(str, CP_UTF8);
+	if (MessageBox( 0, text, m_tszUserName, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 ) == IDYES) {
+		
+		if( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv) )
+		{
 			std::string* id = new std::string(dbv.pszVal);
+
+			if (deleting) {
+				facebook_user* fbu = facy.buddies.find( (*id) );
+				if (fbu != NULL) {
+					fbu->handle = NULL;
+				}
+			}
+
 			ForkThread( &FacebookProto::DeleteContactFromServer, this, ( void* )id );
 			DBFreeVariant(&dbv);
-
-			if ( !DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, 0) )
-				DBWriteContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, ::time(NULL));
 		}
+				
 	}
+	mir_free(text);
 
 	return 0;
 }
 
-int FacebookProto::AddFriend(WPARAM wParam,LPARAM lParam)
+int FacebookProto::RequestFriendship(WPARAM wParam,LPARAM lParam)
 {
-	if (wParam == NULL)
-	{ // self contact
-//		CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(FACEBOOK_URL_PROFILE));
-		return 0;
-	}
-
-	if (isOffline())
+	if (wParam == NULL || isOffline())
 		return 0;
 
 	HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
@@ -546,25 +513,17 @@ int FacebookProto::AddFriend(WPARAM wParam,LPARAM lParam)
 	DBVARIANT dbv;
 	if( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv) )
 	{
-		if (!isOffline()) {
-			std::string* id = new std::string(dbv.pszVal);
-			ForkThread( &FacebookProto::AddContactToServer, this, ( void* )id );
-			DBFreeVariant(&dbv);
-		}
+		std::string* id = new std::string(dbv.pszVal);
+		ForkThread( &FacebookProto::AddContactToServer, this, ( void* )id );
+		DBFreeVariant(&dbv);
 	}
 
 	return 0;
 }
 
-int FacebookProto::ApproveFriend(WPARAM wParam,LPARAM lParam)
+int FacebookProto::ApproveFriendship(WPARAM wParam,LPARAM lParam)
 {
-	if (wParam == NULL)
-	{ // self contact
-//		CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(FACEBOOK_URL_PROFILE));
-		return 0;
-	}
-
-	if (isOffline())
+	if (wParam == NULL || isOffline())
 		return 0;
 
 	HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam));
@@ -572,13 +531,3 @@ int FacebookProto::ApproveFriend(WPARAM wParam,LPARAM lParam)
 
 	return 0;
 }
-
-void FacebookProto::ToggleStatusMenuItems( BOOL bEnable )
-{
-	CLISTMENUITEM clmi = { 0 };
-	clmi.cbSize = sizeof( CLISTMENUITEM );
-	clmi.flags = CMIM_FLAGS | (( bEnable ) ? 0 : CMIF_GRAYED);
-
-	CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuRoot,   ( LPARAM )&clmi );
-	CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hStatusMind, ( LPARAM )&clmi );
-}
diff --git a/protocols/FacebookRM/proto.h b/protocols/FacebookRM/proto.h
index bacdc5c109..d4966874ce 100644
--- a/protocols/FacebookRM/proto.h
+++ b/protocols/FacebookRM/proto.h
@@ -116,9 +116,9 @@ public:
 	int  __cdecl GetAvatarInfo(WPARAM, LPARAM );
 	int  __cdecl GetAvatarCaps(WPARAM, LPARAM );
 	int  __cdecl VisitProfile(WPARAM, LPARAM );
-	int  __cdecl RemoveFriend(WPARAM, LPARAM );
-	int  __cdecl AddFriend(WPARAM, LPARAM );
-	int  __cdecl ApproveFriend(WPARAM, LPARAM );
+	int  __cdecl CancelFriendship(WPARAM, LPARAM );
+	int  __cdecl RequestFriendship(WPARAM, LPARAM );
+	int  __cdecl ApproveFriendship(WPARAM, LPARAM );
 
 	// Events
 	int  __cdecl OnModulesLoaded(WPARAM, LPARAM);
@@ -166,7 +166,7 @@ public:
 	// Contacts handling
 	bool    IsMyContact(HANDLE, bool include_chat = false);
 	HANDLE  ContactIDToHContact(std::string);
-	HANDLE  AddToContactList(facebook_user*, bool dont_check = false, const char *new_name = "");
+	HANDLE  AddToContactList(facebook_user*, BYTE type, bool dont_check = false, const char *new_name = "");
 	void    SetAllContactStatuses(int);	
 
 	// Chats handling
@@ -195,7 +195,6 @@ public:
 	HANDLE  avatar_lock_;
 	HANDLE  log_lock_;
 	HANDLE  update_loop_lock_;
-	//HANDLE  message_loop_lock_;
 
 	HANDLE  m_hNetlibUser;
 
diff --git a/protocols/FacebookRM/resource.h b/protocols/FacebookRM/resource.h
index 14ff2d1e6b..887119bd4b 100644
--- a/protocols/FacebookRM/resource.h
+++ b/protocols/FacebookRM/resource.h
@@ -4,8 +4,9 @@
 //
 #define IDI_FACEBOOK                    101
 #define IDI_MIND                        102
-#define IDI_ADDFRIEND                   103
-#define IDI_REMOVEFRIEND                104
+#define IDI_AUTH_GRANT                  103
+#define IDI_AUTH_ASK                    104
+#define IDI_AUTH_REVOKE                 105
 #define IDD_FACEBOOKACCOUNT             111
 #define IDD_MIND                        112
 #define IDD_OPTIONS                     113
@@ -61,7 +62,7 @@
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        121
+#define _APS_NEXT_RESOURCE_VALUE        126
 #define _APS_NEXT_COMMAND_VALUE         40001
 #define _APS_NEXT_CONTROL_VALUE         1202
 #define _APS_NEXT_SYMED_VALUE           131
diff --git a/protocols/FacebookRM/stubs.cpp b/protocols/FacebookRM/stubs.cpp
index 2bbb2ffb3b..6237a39129 100644
--- a/protocols/FacebookRM/stubs.cpp
+++ b/protocols/FacebookRM/stubs.cpp
@@ -27,16 +27,6 @@ HANDLE FacebookProto::AddToListByEvent(int flags,int iContact,HANDLE hDbEvent)
 	return 0;
 }
 
-int FacebookProto::Authorize(HANDLE hContact)
-{
-	return 0;
-}
-
-int FacebookProto::AuthDeny(HANDLE hContact,const PROTOCHAR *reason)
-{
-	return 0;
-}
-
 int FacebookProto::AuthRecv(HANDLE hContact,PROTORECVEVENT *)
 {
 	return 0;
diff --git a/protocols/FacebookRM/theme.cpp b/protocols/FacebookRM/theme.cpp
index ddf612e4d6..fd8d0be032 100644
--- a/protocols/FacebookRM/theme.cpp
+++ b/protocols/FacebookRM/theme.cpp
@@ -33,13 +33,15 @@ struct
 }
 static const icons[] =
 {
-	{ "facebook",		LPGEN("Facebook Icon"),			IDI_FACEBOOK },
-	{ "mind",			LPGEN("Mind"),					IDI_MIND },
-	{ "removeFriend",	LPGEN("Remove from server"),	IDI_REMOVEFRIEND },
-	{ "addFriend",		LPGEN("Request friendship"),	IDI_ADDFRIEND },
-	{ "approveFriend",	LPGEN("Approve friendship"),	0, "core_main_8" }, // TODO: add better icon
+	{ "facebook",		LPGEN("Facebook Icon"),				IDI_FACEBOOK },
+	{ "mind",			LPGEN("Mind"),						IDI_MIND },
 	
-	{ "homepage",		LPGEN("Visit Profile"),	0, "core_main_2" },
+	{ "authRevoke",		LPGEN("Cancel friendship"),			IDI_AUTH_REVOKE },
+	//{ "authRevokeReq",	LPGEN("Cancel friendship request"),	IDI_AUTH_REVOKE },
+	{ "authAsk",		LPGEN("Request friendship"),		IDI_AUTH_ASK },
+	{ "authGrant",		LPGEN("Approve friendship"),		IDI_AUTH_GRANT },
+	
+	{ "homepage",		LPGEN("Visit Profile"),				0, "core_main_2" },
 };
 
 static HANDLE hIconLibItem[SIZEOF(icons)];
@@ -107,7 +109,9 @@ char *GetIconDescription(const char* name)
 }
 
 // Contact List menu stuff
-HANDLE g_hMenuItems[5];
+HANDLE hHookPreBuildMenu;
+HANDLE g_hContactMenuItems[CMITEMS_COUNT];
+HANDLE g_hContactMenuSvc[CMITEMS_COUNT];
 
 // Helper functions
 static FacebookProto * GetInstanceByHContact(HANDLE hContact)
@@ -133,13 +137,15 @@ INT_PTR GlobalService(WPARAM wParam,LPARAM lParam)
 
 static int PrebuildContactMenu(WPARAM wParam,LPARAM lParam)
 {
-	ShowContactMenus(false);
+	for (size_t i=0; i<SIZEOF(g_hContactMenuItems); i++)
+	{
+		EnableMenuItem(g_hContactMenuItems[i], false);
+	}
 
 	FacebookProto *proto = GetInstanceByHContact(reinterpret_cast<HANDLE>(wParam));
 	return proto ? proto->OnPrebuildContactMenu(wParam,lParam) : 0;
 }
 
-HANDLE hHookPreBuildMenu, sVisitProfile, sAddFriend, sRemoveFriend, sApproveFriend;
 void InitContactMenus()
 {
 	hHookPreBuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU,PrebuildContactMenu);
@@ -151,56 +157,141 @@ void InitContactMenus()
 	mi.icolibItem = GetIconHandle("homepage");
 	mi.pszName = GetIconDescription("homepage");
 	mi.pszService = "FacebookProto/VisitProfile";
-	sVisitProfile = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::VisitProfile>);
-	g_hMenuItems[1] = reinterpret_cast<HANDLE>(
+	g_hContactMenuSvc[CMI_VISIT_PROFILE] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::VisitProfile>);
+	g_hContactMenuItems[CMI_VISIT_PROFILE] = reinterpret_cast<HANDLE>(
 		CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi) );
 
-	mi.position=-2000006000;
-	mi.icolibItem = GetIconHandle("removeFriend");
-	mi.pszName = GetIconDescription("removeFriend");
-	mi.pszService = "FacebookProto/RemoveFriend";
-	sRemoveFriend = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::RemoveFriend>);
-	g_hMenuItems[2] = reinterpret_cast<HANDLE>(
+	mi.position=-2000006001;
+	mi.icolibItem = GetIconHandle("authRevoke");
+	mi.pszName = GetIconDescription("authRevoke");
+	mi.pszService = "FacebookProto/CancelFriendship";
+	g_hContactMenuSvc[CMI_AUTH_REVOKE] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::CancelFriendship>);
+	g_hContactMenuItems[CMI_AUTH_REVOKE] = reinterpret_cast<HANDLE>(
 		CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi) );
 
-	mi.position=-2000006000;
-	mi.icolibItem = GetIconHandle("addFriend");
-	mi.pszName = GetIconDescription("addFriend");
-	mi.pszService = "FacebookProto/AddFriend";
-	sAddFriend = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::AddFriend>);
-	g_hMenuItems[3] = reinterpret_cast<HANDLE>(
+	/* mi.position=-2000006001;
+	mi.icolibItem = GetIconHandle("authRevokeReq");
+	mi.pszName = GetIconDescription("authRevokeReq");
+	mi.pszService = "FacebookProto/CancelFriendshipRequest";
+	g_hContactMenuSvc[CMI_AUTH_REVOKE_REQ] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::CancelFriendshipRequest>);
+	g_hContactMenuItems[CMI_AUTH_REVOKE_REQ] = reinterpret_cast<HANDLE>(
+		CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi) ); */
+
+	mi.position=-2000006002;
+	mi.icolibItem = GetIconHandle("authAsk");
+	mi.pszName = GetIconDescription("authAsk");
+	mi.pszService = "FacebookProto/RequestFriendship";
+	g_hContactMenuSvc[CMI_AUTH_ASK] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::RequestFriendship>);
+	g_hContactMenuItems[CMI_AUTH_ASK] = reinterpret_cast<HANDLE>(
 		CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi) );
 
-	mi.position=-2000006000;
-	mi.icolibItem = GetIconHandle("approveFriend");
-	mi.pszName = GetIconDescription("approveFriend");
-	mi.pszService = "FacebookProto/ApproveFriend";
-	sApproveFriend = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::ApproveFriend>);
-	g_hMenuItems[4] = reinterpret_cast<HANDLE>(
+	mi.position=-2000006003;
+	mi.icolibItem = GetIconHandle("authGrant");
+	mi.pszName = GetIconDescription("authGrant");
+	mi.pszService = "FacebookProto/ApproveFriendship";
+	g_hContactMenuSvc[CMI_AUTH_GRANT] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::ApproveFriendship>);
+	g_hContactMenuItems[CMI_AUTH_GRANT] = reinterpret_cast<HANDLE>(
 		CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&mi) );
 }
 
 void UninitContactMenus()
 {
-	for(size_t i=0; i<SIZEOF(g_hMenuItems); i++)
-		CallService(MS_CLIST_REMOVECONTACTMENUITEM,(WPARAM)g_hMenuItems[i],0);
+	for(size_t i=0; i<SIZEOF(g_hContactMenuItems); i++)
+		CallService(MS_CLIST_REMOVECONTACTMENUITEM,(WPARAM)g_hContactMenuItems[i],0);
+
+	for(size_t i=0; i<SIZEOF(g_hContactMenuSvc); i++)
+		DestroyServiceFunction(g_hContactMenuSvc[i]);
+	
 	UnhookEvent(hHookPreBuildMenu);
-	DestroyServiceFunction(sVisitProfile);
-	DestroyServiceFunction(sRemoveFriend);
-	DestroyServiceFunction(sAddFriend);
-	DestroyServiceFunction(sApproveFriend);
 }
 
-void ShowContactMenus(bool show, bool deleted)
+void EnableMenuItem(HANDLE hMenuItem, bool enable)
 {
-	for(size_t i=0; i<SIZEOF(g_hMenuItems); i++)
+	CLISTMENUITEM clmi = {0};
+	clmi.cbSize = sizeof(CLISTMENUITEM);
+	clmi.flags = CMIM_FLAGS;
+	if (!enable)
+		clmi.flags |= CMIF_HIDDEN;
+
+	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItem, (LPARAM)&clmi);
+}
+
+int FacebookProto::OnPrebuildContactMenu(WPARAM wParam,LPARAM lParam)
+{	
+	HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+
+	EnableMenuItem(g_hContactMenuItems[CMI_VISIT_PROFILE], true);
+
+	if (!isOffline() && !DBGetContactSettingByte(hContact, m_szModuleName, "ChatRoom", 0))
 	{
-		CLISTMENUITEM item = { sizeof(item) };
-		item.flags = CMIM_FLAGS;
-		if(!show || (i == 3 && !deleted) || (i == 2 && deleted) || (i == 4 && !deleted)) // 2 = REMOVE CONTACT; 3 = ADD CONTACT; 4 = APPROVE CONTACT
-			item.flags |= CMIF_HIDDEN;
+		bool ctrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
+
+		BYTE type = DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0);
+
+		EnableMenuItem(g_hContactMenuItems[CMI_AUTH_ASK], ctrlPressed || type == FACEBOOK_CONTACT_NONE || !type);
+		EnableMenuItem(g_hContactMenuItems[CMI_AUTH_GRANT], ctrlPressed || type == FACEBOOK_CONTACT_APPROVE);
+		EnableMenuItem(g_hContactMenuItems[CMI_AUTH_REVOKE], ctrlPressed || type == FACEBOOK_CONTACT_FRIEND);
+		//EnableMenuItem(g_hContactMenuItems[CMI_AUTH_CANCEL], ctrlPressed || type == FACEBOOK_CONTACT_REQUEST);
+	}
 
-		CallService(MS_CLIST_MODIFYMENUITEM,reinterpret_cast<WPARAM>(g_hMenuItems[i]),
-			reinterpret_cast<LPARAM>(&item));
+	return 0;
+}
+
+int FacebookProto::OnBuildStatusMenu(WPARAM wParam,LPARAM lParam)
+{
+	char text[200];
+	strcpy(text,m_szModuleName);
+	char *tDest = text+strlen(text);
+
+	HGENMENU hRoot;
+	CLISTMENUITEM mi = {sizeof(mi)};
+	mi.pszService = text;
+
+	hRoot = MO_GetProtoRootMenu(m_szModuleName);
+	if (hRoot == NULL)
+	{
+		mi.popupPosition = 500085000;
+		mi.hParentMenu = HGENMENU_ROOT;
+		mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED | ( this->isOnline() ? 0 : CMIF_GRAYED );
+		mi.icolibItem = GetIconHandle( "facebook" );
+		mi.ptszName = m_tszUserName;
+		hRoot = m_hMenuRoot = reinterpret_cast<HGENMENU>( CallService(
+			MS_CLIST_ADDPROTOMENUITEM,0,reinterpret_cast<LPARAM>(&mi)) );
+	} else {
+		if ( m_hMenuRoot )
+			CallService( MS_CLIST_REMOVEMAINMENUITEM, ( WPARAM )m_hMenuRoot, 0 );
+		m_hMenuRoot = NULL;
 	}
+
+	mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP | ( this->isOnline() ? 0 : CMIF_GRAYED );
+	mi.position = 201001;
+
+	CreateProtoService(m_szModuleName,"/Mind",&FacebookProto::OnMind,this);
+	strcpy(tDest,"/Mind");
+	mi.hParentMenu = hRoot;
+	mi.pszName = LPGEN("Mind...");
+	mi.icolibItem = GetIconHandle("mind");
+	m_hStatusMind = reinterpret_cast<HGENMENU>( CallService(
+		MS_CLIST_ADDPROTOMENUITEM,0,reinterpret_cast<LPARAM>(&mi)) );
+
+	CreateProtoService(m_szModuleName,"/VisitProfile",&FacebookProto::VisitProfile,this);
+	strcpy(tDest,"/VisitProfile");
+	mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP;
+	mi.pszName = LPGEN("Visit Profile");
+	mi.icolibItem = GetIconHandle("homepage");
+	// TODO RM: remember and properly free in destructor?
+	/*m_hStatusMind = */reinterpret_cast<HGENMENU>( CallService(
+		MS_CLIST_ADDPROTOMENUITEM,0,reinterpret_cast<LPARAM>(&mi)) );
+
+	return 0;
+}
+
+void FacebookProto::ToggleStatusMenuItems( BOOL bEnable )
+{
+	CLISTMENUITEM clmi = { 0 };
+	clmi.cbSize = sizeof( CLISTMENUITEM );
+	clmi.flags = CMIM_FLAGS | (( bEnable ) ? 0 : CMIF_GRAYED);
+
+	CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuRoot,   ( LPARAM )&clmi );
+	CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hStatusMind, ( LPARAM )&clmi );
 }
diff --git a/protocols/FacebookRM/theme.h b/protocols/FacebookRM/theme.h
index 4330c35dce..e6bf689676 100644
--- a/protocols/FacebookRM/theme.h
+++ b/protocols/FacebookRM/theme.h
@@ -27,4 +27,12 @@ HANDLE GetIconHandle(const char *name);
 
 void InitContactMenus(void);
 void UninitContactMenus(void);
-void ShowContactMenus(bool show, bool deleted = false);
+void EnableMenuItem(HANDLE hMenuItem, bool enable);
+
+/* Contact menu item indexes */
+#define CMI_VISIT_PROFILE 0
+#define CMI_AUTH_REVOKE 1
+#define CMI_AUTH_ASK 2
+#define CMI_AUTH_GRANT 3
+
+#define CMITEMS_COUNT 4
\ No newline at end of file
-- 
cgit v1.2.3