2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Author : Maxim Mamontov <faust@stargazer.dp.ua>
25 #include "stg/json_parser.h"
26 #include "stg/json_generator.h"
27 #include "stg/users.h"
29 #include "stg/logger.h"
30 #include "stg/common.h"
32 #include <yajl/yajl_gen.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
44 using STG::JSON::Parser;
45 using STG::JSON::PairsParser;
46 using STG::JSON::EnumParser;
47 using STG::JSON::NodeParser;
49 using STG::JSON::MapGen;
50 using STG::JSON::StringGen;
55 double CONN_TIMEOUT = 60;
56 double PING_TIMEOUT = 10;
74 std::map<std::string, Packet> packetCodes;
75 std::map<std::string, Stage> stageCodes;
77 class PacketParser : public EnumParser<Packet>
80 PacketParser(NodeParser* next, Packet& packet, std::string& packetStr)
81 : EnumParser(next, packet, packetStr, packetCodes)
83 if (!packetCodes.empty())
85 packetCodes["ping"] = PING;
86 packetCodes["pong"] = PONG;
87 packetCodes["data"] = DATA;
91 class StageParser : public EnumParser<Stage>
94 StageParser(NodeParser* next, Stage& stage, std::string& stageStr)
95 : EnumParser(next, stage, stageStr, stageCodes)
97 if (!stageCodes.empty())
99 stageCodes["authorize"] = AUTHORIZE;
100 stageCodes["authenticate"] = AUTHENTICATE;
101 stageCodes["preacct"] = PREACCT;
102 stageCodes["accounting"] = ACCOUNTING;
103 stageCodes["postauth"] = POSTAUTH;
107 class TopParser : public NodeParser
110 typedef void (*Callback) (void* /*data*/);
111 TopParser(Callback callback, void* data)
112 : m_packetParser(this, m_packet, m_packetStr),
113 m_stageParser(this, m_stage, m_stageStr),
114 m_pairsParser(this, m_data),
115 m_callback(callback), m_callbackData(data)
118 virtual NodeParser* parseStartMap() { return this; }
119 virtual NodeParser* parseMapKey(const std::string& value)
121 std::string key = ToLower(value);
124 return &m_packetParser;
125 else if (key == "stage")
126 return &m_stageParser;
127 else if (key == "pairs")
128 return &m_pairsParser;
132 virtual NodeParser* parseEndMap() { m_callback(m_callbackData); return this; }
134 const std::string& packetStr() const { return m_packetStr; }
135 Packet packet() const { return m_packet; }
136 const std::string& stageStr() const { return m_stageStr; }
137 Stage stage() const { return m_stage; }
138 const Config::Pairs& data() const { return m_data; }
141 std::string m_packetStr;
143 std::string m_stageStr;
145 Config::Pairs m_data;
147 PacketParser m_packetParser;
148 StageParser m_stageParser;
149 PairsParser m_pairsParser;
152 void* m_callbackData;
155 class ProtoParser : public Parser
158 ProtoParser(TopParser::Callback callback, void* data)
159 : Parser( &m_topParser ),
160 m_topParser(callback, data)
163 const std::string& packetStr() const { return m_topParser.packetStr(); }
164 Packet packet() const { return m_topParser.packet(); }
165 const std::string& stageStr() const { return m_topParser.stageStr(); }
166 Stage stage() const { return m_topParser.stage(); }
167 const Config::Pairs& data() const { return m_topParser.data(); }
170 TopParser m_topParser;
173 class PacketGen : public Gen
176 PacketGen(const std::string& type)
179 m_gen.add("packet", m_type);
181 void run(yajl_gen_t* handle) const
185 PacketGen& add(const std::string& key, const std::string& value)
187 m_gen.add(key, new StringGen(value));
190 PacketGen& add(const std::string& key, MapGen* map)
195 PacketGen& add(const std::string& key, MapGen& map)
210 Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote);
213 int sock() const { return m_sock; }
218 bool isOk() const { return m_ok; }
222 PLUGIN_LOGGER& m_logger;
223 const Config& m_config;
225 std::string m_remote;
228 time_t m_lastActivity;
229 ProtoParser m_parser;
231 static void process(void* data);
235 bool answer(const USER& user);
240 static bool write(void* data, const char* buf, size_t size);
243 Conn::Conn(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote)
244 : m_impl(new Impl(users, logger, config, fd, remote))
252 int Conn::sock() const
254 return m_impl->sock();
259 return m_impl->read();
264 return m_impl->tick();
267 bool Conn::isOk() const
269 return m_impl->isOk();
272 Conn::Impl::Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote)
279 m_lastPing(time(NULL)),
280 m_lastActivity(m_lastPing),
281 m_parser(&Conn::Impl::process, this)
290 bool Conn::Impl::read()
292 static std::vector<char> buffer(1024);
293 ssize_t res = ::read(m_sock, buffer.data(), buffer.size());
296 m_logger("Failed to read data from '" + m_remote + "': " + strerror(errno));
300 printfd(__FILE__, "Read %d bytes.\n%s\n", res, std::string(buffer.data(), res).c_str());
301 m_lastActivity = time(NULL);
307 return m_parser.append(buffer.data(), res);
310 bool Conn::Impl::tick()
312 time_t now = time(NULL);
313 if (difftime(now, m_lastActivity) > CONN_TIMEOUT)
315 int delta = difftime(now, m_lastActivity);
316 printfd(__FILE__, "Connection to '%s' timed out: %d sec.\n", m_remote.c_str(), delta);
317 m_logger("Connection to " + m_remote + " timed out.");
321 if (difftime(now, m_lastPing) > PING_TIMEOUT)
323 int delta = difftime(now, m_lastPing);
324 printfd(__FILE__, "Ping timeout: %d sec. Sending ping...\n", delta);
330 void Conn::Impl::process(void* data)
332 Impl& impl = *static_cast<Impl*>(data);
333 switch (impl.m_parser.packet())
345 printfd(__FILE__, "Received invalid packet type: '%s'.\n", impl.m_parser.packetStr().c_str());
346 impl.m_logger("Received invalid packet type: " + impl.m_parser.packetStr());
349 void Conn::Impl::processPing()
351 printfd(__FILE__, "Got ping. Sending pong...\n");
355 void Conn::Impl::processPong()
357 printfd(__FILE__, "Got pong.\n");
358 m_lastActivity = time(NULL);
361 void Conn::Impl::processData()
363 printfd(__FILE__, "Got data.\n");
364 int handle = m_users.OpenSearch();
366 USER_PTR user = NULL;
368 while (m_users.SearchNext(handle, &user) == 0)
374 for (Config::Pairs::const_iterator it = m_config.match.begin(); it != m_config.match.end(); ++it)
376 Config::Pairs::const_iterator pos = m_parser.data().find(it->first);
377 if (pos == m_parser.data().end())
382 if (user->GetParamValue(it->second) != pos->second)
397 m_users.CloseSearch(handle);
400 bool Conn::Impl::answer(const USER& user)
402 printfd(__FILE__, "Got match. Sending answer...\n");
404 for (Config::Pairs::const_iterator it = m_config.reply.begin(); it != m_config.reply.end(); ++it)
405 reply.add(it->first, new StringGen(user.GetParamValue(it->second)));
408 for (Config::Pairs::const_iterator it = m_config.modify.begin(); it != m_config.modify.end(); ++it)
409 modify.add(it->first, new StringGen(user.GetParamValue(it->second)));
411 PacketGen gen("data");
412 gen.add("result", "ok")
414 .add("modify", modify);
416 m_lastPing = time(NULL);
418 return generate(gen, &Conn::Impl::write, this);
421 bool Conn::Impl::answerNo()
423 printfd(__FILE__, "No match. Sending answer...\n");
424 PacketGen gen("data");
425 gen.add("result", "no");
427 m_lastPing = time(NULL);
429 return generate(gen, &Conn::Impl::write, this);
432 bool Conn::Impl::sendPing()
434 PacketGen gen("ping");
436 m_lastPing = time(NULL);
438 return generate(gen, &Conn::Impl::write, this);
441 bool Conn::Impl::sendPong()
443 PacketGen gen("pong");
445 m_lastPing = time(NULL);
447 return generate(gen, &Conn::Impl::write, this);
450 bool Conn::Impl::write(void* data, const char* buf, size_t size)
452 std::string json(buf, size);
453 printfd(__FILE__, "Writing JSON:\n%s\n", json.c_str());
454 Conn::Impl& conn = *static_cast<Conn::Impl*>(data);
457 ssize_t res = ::send(conn.m_sock, buf, size, MSG_NOSIGNAL);
460 conn.m_logger("Failed to write pong to '" + conn.m_remote + "': " + strerror(errno));