1 // vim: set noexpandtab tabstop=4 shiftwidth=4:
2 //----------------------------------------------------------------------------
4 //----------------------------------------------------------------------------
5 // This file is part of PlaQui.
7 // PlaQui is free software; you can redistribute it and/or modify it under the
8 // terms of the GNU General Public License as published by the Free Software
9 // Foundation; either version 2 of the License, or (at your option) any later
12 // PlaQui is distributed in the hope that it will be useful, but WITHOUT ANY
13 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 // You should have received a copy of the GNU General Public License along
18 // with PlaQui; if not, write to the Free Software Foundation, Inc., 59 Temple
19 // Place, Suite 330, Boston, MA 02111-1307 USA
20 //----------------------------------------------------------------------------
21 // Creado: Sat Oct 18 18:18:36 2003
22 // Autores: Leandro Lucarella <llucare@fi.uba.ar>
23 //----------------------------------------------------------------------------
28 #include "plaqui/server/server.h"
29 #include "plaqui/server/connection.h"
30 #include "plaqui/server/controlserver.h"
31 #include <sigc++/class_slot.h>
32 #include <glibmm/timer.h>
36 # include "plaqui/server/string.h"
46 Server::~Server(void) {
48 cerr << __FILE__ << "(" << __LINE__ << ")"
49 << ": destructor." << endl;
51 // Mando a terminar todas las plantas.
53 for (PlantList::iterator i = plants.begin(); i != plants.end(); i++) {
56 PlantList::size_type count = plants.size();
57 plants_mutex.unlock();
58 // Espero que terminen realmente.
60 Glib::usleep(10000); // 10 milisegundos
62 count = plants.size();
63 plants_mutex.unlock();
67 Server::Server(const string& plant_filename, const Connection::Port& port)
68 throw(sockerr): TCPServer(port) {
70 cerr << __FILE__ << "(" << __LINE__ << ")"
71 << ": port = " << port << endl;
73 // FIXME - hacer que se puedan cargar mas plantas bien.
74 Glib::Mutex::Lock lock(plants_mutex);
75 plants["default"] = new Plant(plant_filename);
76 plants["default"]->signal_finished().connect(
77 SigC::bind<const char*>(
78 SigC::slot_class(*this, &Server::on_plant_finished),
80 plants["default"]->run();
83 Connection* Server::new_connection(const sockbuf::sockdesc& sd) {
85 cerr << __FILE__ << "(" << __LINE__ << ")"
86 << ": new_connection(sd = " << sd.sock << ")"
89 ControlServer* connection = new ControlServer(sd);
90 // TODO verificar si el new se hace bien? no creo.
91 connection->signal_command_received().connect(
92 SigC::bind<ControlServer*>(
93 SigC::slot_class(*this, &Server::on_control_command_received),
99 void Server::on_plant_finished(const char* plant) {
101 cerr << __FILE__ << "(" << __LINE__ << ")"
102 << ": on_plant_finished(plant_name = " << plant << endl;
104 Glib::Mutex::Lock lock(plants_mutex);
108 /// \todo Terminar de implementar.
109 void Server::on_control_command_received(const Command& command,
110 ControlServer* controlserver) {
112 cerr << __FILE__ << "(" << __LINE__ << ")"
113 << ": on_control_command_received(target = "
114 << command.get_target() << ", command = " << command.get_command()
115 << ", args = [" << String::join(command.get_args(), ", ") << "])"
118 HTTPResponse* response;
119 //bool stop_controlserver = false;
120 if (command.get_target() == "server") {
121 if (command.get_command() == "status") {
122 response = cmd_server_status();
123 } else if (command.get_command() == "stop") {
125 response = new HTTPResponse(HTTPMessage::OK,
126 "<response desc=\"El server se apagará en instantes...\" />");
127 response->headers["Content-Type"] = "text/xml; charset=iso-8859-1";
128 controlserver->send(*response);
130 // Creo una conexión suicida para que el accept() del server retorne
131 // el control y el server pueda terminar realmente.
133 sockinetbuf suicida(sockbuf::sock_stream);
134 suicida.connect(socket.localhost(), socket.localport());
137 signal_error().emit(12345, "ahhhh! no puedo crear conexion suicida");
141 response = new HTTPResponse(HTTPMessage::NOT_FOUND,
142 "<response desc=\"Invalid command for 'server' taget!\" />");
144 } else if (command.get_target() == "connection") {
145 if (command.get_command() == "list") {
146 response = cmd_connection_list();
147 } else if (command.get_command() == "stop") {
148 response = cmd_connection_stop(command);
150 response = new HTTPResponse(HTTPMessage::NOT_FOUND,
151 "<response desc=\"Invalid command for 'connection' taget!\" />");
153 } else if (command.get_target() == "transmission") {
154 if (command.get_command() == "list") {
155 response = cmd_transmission_list();
156 } else if (command.get_command() == "start") {
157 response = cmd_transmission_start(command);
158 } else if (command.get_command() == "stop") {
159 response = cmd_transmission_stop(command);
161 response = new HTTPResponse(HTTPMessage::NOT_FOUND,
162 "<response desc=\"Invalid command for 'transmission' taget!\" />");
164 } else if (command.get_target() == "plant") {
165 if (command.get_command() == "list") {
166 response = cmd_plant_list();
167 } else if (command.get_command() == "get") {
168 response = cmd_plant_get(command);
169 } else if (command.get_command() == "set") {
170 response = cmd_plant_set(command);
171 } else if (command.get_command() == "stop") {
172 response = cmd_plant_stop(command);
174 response = new HTTPResponse(HTTPMessage::NOT_FOUND,
175 "<response desc=\"Invalid command for 'plant' taget!\" />");
178 response = new HTTPResponse(HTTPMessage::NOT_FOUND,
179 "<response desc=\"Invalid taget!\" />");
182 response->headers["Content-Type"] = "text/xml; charset=iso-8859-1";
183 //response->headers["Connection"] = "close";
184 controlserver->send(*response);
186 // FIXME con timeout no debería ser necesario. Verificar cabecera Connection
187 // para saber si hay que finish()earlo o no.
188 //if (stop_controlserver) {
189 // controlserver->finish();
193 HTTPResponse* Server::cmd_server_status(void) const {
196 xml << "<serverstatus>" << endl;
197 xml << "\t<version>" VERSION "</version>" << endl;
198 xml << "\t<authors>" << endl;
199 xml << "\t\t<author>Nicolás Dimov</author>" << endl;
200 xml << "\t\t<author>Leandro Lucarella</author>" << endl;
201 xml << "\t\t<author>Ricardo Markiewicz</author>" << endl;
202 xml << "\t</authors>" << endl;
203 xml << "</serverstatus>" << endl;
204 return new HTTPResponse(HTTPMessage::OK, xml.str());
207 HTTPResponse* Server::cmd_connection_list(void) {
209 TCPServer::ConnectionInfoList cil = get_connected();
211 xml << "<list type=\"connection\">" << endl;
212 for (TCPServer::ConnectionInfoList::const_iterator i = cil.begin();
213 i != cil.end(); i++) {
214 xml << "\t<row>" << endl;
215 xml << "\t\t<cell>" << i->host << "</cell>" << endl;
216 xml << "\t\t<cell>" << i->port << "</cell>" << endl;
217 xml << "\t</row>" << endl;
219 xml << "</list>" << endl;
220 return new HTTPResponse(HTTPMessage::OK, xml.str());
223 HTTPResponse* Server::cmd_connection_stop(const Command& command) {
224 const Command::Arguments& args = command.get_args();
225 Connection::Port port;
226 if (args.size() < 2) {
227 return new HTTPResponse(HTTPMessage::CONFLICT,
228 "<response desc=\"Faltan argumentos.\" />");
229 } else if (disconnect(args[0], to(args[1], port))) {
230 return new HTTPResponse(HTTPMessage::OK,
231 string("<response desc=\"La conexión a ") + args[0] + ":" + args[1]
232 + " se cerrará en instantes...\" />");
234 return new HTTPResponse(HTTPMessage::NOT_FOUND,
235 string("<response desc=\"No existe una conexión a ") + args[0]
236 + ":" + args[1] + "\" />");
240 HTTPResponse* Server::cmd_transmission_list(void) {
243 xml << "<list type=\"transmission\">" << endl;
244 /*TODO plants_mutex.lock();
245 for (PlantList::const_iterator i = plants.begin();
246 i != plants.end(); i++) {
248 xml << " <li>" << (*i)->get_host() << ":"
249 << (*i)->get_port() << " [<a href=\"/transmission/stop/"
250 << (*i)->get_host() << "/" << (*i)->get_port()
251 << "\">desconectar</a>]</li>" << endl;
253 transmissions_mutex.unlock();*/
254 xml << "</list>" << endl;
255 return new HTTPResponse(HTTPMessage::OK, xml.str());
258 HTTPResponse* Server::cmd_transmission_start(const Command& command) {
259 const Command::Arguments& args = command.get_args();
260 if (args.size() < 3) {
261 return new HTTPResponse(HTTPMessage::CONFLICT,
262 "<response desc=\"Faltan argumentos.\" />");
264 string plant = args[0];
265 string host = args[1];
266 Connection::Port port = to(args[2], port);
267 Glib::Mutex::Lock lock(plants_mutex);
268 PlantList::iterator p = plants.find(plant);
269 if (p == plants.end()) {
270 return new HTTPResponse(HTTPMessage::NOT_FOUND,
271 string("<response desc=\"No existe la planta '") + plant + "'.\" />");
272 // TODO - agregar chequeo de que la transmision a ese host:port no
273 // exista para otra planta?
274 } else if (plants[plant]->transmission_start(host, port)) {
275 return new HTTPResponse(HTTPMessage::OK,
276 string("<response desc=\"Se empieza a transmitir la planta '") + plant
277 + "' a " + host + ":" + String().from(port) + ".\" />");
279 return new HTTPResponse(HTTPMessage::INTERNAL_SERVER_ERROR,
280 string("<response desc=\"Error al crear la transmisión a de la planta '")
281 + plant + "' a " + host + ":" + args[2] + ".\" />");
286 HTTPResponse* Server::cmd_transmission_stop(const Command& command) {
287 const Command::Arguments& args = command.get_args();
288 if (args.size() < 2) {
289 return new HTTPResponse(HTTPMessage::CONFLICT,
290 "<response desc=\"Faltan argumentos.\" />");
292 const string& host = args[0];
293 Connection::Port port = to(args[1], port);
294 for (PlantList::iterator i = plants.begin(); i != plants.end(); i++) {
295 // TODO - agregar chequeo para saber si existe una conexion (para
296 // tirar error de que no hay conexion o de que no se pudo
298 if (i->second->transmission_stop(host, port)) {
299 return new HTTPResponse(HTTPMessage::OK,
300 string("<response desc=\"Se finaliza la transmisión de la planta '")
301 + i->first + "' a " + host + ":" + args[1] + ".\" />");
304 return new HTTPResponse(HTTPMessage::NOT_FOUND,
305 string("<response desc=\"No se puede finalizar la transmisión a ")
306 + host + ":" + args[1] + ".\" />");
310 HTTPResponse* Server::cmd_plant_list(void) {
313 xml << "<list type=\"plant\">" << endl;
315 for (PlantList::const_iterator i = plants.begin();
316 i != plants.end(); i++) {
317 xml << "\t<row>" << endl;
318 xml << "\t\t<cell>" << i->first << "</cell>" << endl;
319 xml << "\t</row>" << endl;
321 plants_mutex.unlock();
322 xml << "</list>" << endl;
323 return new HTTPResponse(HTTPMessage::OK, xml.str());
326 HTTPResponse* Server::cmd_plant_get(const Command& command) {
327 if (!command.get_args().size()) {
328 return new HTTPResponse(HTTPMessage::CONFLICT,
329 "<response desc=\"Faltan argumentos.\" />");
331 Glib::Mutex::Lock lock(plants_mutex);
332 string plant = command.get_args()[0];
333 if (plants.find(plant) == plants.end()) {
334 return new HTTPResponse(HTTPMessage::NOT_FOUND,
335 string("<response desc=\"No existe la planta ") + plant + "\" />");
338 string xml = plants[plant]->get_xml();
340 return new HTTPResponse(HTTPMessage::OK, xml);
342 return new HTTPResponse(HTTPMessage::INTERNAL_SERVER_ERROR,
343 ("<response desc=\"No se pudo obtener el XML de la planta ") + plant + "\" />");
347 HTTPResponse* Server::cmd_plant_set(const Command& command) {
348 const Command::Arguments& args = command.get_args();
349 if (args.size() < 4) {
350 return new HTTPResponse(HTTPMessage::CONFLICT,
351 "<response desc=\"Faltan argumentos.\" />");
353 string plant = args[0];
354 string element = args[1];
355 string key = args[2];
357 return new HTTPResponse(HTTPMessage::NOT_FOUND,
358 string("<response desc=\"La clave '") + key + "' es inválida.\" />");
360 string value = args[3];
361 Glib::Mutex::Lock lock(plants_mutex);
362 PlantList::iterator p = plants.find(plant);
363 if (p == plants.end()) {
364 return new HTTPResponse(HTTPMessage::NOT_FOUND,
365 string("<response desc=\"No existe la planta '") + plant + "'.\" />");
368 if ((value == "false") || (value == "0") || (value == "off")
369 || (value == "no")) {
372 if (!plants[plant]->set_open(element, open)) {
373 return new HTTPResponse(HTTPMessage::CONFLICT,
374 string("<response desc=\"No se pudo cambiar el estado del elemento '") + element + "'.\" />");
376 return new HTTPResponse(HTTPMessage::OK,
377 string("<response desc=\"Se cambió el estado del elemento '") + element + "'.\" />");
380 HTTPResponse* Server::cmd_plant_stop(const Command& command) {
381 if (!command.get_args().size()) {
382 return new HTTPResponse(HTTPMessage::CONFLICT,
383 "<response desc=\"Faltan argumentos.\" />");
385 Glib::Mutex::Lock lock(plants_mutex);
386 const string name = command.get_args()[0];
387 if (plants.find(name) == plants.end()) {
388 return new HTTPResponse(HTTPMessage::NOT_FOUND,
389 string("<response desc=\"No existe la planta ") + name + "\" />");
391 // TODO Ver si al frenar la planta se destruye (no deberia!!!)
392 plants[name]->finish();
393 return new HTTPResponse(HTTPMessage::OK,
394 string("<response desc=\"La planta '") + name + "' se cerrará en instantes...\" />");
397 } // namespace Server
399 } // namespace PlaQui