/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set et sw=2 ts=2: */ /*************************************************************************** * msgparser.cc * * Fri Feb 24 14:59:34 CET 2012 * Copyright 2012 Jonas Suhr Christensen * jsc@umbraculum.org ****************************************************************************/ /* * This file is part of Munia. * * Munia is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Munia is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Munia; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "messageparser.h" #include #include #include #include #include #include "nodemanager.h" typedef std::vector TokenVector; typedef std::list MsgTokensList; inline static void parse_into_msg_tokens(std::string& data, MsgTokensList& msgTokensList) { TokenVector tokenVector; std::string token; bool inside_quote = false; char prev_ch = '0'; for(size_t i = 0; i < data.length(); i++) { char ch = data[i]; switch(ch) { case '\"': if(prev_ch != '\\') { inside_quote = !inside_quote; } else { token += ch; } break; case ' ': if(inside_quote) { DEBUG(msgparser, "Appending %c\n", ch); token += ch; } else { if(token.empty()) { continue; // skip multiple white spaces and pre white space } DEBUG(msgparser, "Adding token %s\n", token.c_str()); tokenVector.push_back(token); token.clear(); } break; case ';': if(inside_quote) { token += ch; } else { tokenVector.push_back(token); msgTokensList.push_back(tokenVector); tokenVector.clear(); token.clear(); } break; default: token += ch; break; } prev_ch = ch; } if(token != "") { tokenVector.push_back(token); token.clear(); } if(!tokenVector.empty()) { msgTokensList.push_back(tokenVector); tokenVector.clear(); } //// WARNING: Enabling this will print passwords in plaintext... //// Debug: print tokens //DEBUG(parser, "tokenlists: %d\n", msgTokensList.size()); //MsgTokensList::iterator j = msgTokensList.begin(); //while(j != msgTokensList.end()) //{ // DEBUG(parser, " tokens: %d\n", j->size()); // int idx = 0; // TokenVector::iterator i = j->begin(); // while(i != j->end()) // { // DEBUG(parser, " token[%d]: '%s'\n", idx++, i->c_str()); // i++; // } // j++; //} } static nodeid_t getId(std::map &sym, std::string token) { bool isnumeric = true; for(size_t i = 0; i < token.size(); i++) { isnumeric &= (token[i] >= '0' && token[i] <= '9'); } DEBUG(getid, "======= %d %s\n", isnumeric, token.c_str()); if(isnumeric) { nodeid_t id = atoi(token.c_str()); DEBUG(getid, "======= id: %d\n", (int)id); return id; } if(sym.find(token) == sym.end()) { sym[token] = node_manager.createId(); } return sym[token]; } inline static void create_msg_list(MsgTokensList& msgTokensList, MessageList& msgList, bool clientmode, struct lws* wsi) { size_t origin = 0; if(clientmode) { origin = 1; } std::map sym; MsgTokensList::iterator it_msg; for(it_msg = msgTokensList.begin(); it_msg != msgTokensList.end(); it_msg++) { TokenVector t = *it_msg; //malformed msg if(t.size() < origin+1) { msgList.push_back(create_msg_error(ErrorCode::Unknown, "malformed message", wsi)); continue; } message_t m; DEBUG(msgparser, "t[%d] : %s, running in clientmode: %d\n", (int)origin, t[origin].c_str(), clientmode); // DEBUG(msgparser, "%d\n", t.size()); if(t[origin] == "subscribe") m.cmd = cmd::subscribe; else if(t[origin] == "unsubscribe") m.cmd = cmd::unsubscribe; else if(t[origin] == "login") m.cmd = cmd::login; else if(t[origin] == "logout") m.cmd = cmd::logout; else if(t[origin] == "create") m.cmd = cmd::create; else if(t[origin] == "remove") m.cmd = cmd::remove; else if(t[origin] == "move") m.cmd = cmd::move; else if(t[origin] == "update") m.cmd = cmd::update; else if(t[origin] == "error") m.cmd = cmd::error; else { msgList.push_back(create_msg_error(ErrorCode::Unknown, "unknown command", wsi)); continue; } switch(m.cmd) { case cmd::subscribe: { if(t.size() != origin+1+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.subscribe.id = getId(sym, t[origin+1]); } break; case cmd::unsubscribe: { if(t.size() != origin+1+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.unsubscribe.id = getId(sym, t[origin+1]); DEBUG(msgparser, "unsubscribe @ %d\n", (int)m.unsubscribe.id); } break; case cmd::login: { if(t.size() != origin+1+2) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.login.user = t[origin+1]; m.login.password = t[origin+2]; DEBUG(msgparser, "login %s\n", m.login.user.c_str()); } break; case cmd::logout: if(t.size() != origin+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } break; case cmd::create_with_attributes: // Never sent to the server break; case cmd::create: { if(t.size() != origin+1+2+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } if(clientmode) { m.create.id = getId(sym, t[origin+1]); m.create.parentid = getId(sym, t[origin+2]); m.create.insertbeforeid = getId(sym, t[origin+3]); } else { m.create.id = getId(sym, t[origin+1]); m.create.parentid = getId(sym, t[origin+2]); m.create.insertbeforeid = getId(sym, t[origin+3]); } } break; case cmd::remove: { if(t.size() != origin+1+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.remove.id = getId(sym, t[origin+1]); } break; case cmd::move: { if(t.size() != origin+2+1+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.move.id = getId(sym, t[origin+1]); m.move.parentid = getId(sym, t[origin+2]); m.move.insertbeforeid = getId(sym, t[origin+3]); } break; case cmd::update: { if(t.size() != origin+3+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.update.id = getId(sym, t[origin+1]); m.update.attribute = t[origin+2]; m.update.value = t[origin+3]; } break; case cmd::error: { if(t.size() != origin+2+1) { msgList.push_back( create_msg_error(ErrorCode::WrongNumberOfArguments, "Wrong number of arguments.", wsi)); continue; } m.error.code = static_cast(atoi(t[origin+1].data())); m.error.msg = t[origin+2]; } break; }; msgList.push_back(m); } } MessageList parse_msg(std::string data, struct lws* wsi) { DEBUG(msgparser, "Parsing: '%s'\n", data.c_str()); MsgTokensList msgTokensList; parse_into_msg_tokens(data, msgTokensList); MessageList msgList; create_msg_list(msgTokensList, msgList, false, wsi); return msgList; } MessageList parse_msg_client(std::string data, struct lws* wsi) { DEBUG(msgparser, "Parsing: '%s'\n", data.c_str()); MsgTokensList msgTokensList; parse_into_msg_tokens(data, msgTokensList); MessageList msgList; create_msg_list(msgTokensList, msgList, true, wsi); return msgList; } std::vector msg_tostring(message_t m) { std::vector msgs; std::string msg; switch(m.cmd) { case cmd::create: msg = "create " + std::to_string(m.create.id) + " " + std::to_string(m.create.parentid) + " " + std::to_string(m.create.insertbeforeid) + ";"; msgs.push_back(msg); break; case cmd::create_with_attributes: msg = "create " + std::to_string(m.create.id) + " " + std::to_string(m.create.parentid) + " " + std::to_string(m.create.insertbeforeid) + ";"; msgs.push_back(msg); for(const auto& attribute : m.create.attributes) { msg = "update " + std::to_string(m.create.id) + " \"" + attribute.first + "\" \"" + attribute.second + "\";"; msgs.push_back(msg); } for(const auto &child : m.create.children) { msg = "create " + std::to_string(child.id) + " " + std::to_string(child.parentid) + " " + std::to_string(child.insertbeforeid) + ";"; msgs.push_back(msg); for(const auto& attribute : child.attributes) { msg = "update " + std::to_string(child.id) + " \"" + attribute.first + "\" \"" + attribute.second + "\";"; msgs.push_back(msg); } } break; case cmd::remove: msg = "remove " + std::to_string(m.remove.id) + ";"; msgs.push_back(msg); break; case cmd::move: msg = "move " + std::to_string(m.move.id) + " " + std::to_string(m.move.parentid) + " " + std::to_string(m.move.insertbeforeid) + ";"; msgs.push_back(msg); break; case cmd::update: msg = "update " + std::to_string(m.update.id) + " \"" + m.update.attribute + "\" \"" + m.update.value + "\";"; msgs.push_back(msg); break; case cmd::error: msg = "error " + std::to_string(static_cast(m.error.code)) + " \"" + m.error.msg + "\";"; msgs.push_back(msg); break; case cmd::login: case cmd::logout: case cmd::subscribe: case cmd::unsubscribe: assert(false); // The server never sends these messages so no need to serialise them. break; } return msgs; } message_t create_msg_create(node_t t, nodeid_t insertbeforeid) { message_t m; m.cmd = cmd::create; m.create.id = t.id; m.create.parentid = t.parentid; m.create.insertbeforeid = insertbeforeid; return m; } message_t create_msg_create_with_attributes(node_t node, nodeid_t insertbeforeid) { auto msg = create_msg_create(node, insertbeforeid); msg.cmd = cmd::create_with_attributes; return msg; } message_t create_msg_update(node_t t, const std::string &attr) { message_t m; m.cmd = cmd::update; m.update.id = t.id; m.update.attribute = attr; m.update.value = t.attributes[attr]; return m; } message_t create_msg_remove(node_t t) { message_t m; m.cmd = cmd::remove; m.remove.id = t.id; return m; } message_t create_msg_move(nodeid_t id, nodeid_t to, nodeid_t insertbeforeid) { message_t m; m.cmd = cmd::move; m.move.id = id; m.move.parentid = to; m.move.insertbeforeid = insertbeforeid; return m; } message_t create_msg_error(ErrorCode code, const std::string &msg, struct lws* wsi) { message_t m; m.cmd = cmd::error; m.error.wsi = wsi; m.error.code = code; m.error.msg = msg; return m; } message_t create_msg_error(const ErrorMessage &error, struct lws* wsi) { message_t m; m.cmd = cmd::error; m.error = error; m.error.wsi = wsi; return m; }