", "");
			// Get and strip optional nickname
			std::string::size_type pos = this->self_.real_name.find("
");
			if (pos != std::string::npos) {
				this->self_.nick = utils::text::source_get_value(&this->self_.real_name, 2, "(", ")");
				parent->debugLogA("    Got self nick name: %s", this->self_.nick.c_str());
				this->self_.real_name = this->self_.real_name.substr(0, pos - 1);
			}
			// Another attempt to get optional nickname
			if (this->self_.nick.empty())
				this->self_.nick = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&resp.data, 3, "class=\\\"alternate_name\\\"", ">(", ")\\u003C\\/")));
			this->self_.real_name = utils::text::remove_html(this->self_.real_name);
			parent->debugLogA("    Got self real name (nickname): %s (%s)", this->self_.real_name.c_str(), this->self_.nick.c_str());
			parent->SaveName(0, &this->self_);
			// Get avatar (from touch version)
			if (!touchData.empty())
				this->self_.image_url = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&touchData, 2, "\"pic\":\"", "\"")));
			// Another attempt to get avatar(from mbasic version)
			if (this->self_.image_url.empty())
				this->self_.image_url = utils::text::source_get_value(&resp.data, 3, "id=\"root", " self_.image_url.empty()) {
				this->self_.image_url = utils::text::source_get_value(&resp.data, 3, "id=\"root", "/photo.php?", "\"");
				// Prepare this special url (not direct image url) to be handled correctly in CheckAvatarChange()
				// It must contain "/" at the beginning and also shouldn't contain "?" as parameters after that are stripped
				if (!this->self_.image_url.empty())
					this->self_.image_url = "/" + this->self_.image_url;
			}
			// Final attempt to get avatar as on some pages is only link to photo page and not link to image itself
			if (this->self_.image_url.empty()) {
				http::response resp2 = sendRequest(profilePictureRequest(self_.user_id.c_str()));
				// Get avatar (from mbasic version of photo page)
				this->self_.image_url = utils::text::html_entities_decode(utils::text::source_get_value(&resp2.data, 3, "id=\"root", "
self_.image_url.empty()) {
				this->self_.image_url = utils::text::source_get_value(&resp.data, 3, "id=\"root", "/photo.php?", "\"");
				// Prepare this special url (not direct image url) to be handled correctly in CheckAvatarChange()
				// It must contain "/" at the beginning and also shouldn't contain "?" as parameters after that are stripped
				if (!this->self_.image_url.empty())
					this->self_.image_url = "/" + this->self_.image_url;
			}
			// Final attempt to get avatar as on some pages is only link to photo page and not link to image itself
			if (this->self_.image_url.empty()) {
				http::response resp2 = sendRequest(profilePictureRequest(self_.user_id.c_str()));
				// Get avatar (from mbasic version of photo page)
				this->self_.image_url = utils::text::html_entities_decode(utils::text::source_get_value(&resp2.data, 3, "id=\"root", " self_.image_url.empty())
					this->self_.image_url = utils::text::html_entities_decode(utils::text::source_get_value(&resp2.data, 3, "id=\"root", "background-image: url("", "")"));
				// Sometimes even Facebook doesn't show any picture at all! So just ignore this error in that case...
				if (this->self_.image_url.empty()) {
					parent->debugLogA("!!! Empty avatar even from avatar page. Source code:\n%s", resp2.data.c_str());
					// Set dumb avatar "url" (see how it works in CheckAvatarChange()) so we can't tell if/when the avatar changed, but it will be loaded at least once
					this->self_.image_url = "/NO_AVATAR/";
				}
			}
			parent->debugLogA("    Got self avatar: %s", this->self_.image_url.c_str());
			parent->CheckAvatarChange(0, this->self_.image_url);
			// Get logout hash
			this->logout_hash_ = utils::text::source_get_value2(&resp.data, "/logout.php?h=", "&\"");
			parent->debugLogA("    Got self logout hash: %s", this->logout_hash_.c_str());
			if (this->self_.real_name.empty() || this->self_.image_url.empty() || this->logout_hash_.empty()) {
				parent->debugLogA("!!! Empty nick/avatar/hash. Source code:\n%s", resp.data.c_str());
				client_notify(TranslateT("Could not load all required data. Plugin may still work correctly, but you should report this and wait for plugin update."));
			}
		}
		return handle_success("home");
	case HTTP_CODE_FOUND:
		// Work-around for replica_down, f**king hell what's that?
		parent->debugLogA("!!! REPLICA_DOWN is back in force!");
		return this->home();
	default:
		return handle_error("home", FORCE_QUIT);
	}
}
bool facebook_client::chat_state(bool online)
{
	handle_entry("chat_state");
	http::response resp = sendRequest(setVisibilityRequest(online));
	if (!resp.error_title.empty())
		return handle_error("chat_state");
	return handle_success("chat_state");
}
bool facebook_client::reconnect()
{
	handle_entry("reconnect");
	// Request reconnect
	http::response resp = sendRequest(reconnectRequest());
	switch (resp.code) {
	case HTTP_CODE_OK:
		{
			this->chat_channel_ = utils::text::source_get_value(&resp.data, 2, "\"user_channel\":\"", "\"");
			parent->debugLogA("    Got self channel: %s", this->chat_channel_.c_str());
			this->chat_channel_partition_ = utils::text::source_get_value2(&resp.data, "\"partition\":", ",}");
			parent->debugLogA("    Got self channel partition: %s", this->chat_channel_partition_.c_str());
			this->chat_channel_host_ = utils::text::source_get_value(&resp.data, 2, "\"host\":\"", "\"");
			parent->debugLogA("    Got self channel host: %s", this->chat_channel_host_.c_str());
			this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
			parent->debugLogA("    Got self sequence number: %s", this->chat_sequence_num_.c_str());
			this->chat_conn_num_ = utils::text::source_get_value2(&resp.data, "\"max_conn\":", ",}");
			parent->debugLogA("    Got self max_conn: %s", this->chat_conn_num_.c_str());
			this->chat_sticky_num_ = utils::text::source_get_value(&resp.data, 2, "\"sticky_token\":\"", "\"");
			parent->debugLogA("    Got self sticky_token: %s", this->chat_sticky_num_.c_str());
			//std::string retry_interval = utils::text::source_get_value2(&resp.data, "\"retry_interval\":", ",}");
			//parent->debugLogA("    Got self retry_interval: %s", retry_interval.c_str());
			//std::string visibility = utils::text::source_get_value2(&resp.data, "\"visibility\":", ",}");
			//parent->debugLogA("    Got self visibility: %s", visibility.c_str());
			// Send activity_ping after each reconnect
			activity_ping();
		}
		return handle_success("reconnect");
	default:
		return handle_error("reconnect", FORCE_DISCONNECT);
	}
}
bool facebook_client::channel()
{
	handle_entry("channel");
	// Get updates
	http::response resp = sendRequest(channelRequest(PULL));
	if (resp.data.empty()) // Something went wrong
		return handle_error("channel");
	// Load traceId, if present
	std::string traceId = utils::text::source_get_value(&resp.data, 2, "\"tr\":\"", "\"");
	if (!traceId.empty())
		this->chat_traceid_ = traceId;
	std::string type = utils::text::source_get_value(&resp.data, 2, "\"t\":\"", "\"");
	parent->debugLogA("Pull response type = %s", type.c_str());
	if (type == "continue" || type == "heartbeat") {
		// Everything is OK, no new message received
	}
	else if (type == "lb") {
		// Some new stuff (idk how does it work yet)
		this->chat_sticky_pool_ = utils::text::source_get_value(&resp.data, 2, "\"pool\":\"", "\"");
		parent->debugLogA("    Got self sticky pool: %s", this->chat_sticky_pool_.c_str());
		this->chat_sticky_num_ = utils::text::source_get_value2(&resp.data, "\"sticky\":\"", "\"");
		parent->debugLogA("    Got self sticky number: %s", this->chat_sticky_num_.c_str());
	}
	else if (type == "refresh") {
		// Requested relogin (due to some settings change, removing this session, etc.)
		parent->debugLogA("!!! Requested refresh");
		this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
		parent->debugLogA("    Got self sequence number: %s", this->chat_sequence_num_.c_str());
		this->chat_reconnect_reason_ = utils::text::source_get_value2(&resp.data, "\"reason\":", ",}");
		parent->debugLogA("    Got reconnect reason: %s", this->chat_reconnect_reason_.c_str());
		return this->reconnect();
	}
	else if (!type.empty()) { // for "msg", "fullReload" and maybe also other types
		// Something has been received, throw to new thread to process
		{
			parent->debugLogA("*** Starting processing messages");
			try {
				std::vector messages;
				parent->ParseMessages(resp.data, messages);
				parent->ReceiveMessages(messages);
				if (parent->getBool(FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
					parent->ShowNotifications();
				parent->debugLogA("*** Messages processed");
			}
			catch (const std::exception &e) {
				parent->debugLogA("*** Error processing messages: %s", e.what());
			}
		}
		// Get new sequence number
		std::string seq = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
		parent->debugLogA("    Got self sequence number: %s", seq.c_str());
		if (type == "msg") {
			// Update msgs_recv number for every "msg" type we receive (during fullRefresh/reload responses it stays the same)
			this->chat_msgs_recv_++;
		}
		else if (type == "fullReload") {
			// At fullReload we force our seq number to received value (there may have been some error or something)
			this->chat_sequence_num_ = seq;
		}
		// Check if it's different from our old one (which means we should increment our old one)
		if (seq != this->chat_sequence_num_) {
			// Facebook now often return much bigger number which results in skipping few data requests, so we increment it manually
			// Bigger skips (when there is some problem or something) are handled when fullreload/refresh response type
			int iseq = 0;
			if (utils::conversion::from_string(iseq, this->chat_sequence_num_, std::dec)) {
				// Increment and convert it back to string
				iseq++;
				std::string newSeq = utils::conversion::to_string(&iseq, UTILS_CONV_SIGNED_NUMBER);
				// Check if we have different seq than the one from Facebook
				if (newSeq != seq) {
					parent->debugLogA("!!! Use self incremented sequence number: %s (instead of: %s)", newSeq.c_str(), seq.c_str());
					seq = newSeq;
				}
			}
		}
		this->chat_sequence_num_ = seq;
	}
	else // No type? This shouldn't happen unless there is a big API change.
		return handle_error("channel");
	// Return
	switch (resp.code) {
	case HTTP_CODE_OK:
		return handle_success("channel");
	case HTTP_CODE_GATEWAY_TIMEOUT:
		// Maybe we have same clientid as other connected client, try to generate different one
		this->chat_clientid_ = utils::text::rand_string(8, "0123456789abcdef", &this->random_);
		// Intentionally fall to handle_error() below
	case HTTP_CODE_FAKE_DISCONNECTED:
	case HTTP_CODE_FAKE_ERROR:
	default:
		return handle_error("channel");
	}
}
bool facebook_client::activity_ping()
{
	// Don't send ping when we are not online
	if (parent->m_iStatus != ID_STATUS_ONLINE)
		return true;
	handle_entry("activity_ping");
	http::response resp = sendRequest(channelRequest(PING));
	// Remember this last ping time
	parent->m_pingTS = ::time(0);
	if (resp.data.empty() || resp.data.find("\"t\":\"pong\"") == resp.data.npos) {
		// Something went wrong
		return handle_error("activity_ping");
	}
	return handle_success("activity_ping");
}
int facebook_client::send_message(int seqid, MCONTACT hContact, const std::string &message_text, std::string *error_text, const std::string &captcha_persist_data, const std::string &captcha)
{
	handle_entry("send_message");
	bool isChatRoom = parent->isChatRoom(hContact);
	ptrA userId(parent->getStringA(hContact, FACEBOOK_KEY_ID));
	ptrA threadId(parent->getStringA(hContact, FACEBOOK_KEY_TID));
	// Check if we have userId/threadId to be able to send message
	if ((isChatRoom && (threadId == nullptr || !mir_strcmp(threadId, "null"))) || (!isChatRoom && (userId == nullptr || !mir_strcmp(userId, "null")))) {
		// This shouldn't happen unless user manually deletes some data via Database Editor++
		*error_text = Translate("Contact doesn't have required data in database.");
		handle_error("send_message");
		return SEND_MESSAGE_ERROR;
	}
	// Probably we can generate any random messageID, it just have to be numeric and don't start with "0". We will receive it in response as "client_message_id".
	std::string messageId = utils::text::rand_string(10, "123456789", &this->random_);
	http::response resp;
	{
		mir_cslock s(send_message_lock_);
		resp = sendRequest(sendMessageRequest(userId, threadId, messageId.c_str(), message_text.c_str(), isChatRoom, captcha.c_str(), captcha_persist_data.c_str()));
		*error_text = resp.error_text;
		if (resp.error_number == 0) {
			// Everything is OK
			// Remember this message id
			std::string mid = utils::text::source_get_value(&resp.data, 2, "\"message_id\":\"", "\"");
			if (mid.empty())
				mid = utils::text::source_get_value(&resp.data, 2, "\"mid\":\"", "\""); // TODO: This is probably not used anymore
			// For classic contacts remember last message id
			if (!parent->isChatRoom(hContact))
				parent->setString(hContact, FACEBOOK_KEY_MESSAGE_ID, mid.c_str());
			// Get timestamp
			std::string timestamp = utils::text::source_get_value(&resp.data, 2, "\"timestamp\":", ",");
			time_t time = utils::time::from_string(timestamp);
			// Remember last action timestamp for history sync
			parent->setDword(FACEBOOK_KEY_LAST_ACTION_TS, time);
			// For classic conversation we try to replace timestamp of added event in OnPreCreateEvent()
			if (seqid > 0)
				messages_timestamp.insert(std::make_pair(seqid, time));
			// We have this message in database, so ignore further tries (in channel) to add it again
			messages_ignore.insert(std::make_pair(mid, 0));
		}
	}
	switch (resp.error_number) {
	case 0:
		// Everything is OK
		break;
		// case 1356002: // You are offline (probably you can't use mercury or some other request when chat is offline)
	case 1356003: // Contact is offline
		parent->setWord(hContact, "Status", ID_STATUS_OFFLINE);
		return SEND_MESSAGE_ERROR;
	case 1356026: // Contact has alternative client
		client_notify(TranslateT("Need confirmation for sending messages to other clients.\nOpen Facebook website and try to send message to this contact again!"));
		return SEND_MESSAGE_ERROR;
	case 1357007: // Security check (captcha) is required
		{
			std::string imageUrl = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&resp.data, 3, "img class=\\\"img\\\"", "src=\\\"", "\\\"")));
			std::string captchaPersistData = utils::text::source_get_value(&resp.data, 3, "\\\"captcha_persist_data\\\"", "value=\\\"", "\\\"");
			parent->debugLogA("    Got imageUrl (first): %s", imageUrl.c_str());
			parent->debugLogA("    Got captchaPersistData (first): %s", captchaPersistData.c_str());
			http::response capResp = sendRequest(refreshCaptchaRequest(captchaPersistData.c_str()));
			if (capResp.code == HTTP_CODE_OK) {
				imageUrl = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&capResp.data, 3, "img class=\\\"img\\\"", "src=\\\"", "\\\"")));
				captchaPersistData = utils::text::source_get_value(&capResp.data, 3, "\\\"captcha_persist_data\\\"", "value=\\\"", "\\\"");
				parent->debugLogA("    Got imageUrl (second): %s", imageUrl.c_str());
				parent->debugLogA("    Got captchaPersistData (second): %s", captchaPersistData.c_str());
				std::string result;
				if (!parent->RunCaptchaForm(imageUrl, result)) {
					*error_text = Translate("User cancel captcha challenge.");
					return SEND_MESSAGE_CANCEL;
				}
				return send_message(seqid, hContact, message_text, error_text, captchaPersistData, result);
			}
		}
		return SEND_MESSAGE_CANCEL; // Cancel because we failed to load captcha image so we can't continue only with error
	//case 1404123: // Blocked sending messages (with URLs) because Facebook think our computer is infected with malware
	default: // Other error
		parent->debugLogA("!!! Send message error #%d: %s", resp.error_number, resp.error_text.c_str());
		return SEND_MESSAGE_ERROR;
	}
	switch (resp.code) {
	case HTTP_CODE_OK:
		handle_success("send_message");
		return SEND_MESSAGE_OK;
	case HTTP_CODE_FAKE_ERROR:
	case HTTP_CODE_FAKE_DISCONNECTED:
	default:
		*error_text = Translate("Timeout when sending message.");
		handle_error("send_message");
		return SEND_MESSAGE_ERROR;
	}
}
bool facebook_client::post_status(status_data *status)
{
	if (status == nullptr || (status->text.empty() && status->url.empty()))
		return false;
	handle_entry("post_status");
	if (status->isPage) {
		// Switch to page identity by which name we will share this post
		sendRequest(switchIdentityRequest(status->user_id.c_str()));
	}
	std::string linkData;
	if (!status->url.empty()) {
		http::response resp = sendRequest(linkScraperRequest(status));
		std::string temp = utils::text::html_entities_decode(utils::text::slashu_to_utf8(resp.data));
		std::string form = utils::text::source_get_value(&temp, 2, "
self_.image_url.empty())
					this->self_.image_url = utils::text::html_entities_decode(utils::text::source_get_value(&resp2.data, 3, "id=\"root", "background-image: url("", "")"));
				// Sometimes even Facebook doesn't show any picture at all! So just ignore this error in that case...
				if (this->self_.image_url.empty()) {
					parent->debugLogA("!!! Empty avatar even from avatar page. Source code:\n%s", resp2.data.c_str());
					// Set dumb avatar "url" (see how it works in CheckAvatarChange()) so we can't tell if/when the avatar changed, but it will be loaded at least once
					this->self_.image_url = "/NO_AVATAR/";
				}
			}
			parent->debugLogA("    Got self avatar: %s", this->self_.image_url.c_str());
			parent->CheckAvatarChange(0, this->self_.image_url);
			// Get logout hash
			this->logout_hash_ = utils::text::source_get_value2(&resp.data, "/logout.php?h=", "&\"");
			parent->debugLogA("    Got self logout hash: %s", this->logout_hash_.c_str());
			if (this->self_.real_name.empty() || this->self_.image_url.empty() || this->logout_hash_.empty()) {
				parent->debugLogA("!!! Empty nick/avatar/hash. Source code:\n%s", resp.data.c_str());
				client_notify(TranslateT("Could not load all required data. Plugin may still work correctly, but you should report this and wait for plugin update."));
			}
		}
		return handle_success("home");
	case HTTP_CODE_FOUND:
		// Work-around for replica_down, f**king hell what's that?
		parent->debugLogA("!!! REPLICA_DOWN is back in force!");
		return this->home();
	default:
		return handle_error("home", FORCE_QUIT);
	}
}
bool facebook_client::chat_state(bool online)
{
	handle_entry("chat_state");
	http::response resp = sendRequest(setVisibilityRequest(online));
	if (!resp.error_title.empty())
		return handle_error("chat_state");
	return handle_success("chat_state");
}
bool facebook_client::reconnect()
{
	handle_entry("reconnect");
	// Request reconnect
	http::response resp = sendRequest(reconnectRequest());
	switch (resp.code) {
	case HTTP_CODE_OK:
		{
			this->chat_channel_ = utils::text::source_get_value(&resp.data, 2, "\"user_channel\":\"", "\"");
			parent->debugLogA("    Got self channel: %s", this->chat_channel_.c_str());
			this->chat_channel_partition_ = utils::text::source_get_value2(&resp.data, "\"partition\":", ",}");
			parent->debugLogA("    Got self channel partition: %s", this->chat_channel_partition_.c_str());
			this->chat_channel_host_ = utils::text::source_get_value(&resp.data, 2, "\"host\":\"", "\"");
			parent->debugLogA("    Got self channel host: %s", this->chat_channel_host_.c_str());
			this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
			parent->debugLogA("    Got self sequence number: %s", this->chat_sequence_num_.c_str());
			this->chat_conn_num_ = utils::text::source_get_value2(&resp.data, "\"max_conn\":", ",}");
			parent->debugLogA("    Got self max_conn: %s", this->chat_conn_num_.c_str());
			this->chat_sticky_num_ = utils::text::source_get_value(&resp.data, 2, "\"sticky_token\":\"", "\"");
			parent->debugLogA("    Got self sticky_token: %s", this->chat_sticky_num_.c_str());
			//std::string retry_interval = utils::text::source_get_value2(&resp.data, "\"retry_interval\":", ",}");
			//parent->debugLogA("    Got self retry_interval: %s", retry_interval.c_str());
			//std::string visibility = utils::text::source_get_value2(&resp.data, "\"visibility\":", ",}");
			//parent->debugLogA("    Got self visibility: %s", visibility.c_str());
			// Send activity_ping after each reconnect
			activity_ping();
		}
		return handle_success("reconnect");
	default:
		return handle_error("reconnect", FORCE_DISCONNECT);
	}
}
bool facebook_client::channel()
{
	handle_entry("channel");
	// Get updates
	http::response resp = sendRequest(channelRequest(PULL));
	if (resp.data.empty()) // Something went wrong
		return handle_error("channel");
	// Load traceId, if present
	std::string traceId = utils::text::source_get_value(&resp.data, 2, "\"tr\":\"", "\"");
	if (!traceId.empty())
		this->chat_traceid_ = traceId;
	std::string type = utils::text::source_get_value(&resp.data, 2, "\"t\":\"", "\"");
	parent->debugLogA("Pull response type = %s", type.c_str());
	if (type == "continue" || type == "heartbeat") {
		// Everything is OK, no new message received
	}
	else if (type == "lb") {
		// Some new stuff (idk how does it work yet)
		this->chat_sticky_pool_ = utils::text::source_get_value(&resp.data, 2, "\"pool\":\"", "\"");
		parent->debugLogA("    Got self sticky pool: %s", this->chat_sticky_pool_.c_str());
		this->chat_sticky_num_ = utils::text::source_get_value2(&resp.data, "\"sticky\":\"", "\"");
		parent->debugLogA("    Got self sticky number: %s", this->chat_sticky_num_.c_str());
	}
	else if (type == "refresh") {
		// Requested relogin (due to some settings change, removing this session, etc.)
		parent->debugLogA("!!! Requested refresh");
		this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
		parent->debugLogA("    Got self sequence number: %s", this->chat_sequence_num_.c_str());
		this->chat_reconnect_reason_ = utils::text::source_get_value2(&resp.data, "\"reason\":", ",}");
		parent->debugLogA("    Got reconnect reason: %s", this->chat_reconnect_reason_.c_str());
		return this->reconnect();
	}
	else if (!type.empty()) { // for "msg", "fullReload" and maybe also other types
		// Something has been received, throw to new thread to process
		{
			parent->debugLogA("*** Starting processing messages");
			try {
				std::vector messages;
				parent->ParseMessages(resp.data, messages);
				parent->ReceiveMessages(messages);
				if (parent->getBool(FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
					parent->ShowNotifications();
				parent->debugLogA("*** Messages processed");
			}
			catch (const std::exception &e) {
				parent->debugLogA("*** Error processing messages: %s", e.what());
			}
		}
		// Get new sequence number
		std::string seq = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
		parent->debugLogA("    Got self sequence number: %s", seq.c_str());
		if (type == "msg") {
			// Update msgs_recv number for every "msg" type we receive (during fullRefresh/reload responses it stays the same)
			this->chat_msgs_recv_++;
		}
		else if (type == "fullReload") {
			// At fullReload we force our seq number to received value (there may have been some error or something)
			this->chat_sequence_num_ = seq;
		}
		// Check if it's different from our old one (which means we should increment our old one)
		if (seq != this->chat_sequence_num_) {
			// Facebook now often return much bigger number which results in skipping few data requests, so we increment it manually
			// Bigger skips (when there is some problem or something) are handled when fullreload/refresh response type
			int iseq = 0;
			if (utils::conversion::from_string(iseq, this->chat_sequence_num_, std::dec)) {
				// Increment and convert it back to string
				iseq++;
				std::string newSeq = utils::conversion::to_string(&iseq, UTILS_CONV_SIGNED_NUMBER);
				// Check if we have different seq than the one from Facebook
				if (newSeq != seq) {
					parent->debugLogA("!!! Use self incremented sequence number: %s (instead of: %s)", newSeq.c_str(), seq.c_str());
					seq = newSeq;
				}
			}
		}
		this->chat_sequence_num_ = seq;
	}
	else // No type? This shouldn't happen unless there is a big API change.
		return handle_error("channel");
	// Return
	switch (resp.code) {
	case HTTP_CODE_OK:
		return handle_success("channel");
	case HTTP_CODE_GATEWAY_TIMEOUT:
		// Maybe we have same clientid as other connected client, try to generate different one
		this->chat_clientid_ = utils::text::rand_string(8, "0123456789abcdef", &this->random_);
		// Intentionally fall to handle_error() below
	case HTTP_CODE_FAKE_DISCONNECTED:
	case HTTP_CODE_FAKE_ERROR:
	default:
		return handle_error("channel");
	}
}
bool facebook_client::activity_ping()
{
	// Don't send ping when we are not online
	if (parent->m_iStatus != ID_STATUS_ONLINE)
		return true;
	handle_entry("activity_ping");
	http::response resp = sendRequest(channelRequest(PING));
	// Remember this last ping time
	parent->m_pingTS = ::time(0);
	if (resp.data.empty() || resp.data.find("\"t\":\"pong\"") == resp.data.npos) {
		// Something went wrong
		return handle_error("activity_ping");
	}
	return handle_success("activity_ping");
}
int facebook_client::send_message(int seqid, MCONTACT hContact, const std::string &message_text, std::string *error_text, const std::string &captcha_persist_data, const std::string &captcha)
{
	handle_entry("send_message");
	bool isChatRoom = parent->isChatRoom(hContact);
	ptrA userId(parent->getStringA(hContact, FACEBOOK_KEY_ID));
	ptrA threadId(parent->getStringA(hContact, FACEBOOK_KEY_TID));
	// Check if we have userId/threadId to be able to send message
	if ((isChatRoom && (threadId == nullptr || !mir_strcmp(threadId, "null"))) || (!isChatRoom && (userId == nullptr || !mir_strcmp(userId, "null")))) {
		// This shouldn't happen unless user manually deletes some data via Database Editor++
		*error_text = Translate("Contact doesn't have required data in database.");
		handle_error("send_message");
		return SEND_MESSAGE_ERROR;
	}
	// Probably we can generate any random messageID, it just have to be numeric and don't start with "0". We will receive it in response as "client_message_id".
	std::string messageId = utils::text::rand_string(10, "123456789", &this->random_);
	http::response resp;
	{
		mir_cslock s(send_message_lock_);
		resp = sendRequest(sendMessageRequest(userId, threadId, messageId.c_str(), message_text.c_str(), isChatRoom, captcha.c_str(), captcha_persist_data.c_str()));
		*error_text = resp.error_text;
		if (resp.error_number == 0) {
			// Everything is OK
			// Remember this message id
			std::string mid = utils::text::source_get_value(&resp.data, 2, "\"message_id\":\"", "\"");
			if (mid.empty())
				mid = utils::text::source_get_value(&resp.data, 2, "\"mid\":\"", "\""); // TODO: This is probably not used anymore
			// For classic contacts remember last message id
			if (!parent->isChatRoom(hContact))
				parent->setString(hContact, FACEBOOK_KEY_MESSAGE_ID, mid.c_str());
			// Get timestamp
			std::string timestamp = utils::text::source_get_value(&resp.data, 2, "\"timestamp\":", ",");
			time_t time = utils::time::from_string(timestamp);
			// Remember last action timestamp for history sync
			parent->setDword(FACEBOOK_KEY_LAST_ACTION_TS, time);
			// For classic conversation we try to replace timestamp of added event in OnPreCreateEvent()
			if (seqid > 0)
				messages_timestamp.insert(std::make_pair(seqid, time));
			// We have this message in database, so ignore further tries (in channel) to add it again
			messages_ignore.insert(std::make_pair(mid, 0));
		}
	}
	switch (resp.error_number) {
	case 0:
		// Everything is OK
		break;
		// case 1356002: // You are offline (probably you can't use mercury or some other request when chat is offline)
	case 1356003: // Contact is offline
		parent->setWord(hContact, "Status", ID_STATUS_OFFLINE);
		return SEND_MESSAGE_ERROR;
	case 1356026: // Contact has alternative client
		client_notify(TranslateT("Need confirmation for sending messages to other clients.\nOpen Facebook website and try to send message to this contact again!"));
		return SEND_MESSAGE_ERROR;
	case 1357007: // Security check (captcha) is required
		{
			std::string imageUrl = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&resp.data, 3, "img class=\\\"img\\\"", "src=\\\"", "\\\"")));
			std::string captchaPersistData = utils::text::source_get_value(&resp.data, 3, "\\\"captcha_persist_data\\\"", "value=\\\"", "\\\"");
			parent->debugLogA("    Got imageUrl (first): %s", imageUrl.c_str());
			parent->debugLogA("    Got captchaPersistData (first): %s", captchaPersistData.c_str());
			http::response capResp = sendRequest(refreshCaptchaRequest(captchaPersistData.c_str()));
			if (capResp.code == HTTP_CODE_OK) {
				imageUrl = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&capResp.data, 3, "img class=\\\"img\\\"", "src=\\\"", "\\\"")));
				captchaPersistData = utils::text::source_get_value(&capResp.data, 3, "\\\"captcha_persist_data\\\"", "value=\\\"", "\\\"");
				parent->debugLogA("    Got imageUrl (second): %s", imageUrl.c_str());
				parent->debugLogA("    Got captchaPersistData (second): %s", captchaPersistData.c_str());
				std::string result;
				if (!parent->RunCaptchaForm(imageUrl, result)) {
					*error_text = Translate("User cancel captcha challenge.");
					return SEND_MESSAGE_CANCEL;
				}
				return send_message(seqid, hContact, message_text, error_text, captchaPersistData, result);
			}
		}
		return SEND_MESSAGE_CANCEL; // Cancel because we failed to load captcha image so we can't continue only with error
	//case 1404123: // Blocked sending messages (with URLs) because Facebook think our computer is infected with malware
	default: // Other error
		parent->debugLogA("!!! Send message error #%d: %s", resp.error_number, resp.error_text.c_str());
		return SEND_MESSAGE_ERROR;
	}
	switch (resp.code) {
	case HTTP_CODE_OK:
		handle_success("send_message");
		return SEND_MESSAGE_OK;
	case HTTP_CODE_FAKE_ERROR:
	case HTTP_CODE_FAKE_DISCONNECTED:
	default:
		*error_text = Translate("Timeout when sending message.");
		handle_error("send_message");
		return SEND_MESSAGE_ERROR;
	}
}
bool facebook_client::post_status(status_data *status)
{
	if (status == nullptr || (status->text.empty() && status->url.empty()))
		return false;
	handle_entry("post_status");
	if (status->isPage) {
		// Switch to page identity by which name we will share this post
		sendRequest(switchIdentityRequest(status->user_id.c_str()));
	}
	std::string linkData;
	if (!status->url.empty()) {
		http::response resp = sendRequest(linkScraperRequest(status));
		std::string temp = utils::text::html_entities_decode(utils::text::slashu_to_utf8(resp.data));
		std::string form = utils::text::source_get_value(&temp, 2, "