1 #if 0
2     INDI Driver Functions
3 
4     Copyright (C) 2003-2015 Jasem Mutlaq
5     Copyright (C) 2003-2006 Elwood C. Downey
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 
21 #endif
22 
23 #include "indidriver.h"
24 
25 #include "base64.h"
26 #include "eventloop.h"
27 #include "indicom.h"
28 #include "indidevapi.h"
29 #include "locale_compat.h"
30 
31 #include <errno.h>
32 #include <pthread.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
42 
43 #define MAXRBUF 2048
44 
45 /*! INDI property type */
46 enum
47 {
48     INDI_NUMBER,
49     INDI_SWITCH,
50     INDI_TEXT,
51     INDI_LIGHT,
52     INDI_BLOB,
53     INDI_UNKNOWN
54 };
55 
56 /* Return index of property property if already cached, -1 otherwise */
isPropDefined(const char * property_name,const char * device_name)57 int isPropDefined(const char *property_name, const char *device_name)
58 {
59     int i = 0;
60 
61     for (i = 0; i < nPropCache; i++)
62         if (!strcmp(property_name, propCache[i].propName) && !strcmp(device_name, propCache[i].devName))
63             return i;
64 
65     return -1;
66 }
67 
68 /* output a string expanding special characters into xml/html escape sequences */
69 /* N.B. You must free the returned buffer after use! */
escapeXML(const char * s,unsigned int MAX_BUF_SIZE)70 char *escapeXML(const char *s, unsigned int MAX_BUF_SIZE)
71 {
72     char *buf      = malloc(sizeof(char) * MAX_BUF_SIZE);
73     char *out      = buf;
74     unsigned int i = 0;
75 
76     for (i = 0; i <= strlen(s); i++)
77     {
78         switch (s[i])
79         {
80             case '&':
81                 strncpy(out, "&amp;", 5);
82                 out += 5;
83                 break;
84             case '\'':
85                 strncpy(out, "&apos;", 6);
86                 out += 6;
87                 break;
88             case '"':
89                 strncpy(out, "&quot;", 6);
90                 out += 6;
91                 break;
92             case '<':
93                 strncpy(out, "&lt;", 4);
94                 out += 4;
95                 break;
96             case '>':
97                 strncpy(out, "&gt;", 4);
98                 out += 4;
99                 break;
100             default:
101                 strncpy(out++, s + i, 1);
102                 break;
103         }
104     }
105 
106     return buf;
107 }
108 
109 /* tell Client to delete the property with given name on given device, or
110  * entire device if !name
111  */
IDDelete(const char * dev,const char * name,const char * fmt,...)112 void IDDelete(const char *dev, const char *name, const char *fmt, ...)
113 {
114     pthread_mutex_lock(&stdout_mutex);
115 
116     xmlv1();
117     printf("<delProperty\n  device='%s'\n", dev);
118     if (name)
119         printf(" name='%s'\n", name);
120     printf("  timestamp='%s'\n", timestamp());
121     if (fmt)
122     {
123         va_list ap;
124         va_start(ap, fmt);
125         char message[MAXINDIMESSAGE];
126         printf("  message='");
127         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
128         printf("%s'\n", entityXML(message));
129         va_end(ap);
130     }
131     printf("/>\n");
132     fflush(stdout);
133 
134     pthread_mutex_unlock(&stdout_mutex);
135 }
136 
137 /* tell indiserver we want to snoop on the given device/property.
138  * name ignored if NULL or empty.
139  */
IDSnoopDevice(const char * snooped_device,const char * snooped_property)140 void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
141 {
142     pthread_mutex_lock(&stdout_mutex);
143     xmlv1();
144     if (snooped_property && snooped_property[0])
145         printf("<getProperties version='%g' device='%s' name='%s'/>\n", INDIV, snooped_device, snooped_property);
146     else
147         printf("<getProperties version='%g' device='%s'/>\n", INDIV, snooped_device);
148     fflush(stdout);
149     pthread_mutex_unlock(&stdout_mutex);
150 }
151 
152 /* tell indiserver whether we want BLOBs from the given snooped device.
153  * silently ignored if given device is not already registered for snooping.
154  */
IDSnoopBLOBs(const char * snooped_device,const char * snooped_property,BLOBHandling bh)155 void IDSnoopBLOBs(const char *snooped_device, const char *snooped_property, BLOBHandling bh)
156 {
157     const char *how;
158 
159     switch (bh)
160     {
161         case B_NEVER:
162             how = "Never";
163             break;
164         case B_ALSO:
165             how = "Also";
166             break;
167         case B_ONLY:
168             how = "Only";
169             break;
170         default:
171             return;
172     }
173 
174     pthread_mutex_lock(&stdout_mutex);
175     xmlv1();
176     if (snooped_property && snooped_property[0])
177         printf("<enableBLOB device='%s' name='%s'>%s</enableBLOB>\n", snooped_device, snooped_property, how);
178     else
179         printf("<enableBLOB device='%s'>%s</enableBLOB>\n", snooped_device, how);
180     fflush(stdout);
181     pthread_mutex_unlock(&stdout_mutex);
182 }
183 
184 /* "INDI" wrappers to the more generic eventloop facility. */
185 
IEAddCallback(int readfiledes,IE_CBF * fp,void * p)186 int IEAddCallback(int readfiledes, IE_CBF *fp, void *p)
187 {
188     return (addCallback(readfiledes, (CBF *)fp, p));
189 }
190 
IERmCallback(int callbackid)191 void IERmCallback(int callbackid)
192 {
193     rmCallback(callbackid);
194 }
195 
IEAddTimer(int millisecs,IE_TCF * fp,void * p)196 int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
197 {
198     return (addTimer(millisecs, (TCF *)fp, p));
199 }
200 
IERmTimer(int timerid)201 void IERmTimer(int timerid)
202 {
203     rmTimer(timerid);
204 }
205 
IEAddWorkProc(IE_WPF * fp,void * p)206 int IEAddWorkProc(IE_WPF *fp, void *p)
207 {
208     return (addWorkProc((WPF *)fp, p));
209 }
210 
IERmWorkProc(int workprocid)211 void IERmWorkProc(int workprocid)
212 {
213     rmWorkProc(workprocid);
214 }
215 
IEDeferLoop(int maxms,int * flagp)216 int IEDeferLoop(int maxms, int *flagp)
217 {
218     return (deferLoop(maxms, flagp));
219 }
220 
IEDeferLoop0(int maxms,int * flagp)221 int IEDeferLoop0(int maxms, int *flagp)
222 {
223     return (deferLoop0(maxms, flagp));
224 }
225 
226 /* Update property switches in accord with states and names. */
IUUpdateSwitch(ISwitchVectorProperty * svp,ISState * states,char * names[],int n)227 int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
228 {
229     int i = 0;
230     ISwitch *sp;
231     char sn[MAXINDINAME];
232 
233     /* store On switch name */
234     if (svp->r == ISR_1OFMANY)
235     {
236         sp = IUFindOnSwitch(svp);
237         if (sp)
238             strncpy(sn, sp->name, MAXINDINAME);
239 
240         IUResetSwitch(svp);
241     }
242 
243     for (i = 0; i < n; i++)
244     {
245         sp = IUFindSwitch(svp, names[i]);
246 
247         if (!sp)
248         {
249             svp->s = IPS_IDLE;
250             IDSetSwitch(svp, "Error: %s is not a member of %s (%s) property.", names[i], svp->label, svp->name);
251             return -1;
252         }
253 
254         sp->s = states[i];
255     }
256 
257     /* Consistency checks for ISR_1OFMANY after update. */
258     if (svp->r == ISR_1OFMANY)
259     {
260         int t_count = 0;
261         for (i = 0; i < svp->nsp; i++)
262         {
263             if (svp->sp[i].s == ISS_ON)
264                 t_count++;
265         }
266         if (t_count != 1)
267         {
268             IUResetSwitch(svp);
269             sp = IUFindSwitch(svp, sn);
270             if (sp)
271                 sp->s = ISS_ON;
272             svp->s = IPS_IDLE;
273             IDSetSwitch(svp, "Error: invalid state switch for property %s (%s). %s.", svp->label, svp->name,
274                         t_count == 0 ? "No switch is on" : "Too many switches are on");
275             return -1;
276         }
277     }
278 
279     return 0;
280 }
281 
282 /* Update property numbers in accord with values and names */
IUUpdateNumber(INumberVectorProperty * nvp,double values[],char * names[],int n)283 int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
284 {
285     int i = 0;
286 
287     INumber *np;
288 
289     for (i = 0; i < n; i++)
290     {
291         np = IUFindNumber(nvp, names[i]);
292         if (!np)
293         {
294             nvp->s = IPS_IDLE;
295             IDSetNumber(nvp, "Error: %s is not a member of %s (%s) property.", names[i], nvp->label, nvp->name);
296             return -1;
297         }
298 
299         if (values[i] < np->min || values[i] > np->max)
300         {
301             nvp->s = IPS_ALERT;
302             IDSetNumber(nvp, "Error: Invalid range for %s (%s). Valid range is from %g to %g. Requested value is %g",
303                         np->label, np->name, np->min, np->max, values[i]);
304             return -1;
305         }
306     }
307 
308     /* First loop checks for error, second loop set all values atomically*/
309     for (i = 0; i < n; i++)
310     {
311         np        = IUFindNumber(nvp, names[i]);
312         np->value = values[i];
313     }
314 
315     return 0;
316 }
317 
318 /* Update property text in accord with texts and names */
IUUpdateText(ITextVectorProperty * tvp,char * texts[],char * names[],int n)319 int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
320 {
321     int i = 0;
322 
323     IText *tp;
324 
325     for (i = 0; i < n; i++)
326     {
327         tp = IUFindText(tvp, names[i]);
328         if (!tp)
329         {
330             tvp->s = IPS_IDLE;
331             IDSetText(tvp, "Error: %s is not a member of %s (%s) property.", names[i], tvp->label, tvp->name);
332             return -1;
333         }
334     }
335 
336     /* First loop checks for error, second loop set all values atomically*/
337     for (i = 0; i < n; i++)
338     {
339         tp = IUFindText(tvp, names[i]);
340         IUSaveText(tp, texts[i]);
341     }
342 
343     return 0;
344 }
345 
346 /* Update property BLOB in accord with BLOBs and names */
IUUpdateBLOB(IBLOBVectorProperty * bvp,int sizes[],int blobsizes[],char * blobs[],char * formats[],char * names[],int n)347 int IUUpdateBLOB(IBLOBVectorProperty *bvp, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[],
348                  int n)
349 {
350     int i = 0;
351 
352     IBLOB *bp;
353 
354     for (i = 0; i < n; i++)
355     {
356         bp = IUFindBLOB(bvp, names[i]);
357         if (!bp)
358         {
359             bvp->s = IPS_IDLE;
360             IDSetBLOB(bvp, "Error: %s is not a member of %s (%s) property.", names[i], bvp->label, bvp->name);
361             return -1;
362         }
363     }
364 
365     /* First loop checks for error, second loop set all values atomically*/
366     for (i = 0; i < n; i++)
367     {
368         bp = IUFindBLOB(bvp, names[i]);
369         IUSaveBLOB(bp, sizes[i], blobsizes[i], blobs[i], formats[i]);
370     }
371 
372     return 0;
373 }
374 
IUSaveBLOB(IBLOB * bp,int size,int blobsize,char * blob,char * format)375 int IUSaveBLOB(IBLOB *bp, int size, int blobsize, char *blob, char *format)
376 {
377     bp->bloblen = blobsize;
378     bp->size    = size;
379     bp->blob    = blob;
380     strncpy(bp->format, format, MAXINDIFORMAT);
381     return 0;
382 }
383 
IUFillSwitch(ISwitch * sp,const char * name,const char * label,ISState s)384 void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
385 {
386     char *escapedName  = escapeXML(name, MAXINDINAME);
387     char *escapedLabel = escapeXML(label, MAXINDILABEL);
388 
389     strncpy(sp->name, escapedName, MAXINDINAME);
390     if (label[0])
391         strncpy(sp->label, escapedLabel, MAXINDILABEL);
392     else
393         strncpy(sp->label, escapedName, MAXINDILABEL);
394     sp->s   = s;
395     sp->svp = NULL;
396     sp->aux = NULL;
397 
398     free(escapedName);
399     free(escapedLabel);
400 }
401 
IUFillLight(ILight * lp,const char * name,const char * label,IPState s)402 void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
403 {
404     char *escapedName  = escapeXML(name, MAXINDINAME);
405     char *escapedLabel = escapeXML(label, MAXINDILABEL);
406 
407     strncpy(lp->name, escapedName, MAXINDINAME);
408     if (label[0])
409         strncpy(lp->label, escapedLabel, MAXINDILABEL);
410     else
411         strncpy(lp->label, escapedName, MAXINDILABEL);
412     lp->s   = s;
413     lp->lvp = NULL;
414     lp->aux = NULL;
415 
416     free(escapedName);
417     free(escapedLabel);
418 }
419 
IUFillNumber(INumber * np,const char * name,const char * label,const char * format,double min,double max,double step,double value)420 void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max,
421                   double step, double value)
422 {
423     char *escapedName  = escapeXML(name, MAXINDINAME);
424     char *escapedLabel = escapeXML(label, MAXINDILABEL);
425 
426     strncpy(np->name, escapedName, MAXINDINAME);
427     if (label[0])
428         strncpy(np->label, escapedLabel, MAXINDILABEL);
429     else
430         strncpy(np->label, escapedName, MAXINDILABEL);
431     strncpy(np->format, format, MAXINDIFORMAT);
432 
433     np->min   = min;
434     np->max   = max;
435     np->step  = step;
436     np->value = value;
437     np->nvp   = NULL;
438     np->aux0  = NULL;
439     np->aux1  = NULL;
440 
441     free(escapedName);
442     free(escapedLabel);
443 }
444 
IUFillText(IText * tp,const char * name,const char * label,const char * initialText)445 void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
446 {
447     char *escapedName  = escapeXML(name, MAXINDINAME);
448     char *escapedLabel = escapeXML(label, MAXINDILABEL);
449 
450     strncpy(tp->name, escapedName, MAXINDINAME);
451 
452     if (label[0])
453         strncpy(tp->label, escapedLabel, MAXINDILABEL);
454     else
455         strncpy(tp->label, escapedName, MAXINDILABEL);
456 
457     if (tp->text && tp->text[0])
458         free(tp->text);
459     tp->text = NULL;
460 
461     tp->tvp  = NULL;
462     tp->aux0 = NULL;
463     tp->aux1 = NULL;
464 
465     if (initialText && strlen(initialText) > 0)
466         IUSaveText(tp, initialText);
467 
468     free(escapedName);
469     free(escapedLabel);
470 }
471 
IUFillBLOB(IBLOB * bp,const char * name,const char * label,const char * format)472 void IUFillBLOB(IBLOB *bp, const char *name, const char *label, const char *format)
473 {
474     char *escapedName  = escapeXML(name, MAXINDINAME);
475     char *escapedLabel = escapeXML(label, MAXINDILABEL);
476 
477     memset(bp, 0, sizeof(IBLOB));
478     strncpy(bp->name, escapedName, MAXINDINAME);
479 
480     if (label[0])
481         strncpy(bp->label, escapedLabel, MAXINDILABEL);
482     else
483         strncpy(bp->label, escapedName, MAXINDILABEL);
484 
485     strncpy(bp->format, format, MAXINDIBLOBFMT);
486     bp->blob    = 0;
487     bp->bloblen = 0;
488     bp->size    = 0;
489     bp->bvp     = 0;
490     bp->aux0    = 0;
491     bp->aux1    = 0;
492     bp->aux2    = 0;
493 
494     free(escapedName);
495     free(escapedLabel);
496 }
497 
IUFillSwitchVector(ISwitchVectorProperty * svp,ISwitch * sp,int nsp,const char * dev,const char * name,const char * label,const char * group,IPerm p,ISRule r,double timeout,IPState s)498 void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name,
499                         const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
500 {
501     char *escapedName  = escapeXML(name, MAXINDINAME);
502     char *escapedLabel = escapeXML(label, MAXINDILABEL);
503 
504     strncpy(svp->device, dev, MAXINDIDEVICE);
505     strncpy(svp->name, escapedName, MAXINDINAME);
506 
507     if (label[0])
508         strncpy(svp->label, escapedLabel, MAXINDILABEL);
509     else
510         strncpy(svp->label, escapedName, MAXINDILABEL);
511     strncpy(svp->group, group, MAXINDIGROUP);
512     strcpy(svp->timestamp, "");
513 
514     svp->p       = p;
515     svp->r       = r;
516     svp->timeout = timeout;
517     svp->s       = s;
518     svp->sp      = sp;
519     svp->nsp     = nsp;
520 
521     free(escapedName);
522     free(escapedLabel);
523 }
524 
IUFillLightVector(ILightVectorProperty * lvp,ILight * lp,int nlp,const char * dev,const char * name,const char * label,const char * group,IPState s)525 void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name,
526                        const char *label, const char *group, IPState s)
527 {
528     char *escapedName  = escapeXML(name, MAXINDINAME);
529     char *escapedLabel = escapeXML(label, MAXINDILABEL);
530 
531     strncpy(lvp->device, dev, MAXINDIDEVICE);
532     strncpy(lvp->name, escapedName, MAXINDINAME);
533 
534     if (label[0])
535         strncpy(lvp->label, escapedLabel, MAXINDILABEL);
536     else
537         strncpy(lvp->label, escapedName, MAXINDILABEL);
538     strncpy(lvp->group, group, MAXINDIGROUP);
539     strcpy(lvp->timestamp, "");
540 
541     lvp->s   = s;
542     lvp->lp  = lp;
543     lvp->nlp = nlp;
544 
545     free(escapedName);
546     free(escapedLabel);
547 }
548 
IUFillNumberVector(INumberVectorProperty * nvp,INumber * np,int nnp,const char * dev,const char * name,const char * label,const char * group,IPerm p,double timeout,IPState s)549 void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name,
550                         const char *label, const char *group, IPerm p, double timeout, IPState s)
551 {
552     char *escapedName  = escapeXML(name, MAXINDINAME);
553     char *escapedLabel = escapeXML(label, MAXINDILABEL);
554 
555     strncpy(nvp->device, dev, MAXINDIDEVICE);
556     strncpy(nvp->name, escapedName, MAXINDINAME);
557 
558     if (label[0])
559         strncpy(nvp->label, escapedLabel, MAXINDILABEL);
560     else
561         strncpy(nvp->label, escapedName, MAXINDILABEL);
562     strncpy(nvp->group, group, MAXINDIGROUP);
563     strcpy(nvp->timestamp, "");
564 
565     nvp->p       = p;
566     nvp->timeout = timeout;
567     nvp->s       = s;
568     nvp->np      = np;
569     nvp->nnp     = nnp;
570 
571     free(escapedName);
572     free(escapedLabel);
573 }
574 
IUFillTextVector(ITextVectorProperty * tvp,IText * tp,int ntp,const char * dev,const char * name,const char * label,const char * group,IPerm p,double timeout,IPState s)575 void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name,
576                       const char *label, const char *group, IPerm p, double timeout, IPState s)
577 {
578     char *escapedName  = escapeXML(name, MAXINDINAME);
579     char *escapedLabel = escapeXML(label, MAXINDILABEL);
580 
581     strncpy(tvp->device, dev, MAXINDIDEVICE);
582     strncpy(tvp->name, escapedName, MAXINDINAME);
583 
584     if (label[0])
585         strncpy(tvp->label, escapedLabel, MAXINDILABEL);
586     else
587         strncpy(tvp->label, escapedName, MAXINDILABEL);
588     strncpy(tvp->group, group, MAXINDIGROUP);
589     strcpy(tvp->timestamp, "");
590 
591     tvp->p       = p;
592     tvp->timeout = timeout;
593     tvp->s       = s;
594     tvp->tp      = tp;
595     tvp->ntp     = ntp;
596 
597     free(escapedName);
598     free(escapedLabel);
599 }
600 
IUFillBLOBVector(IBLOBVectorProperty * bvp,IBLOB * bp,int nbp,const char * dev,const char * name,const char * label,const char * group,IPerm p,double timeout,IPState s)601 void IUFillBLOBVector(IBLOBVectorProperty *bvp, IBLOB *bp, int nbp, const char *dev, const char *name,
602                       const char *label, const char *group, IPerm p, double timeout, IPState s)
603 {
604     char *escapedName  = escapeXML(name, MAXINDINAME);
605     char *escapedLabel = escapeXML(label, MAXINDILABEL);
606 
607     memset(bvp, 0, sizeof(IBLOBVectorProperty));
608     strncpy(bvp->device, dev, MAXINDIDEVICE);
609     strncpy(bvp->name, escapedName, MAXINDINAME);
610 
611     if (label[0])
612         strncpy(bvp->label, escapedLabel, MAXINDILABEL);
613     else
614         strncpy(bvp->label, escapedName, MAXINDILABEL);
615 
616     strncpy(bvp->group, group, MAXINDIGROUP);
617     strcpy(bvp->timestamp, "");
618 
619     bvp->p       = p;
620     bvp->timeout = timeout;
621     bvp->s       = s;
622     bvp->bp      = bp;
623     bvp->nbp     = nbp;
624 
625     free(escapedName);
626     free(escapedLabel);
627 }
628 
629 /*****************************************************************************
630  * convenience functions for use in your implementation of ISSnoopDevice().
631  */
632 
633 /* crack the snooped driver setNumberVector or defNumberVector message into
634  * the given INumberVectorProperty.
635  * return 0 if type, device and name match and all members are present, else
636  * return -1
637  */
IUSnoopNumber(XMLEle * root,INumberVectorProperty * nvp)638 int IUSnoopNumber(XMLEle *root, INumberVectorProperty *nvp)
639 {
640     char *dev, *name;
641     XMLEle *ep;
642     int i;
643 
644     /* check and crack type, device, name and state */
645     if (strcmp(tagXMLEle(root) + 3, "NumberVector") || crackDN(root, &dev, &name, NULL) < 0)
646         return (-1);
647     if (strcmp(dev, nvp->device) || strcmp(name, nvp->name))
648         return (-1); /* not this property */
649     (void)crackIPState(findXMLAttValu(root, "state"), &nvp->s);
650 
651     /* match each INumber with a oneNumber */
652     locale_char_t *orig = indi_locale_C_numeric_push();
653     for (i = 0; i < nvp->nnp; i++)
654     {
655         for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
656         {
657             if (!strcmp(tagXMLEle(ep) + 3, "Number") && !strcmp(nvp->np[i].name, findXMLAttValu(ep, "name")))
658             {
659                 if (f_scansexa(pcdataXMLEle(ep), &nvp->np[i].value) < 0)
660                 {
661                     indi_locale_C_numeric_pop(orig);
662                     return (-1); /* bad number format */
663                 }
664                 break;
665             }
666         }
667         if (!ep)
668         {
669             indi_locale_C_numeric_pop(orig);
670             return (-1); /* element not found */
671         }
672     }
673     indi_locale_C_numeric_pop(orig);
674 
675     /* ok */
676     return (0);
677 }
678 
679 /* crack the snooped driver setTextVector or defTextVector message into
680  * the given ITextVectorProperty.
681  * return 0 if type, device and name match and all members are present, else
682  * return -1
683  */
IUSnoopText(XMLEle * root,ITextVectorProperty * tvp)684 int IUSnoopText(XMLEle *root, ITextVectorProperty *tvp)
685 {
686     char *dev, *name;
687     XMLEle *ep;
688     int i;
689 
690     /* check and crack type, device, name and state */
691     if (strcmp(tagXMLEle(root) + 3, "TextVector") || crackDN(root, &dev, &name, NULL) < 0)
692         return (-1);
693     if (strcmp(dev, tvp->device) || strcmp(name, tvp->name))
694         return (-1); /* not this property */
695     (void)crackIPState(findXMLAttValu(root, "state"), &tvp->s);
696 
697     /* match each IText with a oneText */
698     for (i = 0; i < tvp->ntp; i++)
699     {
700         for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
701         {
702             if (!strcmp(tagXMLEle(ep) + 3, "Text") && !strcmp(tvp->tp[i].name, findXMLAttValu(ep, "name")))
703             {
704                 IUSaveText(&tvp->tp[i], pcdataXMLEle(ep));
705                 break;
706             }
707         }
708         if (!ep)
709             return (-1); /* element not found */
710     }
711 
712     /* ok */
713     return (0);
714 }
715 
716 /* crack the snooped driver setLightVector or defLightVector message into
717  * the given ILightVectorProperty. it is not necessary that all ILight names
718  * be found.
719  * return 0 if type, device and name match, else return -1.
720  */
IUSnoopLight(XMLEle * root,ILightVectorProperty * lvp)721 int IUSnoopLight(XMLEle *root, ILightVectorProperty *lvp)
722 {
723     char *dev, *name;
724     XMLEle *ep;
725     int i;
726 
727     /* check and crack type, device, name and state */
728     if (strcmp(tagXMLEle(root) + 3, "LightVector") || crackDN(root, &dev, &name, NULL) < 0)
729         return (-1);
730     if (strcmp(dev, lvp->device) || strcmp(name, lvp->name))
731         return (-1); /* not this property */
732 
733     (void)crackIPState(findXMLAttValu(root, "state"), &lvp->s);
734 
735     /* match each oneLight with one ILight */
736     for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
737     {
738         if (!strcmp(tagXMLEle(ep) + 3, "Light"))
739         {
740             const char *name = findXMLAttValu(ep, "name");
741             for (i = 0; i < lvp->nlp; i++)
742             {
743                 if (!strcmp(lvp->lp[i].name, name))
744                 {
745                     if (crackIPState(pcdataXMLEle(ep), &lvp->lp[i].s) < 0)
746                     {
747                         return (-1); /* unrecognized state */
748                     }
749                     break;
750                 }
751             }
752         }
753     }
754 
755     /* ok */
756     return (0);
757 }
758 
759 /* crack the snooped driver setSwitchVector or defSwitchVector message into the
760  * given ISwitchVectorProperty. it is not necessary that all ISwitch names be
761  * found.
762  * return 0 if type, device and name match, else return -1.
763  */
IUSnoopSwitch(XMLEle * root,ISwitchVectorProperty * svp)764 int IUSnoopSwitch(XMLEle *root, ISwitchVectorProperty *svp)
765 {
766     char *dev, *name;
767     XMLEle *ep;
768     int i;
769 
770     /* check and crack type, device, name and state */
771     if (strcmp(tagXMLEle(root) + 3, "SwitchVector") || crackDN(root, &dev, &name, NULL) < 0)
772         return (-1);
773     if (strcmp(dev, svp->device) || strcmp(name, svp->name))
774         return (-1); /* not this property */
775     (void)crackIPState(findXMLAttValu(root, "state"), &svp->s);
776 
777     /* match each oneSwitch with one ISwitch */
778     for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
779     {
780         if (!strcmp(tagXMLEle(ep) + 3, "Switch"))
781         {
782             const char *name = findXMLAttValu(ep, "name");
783             for (i = 0; i < svp->nsp; i++)
784             {
785                 if (!strcmp(svp->sp[i].name, name))
786                 {
787                     if (crackISState(pcdataXMLEle(ep), &svp->sp[i].s) < 0)
788                     {
789                         return (-1); /* unrecognized state */
790                     }
791                     break;
792                 }
793             }
794         }
795     }
796 
797     /* ok */
798     return (0);
799 }
800 
801 /* crack the snooped driver setBLOBVector message into the given
802  * IBLOBVectorProperty. it is not necessary that all IBLOB names be found.
803  * return 0 if type, device and name match, else return -1.
804  * N.B. we assume any existing blob in bvp has been malloced, which we free
805  *   and replace with a newly malloced blob if found.
806  */
IUSnoopBLOB(XMLEle * root,IBLOBVectorProperty * bvp)807 int IUSnoopBLOB(XMLEle *root, IBLOBVectorProperty *bvp)
808 {
809     char *dev, *name;
810     XMLEle *ep;
811 
812     /* check and crack type, device, name and state */
813     if (strcmp(tagXMLEle(root), "setBLOBVector") || crackDN(root, &dev, &name, NULL) < 0)
814         return (-1);
815 
816     if (strcmp(dev, bvp->device) || strcmp(name, bvp->name))
817         return (-1); /* not this property */
818 
819     crackIPState(findXMLAttValu(root, "state"), &bvp->s);
820 
821     for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
822     {
823         if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
824         {
825             XMLAtt *na = findXMLAtt(ep, "name");
826             if (na == NULL)
827                 return (-1);
828 
829             IBLOB *bp = IUFindBLOB(bvp, valuXMLAtt(na));
830 
831             if (bp == NULL)
832                 return (-1);
833 
834             XMLAtt *fa = findXMLAtt(ep, "format");
835             XMLAtt *sa = findXMLAtt(ep, "size");
836             XMLAtt *ec = findXMLAtt(ep, "enclen");
837             if (fa && sa && ec)
838             {
839                 int enclen  = atoi(valuXMLAtt(ec));
840                 bp->blob    = realloc(bp->blob, 3 * enclen / 4);
841                 bp->bloblen = from64tobits_fast(bp->blob, pcdataXMLEle(ep), enclen);
842                 strncpy(bp->format, valuXMLAtt(fa), MAXINDIFORMAT);
843                 bp->size = atoi(valuXMLAtt(sa));
844             }
845         }
846     }
847 
848     /* ok */
849     return (0);
850 }
851 
852 /* callback when INDI client message arrives on stdin.
853  * collect and dispatch when see outter element closure.
854  * exit if OS trouble or see incompatable INDI version.
855  * arg is not used.
856  */
clientMsgCB(int fd,void * arg)857 void clientMsgCB(int fd, void *arg)
858 {
859     (void)arg;
860     char buf[MAXRBUF], msg[MAXRBUF], *bp;
861     int nr;
862 
863     /* one read */
864     nr = read(fd, buf, sizeof(buf));
865     if (nr < 0)
866     {
867         fprintf(stderr, "%s: %s\n", me, strerror(errno));
868         exit(1);
869     }
870     if (nr == 0)
871     {
872         fprintf(stderr, "%s: EOF\n", me);
873         exit(1);
874     }
875 
876     /* crack and dispatch when complete */
877     for (bp = buf; nr-- > 0; bp++)
878     {
879         XMLEle *root = readXMLEle(clixml, *bp, msg);
880         if (root)
881         {
882             if (dispatch(root, msg) < 0)
883                 fprintf(stderr, "%s dispatch error: %s\n", me, msg);
884             delXMLEle(root);
885         }
886         else if (msg[0])
887             fprintf(stderr, "%s XML error: %s\n", me, msg);
888     }
889 }
890 
891 /* crack the given INDI XML element and call driver's IS* entry points as they
892  *   are recognized.
893  * return 0 if ok else -1 with reason in msg[].
894  * N.B. exit if getProperties does not proclaim a compatible version.
895  */
dispatch(XMLEle * root,char msg[])896 int dispatch(XMLEle *root, char msg[])
897 {
898     char *rtag = tagXMLEle(root);
899     XMLEle *ep;
900     int n, i = 0;
901 
902     if (verbose)
903         prXMLEle(stderr, root, 0);
904 
905     if (!strcmp(rtag, "getProperties"))
906     {
907         XMLAtt *ap, *name, *dev;
908         double v;
909 
910         /* check version */
911         ap = findXMLAtt(root, "version");
912         if (!ap)
913         {
914             fprintf(stderr, "%s: getProperties missing version\n", me);
915             exit(1);
916         }
917         v = atof(valuXMLAtt(ap));
918         if (v > INDIV)
919         {
920             fprintf(stderr, "%s: client version %g > %g\n", me, v, INDIV);
921             exit(1);
922         }
923 
924         // Get device
925         dev = findXMLAtt(root, "device");
926 
927         // Get property name
928         name = findXMLAtt(root, "name");
929 
930         if (name && dev)
931         {
932             int index = isPropDefined(valuXMLAtt(name), valuXMLAtt(dev));
933             if (index < 0)
934                 return 0;
935 
936             ROSC *prop = propCache + index;
937             switch (prop->type)
938             {
939                 /* JM 2019-07-18: Why are we using setXXX here? should be defXXX */
940                 case INDI_NUMBER:
941                     //IDSetNumber((INumberVectorProperty *)(prop->ptr), NULL);
942                     IDDefNumber((INumberVectorProperty *)(prop->ptr), NULL);
943                     return 0;
944 
945                 case INDI_SWITCH:
946                     //IDSetSwitch((ISwitchVectorProperty *)(prop->ptr), NULL);
947                     IDDefSwitch((ISwitchVectorProperty *)(prop->ptr), NULL);
948                     return 0;
949 
950                 case INDI_TEXT:
951                     //IDSetText((ITextVectorProperty *)(prop->ptr), NULL);
952                     IDDefText((ITextVectorProperty *)(prop->ptr), NULL);
953                     return 0;
954 
955                 case INDI_BLOB:
956                     //IDSetBLOB((IBLOBVectorProperty *)(prop->ptr), NULL);
957                     IDDefBLOB((IBLOBVectorProperty *)(prop->ptr), NULL);
958                     return 0;
959                 default:
960                     return 0;
961             }
962         }
963 
964         ISGetProperties(dev ? valuXMLAtt(dev) : NULL);
965         return (0);
966     }
967 
968     /* other commands might be from a snooped device.
969          * we don't know here which devices are being snooped so we send
970          * all remaining valid messages
971          */
972     if (!strcmp(rtag, "setNumberVector") || !strcmp(rtag, "setTextVector") || !strcmp(rtag, "setLightVector") ||
973         !strcmp(rtag, "setSwitchVector") || !strcmp(rtag, "setBLOBVector") || !strcmp(rtag, "defNumberVector") ||
974         !strcmp(rtag, "defTextVector") || !strcmp(rtag, "defLightVector") || !strcmp(rtag, "defSwitchVector") ||
975         !strcmp(rtag, "defBLOBVector") || !strcmp(rtag, "message") || !strcmp(rtag, "delProperty"))
976     {
977         ISSnoopDevice(root);
978         return (0);
979     }
980 
981     char *dev, *name;
982     /* pull out device and name */
983     if (crackDN(root, &dev, &name, msg) < 0)
984         return (-1);
985 
986     if (isPropDefined(name, dev) < 0)
987     {
988         snprintf(msg, MAXRBUF, "Property %s is not defined in %s.", name, dev);
989         return -1;
990     }
991 
992     /* ensure property is not RO */
993     for (i = 0; i < nPropCache; i++)
994     {
995         if (!strcmp(propCache[i].propName, name) && !strcmp(propCache[i].devName, dev))
996         {
997             if (propCache[i].perm == IP_RO)
998             {
999                 snprintf(msg, MAXRBUF, "Cannot set read-only property %s", name);
1000                 return -1;
1001             }
1002             else
1003                 break;
1004         }
1005     }
1006 
1007     /* check tag in surmised decreasing order of likelyhood */
1008 
1009     if (!strcmp(rtag, "newNumberVector"))
1010     {
1011         static double *doubles;
1012         static char **names;
1013         static int maxn;
1014 
1015         /* seed for reallocs */
1016         if (!doubles)
1017         {
1018             doubles = (double *)malloc(1);
1019             names   = (char **)malloc(1);
1020         }
1021 
1022         // Set locale to C and save previous value
1023         locale_char_t *orig = indi_locale_C_numeric_push();
1024 
1025         /* pull out each name/value pair */
1026         for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1027         {
1028             if (strcmp(tagXMLEle(ep), "oneNumber") == 0)
1029             {
1030                 XMLAtt *na = findXMLAtt(ep, "name");
1031                 if (na)
1032                 {
1033                     if (n >= maxn)
1034                     {
1035                         /* grow for this and another */
1036                         int newsz = (maxn = n + 1) * sizeof(double);
1037                         doubles   = (double *)realloc(doubles, newsz);
1038                         newsz     = maxn * sizeof(char *);
1039                         names     = (char **)realloc(names, newsz);
1040                     }
1041                     if (f_scansexa(pcdataXMLEle(ep), &doubles[n]) < 0)
1042                         IDMessage(dev, "[ERROR] %s: Bad format %s", name, pcdataXMLEle(ep));
1043                     else
1044                         names[n++] = valuXMLAtt(na);
1045                 }
1046             }
1047         }
1048 
1049         // Reset locale settings to original value
1050         indi_locale_C_numeric_pop(orig);
1051 
1052         /* invoke driver if something to do, but not an error if not */
1053         if (n > 0)
1054             ISNewNumber(dev, name, doubles, names, n);
1055         else
1056             IDMessage(dev, "[ERROR] %s: newNumberVector with no valid members", name);
1057         return (0);
1058     }
1059 
1060     if (!strcmp(rtag, "newSwitchVector"))
1061     {
1062         static ISState *states;
1063         static char **names;
1064         static int maxn;
1065         XMLEle *ep;
1066 
1067         /* seed for reallocs */
1068         if (!states)
1069         {
1070             states = (ISState *)malloc(1);
1071             names  = (char **)malloc(1);
1072         }
1073 
1074         /* pull out each name/state pair */
1075         for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1076         {
1077             if (strcmp(tagXMLEle(ep), "oneSwitch") == 0)
1078             {
1079                 XMLAtt *na = findXMLAtt(ep, "name");
1080                 if (na)
1081                 {
1082                     if (n >= maxn)
1083                     {
1084                         int newsz = (maxn = n + 1) * sizeof(ISState);
1085                         states    = (ISState *)realloc(states, newsz);
1086                         newsz     = maxn * sizeof(char *);
1087                         names     = (char **)realloc(names, newsz);
1088                     }
1089                     if (strncmp(pcdataXMLEle(ep), "On", 2) == 0)
1090                     {
1091                         states[n] = ISS_ON;
1092                         names[n]  = valuXMLAtt(na);
1093                         n++;
1094                     }
1095                     else if (strcmp(pcdataXMLEle(ep), "Off") == 0)
1096                     {
1097                         states[n] = ISS_OFF;
1098                         names[n]  = valuXMLAtt(na);
1099                         n++;
1100                     }
1101                     else
1102                         IDMessage(dev, "[ERROR] %s: must be On or Off: %s", name, pcdataXMLEle(ep));
1103                 }
1104             }
1105         }
1106 
1107         /* invoke driver if something to do, but not an error if not */
1108         if (n > 0)
1109             ISNewSwitch(dev, name, states, names, n);
1110         else
1111             IDMessage(dev, "[ERROR] %s: newSwitchVector with no valid members", name);
1112         return (0);
1113     }
1114 
1115     if (!strcmp(rtag, "newTextVector"))
1116     {
1117         static char **texts;
1118         static char **names;
1119         static int maxn;
1120 
1121         /* seed for reallocs */
1122         if (!texts)
1123         {
1124             texts = (char **)malloc(1);
1125             names = (char **)malloc(1);
1126         }
1127 
1128         /* pull out each name/text pair */
1129         for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1130         {
1131             if (strcmp(tagXMLEle(ep), "oneText") == 0)
1132             {
1133                 XMLAtt *na = findXMLAtt(ep, "name");
1134                 if (na)
1135                 {
1136                     if (n >= maxn)
1137                     {
1138                         int newsz = (maxn = n + 1) * sizeof(char *);
1139                         texts     = (char **)realloc(texts, newsz);
1140                         names     = (char **)realloc(names, newsz);
1141                     }
1142                     texts[n] = pcdataXMLEle(ep);
1143                     names[n] = valuXMLAtt(na);
1144                     n++;
1145                 }
1146             }
1147         }
1148 
1149         /* invoke driver if something to do, but not an error if not */
1150         if (n > 0)
1151             ISNewText(dev, name, texts, names, n);
1152         else
1153             IDMessage(dev, "[ERROR] %s: set with no valid members", name);
1154         return (0);
1155     }
1156 
1157     if (!strcmp(rtag, "newBLOBVector"))
1158     {
1159         static char **blobs;
1160         static char **names;
1161         static char **formats;
1162         static int *blobsizes;
1163         static int *sizes;
1164         static int maxn;
1165         int i;
1166 
1167         /* seed for reallocs */
1168         if (!blobs)
1169         {
1170             blobs     = (char **)malloc(1);
1171             names     = (char **)malloc(1);
1172             formats   = (char **)malloc(1);
1173             blobsizes = (int *)malloc(1);
1174             sizes     = (int *)malloc(1);
1175         }
1176 
1177         /* pull out each name/BLOB pair, decode */
1178         for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1179         {
1180             if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
1181             {
1182                 XMLAtt *na = findXMLAtt(ep, "name");
1183                 XMLAtt *fa = findXMLAtt(ep, "format");
1184                 XMLAtt *sa = findXMLAtt(ep, "size");
1185                 XMLAtt *el = findXMLAtt(ep, "enclen");
1186                 if (na && fa && sa)
1187                 {
1188                     if (n >= maxn)
1189                     {
1190                         int newsz = (maxn = n + 1) * sizeof(char *);
1191                         blobs     = (char **)realloc(blobs, newsz);
1192                         names     = (char **)realloc(names, newsz);
1193                         formats   = (char **)realloc(formats, newsz);
1194                         newsz     = maxn * sizeof(int);
1195                         sizes     = (int *)realloc(sizes, newsz);
1196                         blobsizes = (int *)realloc(blobsizes, newsz);
1197                     }
1198                     int bloblen = pcdatalenXMLEle(ep);
1199                     // enclen is optional and not required by INDI protocol
1200                     if (el)
1201                         bloblen = atoi(valuXMLAtt(el));
1202                     blobs[n]     = malloc(3 * bloblen / 4);
1203                     blobsizes[n] = from64tobits_fast(blobs[n], pcdataXMLEle(ep), bloblen);
1204                     names[n]     = valuXMLAtt(na);
1205                     formats[n]   = valuXMLAtt(fa);
1206                     sizes[n]     = atoi(valuXMLAtt(sa));
1207                     n++;
1208                 }
1209             }
1210         }
1211 
1212         /* invoke driver if something to do, but not an error if not */
1213         if (n > 0)
1214         {
1215             ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
1216             for (i = 0; i < n; i++)
1217                 free(blobs[i]);
1218         }
1219         else
1220             IDMessage(dev, "[ERROR] %s: newBLOBVector with no valid members", name);
1221         return (0);
1222     }
1223 
1224     sprintf(msg, "Unknown command: %s", rtag);
1225     return (1);
1226 }
1227 
IUReadConfig(const char * filename,const char * dev,const char * property,int silent,char errmsg[])1228 int IUReadConfig(const char *filename, const char *dev, const char *property, int silent, char errmsg[])
1229 {
1230     char *rname, *rdev;
1231     XMLEle *root = NULL, *fproot = NULL;
1232     LilXML *lp = newLilXML();
1233 
1234     FILE *fp = IUGetConfigFP(filename, dev, "r", errmsg);
1235 
1236     if (fp == NULL)
1237         return -1;
1238 
1239     char whynot[MAXRBUF];
1240     fproot = readXMLFile(fp, lp, whynot);
1241 
1242     delLilXML(lp);
1243 
1244     if (fproot == NULL)
1245     {
1246         snprintf(errmsg, MAXRBUF, "Unable to parse config XML: %s", whynot);
1247         fclose(fp);
1248         return -1;
1249     }
1250 
1251     if (nXMLEle(fproot) > 0 && silent != 1)
1252         IDMessage(dev, "[INFO] Loading device configuration...");
1253 
1254     for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1255     {
1256         /* pull out device and name */
1257         if (crackDN(root, &rdev, &rname, errmsg) < 0)
1258         {
1259             fclose(fp);
1260             delXMLEle(fproot);
1261             return -1;
1262         }
1263 
1264         // It doesn't belong to our device??
1265         if (strcmp(dev, rdev))
1266             continue;
1267 
1268         if ((property && !strcmp(property, rname)) || property == NULL)
1269         {
1270             dispatch(root, errmsg);
1271             if (property)
1272                 break;
1273         }
1274     }
1275 
1276     if (nXMLEle(fproot) > 0 && silent != 1)
1277         IDMessage(dev, "[INFO] Device configuration applied.");
1278 
1279     fclose(fp);
1280     delXMLEle(fproot);
1281 
1282     return (0);
1283 }
1284 
IUSaveDefaultConfig(const char * source_config,const char * dest_config,const char * dev)1285 void IUSaveDefaultConfig(const char *source_config, const char *dest_config, const char *dev)
1286 {
1287     char configFileName[MAXRBUF], configDefaultFileName[MAXRBUF];
1288 
1289     if (source_config)
1290         strncpy(configFileName, source_config, MAXRBUF);
1291     else
1292     {
1293         if (getenv("INDICONFIG"))
1294             strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1295         else
1296             snprintf(configFileName, MAXRBUF, "%s/.indi/%s_config.xml", getenv("HOME"), dev);
1297     }
1298 
1299     if (dest_config)
1300         strncpy(configDefaultFileName, dest_config, MAXRBUF);
1301     else if (getenv("INDICONFIG"))
1302         snprintf(configDefaultFileName, MAXRBUF, "%s.default", getenv("INDICONFIG"));
1303     else
1304         snprintf(configDefaultFileName, MAXRBUF, "%s/.indi/%s_config.xml.default", getenv("HOME"), dev);
1305 
1306     // If the default doesn't exist, create it.
1307     if (access(configDefaultFileName, F_OK))
1308     {
1309         FILE *fpin = fopen(configFileName, "r");
1310         if (fpin != NULL)
1311         {
1312             FILE *fpout = fopen(configDefaultFileName, "w");
1313             if (fpout != NULL)
1314             {
1315                 int ch = 0;
1316                 while ((ch = getc(fpin)) != EOF)
1317                     putc(ch, fpout);
1318         fclose(fpout);
1319             }
1320         fclose(fpin);
1321         }
1322     }
1323 }
1324 
IUGetConfigSwitch(const char * dev,const char * property,const char * member,ISState * value)1325 int IUGetConfigSwitch(const char *dev, const char *property, const char *member, ISState *value)
1326 {
1327     char *rname, *rdev;
1328     XMLEle *root = NULL, *fproot = NULL;
1329     char errmsg[MAXRBUF];
1330     LilXML *lp = newLilXML();
1331     int valueFound=0;
1332 
1333     FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1334 
1335     if (fp == NULL)
1336         return -1;
1337 
1338     fproot = readXMLFile(fp, lp, errmsg);
1339 
1340     if (fproot == NULL)
1341     {
1342         fclose(fp);
1343         return -1;
1344     }
1345 
1346     for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1347     {
1348         /* pull out device and name */
1349         if (crackDN(root, &rdev, &rname, errmsg) < 0)
1350         {
1351             fclose(fp);
1352             delXMLEle(fproot);
1353             return -1;
1354         }
1355 
1356         // It doesn't belong to our device??
1357         if (strcmp(dev, rdev))
1358             continue;
1359 
1360         if ((property && !strcmp(property, rname)) || property == NULL)
1361         {
1362             XMLEle *oneSwitch = NULL;
1363             for (oneSwitch = nextXMLEle(root, 1); oneSwitch != NULL; oneSwitch = nextXMLEle(root, 0))
1364             {
1365                 if (!strcmp(member, findXMLAttValu(oneSwitch, "name")))
1366                 {
1367                     if (crackISState(pcdataXMLEle(oneSwitch), value) == 0)
1368                         valueFound = 1;
1369                     break;
1370                 }
1371             }
1372             break;
1373         }
1374     }
1375 
1376     fclose(fp);
1377     delXMLEle(fproot);
1378     delLilXML(lp);
1379 
1380     return (valueFound == 1 ? 0 : -1);
1381 }
1382 
IUGetConfigNumber(const char * dev,const char * property,const char * member,double * value)1383 int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
1384 {
1385     char *rname, *rdev;
1386     XMLEle *root = NULL, *fproot = NULL;
1387     char errmsg[MAXRBUF];
1388     LilXML *lp = newLilXML();
1389     int valueFound=0;
1390 
1391     FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1392 
1393     if (fp == NULL)
1394         return -1;
1395 
1396     fproot = readXMLFile(fp, lp, errmsg);
1397 
1398     if (fproot == NULL)
1399     {
1400         fclose(fp);
1401         return -1;
1402     }
1403 
1404     for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1405     {
1406         /* pull out device and name */
1407         if (crackDN(root, &rdev, &rname, errmsg) < 0)
1408         {
1409             fclose(fp);
1410             delXMLEle(fproot);
1411             return -1;
1412         }
1413 
1414         // It doesn't belong to our device??
1415         if (strcmp(dev, rdev))
1416             continue;
1417 
1418         if ((property && !strcmp(property, rname)) || property == NULL)
1419         {
1420             XMLEle *oneNumber = NULL;
1421             for (oneNumber = nextXMLEle(root, 1); oneNumber != NULL; oneNumber = nextXMLEle(root, 0))
1422             {
1423                 if (!strcmp(member, findXMLAttValu(oneNumber, "name")))
1424                 {
1425                     *value = atof(pcdataXMLEle(oneNumber));
1426                     valueFound = 1;
1427                     break;
1428                 }
1429             }
1430             break;
1431         }
1432     }
1433 
1434     fclose(fp);
1435     delXMLEle(fproot);
1436     delLilXML(lp);
1437 
1438     return (valueFound == 1 ? 0 : -1);
1439 }
1440 
IUGetConfigText(const char * dev,const char * property,const char * member,char * value,int len)1441 int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
1442 {
1443     char *rname, *rdev;
1444     XMLEle *root = NULL, *fproot = NULL;
1445     char errmsg[MAXRBUF];
1446     LilXML *lp = newLilXML();
1447     int valueFound=0;
1448 
1449     FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1450 
1451     if (fp == NULL)
1452         return -1;
1453 
1454     fproot = readXMLFile(fp, lp, errmsg);
1455 
1456     if (fproot == NULL)
1457     {
1458         fclose(fp);
1459         return -1;
1460     }
1461 
1462     for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1463     {
1464         /* pull out device and name */
1465         if (crackDN(root, &rdev, &rname, errmsg) < 0)
1466         {
1467             fclose(fp);
1468             delXMLEle(fproot);
1469             return -1;
1470         }
1471 
1472         // It doesn't belong to our device??
1473         if (strcmp(dev, rdev))
1474             continue;
1475 
1476         if ((property && !strcmp(property, rname)) || property == NULL)
1477         {
1478             XMLEle *oneText = NULL;
1479             for (oneText = nextXMLEle(root, 1); oneText != NULL; oneText = nextXMLEle(root, 0))
1480             {
1481                 if (!strcmp(member, findXMLAttValu(oneText, "name")))
1482                 {
1483                     strncpy(value, pcdataXMLEle(oneText), len);
1484                     valueFound = 1;
1485                     break;
1486                 }
1487             }
1488             break;
1489         }
1490     }
1491 
1492     fclose(fp);
1493     delXMLEle(fproot);
1494     delLilXML(lp);
1495 
1496     return (valueFound == 1 ? 0 : -1);
1497 }
1498 
1499 /* send client a message for a specific device or at large if !dev */
IDMessage(const char * dev,const char * fmt,...)1500 void IDMessage(const char *dev, const char *fmt, ...)
1501 {
1502     pthread_mutex_lock(&stdout_mutex);
1503 
1504     xmlv1();
1505     printf("<message\n");
1506     if (dev)
1507         printf(" device='%s'\n", dev);
1508     printf("  timestamp='%s'\n", timestamp());
1509     if (fmt)
1510     {
1511         va_list ap;
1512         va_start(ap, fmt);
1513         char message[MAXINDIMESSAGE];
1514         printf("  message='");
1515         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1516         char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1517         printf("%s'\n", escapedMessage);
1518         free(escapedMessage);
1519         va_end(ap);
1520     }
1521     printf("/>\n");
1522     fflush(stdout);
1523 
1524     pthread_mutex_unlock(&stdout_mutex);
1525 }
1526 
IUPurgeConfig(const char * filename,const char * dev,char errmsg[])1527 int IUPurgeConfig(const char *filename, const char *dev, char errmsg[])
1528 {
1529     char configFileName[MAXRBUF];
1530     char configDir[MAXRBUF];
1531 
1532     snprintf(configDir, MAXRBUF, "%s/.indi/", getenv("HOME"));
1533 
1534     if (filename)
1535         strncpy(configFileName, filename, MAXRBUF);
1536     else
1537     {
1538         if (getenv("INDICONFIG"))
1539             strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1540         else
1541             snprintf(configFileName, MAXRBUF, "%s%s_config.xml", configDir, dev);
1542     }
1543 
1544     if (remove(configFileName) != 0)
1545     {
1546         snprintf(errmsg, MAXRBUF, "Unable to purge configuration file %s. Error %s", configFileName, strerror(errno));
1547         return -1;
1548     }
1549 
1550     return 0;
1551 }
1552 
IUGetConfigFP(const char * filename,const char * dev,const char * mode,char errmsg[])1553 FILE *IUGetConfigFP(const char *filename, const char *dev, const char *mode, char errmsg[])
1554 {
1555     char configFileName[MAXRBUF];
1556     char configDir[MAXRBUF];
1557     struct stat st;
1558     FILE *fp = NULL;
1559 
1560     snprintf(configDir, MAXRBUF, "%s/.indi/", getenv("HOME"));
1561 
1562     if (filename)
1563         strncpy(configFileName, filename, MAXRBUF);
1564     else
1565     {
1566         if (getenv("INDICONFIG"))
1567             strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1568         else
1569             snprintf(configFileName, MAXRBUF, "%s%s_config.xml", configDir, dev);
1570     }
1571 
1572     if (stat(configDir, &st) != 0)
1573     {
1574         if (mkdir(configDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
1575         {
1576             snprintf(errmsg, MAXRBUF, "Unable to create config directory. Error %s: %s", configDir, strerror(errno));
1577             return NULL;
1578         }
1579     }
1580 
1581     stat(configFileName, &st);
1582     /* If file is owned by root and current user is NOT root then abort */
1583     if ( (st.st_uid == 0 && getuid() != 0) || (st.st_gid == 0 && getgid() != 0) )
1584     {
1585         strncpy(errmsg, "Config file is owned by root! This will lead to serious errors. To fix this, run: sudo chown -R $USER:$USER ~/.indi", MAXRBUF);
1586         return NULL;
1587     }
1588 
1589     fp = fopen(configFileName, mode);
1590     if (fp == NULL)
1591     {
1592         snprintf(errmsg, MAXRBUF, "Unable to open config file. Error loading file %s: %s", configFileName,
1593                  strerror(errno));
1594         return NULL;
1595     }
1596 
1597     return fp;
1598 }
1599 
IUSaveConfigTag(FILE * fp,int ctag,const char * dev,int silent)1600 void IUSaveConfigTag(FILE *fp, int ctag, const char *dev, int silent)
1601 {
1602     if (!fp)
1603         return;
1604 
1605     /* Opening tag */
1606     if (ctag == 0)
1607     {
1608         fprintf(fp, "<INDIDriver>\n");
1609         if (silent != 1)
1610             IDMessage(dev, "[INFO] Saving device configuration...");
1611     }
1612     /* Closing tag */
1613     else
1614     {
1615         fprintf(fp, "</INDIDriver>\n");
1616         if (silent != 1)
1617             IDMessage(dev, "[INFO] Device configuration saved.");
1618     }
1619 }
1620 
IUSaveConfigNumber(FILE * fp,const INumberVectorProperty * nvp)1621 void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
1622 {
1623     int i;
1624 
1625     locale_char_t *orig = indi_locale_C_numeric_push();
1626     fprintf(fp, "<newNumberVector device='%s' name='%s'>\n", nvp->device, nvp->name);
1627 
1628     for (i = 0; i < nvp->nnp; i++)
1629     {
1630         INumber *np = &nvp->np[i];
1631         fprintf(fp, "  <oneNumber name='%s'>\n", np->name);
1632         fprintf(fp, "      %.20g\n", np->value);
1633         fprintf(fp, "  </oneNumber>\n");
1634     }
1635 
1636     fprintf(fp, "</newNumberVector>\n");
1637     indi_locale_C_numeric_pop(orig);
1638 }
1639 
IUSaveConfigText(FILE * fp,const ITextVectorProperty * tvp)1640 void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
1641 {
1642     int i;
1643 
1644     fprintf(fp, "<newTextVector device='%s' name='%s'>\n", tvp->device, tvp->name);
1645 
1646     for (i = 0; i < tvp->ntp; i++)
1647     {
1648         IText *tp = &tvp->tp[i];
1649         fprintf(fp, "  <oneText name='%s'>\n", tp->name);
1650         fprintf(fp, "      %s\n", tp->text ? tp->text : "");
1651         fprintf(fp, "  </oneText>\n");
1652     }
1653 
1654     fprintf(fp, "</newTextVector>\n");
1655 }
1656 
IUSaveConfigSwitch(FILE * fp,const ISwitchVectorProperty * svp)1657 void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
1658 {
1659     int i;
1660 
1661     fprintf(fp, "<newSwitchVector device='%s' name='%s'>\n", svp->device, svp->name);
1662 
1663     for (i = 0; i < svp->nsp; i++)
1664     {
1665         ISwitch *sp = &svp->sp[i];
1666         fprintf(fp, "  <oneSwitch name='%s'>\n", sp->name);
1667         fprintf(fp, "      %s\n", sstateStr(sp->s));
1668         fprintf(fp, "  </oneSwitch>\n");
1669     }
1670 
1671     fprintf(fp, "</newSwitchVector>\n");
1672 }
1673 
IUSaveConfigBLOB(FILE * fp,const IBLOBVectorProperty * bvp)1674 void IUSaveConfigBLOB(FILE *fp, const IBLOBVectorProperty *bvp)
1675 {
1676     int i;
1677 
1678     fprintf(fp, "<newBLOBVector device='%s' name='%s'>\n", bvp->device, bvp->name);
1679 
1680     for (i = 0; i < bvp->nbp; i++)
1681     {
1682         IBLOB *bp = &bvp->bp[i];
1683         unsigned char *encblob = NULL;
1684         int l = 0;
1685 
1686         fprintf(fp, "  <oneBLOB\n");
1687         fprintf(fp, "    name='%s'\n", bp->name);
1688         fprintf(fp, "    size='%d'\n", bp->size);
1689         fprintf(fp, "    format='%s'>\n", bp->format);
1690 
1691         encblob        = malloc(4 * bp->bloblen / 3 + 4);
1692         l              = to64frombits(encblob, bp->blob, bp->bloblen);
1693         size_t written = 0;
1694 
1695         while ((int)written < l)
1696         {
1697             size_t towrite = ((l - written) > 72) ? 72 : l - written;
1698             size_t wr      = fwrite(encblob + written, 1, towrite, fp);
1699 
1700             fputc('\n', fp);
1701             if (wr > 0)
1702                 written += wr;
1703         }
1704         free(encblob);
1705 
1706         fprintf(fp, "  </oneBLOB>\n");
1707     }
1708 
1709     fprintf(fp, "</newBLOBVector>\n");
1710 }
1711 
1712 /* tell client to create a text vector property */
IDDefText(const ITextVectorProperty * tvp,const char * fmt,...)1713 void IDDefText(const ITextVectorProperty *tvp, const char *fmt, ...)
1714 {
1715     int i;
1716     ROSC *SC;
1717 
1718     pthread_mutex_lock(&stdout_mutex);
1719 
1720     xmlv1();
1721     locale_char_t *orig = indi_locale_C_numeric_push();
1722     printf("<defTextVector\n");
1723     printf("  device='%s'\n", tvp->device);
1724     printf("  name='%s'\n", tvp->name);
1725     printf("  label='%s'\n", tvp->label);
1726     printf("  group='%s'\n", tvp->group);
1727     printf("  state='%s'\n", pstateStr(tvp->s));
1728     printf("  perm='%s'\n", permStr(tvp->p));
1729     printf("  timeout='%g'\n", tvp->timeout);
1730     printf("  timestamp='%s'\n", timestamp());
1731     if (fmt)
1732     {
1733         va_list ap;
1734         va_start(ap, fmt);
1735         char message[MAXINDIMESSAGE];
1736         printf("  message='");
1737         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1738         char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1739         printf("%s'\n", escapedMessage);
1740         free(escapedMessage);
1741         va_end(ap);
1742     }
1743     printf(">\n");
1744 
1745     for (i = 0; i < tvp->ntp; i++)
1746     {
1747         IText *tp = &tvp->tp[i];
1748         printf("  <defText\n");
1749         printf("    name='%s'\n", tp->name);
1750         printf("    label='%s'>\n", tp->label);
1751         printf("      %s\n", tp->text ? tp->text : "");
1752         printf("  </defText>\n");
1753     }
1754 
1755     printf("</defTextVector>\n");
1756 
1757     if (isPropDefined(tvp->name, tvp->device) < 0)
1758     {
1759         /* Add this property to insure proper sanity check */
1760         propCache =
1761             propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1762         SC = &propCache[nPropCache++];
1763 
1764         strcpy(SC->propName, tvp->name);
1765         strcpy(SC->devName, tvp->device);
1766         SC->perm = tvp->p;
1767         SC->ptr  = tvp;
1768         SC->type = INDI_TEXT;
1769     }
1770 
1771     indi_locale_C_numeric_pop(orig);
1772     fflush(stdout);
1773 
1774     pthread_mutex_unlock(&stdout_mutex);
1775 }
1776 
1777 /* tell client to create a new numeric vector property */
IDDefNumber(const INumberVectorProperty * n,const char * fmt,...)1778 void IDDefNumber(const INumberVectorProperty *n, const char *fmt, ...)
1779 {
1780     int i;
1781     ROSC *SC;
1782 
1783     pthread_mutex_lock(&stdout_mutex);
1784 
1785     xmlv1();
1786     locale_char_t *orig = indi_locale_C_numeric_push();
1787     printf("<defNumberVector\n");
1788     printf("  device='%s'\n", n->device);
1789     printf("  name='%s'\n", n->name);
1790     printf("  label='%s'\n", n->label);
1791     printf("  group='%s'\n", n->group);
1792     printf("  state='%s'\n", pstateStr(n->s));
1793     printf("  perm='%s'\n", permStr(n->p));
1794     printf("  timeout='%g'\n", n->timeout);
1795     printf("  timestamp='%s'\n", timestamp());
1796 
1797     if (fmt)
1798     {
1799         va_list ap;
1800         va_start(ap, fmt);
1801         char message[MAXINDIMESSAGE];
1802         printf("  message='");
1803         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1804         char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1805         printf("%s'\n", escapedMessage);
1806         free(escapedMessage);
1807         va_end(ap);
1808     }
1809     printf(">\n");
1810 
1811     for (i = 0; i < n->nnp; i++)
1812     {
1813         INumber *np = &n->np[i];
1814 
1815         printf("  <defNumber\n");
1816         printf("    name='%s'\n", np->name);
1817         printf("    label='%s'\n", np->label);
1818         printf("    format='%s'\n", np->format);
1819         printf("    min='%.20g'\n", np->min);
1820         printf("    max='%.20g'\n", np->max);
1821         printf("    step='%.20g'>\n", np->step);
1822         printf("      %.20g\n", np->value);
1823 
1824         printf("  </defNumber>\n");
1825     }
1826 
1827     printf("</defNumberVector>\n");
1828 
1829     if (isPropDefined(n->name, n->device) < 0)
1830     {
1831         /* Add this property to insure proper sanity check */
1832         propCache =
1833             propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1834         SC = &propCache[nPropCache++];
1835 
1836         strcpy(SC->propName, n->name);
1837         strcpy(SC->devName, n->device);
1838         SC->perm = n->p;
1839         SC->ptr  = n;
1840         SC->type = INDI_NUMBER;
1841     }
1842 
1843     indi_locale_C_numeric_pop(orig);
1844     fflush(stdout);
1845 
1846     pthread_mutex_unlock(&stdout_mutex);
1847 }
1848 
1849 /* tell client to create a new switch vector property */
IDDefSwitch(const ISwitchVectorProperty * s,const char * fmt,...)1850 void IDDefSwitch(const ISwitchVectorProperty *s, const char *fmt, ...)
1851 
1852 {
1853     int i;
1854     ROSC *SC;
1855 
1856     pthread_mutex_lock(&stdout_mutex);
1857 
1858     xmlv1();
1859     locale_char_t *orig = indi_locale_C_numeric_push();
1860     printf("<defSwitchVector\n");
1861     printf("  device='%s'\n", s->device);
1862     printf("  name='%s'\n", s->name);
1863     printf("  label='%s'\n", s->label);
1864     printf("  group='%s'\n", s->group);
1865     printf("  state='%s'\n", pstateStr(s->s));
1866     printf("  perm='%s'\n", permStr(s->p));
1867     printf("  rule='%s'\n", ruleStr(s->r));
1868     printf("  timeout='%g'\n", s->timeout);
1869     printf("  timestamp='%s'\n", timestamp());
1870     if (fmt)
1871     {
1872         va_list ap;
1873         va_start(ap, fmt);
1874         char message[MAXINDIMESSAGE];
1875         printf("  message='");
1876         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1877         char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1878         printf("%s'\n", escapedMessage);
1879         free(escapedMessage);
1880         va_end(ap);
1881     }
1882     printf(">\n");
1883 
1884     for (i = 0; i < s->nsp; i++)
1885     {
1886         ISwitch *sp = &s->sp[i];
1887         printf("  <defSwitch\n");
1888         printf("    name='%s'\n", sp->name);
1889         printf("    label='%s'>\n", sp->label);
1890         printf("      %s\n", sstateStr(sp->s));
1891         printf("  </defSwitch>\n");
1892     }
1893 
1894     printf("</defSwitchVector>\n");
1895 
1896     if (isPropDefined(s->name, s->device) < 0)
1897     {
1898         /* Add this property to insure proper sanity check */
1899         propCache =
1900             propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1901         SC = &propCache[nPropCache++];
1902 
1903         strcpy(SC->propName, s->name);
1904         strcpy(SC->devName, s->device);
1905         SC->perm = s->p;
1906         SC->ptr  = s;
1907         SC->type = INDI_SWITCH;
1908     }
1909 
1910     indi_locale_C_numeric_pop(orig);
1911     fflush(stdout);
1912 
1913     pthread_mutex_unlock(&stdout_mutex);
1914 }
1915 
1916 /* tell client to create a new lights vector property */
IDDefLight(const ILightVectorProperty * lvp,const char * fmt,...)1917 void IDDefLight(const ILightVectorProperty *lvp, const char *fmt, ...)
1918 {
1919     int i;
1920 
1921     pthread_mutex_lock(&stdout_mutex);
1922 
1923     xmlv1();
1924     printf("<defLightVector\n");
1925     printf("  device='%s'\n", lvp->device);
1926     printf("  name='%s'\n", lvp->name);
1927     printf("  label='%s'\n", lvp->label);
1928     printf("  group='%s'\n", lvp->group);
1929     printf("  state='%s'\n", pstateStr(lvp->s));
1930     printf("  timestamp='%s'\n", timestamp());
1931     if (fmt)
1932     {
1933         va_list ap;
1934         va_start(ap, fmt);
1935         char message[MAXINDIMESSAGE];
1936         printf("  message='");
1937         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1938         char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1939         printf("%s'\n", escapedMessage);
1940         free(escapedMessage);
1941         va_end(ap);
1942     }
1943     printf(">\n");
1944 
1945     for (i = 0; i < lvp->nlp; i++)
1946     {
1947         ILight *lp = &lvp->lp[i];
1948         printf("  <defLight\n");
1949         printf("    name='%s'\n", lp->name);
1950         printf("    label='%s'>\n", lp->label);
1951         printf("      %s\n", pstateStr(lp->s));
1952         printf("  </defLight>\n");
1953     }
1954 
1955     printf("</defLightVector>\n");
1956     fflush(stdout);
1957 
1958     pthread_mutex_unlock(&stdout_mutex);
1959 }
1960 
1961 /* tell client to create a new BLOB vector property */
IDDefBLOB(const IBLOBVectorProperty * b,const char * fmt,...)1962 void IDDefBLOB(const IBLOBVectorProperty *b, const char *fmt, ...)
1963 {
1964     int i;
1965     ROSC *SC;
1966 
1967     pthread_mutex_lock(&stdout_mutex);
1968 
1969     xmlv1();
1970     locale_char_t *orig = indi_locale_C_numeric_push();
1971     printf("<defBLOBVector\n");
1972     printf("  device='%s'\n", b->device);
1973     printf("  name='%s'\n", b->name);
1974     printf("  label='%s'\n", b->label);
1975     printf("  group='%s'\n", b->group);
1976     printf("  state='%s'\n", pstateStr(b->s));
1977     printf("  perm='%s'\n", permStr(b->p));
1978     printf("  timeout='%g'\n", b->timeout);
1979     printf("  timestamp='%s'\n", timestamp());
1980     if (fmt)
1981     {
1982         va_list ap;
1983         va_start(ap, fmt);
1984         char message[MAXINDIMESSAGE];
1985         printf("  message='");
1986         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1987         char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1988         printf("%s'\n", escapedMessage);
1989         free(escapedMessage);
1990         va_end(ap);
1991     }
1992     printf(">\n");
1993 
1994     for (i = 0; i < b->nbp; i++)
1995     {
1996         IBLOB *bp = &b->bp[i];
1997         printf("  <defBLOB\n");
1998         printf("    name='%s'\n", bp->name);
1999         printf("    label='%s'\n", bp->label);
2000         printf("  />\n");
2001     }
2002 
2003     printf("</defBLOBVector>\n");
2004 
2005     if (isPropDefined(b->name, b->device) < 0)
2006     {
2007         /* Add this property to insure proper sanity check */
2008         propCache =
2009             propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
2010         SC = &propCache[nPropCache++];
2011 
2012         strcpy(SC->propName, b->name);
2013         strcpy(SC->devName, b->device);
2014         SC->perm = b->p;
2015         SC->ptr  = b;
2016         SC->type = INDI_BLOB;
2017     }
2018 
2019     indi_locale_C_numeric_pop(orig);
2020     fflush(stdout);
2021 
2022     pthread_mutex_unlock(&stdout_mutex);
2023 }
2024 
2025 /* tell client to update an existing text vector property */
IDSetText(const ITextVectorProperty * tvp,const char * fmt,...)2026 void IDSetText(const ITextVectorProperty *tvp, const char *fmt, ...)
2027 {
2028     int i;
2029 
2030     pthread_mutex_lock(&stdout_mutex);
2031 
2032     xmlv1();
2033     locale_char_t *orig = indi_locale_C_numeric_push();
2034     printf("<setTextVector\n");
2035     printf("  device='%s'\n", tvp->device);
2036     printf("  name='%s'\n", tvp->name);
2037     printf("  state='%s'\n", pstateStr(tvp->s));
2038     printf("  timeout='%g'\n", tvp->timeout);
2039     printf("  timestamp='%s'\n", timestamp());
2040     if (fmt)
2041     {
2042         va_list ap;
2043         va_start(ap, fmt);
2044         char message[MAXINDIMESSAGE];
2045         printf("  message='");
2046         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2047         printf("%s'\n", entityXML(message));
2048         va_end(ap);
2049     }
2050     printf(">\n");
2051 
2052     for (i = 0; i < tvp->ntp; i++)
2053     {
2054         IText *tp = &tvp->tp[i];
2055         printf("  <oneText name='%s'>\n", tp->name);
2056         printf("      %s\n", tp->text ? entityXML(tp->text) : "");
2057         printf("  </oneText>\n");
2058     }
2059 
2060     printf("</setTextVector>\n");
2061     indi_locale_C_numeric_pop(orig);
2062     fflush(stdout);
2063 
2064     pthread_mutex_unlock(&stdout_mutex);
2065 }
2066 
2067 /* tell client to update an existing numeric vector property */
IDSetNumber(const INumberVectorProperty * nvp,const char * fmt,...)2068 void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt, ...)
2069 {
2070     int i;
2071 
2072     pthread_mutex_lock(&stdout_mutex);
2073 
2074     xmlv1();
2075     locale_char_t *orig = indi_locale_C_numeric_push();
2076     printf("<setNumberVector\n");
2077     printf("  device='%s'\n", nvp->device);
2078     printf("  name='%s'\n", nvp->name);
2079     printf("  state='%s'\n", pstateStr(nvp->s));
2080     printf("  timeout='%g'\n", nvp->timeout);
2081     printf("  timestamp='%s'\n", timestamp());
2082     if (fmt)
2083     {
2084         va_list ap;
2085         va_start(ap, fmt);
2086         char message[MAXINDIMESSAGE];
2087         printf("  message='");
2088         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2089         printf("%s'\n", entityXML(message));
2090         va_end(ap);
2091     }
2092     printf(">\n");
2093 
2094     for (i = 0; i < nvp->nnp; i++)
2095     {
2096         INumber *np = &nvp->np[i];
2097         printf("  <oneNumber name='%s'>\n", np->name);
2098         printf("      %.20g\n", np->value);
2099         printf("  </oneNumber>\n");
2100     }
2101 
2102     printf("</setNumberVector>\n");
2103     indi_locale_C_numeric_pop(orig);
2104     fflush(stdout);
2105 
2106     pthread_mutex_unlock(&stdout_mutex);
2107 }
2108 
2109 /* tell client to update an existing switch vector property */
IDSetSwitch(const ISwitchVectorProperty * svp,const char * fmt,...)2110 void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt, ...)
2111 {
2112     int i;
2113 
2114     pthread_mutex_lock(&stdout_mutex);
2115 
2116     xmlv1();
2117     locale_char_t *orig = indi_locale_C_numeric_push();
2118     printf("<setSwitchVector\n");
2119     printf("  device='%s'\n", svp->device);
2120     printf("  name='%s'\n", svp->name);
2121     printf("  state='%s'\n", pstateStr(svp->s));
2122     printf("  timeout='%g'\n", svp->timeout);
2123     printf("  timestamp='%s'\n", timestamp());
2124     if (fmt)
2125     {
2126         va_list ap;
2127         va_start(ap, fmt);
2128         char message[MAXINDIMESSAGE];
2129         printf("  message='");
2130         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2131         printf("%s'\n", entityXML(message));
2132         va_end(ap);
2133     }
2134     printf(">\n");
2135 
2136     for (i = 0; i < svp->nsp; i++)
2137     {
2138         ISwitch *sp = &svp->sp[i];
2139         printf("  <oneSwitch name='%s'>\n", sp->name);
2140         printf("      %s\n", sstateStr(sp->s));
2141         printf("  </oneSwitch>\n");
2142     }
2143 
2144     printf("</setSwitchVector>\n");
2145     indi_locale_C_numeric_pop(orig);
2146     fflush(stdout);
2147 
2148     pthread_mutex_unlock(&stdout_mutex);
2149 }
2150 
2151 /* tell client to update an existing lights vector property */
IDSetLight(const ILightVectorProperty * lvp,const char * fmt,...)2152 void IDSetLight(const ILightVectorProperty *lvp, const char *fmt, ...)
2153 {
2154     int i;
2155 
2156     pthread_mutex_lock(&stdout_mutex);
2157 
2158     xmlv1();
2159     printf("<setLightVector\n");
2160     printf("  device='%s'\n", lvp->device);
2161     printf("  name='%s'\n", lvp->name);
2162     printf("  state='%s'\n", pstateStr(lvp->s));
2163     printf("  timestamp='%s'\n", timestamp());
2164     if (fmt)
2165     {
2166         va_list ap;
2167         va_start(ap, fmt);
2168         char message[MAXINDIMESSAGE];
2169         printf("  message='");
2170         vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2171         printf("%s'\n", entityXML(message));
2172         va_end(ap);
2173     }
2174     printf(">\n");
2175 
2176     for (i = 0; i < lvp->nlp; i++)
2177     {
2178         ILight *lp = &lvp->lp[i];
2179         printf("  <oneLight name='%s'>\n", lp->name);
2180         printf("      %s\n", pstateStr(lp->s));
2181         printf("  </oneLight>\n");
2182     }
2183 
2184     printf("</setLightVector>\n");
2185     fflush(stdout);
2186 
2187     pthread_mutex_unlock(&stdout_mutex);
2188 }
2189 
2190 /* tell client to update an existing BLOB vector property */
IDSetBLOB(const IBLOBVectorProperty * bvp,const char * fmt,...)2191 void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt, ...)
2192 {
2193     int i;
2194 
2195     pthread_mutex_lock(&stdout_mutex);
2196 
2197     xmlv1();
2198     locale_char_t *orig = indi_locale_C_numeric_push();
2199     printf("<setBLOBVector\n");
2200     printf("  device='%s'\n", bvp->device);
2201     printf("  name='%s'\n", bvp->name);
2202     printf("  state='%s'\n", pstateStr(bvp->s));
2203     printf("  timeout='%g'\n", bvp->timeout);
2204     printf("  timestamp='%s'\n", timestamp());
2205     if (fmt)
2206     {
2207         va_list ap;
2208         va_start(ap, fmt);
2209         printf("  message='");
2210         vprintf(fmt, ap);
2211         printf("'\n");
2212         va_end(ap);
2213     }
2214     printf(">\n");
2215 
2216     for (i = 0; i < bvp->nbp; i++)
2217     {
2218         IBLOB *bp = &bvp->bp[i];
2219         unsigned char *encblob;
2220         int l;
2221 
2222         printf("  <oneBLOB\n");
2223         printf("    name='%s'\n", bp->name);
2224         printf("    size='%d'\n", bp->size);
2225 
2226         // If size is zero, we are only sending a state-change
2227         if (bp->size == 0)
2228         {
2229             printf("    enclen='0'\n");
2230             printf("    format='%s'>\n", bp->format);
2231         }
2232         else
2233         {
2234             encblob = malloc(4 * bp->bloblen / 3 + 4);
2235             l       = to64frombits(encblob, bp->blob, bp->bloblen);
2236             printf("    enclen='%d'\n", l);
2237             printf("    format='%s'>\n", bp->format);
2238             size_t written = 0;
2239 
2240             while ((int)written < l)
2241             {
2242                 size_t towrite = ((l - written) > 72) ? 72 : l - written;
2243                 size_t wr      = fwrite(encblob + written, 1, towrite, stdout);
2244 
2245                 if (wr > 0)
2246                     written += wr;
2247                 if ((written % 72) == 0)
2248                     fputc('\n', stdout);
2249             }
2250 
2251             if ((written % 72) != 0)
2252                 fputc('\n', stdout);
2253 
2254             free(encblob);
2255         }
2256 
2257         printf("  </oneBLOB>\n");
2258     }
2259 
2260     printf("</setBLOBVector>\n");
2261     indi_locale_C_numeric_pop(orig);
2262     fflush(stdout);
2263 
2264     pthread_mutex_unlock(&stdout_mutex);
2265 }
2266 
2267 /* tell client to update min/max elements of an existing number vector property */
IUUpdateMinMax(const INumberVectorProperty * nvp)2268 void IUUpdateMinMax(const INumberVectorProperty *nvp)
2269 {
2270     int i;
2271 
2272     pthread_mutex_lock(&stdout_mutex);
2273     xmlv1();
2274     locale_char_t *orig = indi_locale_C_numeric_push();
2275     printf("<setNumberVector\n");
2276     printf("  device='%s'\n", nvp->device);
2277     printf("  name='%s'\n", nvp->name);
2278     printf("  state='%s'\n", pstateStr(nvp->s));
2279     printf("  timeout='%g'\n", nvp->timeout);
2280     printf("  timestamp='%s'\n", timestamp());
2281     printf(">\n");
2282 
2283     for (i = 0; i < nvp->nnp; i++)
2284     {
2285         INumber *np = &nvp->np[i];
2286         printf("  <oneNumber name='%s'\n", np->name);
2287         printf("    min='%g'\n", np->min);
2288         printf("    max='%g'\n", np->max);
2289         printf("    step='%g'\n", np->step);
2290         printf(">\n");
2291         printf("      %g\n", np->value);
2292         printf("  </oneNumber>\n");
2293     }
2294 
2295     printf("</setNumberVector>\n");
2296     indi_locale_C_numeric_pop(orig);
2297     fflush(stdout);
2298     pthread_mutex_unlock(&stdout_mutex);
2299 }
2300 
IUFindIndex(const char * needle,char ** hay,unsigned int n)2301 int IUFindIndex(const char *needle, char **hay, unsigned int n)
2302 {
2303     int i = 0;
2304 
2305     for (i = 0; i < (int)n; i++)
2306     {
2307         if (!strcmp(hay[i], needle))
2308             return i;
2309     }
2310     return -1;
2311 }
2312