Index: /trunk/cf/network.cpp
===================================================================
--- /trunk/cf/network.cpp	(revision 5)
+++ /trunk/cf/network.cpp	(revision 6)
@@ -182,5 +182,4 @@
 cf::network::tcp::~tcp()
 {
-	close();
 }
 
Index: /trunk/msg.cpp
===================================================================
--- /trunk/msg.cpp	(revision 5)
+++ /trunk/msg.cpp	(revision 6)
@@ -5,13 +5,15 @@
 	makeTemplate(_obj, _type);					\
 
-namespace Message
+#define DELIMITER	"$"
+
+namespace Protocol
 {
 	// parser
-	void Parser::parse(const std::string & message)
+	void Message::parse(const std::string & message)
 	{
 		mObject = json::Deserialize(message);
 	}
 
-	std::string Parser::type() const
+	std::string Message::type() const
 	{
 		return get<std::string>("type");
@@ -31,22 +33,13 @@
 	void Request::makeTemplate(json::Object & obj, const std::string & type) const
 	{
-		obj["id"] = mID;
-		obj["type"] = type;
-	}
-
-	std::string Request::command(const std::string & cmd) const
-	{
-		DECLARE_TEMPLATE_OBJECT(obj, "command");
-
-		obj["command"] = cmd;
-
-		return json::Serialize(obj);
+		obj[ProtocolType::ID  ] = mID;
+		obj[ProtocolType::TYPE] = type;
 	}
 
 	std::string Request::sms(const std::string & phone) const
 	{
-		DECLARE_TEMPLATE_OBJECT(obj, "sms");
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::SMS);
 
-		obj["phone"] = phone;
+		obj[ProtocolType::PHONE] = phone;
 
 		return json::Serialize(obj);
@@ -55,9 +48,9 @@
 	std::string Request::join(const std::string & id, const std::string & pw, const std::string & sms)
 	{
-		DECLARE_TEMPLATE_OBJECT(obj, "join");
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::JOIN);
 
-		obj["id" ] = id;
-		obj["pw" ] = pw;
-		obj["sms"] = sms;
+		obj[ProtocolType::ID ] = id;
+		obj[ProtocolType::PW ] = pw;
+		obj[ProtocolType::SMS] = sms;
 
 		return json::Serialize(obj);
@@ -66,26 +59,44 @@
 	std::string Request::login(const std::string & pw) const
 	{
-		DECLARE_TEMPLATE_OBJECT(obj, "login");
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::LOGIN);
 
-		obj["pw"] = mID + "$" + pw;
+		obj[ProtocolType::PW] = mID + DELIMITER + pw;
 
 		return json::Serialize(obj);
 	}
 
-	std::string Request::chat(const std::string & to, const std::string & sessid, const std::string & message) const
+	std::string Request::addFriend(const std::string & id) const
 	{
-		DECLARE_TEMPLATE_OBJECT(obj, "chat");
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::ADD_FRIEND);
 
-		obj["from"   ] = mID;
-		obj["to"     ] = to;
-		obj["sess-id"] = sessid;
-		obj["message"] = message;
+		obj[ProtocolType::ID] = id;
 
 		return json::Serialize(obj);
 	}
 
-	std::string Request::friendList() const
+	std::string Request::openSession(const std::string & to) const
 	{
-		DECLARE_TEMPLATE_OBJECT(obj, "friend-list");
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::OPEN_SESSION);
+
+		obj[ProtocolType::TO] = to;
+
+		return json::Serialize(obj);
+	}
+
+	std::string Request::chat(const std::string & sessid, const std::string & message, const int sensitive) const
+	{
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::CHAT);
+
+		obj[ProtocolType::FROM      ] = mID;
+		obj[ProtocolType::SESSION_ID] = sessid;
+		obj[ProtocolType::MESSAGE   ] = message;
+		obj[ProtocolType::SENSITIVE ] = sensitive;
+
+		return json::Serialize(obj);
+	}
+
+	std::string Request::getFriendList() const
+	{
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::GET_FRIEND_LIST);
 
 		return json::Serialize(obj);
@@ -95,12 +106,12 @@
 	void Response::makeTemplate(json::Object & obj, const std::string & type) const
 	{
-		obj["type"] = type;
+		obj[ProtocolType::TYPE] = type;
 	}
 
-	std::string Response::result(const bool status) const
+	std::string Response::result(const std::string & requestType, const bool status) const
 	{
-		DECLARE_TEMPLATE_OBJECT(obj, "result");
+		DECLARE_TEMPLATE_OBJECT(obj, ProtocolType::RESULT);
 
-		obj["result"] = status;
+		obj[ProtocolType::RESULT] = status;
 
 		return json::Serialize(obj);
Index: /trunk/msg.hpp
===================================================================
--- /trunk/msg.hpp	(revision 5)
+++ /trunk/msg.hpp	(revision 6)
@@ -8,4 +8,102 @@
 #include <string>
 #include <vector>
+
+typedef struct SFriend
+{
+	std::string id;
+	std::string name;
+} SFriend;
+
+// --------------------------------------------------------------
+
+namespace ProtocolType
+{
+#define DECLARE_KEY(_t)	static const std::string _t = #_t
+
+	DECLARE_KEY(ID);
+	DECLARE_KEY(TYPE);
+	DECLARE_KEY(SMS);
+	DECLARE_KEY(PHONE);
+	DECLARE_KEY(JOIN);
+	DECLARE_KEY(PW);
+	DECLARE_KEY(LOGIN);
+	DECLARE_KEY(ADD_FRIEND);
+	DECLARE_KEY(OPEN_SESSION);
+	DECLARE_KEY(TO);
+	DECLARE_KEY(CHAT);
+	DECLARE_KEY(FROM);
+	DECLARE_KEY(SESSION_ID);
+	DECLARE_KEY(MESSAGE);
+	DECLARE_KEY(SENSITIVE);
+	DECLARE_KEY(GET_FRIEND_LIST);
+	DECLARE_KEY(RESULT);
+}
+
+namespace Protocol
+{
+	class Message
+	{
+	private:
+		json::Object mObject;
+
+	public:
+		void parse(const std::string & message);
+
+		template<typename T>
+		inline T get(const std::string & key) const
+		{
+			return (T)mObject[key];
+		}
+
+		std::string type() const;
+	};
+
+	class IProtocol
+	{
+	public:
+		virtual void makeTemplate(json::Object & obj, const std::string & type) const = 0;
+	};
+
+	class Request : public IProtocol
+	{
+	private:
+		std::string mID;
+
+	public:
+		void setUserID(const std::string & id);
+
+		const std::string & getUserID();
+
+		void makeTemplate(json::Object & obj, const std::string & type) const;
+
+		std::string sms(const std::string & phone) const;
+
+		std::string join(const std::string & id, const std::string & pw, const std::string & sms);
+
+		std::string login(const std::string & pw) const;
+
+		std::string addFriend(const std::string & id) const;
+
+		std::string openSession(const std::string & to) const;
+
+		std::string chat(const std::string & sessid, const std::string & message, const int sensitive) const;
+
+		std::string getFriendList() const;
+	};
+
+	class Response : public IProtocol
+	{
+	public:
+		void makeTemplate(json::Object & obj, const std::string & type) const;
+
+		std::string result(const std::string & requestType, const bool status) const;
+
+		std::string friendList(const std::vector<SFriend> & friendList) const;
+
+		std::string openSession(const std::vector<std::string> & idList) const;
+	};
+};
+
+// --------------------------------------------------------------
 
 #define LOG(message)	log(__FILE__,__func__,__LINE__,message)
@@ -30,69 +128,2 @@
 			  << message << std::endl;
 }
-
-// --------------------------------------------------------------
-namespace Message
-{
-	class Parser
-	{
-	private:
-		json::Object mObject;
-
-	public:
-		void parse(const std::string & message);
-
-		template<typename T>
-		inline T get(const std::string & key) const
-		{
-			return (T)mObject[key];
-		}
-
-		std::string type() const;
-	};
-
-	class IMessage
-	{
-	public:
-		virtual void makeTemplate(json::Object & obj, const std::string & type) const = 0;
-	};
-
-	class Request : public IMessage
-	{
-	private:
-		std::string mID;
-
-	public:
-		void setUserID(const std::string & id);
-
-		const std::string & getUserID();
-
-		void makeTemplate(json::Object & obj, const std::string & type) const;
-
-		std::string command(const std::string & cmd) const;
-
-		std::string sms(const std::string & phone) const;
-
-		std::string Request::join(const std::string & id, const std::string & pw, const std::string & sms);
-
-		std::string login(const std::string & pw) const;
-
-		std::string Request::chat(const std::string & to, const std::string & sessid, const std::string & message) const;
-
-		std::string friendList() const;
-	};
-
-	class Response : public IMessage
-	{
-	public:
-		typedef struct friendInfo
-		{
-			std::string id;
-			std::string name;
-		} friendInfo;
-
-	public:
-		void makeTemplate(json::Object & obj, const std::string & type) const;
-
-		std::string result(const bool status) const;
-	};
-};
Index: /trunk/msgclnt.cpp
===================================================================
--- /trunk/msgclnt.cpp	(revision 5)
+++ /trunk/msgclnt.cpp	(revision 6)
@@ -10,5 +10,5 @@
 {
 	cf::network::tcp * socket;
-	SCallback * callback;
+	MessageQ * messageQ;
 } SWorkerArg;
 
@@ -17,6 +17,5 @@
 	SWorkerArg * inst = (SWorkerArg *) arg;
 
-	bool isError = false;
-	Message::Parser parser;
+	Protocol::Message parser;
 	cf::bin raw;
 
@@ -27,6 +26,4 @@
 			raw = inst->socket->receive();
 			parser.parse(raw.toString());
-
-			isError = false;
 		}
 		catch (cf::exception & e)
@@ -37,33 +34,40 @@
 			if (raw.size() == 0)
 				break;
-
-			isError = true;
-		}
-
-		if (parser.type() == "result")
-		{
-			SResultResponse recv;
-
-			recv.result  = parser.get<bool>("result");
-			recv.isError = isError;
-
-			inst->callback->result(recv);
-		}
-		else if (parser.type() == "chat")
-		{
-			SReceivedMessage recv;
-
-			recv.sessid  = parser.get<std::string>("sess-id");
-			recv.sender  = parser.get<std::string>("from");
-			recv.message = parser.get<std::string>("message");
-			recv.isError = isError;
-
-			inst->callback->message(recv);
-		}
-	}
-
-	free(arg);
+		}
+
+		inst->messageQ->push(parser);
+	}
+
+	free(inst);
 
 	return 0;
+}
+
+// --------------------------------------------------------------
+
+void MessageQ::push(const Protocol::Message & parser)
+{
+	SYNCHRONIZED(mutex)
+	{
+		messageQ.push_back(parser);
+	}
+}
+
+Protocol::Message MessageQ::pop(const std::string & requestType)
+{
+	while (true)
+	{
+		SYNCHRONIZED(mutex)
+		{
+			Protocol::Message parser = messageQ.front();
+
+			if (parser.type() == requestType)
+			{
+				messageQ.erase(messageQ.begin());
+
+				return parser;
+			}
+		}
+	}
 }
 
@@ -75,4 +79,14 @@
 }
 
+chev::~chev()
+{
+	socket.close();
+}
+
+const std::string & chev::getLastError() const
+{
+	return error;
+}
+
 bool chev::connect(const std::string & host, const unsigned short port)
 {
@@ -81,4 +95,13 @@
 		socket.connect(host, port);
 
+		SWorkerArg * arg = (SWorkerArg *)malloc(sizeof(SWorkerArg));
+		if (!arg)
+			return false;
+
+		arg->socket = &socket;
+		arg->messageQ = &messageQ;
+
+		listener.start(arg);
+
 		return true;
 	}
@@ -97,9 +120,5 @@
 		socket.send(request.join(id, pw, sms));
 
-		std::string response = socket.receive().toString();
-		Message::Parser parser;
-		parser.parse(response);
-
-		return parser.get<bool>("result");
+		return messageQ.pop(ProtocolType::JOIN).get<bool>(ProtocolType::RESULT);
 	}
 	catch (cf::exception & e)
@@ -119,9 +138,5 @@
 		socket.send(request.login(pw));
 
-		std::string response = socket.receive().toString();
-		Message::Parser parser;
-		parser.parse(response);
-
-		return parser.get<bool>("result");
+		return messageQ.pop(ProtocolType::LOGIN).get<bool>(ProtocolType::RESULT);
 	}
 	catch (cf::exception & e)
@@ -139,54 +154,106 @@
 		socket.send(request.sms(phone));
 
-		std::string response = socket.receive().toString();
-		Message::Parser parser;
-		parser.parse(response);
-
-		return parser.get<bool>("result");
-	}
-	catch (cf::exception & e)
-	{
-		LOG(e.what());
-
-		return false;
-	}
-}
-
-bool chev::runReceiver(const SCallback & cb)
-{
-	if (listener.isRunning())
-		return true;
-
-	callback = cb;
-
-	SWorkerArg * arg = (SWorkerArg *)malloc(sizeof(SWorkerArg));
-	if (!arg)
-		return false;
-
-	arg->socket = &socket;
-	arg->callback = &callback;
-
-	listener.start(arg);
-
-	return true;
-}
-
-bool chev::sendMessage(const std::string & friendid, const std::string & sessid, const std::string & message)
-{
-	try
-	{
-		socket.send(request.chat(friendid, sessid, message));
-
-		std::string response = socket.receive().toString();
-		Message::Parser parser;
-		parser.parse(response);
-
-		return parser.get<bool>("result");
-	}
-	catch (cf::exception & e)
-	{
-		LOG(e.what());
-
-		return false;
-	}
-}
+		return messageQ.pop(ProtocolType::SMS).get<bool>(ProtocolType::RESULT);
+	}
+	catch (cf::exception & e)
+	{
+		LOG(e.what());
+
+		return false;
+	}
+}
+
+bool chev::addFriend(const std::string & id)
+{
+	try
+	{
+		socket.send(request.addFriend(id));
+
+		return messageQ.pop(ProtocolType::ADD_FRIEND).get<bool>(ProtocolType::RESULT);
+	}
+	catch (cf::exception & e)
+	{
+		LOG(e.what());
+
+		return false;
+	}
+}
+
+std::vector<SFriend> chev::getFriendList()
+{
+	try
+	{
+		socket.send(request.getFriendList());
+	}
+	catch (cf::exception & e)
+	{
+		LOG(e.what());
+	}
+	return std::vector<SFriend>();
+}
+
+std::string chev::getSessionID(const std::vector<std::string> & idList)
+{
+	try
+	{
+		std::string concat = idList[0];
+
+		for (int iter = 1 ; iter < idList.size() ; iter++)
+			concat += "," + idList[iter];
+
+		if (sessionMap.find(concat) == sessionMap.end())
+		{
+			socket.send(request.openSession(concat));
+
+			std::string response = socket.receive().toString();
+			Protocol::Message parser;
+			parser.parse(response);
+
+			if (!parser.get<bool>("result"))
+				THROW_EXCEPTION("failed to open session id");
+
+			sessionMap[concat] = parser.get<std::string>("sess-id");
+		}
+
+		return sessionMap[concat];
+	}
+	catch (cf::exception & e)
+	{
+		LOG(e.what());
+
+		return "";
+	}
+}
+
+bool chev::tell(const SConversation & conversation)
+{
+	try
+	{
+		const SConversation & c = conversation;
+
+		socket.send(request.chat(c.sessid, c.message, c.sensitive));
+
+		return messageQ.pop(ProtocolType::CHAT).get<bool>(ProtocolType::RESULT);
+	}
+	catch (cf::exception & e)
+	{
+		LOG(e.what());
+
+		return false;
+	}
+}
+
+SConversation chev::listen()
+{
+	SConversation c;
+	Protocol::Message parser = messageQ.pop(ProtocolType::CHAT);
+
+	c.sessid    = parser.get<std::string>(ProtocolType::SESSION_ID);
+	c.message   = parser.get<std::string>(ProtocolType::MESSAGE);
+	c.sensitive = parser.get<int>(ProtocolType::SENSITIVE);
+	c.isError   = parser.get<bool>(ProtocolType::RESULT);
+
+	c.from      = parser.get<std::string>(ProtocolType::FROM);
+
+	return c;
+}
Index: /trunk/msgclnt.h
===================================================================
--- /trunk/msgclnt.h	(revision 5)
+++ /trunk/msgclnt.h	(revision 6)
@@ -9,46 +9,46 @@
 #include "cf/task.h"
 
-typedef struct SFriend
+typedef struct SConversation
 {
-	std::string id;
-	std::string name;
-} SFriend;
+	// tell and listen
+	std::string sessid;
+	std::string message;
+	int sensitive;
+	bool isError;
 
-// for callback
+	// only for listen
+	std::string from;
+} SConversation;
 
-typedef struct SResultResponse
+class MessageQ
 {
-	bool result;
-	bool isError;
-} SResultResponse;
+private:
+	cf::task::mutex					mutex;
+	std::vector<Protocol::Message>	messageQ;
 
-typedef struct SReceivedMessage
-{
-	std::string sessid;
-	std::string sender;
-	std::string message;
-	bool isError;
-} SReceivedMessage;
+public:
+	void push(const Protocol::Message & parser);
 
-typedef int (* callback_onResultResponse)(SResultResponse &);
-typedef int (* callback_onReceiveMessage)(SReceivedMessage &);
-
-typedef struct SCallback
-{
-	callback_onResultResponse result;
-	callback_onReceiveMessage message;
-} SCallback;
+	Protocol::Message pop(const std::string & requestType);
+};
 
 class chev
 {
 private:
-	Message::Request	request;
+	Protocol::Request	request;
 	std::string			error;
 	cf::network::tcp	socket;
 	cf::task::thread	listener;
-	SCallback			callback;
+
+	MessageQ			messageQ;
+
+	std::map<std::string, std::string>	sessionMap;
 
 public:
 	chev();
+
+	~chev();
+
+	const std::string & getLastError() const;
 
 	bool connect(const std::string & host, unsigned short port);
@@ -64,9 +64,8 @@
 	std::vector<SFriend> getFriendList();
 
-	bool sendMessage(const std::string & friendid, const std::string & sessionID, const std::string & message);
+	std::string getSessionID(const std::vector<std::string> & idList);
 
-	// call once
-	bool runReceiver(const SCallback & cb);
+	bool tell(const SConversation & conversation);
 
-	const std::string & getLastError();
+	SConversation listen();
 };
Index: /trunk/msgsrv.cpp
===================================================================
--- /trunk/msgsrv.cpp	(revision 5)
+++ /trunk/msgsrv.cpp	(revision 6)
@@ -283,5 +283,5 @@
 }
 
-static bool command(const Message::Parser & parser)
+static bool command(const Protocol::Message & parser)
 {
 	bool isContinued = true;
@@ -305,5 +305,5 @@
 }
 
-static std::string httpSMS(const Message::Parser & parser)
+static std::string httpSMS(const Protocol::Message & parser)
 {
 	std::string phone = parser.get<std::string>("phone");
@@ -319,5 +319,5 @@
 		CRLF;
 
-	Message::Response response;
+	Protocol::Response response;
 	cf::network::tcp smsSock;
 	cf::network::host smsServer(url, 80);
@@ -330,5 +330,5 @@
 }
 
-static bool join(const Message::Parser & parser, const std::string & sms, const std::string & address)
+static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address)
 	throw (cf::exception)
 {
@@ -342,5 +342,5 @@
 }
 
-static bool login(const Message::Parser & parser)
+static bool login(const Protocol::Message & parser)
 	throw (cf::exception)
 {
@@ -351,5 +351,5 @@
 }
 
-static bool chat(const Message::Parser & parser,
+static bool chat(const Protocol::Message & parser,
 				 const std::string & message)
 {
@@ -381,5 +381,5 @@
 		while (true)
 		{
-			Message::Parser parser;
+			Protocol::Message parser;
 			parser.parse(sock.receive().toString());
 
@@ -417,5 +417,5 @@
 		workerInitiator(*sock);
 
-		Message::Response response;
+		Protocol::Response response;
 		bool result = true;
 
@@ -423,5 +423,5 @@
 		{
 			std::string message = sock->receive().toString();
-			Message::Parser parser;
+			Protocol::Message parser;
 			parser.parse(message);
 
@@ -433,5 +433,5 @@
 				result = chat(parser, message);
 
-			sock->send(response.result(result));
+			sock->send(response.result(parser.type(), result));
 		}
 	}
