1 /*******************************************************************************
2   Copyright(c) 2016 Jasem Mutlaq. All rights reserved.
3 
4  INDI Qt Client
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB.  If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 *******************************************************************************/
20 
21 #include "baseclientqt.h"
22 
23 #include "base64.h"
24 #include "basedevice.h"
25 #include "locale_compat.h"
26 #include "indistandardproperty.h"
27 
28 #include <iostream>
29 #include <string>
30 #include <algorithm>
31 
32 #include <cstdlib>
33 
34 #define MAXINDIBUF 49152
35 
36 #if defined(_MSC_VER)
37 #define snprintf _snprintf
38 #pragma warning(push)
39 ///@todo Introduce plattform indipendent safe functions as macros to fix this
40 #pragma warning(disable : 4996)
41 #endif
42 
BaseClientQt(QObject * parent)43 INDI::BaseClientQt::BaseClientQt(QObject *parent) : QObject(parent), cServer("localhost"), cPort(7624)
44 {
45     sConnected = false;
46     verbose    = false;
47     lillp      = nullptr;
48 
49     timeout_sec = 3;
50     timeout_us  = 0;
51 
52     connect(&client_socket, SIGNAL(readyRead()), this, SLOT(listenINDI()));
53     connect(&client_socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
54             SLOT(processSocketError(QAbstractSocket::SocketError)));
55 }
56 
~BaseClientQt()57 INDI::BaseClientQt::~BaseClientQt()
58 {
59     clear();
60 }
61 
clear()62 void INDI::BaseClientQt::clear()
63 {
64     while (!cDevices.empty())
65     {
66         delete cDevices.back();
67         cDevices.pop_back();
68     }
69     cDevices.clear();
70     while (!blobModes.empty())
71     {
72         delete blobModes.back();
73         blobModes.pop_back();
74     }
75     blobModes.clear();
76 }
77 
setServer(const char * hostname,unsigned int port)78 void INDI::BaseClientQt::setServer(const char *hostname, unsigned int port)
79 {
80     cServer = hostname;
81     cPort   = port;
82 }
83 
watchDevice(const char * deviceName)84 void INDI::BaseClientQt::watchDevice(const char *deviceName)
85 {
86     // Watch for duplicates. Should have used std::set from the beginning but let's
87     // avoid changing API now.
88     if (std::find(cDeviceNames.begin(), cDeviceNames.end(), deviceName) != cDeviceNames.end())
89         return;
90 
91     cDeviceNames.push_back(deviceName);
92 }
93 
watchProperty(const char * deviceName,const char * propertyName)94 void INDI::BaseClientQt::watchProperty(const char *deviceName, const char *propertyName)
95 {
96     watchDevice(deviceName);
97     cWatchProperties[deviceName].insert(propertyName);
98 }
99 
connectServer()100 bool INDI::BaseClientQt::connectServer()
101 {
102     client_socket.connectToHost(cServer.c_str(), cPort);
103 
104     if (client_socket.waitForConnected(timeout_sec * 1000) == false)
105     {
106         sConnected = false;
107         return false;
108     }
109 
110     clear();
111 
112     lillp = newLilXML();
113 
114     sConnected = true;
115 
116     serverConnected();
117 
118     AutoCNumeric locale;
119 
120     QString getProp;
121     if (cDeviceNames.empty())
122     {
123         getProp = QString("<getProperties version='%1'/>\n").arg(QString::number(INDIV));
124 
125         client_socket.write(getProp.toLatin1());
126 
127         if (verbose)
128             std::cerr << getProp.toLatin1().constData() << std::endl;
129     }
130     else
131     {
132         for (auto &str : cDeviceNames)
133         {
134             getProp =
135                 QString("<getProperties version='%1' device='%2'/>\n").arg(QString::number(INDIV)).arg(str.c_str());
136 
137             client_socket.write(getProp.toLatin1());
138             if (verbose)
139                 std::cerr << getProp.toLatin1().constData() << std::endl;
140         }
141     }
142 
143     return true;
144 }
145 
disconnectServer()146 bool INDI::BaseClientQt::disconnectServer()
147 {
148     if (sConnected == false)
149         return true;
150 
151     sConnected = false;
152 
153     client_socket.close();
154     if (lillp)
155     {
156         delLilXML(lillp);
157         lillp = nullptr;
158     }
159 
160     clear();
161 
162     cDeviceNames.clear();
163 
164     serverDisconnected(0);
165 
166     return true;
167 }
168 
connectDevice(const char * deviceName)169 void INDI::BaseClientQt::connectDevice(const char *deviceName)
170 {
171     setDriverConnection(true, deviceName);
172 }
173 
disconnectDevice(const char * deviceName)174 void INDI::BaseClientQt::disconnectDevice(const char *deviceName)
175 {
176     setDriverConnection(false, deviceName);
177 }
178 
setDriverConnection(bool status,const char * deviceName)179 void INDI::BaseClientQt::setDriverConnection(bool status, const char *deviceName)
180 {
181     INDI::BaseDevice *drv                 = getDevice(deviceName);
182     ISwitchVectorProperty *drv_connection = nullptr;
183 
184     if (drv == nullptr)
185     {
186         IDLog("INDI::BaseClientQt: Error. Unable to find driver %s\n", deviceName);
187         return;
188     }
189 
190     drv_connection = drv->getSwitch(INDI::SP::CONNECTION);
191 
192     if (drv_connection == nullptr)
193         return;
194 
195     // If we need to connect
196     if (status)
197     {
198         // If there is no need to do anything, i.e. already connected.
199         if (drv_connection->sp[0].s == ISS_ON)
200             return;
201 
202         IUResetSwitch(drv_connection);
203         drv_connection->s       = IPS_BUSY;
204         drv_connection->sp[0].s = ISS_ON;
205         drv_connection->sp[1].s = ISS_OFF;
206 
207         sendNewSwitch(drv_connection);
208     }
209     else
210     {
211         // If there is no need to do anything, i.e. already disconnected.
212         if (drv_connection->sp[1].s == ISS_ON)
213             return;
214 
215         IUResetSwitch(drv_connection);
216         drv_connection->s       = IPS_BUSY;
217         drv_connection->sp[0].s = ISS_OFF;
218         drv_connection->sp[1].s = ISS_ON;
219 
220         sendNewSwitch(drv_connection);
221     }
222 }
223 
getDevice(const char * deviceName)224 INDI::BaseDevice *INDI::BaseClientQt::getDevice(const char *deviceName)
225 {
226     for (auto &dev : cDevices)
227     {
228         if (!strcmp(deviceName, dev->getDeviceName()))
229             return dev;
230     }
231     return nullptr;
232 }
233 
listenHelper(void * context)234 void *INDI::BaseClientQt::listenHelper(void *context)
235 {
236     (static_cast<INDI::BaseClientQt *>(context))->listenINDI();
237     return nullptr;
238 }
239 
listenINDI()240 void INDI::BaseClientQt::listenINDI()
241 {
242     char buffer[MAXINDIBUF];
243     char errorMsg[MAXRBUF];
244     int err_code = 0;
245 
246     XMLEle **nodes;
247     int inode = 0;
248 
249     if (sConnected == false)
250         return;
251 
252     while (client_socket.bytesAvailable() > 0)
253     {
254         qint64 readBytes = client_socket.read(buffer, MAXINDIBUF - 1);
255         if (readBytes > 0)
256             buffer[readBytes] = '\0';
257 
258         nodes = parseXMLChunk(lillp, buffer, readBytes, errorMsg);
259         if (!nodes)
260         {
261             if (errorMsg[0])
262             {
263                 fprintf(stderr, "Bad XML from %s/%ud: %s\n%s\n", cServer.c_str(), cPort, errorMsg, buffer);
264                 return;
265             }
266             return;
267         }
268         XMLEle *root = nodes[inode];
269         while (root)
270         {
271             if (verbose)
272                 prXMLEle(stderr, root, 0);
273 
274             if ((err_code = dispatchCommand(root, errorMsg)) < 0)
275             {
276                 // Silenty ignore property duplication errors
277                 if (err_code != INDI_PROPERTY_DUPLICATED)
278                 {
279                     IDLog("Dispatch command error(%d): %s\n", err_code, errorMsg);
280                     prXMLEle(stderr, root, 0);
281                 }
282             }
283 
284             delXMLEle(root); // not yet, delete and continue
285             inode++;
286             root = nodes[inode];
287         }
288         free(nodes);
289         inode = 0;
290     }
291 }
292 
dispatchCommand(XMLEle * root,char * errmsg)293 int INDI::BaseClientQt::dispatchCommand(XMLEle *root, char *errmsg)
294 {
295     if (!strcmp(tagXMLEle(root), "message"))
296         return messageCmd(root, errmsg);
297     else if (!strcmp(tagXMLEle(root), "delProperty"))
298         return delPropertyCmd(root, errmsg);
299     // Just ignore any getProperties we might get
300     else if (!strcmp(tagXMLEle(root), "getProperties"))
301         return INDI_PROPERTY_DUPLICATED;
302 
303     /* Get the device, if not available, create it */
304     INDI::BaseDevice *dp = findDev(root, 1, errmsg);
305     if (dp == nullptr)
306     {
307         strcpy(errmsg, "No device available and none was created");
308         return INDI_DEVICE_NOT_FOUND;
309     }
310 
311     // Ignore echoed newXXX
312     if (strstr(tagXMLEle(root), "new"))
313         return 0;
314 
315     // If device is set to BLOB_ONLY, we ignore everything else
316     // not related to blobs
317     if (getBLOBMode(dp->getDeviceName()) == B_ONLY)
318     {
319         if (!strcmp(tagXMLEle(root), "defBLOBVector"))
320             return dp->buildProp(root, errmsg);
321         else if (!strcmp(tagXMLEle(root), "setBLOBVector"))
322             return dp->setValue(root, errmsg);
323 
324         // Ignore everything else
325         return 0;
326     }
327 
328     // If we are asked to watch for specific properties only, we ignore everything else
329     if (cWatchProperties.size() > 0)
330     {
331         const char *device = findXMLAttValu(root, "device");
332         const char *name = findXMLAttValu(root, "name");
333         if (device && name)
334         {
335             if (cWatchProperties.find(device) == cWatchProperties.end() ||
336                     cWatchProperties[device].find(name) == cWatchProperties[device].end())
337                 return 0;
338         }
339     }
340 
341     if ((!strcmp(tagXMLEle(root), "defTextVector")) || (!strcmp(tagXMLEle(root), "defNumberVector")) ||
342             (!strcmp(tagXMLEle(root), "defSwitchVector")) || (!strcmp(tagXMLEle(root), "defLightVector")) ||
343             (!strcmp(tagXMLEle(root), "defBLOBVector")))
344         return dp->buildProp(root, errmsg);
345     else if (!strcmp(tagXMLEle(root), "setTextVector") || !strcmp(tagXMLEle(root), "setNumberVector") ||
346              !strcmp(tagXMLEle(root), "setSwitchVector") || !strcmp(tagXMLEle(root), "setLightVector") ||
347              !strcmp(tagXMLEle(root), "setBLOBVector"))
348         return dp->setValue(root, errmsg);
349 
350     return INDI_DISPATCH_ERROR;
351 }
352 
353 /* delete the property in the given device, including widgets and data structs.
354  * when last property is deleted, delete the device too.
355  * if no property name attribute at all, delete the whole device regardless.
356  * return 0 if ok, else -1 with reason in errmsg[].
357  */
delPropertyCmd(XMLEle * root,char * errmsg)358 int INDI::BaseClientQt::delPropertyCmd(XMLEle *root, char *errmsg)
359 {
360     XMLAtt *ap;
361     INDI::BaseDevice *dp;
362 
363     /* dig out device and optional property name */
364     dp = findDev(root, 0, errmsg);
365     if (!dp)
366         return INDI_DEVICE_NOT_FOUND;
367 
368     dp->checkMessage(root);
369 
370     ap = findXMLAtt(root, "name");
371 
372     /* Delete property if it exists, otherwise, delete the whole device */
373     if (ap)
374     {
375         INDI::Property *rProp = dp->getProperty(valuXMLAtt(ap));
376         if (rProp == nullptr)
377         {
378             snprintf(errmsg, MAXRBUF, "Cannot delete property %s as it is not defined yet. Check driver.", valuXMLAtt(ap));
379             return -1;
380         }
381 
382         removeProperty(rProp);
383         int errCode = dp->removeProperty(valuXMLAtt(ap), errmsg);
384 
385         return errCode;
386     }
387     // delete the whole device
388     else
389         return deleteDevice(dp->getDeviceName(), errmsg);
390 }
391 
deleteDevice(const char * devName,char * errmsg)392 int INDI::BaseClientQt::deleteDevice(const char *devName, char *errmsg)
393 {
394     std::vector<INDI::BaseDevice *>::iterator devicei;
395 
396     for (devicei = cDevices.begin(); devicei != cDevices.end();)
397     {
398         if (!strcmp(devName, (*devicei)->getDeviceName()))
399         {
400             removeDevice(*devicei);
401             delete *devicei;
402             devicei = cDevices.erase(devicei);
403             return 0;
404         }
405         else
406             ++devicei;
407     }
408 
409     snprintf(errmsg, MAXRBUF, "Device %s not found", devName);
410     return INDI_DEVICE_NOT_FOUND;
411 }
412 
findDev(const char * devName,char * errmsg)413 INDI::BaseDevice *INDI::BaseClientQt::findDev(const char *devName, char *errmsg)
414 {
415     std::vector<INDI::BaseDevice *>::const_iterator devicei;
416 
417     for (devicei = cDevices.begin(); devicei != cDevices.end(); devicei++)
418     {
419         if (!strcmp(devName, (*devicei)->getDeviceName()))
420             return (*devicei);
421     }
422 
423     snprintf(errmsg, MAXRBUF, "Device %s not found", devName);
424     return nullptr;
425 }
426 
427 /* add new device */
addDevice(XMLEle * dep,char * errmsg)428 INDI::BaseDevice *INDI::BaseClientQt::addDevice(XMLEle *dep, char *errmsg)
429 {
430     //devicePtr dp(new INDI::BaseDriver());
431     INDI::BaseDevice *dp = new INDI::BaseDevice();
432     XMLAtt *ap;
433     char *device_name;
434 
435     /* allocate new INDI::BaseDriver */
436     ap = findXMLAtt(dep, "device");
437     if (!ap)
438     {
439         strncpy(errmsg, "Unable to find device attribute in XML element. Cannot add device.", MAXRBUF);
440         return nullptr;
441     }
442 
443     device_name = valuXMLAtt(ap);
444 
445     dp->setMediator(this);
446     dp->setDeviceName(device_name);
447 
448     cDevices.push_back(dp);
449 
450     newDevice(dp);
451 
452     /* ok */
453     return dp;
454 }
455 
findDev(XMLEle * root,int create,char * errmsg)456 INDI::BaseDevice *INDI::BaseClientQt::findDev(XMLEle *root, int create, char *errmsg)
457 {
458     XMLAtt *ap;
459     INDI::BaseDevice *dp;
460     char *dn;
461 
462     /* get device name */
463     ap = findXMLAtt(root, "device");
464     if (!ap)
465     {
466         snprintf(errmsg, MAXRBUF, "No device attribute found in element %s", tagXMLEle(root));
467         return (nullptr);
468     }
469 
470     dn = valuXMLAtt(ap);
471 
472     if (*dn == '\0')
473     {
474         snprintf(errmsg, MAXRBUF, "Device name is empty! %s", tagXMLEle(root));
475         return (nullptr);
476     }
477 
478     dp = findDev(dn, errmsg);
479 
480     if (dp)
481         return dp;
482 
483     /* not found, create if ok */
484     if (create)
485         return (addDevice(root, errmsg));
486 
487     snprintf(errmsg, MAXRBUF, "INDI: <%s> no such device %s", tagXMLEle(root), dn);
488     return nullptr;
489 }
490 
491 /* a general message command received from the device.
492  * return 0 if ok, else -1 with reason in errmsg[].
493  */
messageCmd(XMLEle * root,char * errmsg)494 int INDI::BaseClientQt::messageCmd(XMLEle *root, char *errmsg)
495 {
496     INDI::BaseDevice *dp = findDev(root, 0, errmsg);
497 
498     if (dp)
499         dp->checkMessage(root);
500 
501     return (0);
502 }
503 
sendNewText(ITextVectorProperty * tvp)504 void INDI::BaseClientQt::sendNewText(ITextVectorProperty *tvp)
505 {
506     AutoCNumeric locale;
507 
508     tvp->s = IPS_BUSY;
509 
510     QString prop;
511 
512     prop += QString("<newTextVector\n");
513     prop += QString("  device='%1'\n").arg(tvp->device);
514     prop += QString("  name='%1'\n>").arg(tvp->name);
515 
516     for (int i = 0; i < tvp->ntp; i++)
517     {
518         prop += QString("  <oneText\n");
519         prop += QString("    name='%1'>\n").arg(tvp->tp[i].name);
520         prop += QString("      %1\n").arg(tvp->tp[i].text);
521         prop += QString("  </oneText>\n");
522     }
523     prop += QString("</newTextVector>\n");
524 
525     client_socket.write(prop.toLatin1());
526 }
527 
sendNewText(const char * deviceName,const char * propertyName,const char * elementName,const char * text)528 void INDI::BaseClientQt::sendNewText(const char *deviceName, const char *propertyName, const char *elementName,
529                                      const char *text)
530 {
531     INDI::BaseDevice *drv = getDevice(deviceName);
532 
533     if (drv == nullptr)
534         return;
535 
536     ITextVectorProperty *tvp = drv->getText(propertyName);
537 
538     if (tvp == nullptr)
539         return;
540 
541     IText *tp = IUFindText(tvp, elementName);
542 
543     if (tp == nullptr)
544         return;
545 
546     IUSaveText(tp, text);
547 
548     sendNewText(tvp);
549 }
550 
sendNewNumber(INumberVectorProperty * nvp)551 void INDI::BaseClientQt::sendNewNumber(INumberVectorProperty *nvp)
552 {
553     AutoCNumeric locale;
554 
555     nvp->s = IPS_BUSY;
556 
557     QString prop;
558 
559     prop += QString("<newNumberVector\n");
560     prop += QString("  device='%1'\n").arg(nvp->device);
561     prop += QString("  name='%1'\n>").arg(nvp->name);
562 
563     for (int i = 0; i < nvp->nnp; i++)
564     {
565         prop += QString("  <oneNumber\n");
566         prop += QString("    name='%1'>\n").arg(nvp->np[i].name);
567         prop += QString("      %1\n").arg(QString::number(nvp->np[i].value));
568         prop += QString("  </oneNumber>\n");
569     }
570     prop += QString("</newNumberVector>\n");
571 
572     client_socket.write(prop.toLatin1());
573 }
574 
sendNewNumber(const char * deviceName,const char * propertyName,const char * elementName,double value)575 void INDI::BaseClientQt::sendNewNumber(const char *deviceName, const char *propertyName, const char *elementName,
576                                        double value)
577 {
578     INDI::BaseDevice *drv = getDevice(deviceName);
579 
580     if (drv == nullptr)
581         return;
582 
583     INumberVectorProperty *nvp = drv->getNumber(propertyName);
584 
585     if (nvp == nullptr)
586         return;
587 
588     INumber *np = IUFindNumber(nvp, elementName);
589 
590     if (np == nullptr)
591         return;
592 
593     np->value = value;
594 
595     sendNewNumber(nvp);
596 }
597 
sendNewSwitch(ISwitchVectorProperty * svp)598 void INDI::BaseClientQt::sendNewSwitch(ISwitchVectorProperty *svp)
599 {
600     svp->s            = IPS_BUSY;
601     ISwitch *onSwitch = IUFindOnSwitch(svp);
602 
603     QString prop;
604 
605     prop += QString("<newSwitchVector\n");
606 
607     prop += QString("  device='%1'\n").arg(svp->device);
608     prop += QString("  name='%1'>\n").arg(svp->name);
609 
610     if (svp->r == ISR_1OFMANY && onSwitch)
611     {
612         prop += QString("  <oneSwitch\n");
613         prop += QString("    name='%1'>\n").arg(onSwitch->name);
614         prop += QString("      %1\n").arg((onSwitch->s == ISS_ON) ? "On" : "Off");
615         prop += QString("  </oneSwitch>\n");
616     }
617     else
618     {
619         for (int i = 0; i < svp->nsp; i++)
620         {
621             prop += QString("  <oneSwitch\n");
622             prop += QString("    name='%1'>\n").arg(svp->sp[i].name);
623             prop += QString("      %1\n").arg((svp->sp[i].s == ISS_ON) ? "On" : "Off");
624             prop += QString("  </oneSwitch>\n");
625         }
626     }
627 
628     prop += QString("</newSwitchVector>\n");
629 
630     client_socket.write(prop.toLatin1());
631 }
632 
sendNewSwitch(const char * deviceName,const char * propertyName,const char * elementName)633 void INDI::BaseClientQt::sendNewSwitch(const char *deviceName, const char *propertyName, const char *elementName)
634 {
635     INDI::BaseDevice *drv = getDevice(deviceName);
636 
637     if (drv == nullptr)
638         return;
639 
640     ISwitchVectorProperty *svp = drv->getSwitch(propertyName);
641 
642     if (svp == nullptr)
643         return;
644 
645     ISwitch *sp = IUFindSwitch(svp, elementName);
646 
647     if (sp == nullptr)
648         return;
649 
650     sp->s = ISS_ON;
651 
652     sendNewSwitch(svp);
653 }
654 
startBlob(const char * devName,const char * propName,const char * timestamp)655 void INDI::BaseClientQt::startBlob(const char *devName, const char *propName, const char *timestamp)
656 {
657     QString prop;
658 
659     prop += QString("<newBLOBVector\n");
660     prop += QString("  device='%1'\n").arg(devName);
661     prop += QString("  name='%1'\n").arg(propName);
662     prop += QString("  timestamp='%1'>\n").arg(timestamp);
663 
664     client_socket.write(prop.toLatin1());
665 }
666 
sendOneBlob(IBLOB * bp)667 void INDI::BaseClientQt::sendOneBlob(IBLOB *bp)
668 {
669     QString prop;
670     unsigned char *encblob;
671     int l;
672 
673     encblob = static_cast<unsigned char *>(malloc(4 * bp->size / 3 + 4));
674     l       = to64frombits(encblob, reinterpret_cast<const unsigned char *>(bp->blob), bp->size);
675 
676     prop += QString("  <oneBLOB\n");
677     prop += QString("    name='%1'\n").arg(bp->name);
678     prop += QString("    size='%1'\n").arg(QString::number(bp->size));
679     prop += QString("    enclen='%1'\n").arg(QString::number(l));
680     prop += QString("    format='%1'>\n").arg(bp->format);
681 
682     client_socket.write(prop.toLatin1());
683 
684     size_t written = 0;
685     size_t towrite = l;
686 
687     while ((int)written < l)
688     {
689         towrite   = ((l - written) > 72) ? 72 : l - written;
690         size_t wr = client_socket.write(reinterpret_cast<const char *>(encblob + written), towrite);
691         if (wr > 0)
692             written += wr;
693         if ((written % 72) == 0)
694             client_socket.write("\n");
695     }
696 
697     if ((written % 72) != 0)
698         client_socket.write("\n");
699 
700     free(encblob);
701 
702     client_socket.write("   </oneBLOB>\n");
703 }
704 
sendOneBlob(const char * blobName,unsigned int blobSize,const char * blobFormat,void * blobBuffer)705 void INDI::BaseClientQt::sendOneBlob(const char *blobName, unsigned int blobSize, const char *blobFormat,
706                                      void *blobBuffer)
707 {
708     unsigned char *encblob;
709     int l;
710 
711     encblob = static_cast<unsigned char *>(malloc(4 * blobSize / 3 + 4));
712     l       = to64frombits(encblob, reinterpret_cast<const unsigned char *>(blobBuffer), blobSize);
713 
714     QString prop;
715 
716     prop += QString("  <oneBLOB\n");
717     prop += QString("    name='%1'\n").arg(blobName);
718     prop += QString("    size='%1'\n").arg(QString::number(blobSize));
719     prop += QString("    enclen='%1'\n").arg(QString::number(l));
720     prop += QString("    format='%1'>\n").arg(blobFormat);
721 
722     client_socket.write(prop.toLatin1());
723 
724     size_t written = 0;
725     size_t towrite = l;
726 
727     while ((int)written < l)
728     {
729         towrite   = ((l - written) > 72) ? 72 : l - written;
730         size_t wr = client_socket.write(reinterpret_cast<const char *>(encblob + written), towrite);
731         if (wr > 0)
732             written += wr;
733         if ((written % 72) == 0)
734             client_socket.write("\n");
735     }
736 
737     if ((written % 72) != 0)
738         client_socket.write("\n");
739 
740     free(encblob);
741 
742     client_socket.write("   </oneBLOB>\n");
743 }
744 
finishBlob()745 void INDI::BaseClientQt::finishBlob()
746 {
747     client_socket.write("</newBLOBVector>\n");
748 }
749 
setBLOBMode(BLOBHandling blobH,const char * dev,const char * prop)750 void INDI::BaseClientQt::setBLOBMode(BLOBHandling blobH, const char *dev, const char *prop)
751 {
752     if (!dev[0])
753         return;
754 
755     BLOBMode *bMode = findBLOBMode(std::string(dev), prop ? std::string(prop) : std::string());
756 
757     if (bMode == nullptr)
758     {
759         BLOBMode *newMode = new BLOBMode();
760         newMode->device   = std::string(dev);
761         newMode->property = (prop ? std::string(prop) : std::string());
762         newMode->blobMode = blobH;
763         blobModes.push_back(newMode);
764     }
765     else
766     {
767         // If nothing changed, nothing to to do
768         if (bMode->blobMode == blobH)
769             return;
770 
771         bMode->blobMode = blobH;
772     }
773 
774     QString blobOpenTag;
775     QString blobEnableTag;
776     if (prop != nullptr)
777         blobOpenTag = QString("<enableBLOB device='%1' name='%2'>").arg(dev).arg(prop);
778     else
779         blobOpenTag = QString("<enableBLOB device='%1'>").arg(dev);
780 
781     switch (blobH)
782     {
783         case B_NEVER:
784             blobEnableTag = QString("%1Never</enableBLOB>\n").arg(blobOpenTag);
785             break;
786         case B_ALSO:
787             blobEnableTag = QString("%1Also</enableBLOB>\n").arg(blobOpenTag);
788             break;
789         case B_ONLY:
790             blobEnableTag = QString("%1Only</enableBLOB>\n").arg(blobOpenTag);
791             break;
792     }
793 
794     client_socket.write(blobEnableTag.toLatin1());
795 }
796 
getBLOBMode(const char * dev,const char * prop)797 BLOBHandling INDI::BaseClientQt::getBLOBMode(const char *dev, const char *prop)
798 {
799     BLOBHandling bHandle = B_ALSO;
800 
801     BLOBMode *bMode = findBLOBMode(dev, (prop ? std::string(prop) : std::string()));
802 
803     if (bMode)
804         bHandle = bMode->blobMode;
805 
806     return bHandle;
807 }
808 
findBLOBMode(const std::string & device,const std::string & property)809 INDI::BaseClientQt::BLOBMode *INDI::BaseClientQt::findBLOBMode(const std::string &device, const std::string &property)
810 {
811     for (auto &blob : blobModes)
812     {
813         if (blob->device == device && blob->property == property)
814             return blob;
815     }
816 
817     return nullptr;
818 }
819 
processSocketError(QAbstractSocket::SocketError socketError)820 void INDI::BaseClientQt::processSocketError(QAbstractSocket::SocketError socketError)
821 {
822     if (sConnected == false)
823         return;
824 
825     // TODO Handle what happens on socket failure!
826     INDI_UNUSED(socketError);
827     IDLog("Socket Error: %s\n", client_socket.errorString().toLatin1().constData());
828     fprintf(stderr, "INDI server %s/%d disconnected.\n", cServer.c_str(), cPort);
829     delLilXML(lillp);
830     client_socket.close();
831     // Let client handle server disconnection
832     serverDisconnected(-1);
833 }
834 
getDevices(std::vector<INDI::BaseDevice * > & deviceList,uint16_t driverInterface)835 bool INDI::BaseClientQt::getDevices(std::vector<INDI::BaseDevice *> &deviceList, uint16_t driverInterface )
836 {
837     for (INDI::BaseDevice *device : cDevices)
838     {
839         if (device->getDriverInterface() & driverInterface)
840             deviceList.push_back(device);
841     }
842 
843     return (deviceList.size() > 0);
844 }
845 
isServerConnected() const846 bool INDI::BaseClientQt::isServerConnected() const
847 {
848     return sConnected;
849 }
850 
851 #if defined(_MSC_VER)
852 #undef snprintf
853 #pragma warning(pop)
854 #endif
855