1 /*
2 LinKNX KNX home automation platform
3 Copyright (C) 2007 Jean-François Meessen <linknx@ouaye.net>
4
5 Portions of code borrowed to EIBD (http://bcusdk.sourceforge.net/)
6 Copyright (C) 2005-2006 Martin Kögler <mkoegler@auto.tuwien.ac.at>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include <unistd.h>
24 #include "xmlserver.h"
25 #include <sys/un.h>
26 #include <netinet/in.h>
27 #include <iostream>
28 #include "ruleserver.h"
29 #include "objectcontroller.h"
30 #include "timermanager.h"
31 #include "services.h"
32
~XmlServer()33 XmlServer::~XmlServer ()
34 {
35 Stop ();
36 std::list<ClientConnection*>::iterator it;
37 for (it = connections_m.begin(); it != connections_m.end(); it++)
38 {
39 (*it)->RemoveServer();
40 (*it)->StopDelete();
41 }
42 if (!connections_m.empty())
43 pth_sleep (1); // Wait some time to let client connections close
44
45 close (fd_m);
46 }
47
48 bool
deregister(ClientConnection * con)49 XmlServer::deregister (ClientConnection * con)
50 {
51 connections_m.remove(con);
52 return 1;
53 }
54
create(ticpp::Element * pConfig)55 XmlServer* XmlServer::create(ticpp::Element* pConfig)
56 {
57 std::string type = pConfig->GetAttributeOrDefault("type", "inet");
58 if (type == "inet")
59 {
60 int port = 0;
61 pConfig->GetAttributeOrDefault("port", &port, 1028);
62 return new XmlInetServer(port);
63 }
64 else if (type == "unix")
65 {
66 std::string path = pConfig->GetAttributeOrDefault("path", "/tmp/xmlserver.sock");
67 return new XmlUnixServer(path.c_str());
68 }
69 else
70 {
71 std::stringstream msg;
72 msg << "XmlServer: server type not supported: '" << type << "'" << std::endl;
73 throw ticpp::Exception(msg.str());
74 }
75 }
76
XmlInetServer(int port)77 XmlInetServer::XmlInetServer (int port)
78 {
79 struct sockaddr_in addr;
80 int reuse = 1;
81
82 port_m = port;
83 infoStream("XmlInetServer") << "Starting on port " << port_m << endlog;
84
85 memset (&addr, 0, sizeof (addr));
86 addr.sin_family = AF_INET;
87 addr.sin_port = htons (port);
88 addr.sin_addr.s_addr = htonl (INADDR_ANY);
89
90 fd_m = socket (AF_INET, SOCK_STREAM, 0);
91 if (fd_m == -1)
92 throw ticpp::Exception("XmlServer: Unable to create TCP socket");
93
94 setsockopt (fd_m, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
95
96 if (bind (fd_m, (struct sockaddr *) &addr, sizeof (addr)) == -1)
97 {
98 std::stringstream msg;
99 msg << "XmlServer: Unable to register server on TCP port " << port << ". Server is probably already started or was not cleanly stopped." << std::endl;
100 throw ticpp::Exception(msg.str());
101 }
102
103 if (listen (fd_m, 10) == -1)
104 throw ticpp::Exception("XmlServer: Unable to listen on TCP socket");
105
106 Start ();
107 }
108
exportXml(ticpp::Element * pConfig)109 void XmlInetServer::exportXml(ticpp::Element* pConfig)
110 {
111 pConfig->SetAttribute("type", "inet");
112 pConfig->SetAttribute("port", port_m);
113 }
114
XmlUnixServer(const char * path)115 XmlUnixServer::XmlUnixServer (const char *path)
116 {
117 struct sockaddr_un addr;
118 addr.sun_family = AF_LOCAL;
119 if (strlen(path) >= sizeof (addr.sun_path))
120 throw ticpp::Exception("XmlServer: Unable to create UNIX socket (path is too long)");
121 strncpy (addr.sun_path, path, sizeof (addr.sun_path));
122
123 path_m = path;
124 infoStream("XmlUnixServer") << "Starting on socket " << path_m << endlog;
125
126 fd_m = socket (AF_LOCAL, SOCK_STREAM, 0);
127 if (fd_m == -1)
128 throw ticpp::Exception("XmlServer: Unable to create UNIX socket");
129
130 unlink (path);
131 if (bind (fd_m, (struct sockaddr *) &addr, sizeof (addr)) == -1)
132 {
133 std::stringstream msg;
134 msg << "XmlServer: Unable to register server on UNIX path " << path << std::endl;
135 throw ticpp::Exception(msg.str());
136 }
137
138 if (listen (fd_m, 10) == -1)
139 throw ticpp::Exception("XmlServer: Unable to listen on UNIX socket");
140
141 Start ();
142 }
143
exportXml(ticpp::Element * pConfig)144 void XmlUnixServer::exportXml(ticpp::Element* pConfig)
145 {
146 pConfig->SetAttribute("type", "unix");
147 pConfig->SetAttribute("path", path_m);
148 }
149
Run(pth_sem_t * stop1)150 void XmlServer::Run (pth_sem_t *stop1)
151 {
152 pth_event_t stop = pth_event (PTH_EVENT_SEM, stop1);
153 while (pth_event_status (stop) != PTH_STATUS_OCCURRED)
154 {
155 int cfd;
156 cfd = pth_accept_ev (fd_m, 0, 0, stop);
157 if (cfd != -1)
158 {
159 ClientConnection *c = new ClientConnection (this, cfd);
160 connections_m.push_back(c);
161 c->Start ();
162 }
163 }
164 pth_event_free (stop, PTH_FREE_THIS);
165 }
166
ClientConnection(XmlServer * server,int fd)167 ClientConnection::ClientConnection (XmlServer *server, int fd)
168 {
169 fd_m = fd;
170 server_m = server;
171 }
172
~ClientConnection()173 ClientConnection::~ClientConnection ()
174 {
175 NotifyList_t::iterator it;
176 for (it = notifyList_m.begin(); it != notifyList_m.end(); it++)
177 {
178 (*it)->removeChangeListener(this);
179 (*it)->decRefCount();
180 }
181 notifyList_m.clear();
182 if (server_m)
183 server_m->deregister (this);
184 close (fd_m);
185 }
186
Run(pth_sem_t * stop1)187 void ClientConnection::Run (pth_sem_t * stop1)
188 {
189 pth_event_t stop = pth_event (PTH_EVENT_SEM, stop1);
190 while (pth_event_status (stop) != PTH_STATUS_OCCURRED)
191 {
192 if (readmessage (stop) == -1)
193 break;
194 std::string msgType;
195 try
196 {
197 // Load a document
198 ticpp::Document doc;
199 debugStream("ClientConnection") << "PROCESSING MESSAGE:" << endlog << msg_m << endlog << "END OF MESSAGE" << endlog;
200 doc.LoadFromString(msg_m);
201
202 ticpp::Element* pMsg = doc.FirstChildElement();
203 msgType = pMsg->Value();
204 if (msgType == "read")
205 {
206 ticpp::Element* pRead = pMsg->FirstChildElement();
207 if (pRead->Value() == "object")
208 {
209 std::string id = pRead->GetAttribute("id");
210 Object* obj = ObjectController::instance()->getObject(id);
211 std::stringstream msg;
212 msg << "<read status='success'>" << obj->getValue() << "</read>" << std::endl;
213 obj->decRefCount();
214 debugStream("ClientConnection") << "SENDING MESSAGE:" << endlog << msg.str() << endlog << "END OF MESSAGE" << endlog;
215 sendmessage (msg.str(), stop);
216 }
217 else if (pRead->Value() == "objects")
218 {
219 if (pRead->NoChildren())
220 {
221 ObjectController::instance()->exportObjectValues(pRead);
222 }
223 else
224 {
225 ticpp::Iterator< ticpp::Element > pObjects;
226 for ( pObjects = pRead->FirstChildElement(); pObjects != pObjects.end(); pObjects++ )
227 {
228 if (pObjects->Value() == "object")
229 {
230 std::string id = pObjects->GetAttribute("id");
231 Object* obj = ObjectController::instance()->getObject(id);
232 pObjects->SetAttribute("value", obj->getValue());
233 obj->decRefCount();
234 }
235 else
236 throw "Unknown objects element";
237 }
238 }
239 pMsg->SetAttribute("status", "success");
240 sendmessage (doc.GetAsString(), stop);
241 }
242 else if (pRead->Value() == "config")
243 {
244 ticpp::Element* pConfig = pRead->FirstChildElement(false);
245 if (pConfig == 0)
246 {
247 ticpp::Element objects("objects");
248 ObjectController::instance()->exportXml(&objects);
249 pRead->LinkEndChild(&objects);
250
251 ticpp::Element rules("rules");
252 RuleServer::instance()->exportXml(&rules);
253 pRead->LinkEndChild(&rules);
254
255 ticpp::Element services("services");
256 Services::instance()->exportXml(&services);
257 pRead->LinkEndChild(&services);
258
259 ticpp::Element logging("logging");
260 Logging::instance()->exportXml(&logging);
261 pRead->LinkEndChild(&logging);
262 }
263 else if (pConfig->Value() == "objects")
264 {
265 ObjectController::instance()->exportXml(pConfig);
266 }
267 else if (pConfig->Value() == "rules")
268 {
269 RuleServer::instance()->exportXml(pConfig);
270 }
271 else if (pConfig->Value() == "services")
272 {
273 Services::instance()->exportXml(pConfig);
274 }
275 else if (pConfig->Value() == "logging")
276 {
277 Logging::instance()->exportXml(pConfig);
278 }
279 pMsg->SetAttribute("status", "success");
280 sendmessage (doc.GetAsString(), stop);
281 }
282 else if (pRead->Value() == "status")
283 {
284 ticpp::Element* pConfig = pRead->FirstChildElement(false);
285 if (pConfig == 0)
286 {
287 ticpp::Element timers("timers");
288 Services::instance()->getTimerManager()->statusXml(&timers);
289 pRead->LinkEndChild(&timers);
290
291 ticpp::Element rules("rules");
292 RuleServer::instance()->statusXml(&rules);
293 pRead->LinkEndChild(&rules);
294 }
295 else if (pConfig->Value() == "timers")
296 {
297 Services::instance()->getTimerManager()->statusXml(pConfig);
298 }
299 else if (pConfig->Value() == "rules")
300 {
301 RuleServer::instance()->statusXml(pConfig);
302 }
303 pMsg->SetAttribute("status", "success");
304 sendmessage (doc.GetAsString(), stop);
305 }
306 else if (pRead->Value() == "calendar")
307 {
308 int year, month, day, h,m;
309 time_t ts = time(0);
310 struct tm * date = localtime(&ts);
311 pRead->GetAttributeOrDefault("year", &year, 0);
312 pRead->GetAttributeOrDefault("month", &month, 0);
313 pRead->GetAttributeOrDefault("day", &day, 0);
314 if (year != 0 || month != 0 || day != 0) {
315 if (year == 0 && month == 0) {
316 date->tm_mday += day;
317 }
318 else {
319 if (year >= 1900)
320 year -= 1900;
321 if (month > 0)
322 date->tm_mon = month-1;
323 if (year > 0)
324 date->tm_year = year;
325 date->tm_mday = day;
326 }
327 ts = mktime(date);
328 pRead->SetAttribute("year", date->tm_year+1900);
329 pRead->SetAttribute("month", date->tm_mon+1);
330 pRead->SetAttribute("day", date->tm_mday);
331 }
332
333 SolarInfo info(date);
334 ticpp::Element* pConfig = pRead->FirstChildElement(false);
335 if (pConfig == 0)
336 {
337 bool isException = Services::instance()->getExceptionDays()->isException(ts);
338 ticpp::Element exceptionday("exception-day");
339 exceptionday.SetText(isException ? "true" : "false");
340 pRead->LinkEndChild(&exceptionday);
341
342 if (info.getSunrise(&m, &h)) {
343 ticpp::Element sunrise("sunrise");
344 sunrise.SetAttribute("hour", h);
345 sunrise.SetAttribute("min", m);
346 pRead->LinkEndChild(&sunrise);
347 }
348 if (info.getSunset(&m, &h)) {
349 ticpp::Element sunset("sunset");
350 sunset.SetAttribute("hour", h);
351 sunset.SetAttribute("min", m);
352 pRead->LinkEndChild(&sunset);
353 }
354 if (info.getNoon(&m, &h)) {
355 ticpp::Element noon("noon");
356 noon.SetAttribute("hour", h);
357 noon.SetAttribute("min", m);
358 pRead->LinkEndChild(&noon);
359 }
360 }
361 else if (pConfig->Value() == "exception-day")
362 {
363 bool isException = Services::instance()->getExceptionDays()->isException(ts);
364 pConfig->SetText(isException ? "true" : "false");
365 }
366 else if (pConfig->Value() == "sunrise")
367 {
368 if (!info.getSunrise(&m, &h))
369 throw "Error while calculating sunrise";
370 pConfig->SetAttribute("hour", h);
371 pConfig->SetAttribute("min", m);
372 }
373 else if (pConfig->Value() == "sunset")
374 {
375 if (!info.getSunset(&m, &h))
376 throw "Error while calculating sunset";
377 pConfig->SetAttribute("hour", h);
378 pConfig->SetAttribute("min", m);
379 }
380 else if (pConfig->Value() == "noon")
381 {
382 if (!info.getNoon(&m, &h))
383 throw "Error while calculating solar noon";
384 pConfig->SetAttribute("hour", h);
385 pConfig->SetAttribute("min", m);
386 }
387 pMsg->SetAttribute("status", "success");
388 sendmessage (doc.GetAsString(), stop);
389 }
390 else if (pRead->Value() == "version")
391 {
392 ticpp::Element value("value");
393 value.SetText(VERSION);
394 pRead->LinkEndChild(&value);
395
396 ticpp::Element features("features");
397 #ifdef HAVE_LIBCURL
398 ticpp::Element sms("sms");
399 features.LinkEndChild(&sms);
400 #endif
401 #ifdef HAVE_LIBESMTP
402 ticpp::Element email("e-mail");
403 features.LinkEndChild(&email);
404 #endif
405 #ifdef HAVE_MYSQL
406 ticpp::Element mysql("mysql");
407 features.LinkEndChild(&mysql);
408 #endif
409 #ifdef HAVE_LUA
410 ticpp::Element lua("lua");
411 features.LinkEndChild(&lua);
412 #endif
413 #ifdef HAVE_LOG4CPP
414 ticpp::Element log4cpp("log4cpp");
415 features.LinkEndChild(&log4cpp);
416 #endif
417
418 pRead->LinkEndChild(&features);
419
420 pMsg->SetAttribute("status", "success");
421 sendmessage (doc.GetAsString(), stop);
422 }
423 else
424 throw "Unknown read element";
425 }
426 else if (msgType == "write")
427 {
428 ticpp::Iterator< ticpp::Element > pWrite;
429 for ( pWrite = pMsg->FirstChildElement(); pWrite != pWrite.end(); pWrite++ )
430 {
431 if (pWrite->Value() == "object")
432 {
433 std::string id = pWrite->GetAttribute("id");
434 Object* obj = ObjectController::instance()->getObject(id);
435 obj->setValue(pWrite->GetAttribute("value"));
436 obj->decRefCount();
437 }
438 else if (pWrite->Value() == "config")
439 {
440 ticpp::Iterator< ticpp::Element > pConfigItem;
441 for ( pConfigItem = pWrite->FirstChildElement(); pConfigItem != pConfigItem.end(); pConfigItem++ )
442 {
443 if (pConfigItem->Value() == "objects")
444 ObjectController::instance()->importXml(&(*pConfigItem));
445 else if (pConfigItem->Value() == "rules")
446 RuleServer::instance()->importXml(&(*pConfigItem));
447 else if (pConfigItem->Value() == "services")
448 Services::instance()->importXml(&(*pConfigItem));
449 else if (pConfigItem->Value() == "logging")
450 Logging::instance()->importXml(&(*pConfigItem));
451 else
452 throw "Unknown config element";
453 }
454 }
455 else
456 throw "Unknown write element";
457 }
458 sendmessage ("<write status='success'/>\n", stop);
459 }
460 else if (msgType == "execute")
461 {
462 std::list<Action*> al;
463 int timeout;
464 int count = 0;
465 pMsg->GetAttributeOrDefault("timeout", &timeout, 60);
466 ticpp::Iterator< ticpp::Element > pExecute;
467 for ( pExecute = pMsg->FirstChildElement(); pExecute != pExecute.end(); pExecute++ )
468 {
469 if (pExecute->Value() == "action")
470 {
471 Action *action = Action::create(&(*pExecute));
472 action->execute();
473 al.push_back(action);
474 }
475 else if (pExecute->Value() == "rule-actions")
476 {
477 std::string id = pExecute->GetAttribute("id");
478 std::string list = pExecute->GetAttribute("list");
479 Rule* rule = RuleServer::instance()->getRule(id.c_str());
480 if (rule == 0)
481 throw "Unknown rule id";
482 if (list == "true")
483 rule->executeActionsTrue();
484 else if (list == "false")
485 rule->executeActionsFalse();
486 else
487 throw "Invalid list attribute. (Must be 'true' or 'false')";
488 }
489 else
490 throw "Unknown execute element";
491 }
492 while (!al.empty()) {
493 pth_yield(NULL);
494 if (al.front()->isFinished() || count == timeout) {
495 delete al.front();
496 al.pop_front();
497 }
498 else {
499 if (count++ == 0)
500 sendmessage ("<execute status='ongoing'/>\n", stop);
501 pth_sleep(1);
502 }
503 }
504
505 if (count == timeout)
506 sendmessage ("<execute status='timeout'/>\n", stop);
507 else
508 sendmessage ("<execute status='success'/>\n", stop);
509 }
510 else if (msgType == "admin")
511 {
512 ticpp::Iterator< ticpp::Element > pAdmin;
513 for ( pAdmin = pMsg->FirstChildElement(); pAdmin != pAdmin.end(); pAdmin++ )
514 {
515 if (pAdmin->Value() == "save")
516 {
517 std::string filename = pAdmin->GetAttribute("file");
518 if (filename == "")
519 filename = Services::instance()->getConfigFile();
520 if (filename == "")
521 throw "No file to write config to";
522 try
523 {
524 // Save a document
525 ticpp::Document doc;
526 ticpp::Declaration decl("1.0", "", "");
527 doc.LinkEndChild(&decl);
528
529 ticpp::Element pConfig("config");
530
531 ticpp::Element pServices("services");
532 Services::instance()->exportXml(&pServices);
533 pConfig.LinkEndChild(&pServices);
534 ticpp::Element pObjects("objects");
535 ObjectController::instance()->exportXml(&pObjects);
536 pConfig.LinkEndChild(&pObjects);
537 ticpp::Element pRules("rules");
538 RuleServer::instance()->exportXml(&pRules);
539 pConfig.LinkEndChild(&pRules);
540 ticpp::Element pLogging("logging");
541 Logging::instance()->exportXml(&pLogging);
542 pConfig.LinkEndChild(&pLogging);
543
544 doc.LinkEndChild(&pConfig);
545 doc.SaveFile(filename);
546 }
547 catch( ticpp::Exception& ex )
548 {
549 // If any function has an error, execution will enter here.
550 // Report the error
551 errorStream("ClientConnection") << "Unable to write config to file: " << ex.m_details << endlog;
552 throw "Error writing config to file";
553 }
554 }
555 else if (pAdmin->Value() == "notification")
556 {
557 ticpp::Iterator< ticpp::Element > pObjects;
558 for ( pObjects = pAdmin->FirstChildElement(); pObjects != pObjects.end(); pObjects++ )
559 {
560 if (pObjects->Value() == "register")
561 {
562 std::string id = pObjects->GetAttribute("id");
563 Object* obj = ObjectController::instance()->getObject(id);
564 notifyList_m.push_back(obj);
565 obj->addChangeListener(this);
566 }
567 else if (pObjects->Value() == "unregister")
568 {
569 std::string id = pObjects->GetAttribute("id");
570 Object* obj = ObjectController::instance()->getObject(id);
571 notifyList_m.remove(obj);
572 obj->decRefCount();
573 obj->removeChangeListener(this);
574 obj->decRefCount();
575 }
576 else if (pObjects->Value() == "registerall" || pObjects->Value() == "unregisterall")
577 {
578 NotifyList_t::iterator it;
579 for (it=notifyList_m.begin(); it != notifyList_m.end(); it++)
580 {
581 (*it)->removeChangeListener(this);
582 (*it)->decRefCount();
583 }
584 notifyList_m.clear();
585
586 if (pObjects->Value() == "registerall")
587 {
588 std::list<Object*> objList = ObjectController::instance()->getObjects();
589 std::list<Object*>::iterator it;
590 for (it=objList.begin(); it != objList.end(); it++)
591 {
592 notifyList_m.push_back((*it));
593 (*it)->addChangeListener(this);
594 }
595 }
596 }
597 else
598 throw "Unknown objects element";
599 }
600 }
601 else
602 throw "Unknown admin element";
603 }
604 sendmessage ("<admin status='success'/>\n", stop);
605 }
606 else
607 throw "Unknown element";
608 }
609 catch( const char* ex )
610 {
611 sendreject (ex, msgType, stop);
612 }
613 catch( ticpp::Exception& ex )
614 {
615 sendreject (ex.m_details.c_str(), msgType, stop);
616 }
617 }
618 pth_event_free (stop, PTH_FREE_THIS);
619 StopDelete ();
620 }
621
sendreject(const char * msgstr,const std::string & type,pth_event_t stop)622 int ClientConnection::sendreject (const char* msgstr, const std::string& type, pth_event_t stop)
623 {
624 std::stringstream msg;
625 if (type == "")
626 msg << "<error>" << msgstr << "</error>" << std::endl;
627 else
628 msg << "<" << type << " status='error'>" << msgstr << "</" << type << ">" << std::endl;
629 return sendmessage (msg.str(), stop);
630 }
631
sendmessage(std::string msg,pth_event_t stop)632 int ClientConnection::sendmessage (std::string msg, pth_event_t stop)
633 {
634 msg.push_back('\4');
635 return sendmessage(msg.length(), msg.c_str(), stop);
636 }
637
sendmessage(int size,const char * msg,pth_event_t stop)638 int ClientConnection::sendmessage (int size, const char * msg, pth_event_t stop)
639 {
640 int i;
641 int start = 0;
642
643 start = 0;
644 while (start < size)
645 {
646 i = pth_write_ev (fd_m, msg + start, size - start, stop);
647 if (i <= 0)
648 return -1;
649 start += i;
650 }
651 return 0;
652 }
653
readmessage(pth_event_t stop)654 int ClientConnection::readmessage (pth_event_t stop)
655 {
656 char buf[256];
657 int i;
658 std::stringstream msg;
659
660 if (msgbuf_m.size() > 0)
661 {
662 // we have already some data in the message buffer
663 std::string::size_type len = msgbuf_m.find_first_of('\004');
664 if (std::string::npos != len)
665 {
666 // Complete message in the buffer
667 msg_m = msgbuf_m.substr(0, len);
668 msgbuf_m.erase(0, len+1);
669 return 1;
670 }
671 msg << msgbuf_m;
672 }
673
674 while ((i = pth_read_ev (fd_m, &buf, 256, stop)) > 0)
675 {
676 std::string tstr(buf, i);
677 std::string::size_type len = tstr.find_first_of('\004');
678 if (std::string::npos != len)
679 {
680 // Complete message in the buffer
681 msg << tstr.substr(0, len);
682 msg_m = msg.str();
683 msgbuf_m = tstr.substr(len+1);
684 return 1;
685 }
686 msg << tstr;
687 }
688
689 return -1;
690 }
691
onChange(Object * object)692 void ClientConnection::onChange(Object* object)
693 {
694 // sendmessage ("<notify id=status='success'/>\n", stop);
695 std::stringstream msg;
696 msg << "<notify id='" << object->getID() << "'>" << object->getValue() << "</notify>" << std::endl;
697 sendmessage (msg.str(), NULL);
698 }
699
700