#include "cf/network.h" #include "cf/task.h" #include "cf/codec.h" #include "cf/file.h" #include "crypto.h" #include "msg.hpp" #include #include #include #include "sqlite3.h" // -------------------------------------------------------------- typedef struct LoginSession { cf::network::tcp * sock; cf::bin key; } LoginSession; std::map gOnlineUsers; std::map > gSessionMap; // -------------------------------------------------------------- int cb_getFriendList(void * userArg, int argc, char ** argv, char ** colName) { std::string * friendList = (std::string *) userArg; *friendList = argv[1]; return 0; } int cb_getSMS(void * userArg, int argc, char ** argv, char ** colName) { std::string * SMS = (std::string *) userArg; *SMS = argv[1]; return 0; } int cb_login(void * userArg, int argc, char ** argv, char ** colName) { bool * result = (bool *)userArg; *result = true; return 0; } int cb_join(void * userArg, int argc, char ** argv, char ** colName) { bool * isExist = (bool *)userArg; *isExist = true; return 0; } //=============================================================== // -------------------------------------------------------------- class DBManager { private: sqlite3 * db; public: DBManager() throw (cf::exception) : db(NULL) { Init(); } void Init() throw (cf::exception) { int result = 0; bool isExist = false; isExist = cf::file("account.db").exists(); result = sqlite3_open("account.db", &db); if (result) fprintf(stderr, "sqlite_open failed\n"); if (!isExist) setup(); } void setup() throw (cf::exception) { std::string sqlCreateAccountTable; std::string sqlCreateAuthTable; std::string sqlCreateFriendsTable; // declaration // query sqlCreateAccountTable = "CREATE TABLE T_ACCOUNT" "(" " num INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," " id TEXT," " pw TEXT" ")"; sqlCreateAuthTable = "CREATE TABLE T_AUTH" "(" " id TEXT PRIMARY KEY NOT NULL," " sms1 TEXT," " ip1 TEXT," " sms2 TEXT," " ip2 TEXT," " sms3 TEXT," " ip3 TEXT," " sms4 TEXT," " ip4 TEXT," " sms5 TEXT," " ip5 TEXT" ")"; sqlCreateFriendsTable = "CREATE TABLE T_FRIENDS" "(" " id TEXT PRIMARY KEY NOT NULL," " friend TEXT" ")"; exec(sqlCreateAccountTable, NULL, NULL); exec(sqlCreateAuthTable, NULL, NULL); exec(sqlCreateFriendsTable, NULL, NULL); } void exec(std::string & query, sqlite3_callback cb, void * userArg) throw(cf::exception) { int result = 0; char * errMsg = NULL; result = sqlite3_exec(db, query.c_str(), cb, userArg, &errMsg); if (result != SQLITE_OK) THROW_EXCEPTION("[DBERROR][" << result << "] " << errMsg); } bool login(const std::string & id, const std::string & pw) throw (cf::exception) { try { bool result = false; std::string query = "select * from T_ACCOUNT where id ='" + id + "' and pw = '" + pw + "'"; this->exec(query, cb_login, &result); return result; } catch (cf::exception & e) { FORWARD_EXCEPTION(e); } } bool join(const std::string & id, const std::string & pw, const std::string & sms, const std::string & ip) throw (cf::exception) { try { bool isExist = false; std::string existQuery = "select * from T_ACCOUNT where id = '" + id + "'"; std::string insertQuery = "insert into T_ACCOUNT(id, pw) values('" + id + "', '" + pw + "')"; this->exec(existQuery, cb_join, &isExist); if (!isExist) this->exec(insertQuery, NULL, NULL); } catch (cf::exception & e) { FORWARD_EXCEPTION(e); } return true; } std::string getFriendList(std::string & id) throw(cf::exception) { int result = 0; std::string friendList; try { std::string query = "select * from T_FRIENDS where id = '" + id + "'"; this->exec(query, cb_getFriendList, &friendList); } catch (cf::exception & e) { FORWARD_EXCEPTION(e); } } int getSMS(std::string & id) { int result = 0; std::string smsMsg; try { std::string query = "select * from T_AUTH where id = '" + id + "'"; this->exec(query, cb_getSMS, &smsMsg); } catch (cf::exception & e) { FORWARD_EXCEPTION(e); } } int addFriend(std::string & id) { try { std::string query = "insert into T_FRIENDS values('" + id + "')"; this->exec(query, NULL, NULL); } catch (cf::exception & e) { FORWARD_EXCEPTION(e); } } }; DBManager dbmgr; class Runner { public: cf::network::tcp * mSock; cf::task::thread * mThread; Runner(cf::network::tcp & sock, int (*worker)(void *)) : mSock(NULL) , mThread(NULL) { mSock = new(std::nothrow) cf::network::tcp(sock.detach()); mThread = new(std::nothrow) cf::task::thread(worker); if (!mSock || !mThread) { dispose(); THROW_EXCEPTION("cannot allocate thread arguments"); } mThread->start(this); } ~Runner() { dispose(); } void dispose() { if (mSock) delete mSock; if (mThread) delete mThread; } }; static bool isOnline(const std::string & id) { return gOnlineUsers.find(id) != gOnlineUsers.end(); } static void logout(const std::string & id) { gOnlineUsers.erase(id); LOG(STR(id << " was logged out")); } static unsigned int generateSeed() { unsigned int ret = 0; int t = (int)time(NULL); cf::bin b; b.resize(sizeof(int)); b.set((cf::uint8_t*)&t, sizeof(int)); cf::bin s = crypto().sha256(b); memcpy(&ret, s.buffer(), sizeof(unsigned int)); return ret; } static std::string getRandomCode() { char random[8] = {0x00,}; sprintf(random, "%06d", generateSeed() % 1000000); return random; } static std::string httpSMS(const Protocol::Message & parser) { std::string phone = parser.get(ProtocolType::PHONE); #define CRLF "\r\n" std::string code = getRandomCode(); std::string url = "unsigned.kr"; std::string uri = "/~cheese/sms/req.php?to=" + phone + "&code=" + code; std::string http = "GET " + uri + " HTTP/1.1" CRLF "Host: " + url + CRLF "Accept: text/plain" CRLF CRLF; cf::network::tcp smsSock; cf::network::host smsServer(url, 80); smsSock.connect(smsServer); smsSock.send(http); smsSock.receive(); smsSock.close(); return code; } static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address) throw (cf::exception) { if (sms != parser.get(ProtocolType::SMS)) THROW_EXCEPTION("SMS is not same"); std::string id = parser.get(ProtocolType::ID); std::string pw = parser.get(ProtocolType::PW); return dbmgr.join(id, pw, sms, address); } static bool login(const Protocol::Message & parser, cf::network::tcp & sock, cf::bin & key) throw (cf::exception) { std::string id = parser.get(ProtocolType::ID); std::string pw = parser.get(ProtocolType::PW); bool result = dbmgr.login(id, pw); if (result) { LoginSession loginSess; loginSess.sock = &sock; loginSess.key = key; gOnlineUsers[id] = loginSess; } return result; } static bool chat(const Protocol::Message & message) { bool result = false; Protocol::Message parser = message; std::string sessid = parser.get(ProtocolType::SESSION_ID); std::vector idList = gSessionMap[sessid]; std::string sender = parser.get(ProtocolType::ID); parser.mObject[ProtocolType::TYPE] = ProtocolType::LISTEN; std::string serialized = parser.serialize(); for (size_t iter = 0; iter < idList.size(); iter++) { std::string id = idList[iter]; if (sender != id && isOnline(id)) gOnlineUsers[id].sock->send(serialized); result = true; } return result; } static std::string createSessionID(std::string & idList) { cf::bin sessid; sessid = crypto().sha256(cf::bin(idList)); return cf::codec::hex::getInstance()->encode(sessid); } static bool openSession(const Protocol::Message & message) { Protocol::Message parser = message; bool result = false; std::string sessid; std::vector idList = parser.getList(ProtocolType::ID_LIST); std::string concat = joinStrings(idList); sessid = createSessionID(concat); parser.mObject[ProtocolType::SESSION_ID] = sessid; std::string serialized = parser.serialize(); for (size_t iter = 0; iter < idList.size(); iter++) { std::string id = idList[iter]; if (isOnline(id)) gOnlineUsers[id].sock->send(serialized); result = true; } gSessionMap[sessid] = idList; return result; } static std::string keyExchange(const std::string sms, const std::string address) { cf::bin sessKey; sessKey = crypto().sha256(cf::bin(sms + address)); return cf::codec::hex::getInstance()->encode(sessKey); } static std::string workerInitiator(cf::network::tcp & sock) { Protocol::Message parser; try { std::string sms; std::string id; bool loggedIn = false; while (true) { parser.parse(sock.receive().toString()); if (parser.type() == ProtocolType::SMS) { sms = httpSMS(parser); } else if (parser.type() == ProtocolType::JOIN) { if (!join(parser, sms, sock.peer().address())) THROW_EXCEPTION("user(" << parser.get(ProtocolType::ID) << ") cannot join"); } else if (parser.type() == ProtocolType::LOGIN) { std::string ip = sock.peer().address(); cf::bin seed = sms + DELIMITER + ip; cf::bin key = crypto().sha256(seed); if (login(parser, sock, key)) id = parser.get(ProtocolType::ID); loggedIn = true; } // success sock.send(Protocol::Response().result(parser.type(), true)); if (loggedIn) return id; } } catch (cf::exception & e) { sock.send(Protocol::Response().result(parser.type(), false)); FORWARD_EXCEPTION(e); } } static int worker(void * arg) { Runner * runner = reinterpret_cast(arg); cf::network::tcp * sock = runner->mSock; std::string id; try { id = workerInitiator(*sock); Protocol::Response response; bool result = true; while (result) { std::string message = sock->receive().toString(); Protocol::Message parser; parser.parse(message); LOG(message); if (parser.type() == ProtocolType::TELL) { result = chat(parser); sock->send(response.result(parser.type(), result)); } else if (parser.type() == ProtocolType::OPEN_SESSION) { result = openSession(parser); } } } catch (cf::exception & e) { LOG(e.stackTrace()); } logout(id); delete runner; return 0; } static int server(unsigned short port) { cf::network::tcp sock; try { sock.bind(port); sock.listen(); } catch(cf::exception & e) { LOG(e.what()); return -1; } while (true) { try { cf::network::tcp client = sock.accept(); Runner * runner = new(std::nothrow) Runner(client, worker); if (!runner) THROW_EXCEPTION("cannot create thread argument"); } catch(cf::exception & e) { LOG(e.what()); } } return 0; } int main(int argc, char ** argv) { if (argc != 2) { std::cerr << "-_-^" << std::endl; return -1; } return server((unsigned short)atoi(argv[1])); }