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