1 // Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
2 // Copyright (C) 2015 Cherokees of Idaho.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 #include <sipwitch-config.h>
18 #include <ucommon/ucommon.h>
19 #include <ucommon/export.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <new>
30
31 #define RUNLEVELS (sizeof(callback::runlevels) / sizeof(LinkedObject *))
32
33 namespace sipwitch {
34
59
64
65 static struct sockaddr_storage peering;
66 static time_t started = 0l;
67 static time_t periodic = 0l;
68
69 static size_t xmldecode(char *out, size_t limit, const char *src)
70 {
71 assert(out != NULL);
72 assert(limit > 0);
73 assert(src != NULL);
74
75 char *ret = out;
76
77 if(*src == '\'' || *src == '\"')
78 ++src;
79 while(src && limit-- > 1 && !strchr("<\'\">", *src)) {
80 if(!strncmp(src, "&", 5)) {
81 *(out++) = '&';
82 src += 5;
83 }
84 else if(!strncmp(src, "<", 4)) {
85 src += 4;
86 *(out++) = '<';
87 }
88 else if(!strncmp(src, ">", 4)) {
89 src += 4;
90 *(out++) = '>';
91 }
92 else if(!strncmp(src, """, 6)) {
93 src += 6;
94 *(out++) = '\"';
95 }
96 else if(!strncmp(src, "'", 6)) {
97 src += 6;
98 *(out++) = '\'';
99 }
100 else
101 *(out++) = *(src++);
102 }
103 *out = 0;
104 return out - ret;
105 }
106
108 {
111 }
112
114 {
115 node = NULL;
116 }
117
119 {
120 assert(id != NULL && *id != 0);
121
124 }
125
127 {
130 }
131
133 {
135 }
136
138 {
140 node = p;
141 }
142
145 {
146 crit(rl < (
int)
RUNLEVELS,
"service runlevel invalid");
147 if(rl < 0) {
149 crit(rl > 0, "service runlevel invalid");
150 }
155 }
156
158 {
159 LinkedObject::delist(&runlevels[runlevel]);
160 }
161
163 {
164 if(!uri)
165 return NULL;
166
167 if(eq(uri, "sip:", 4) || eq(uri, "sip"))
168 return out_context;
169
170 if(eq(uri, "sips:", 5) || eq(uri, "sips"))
171 return tls_context;
172
173 if(eq(uri, "tcp:", 4) || eq(uri, "tcp"))
174 return tcp_context;
175
176 if(eq(uri, "udp:", 4) || eq(uri, "udp"))
177 return udp_context;
178
179 return NULL;
180 }
181
183 {
184 }
185
187 {
188 return true;
189 }
190
192 {
193 }
194
196 {
197 }
198
200 {
201 }
202
204 {
205 }
206
208 {
209 }
210
212 {
213 }
214
216 {
217 if(!addr)
218 return;
219
220 #ifdef AF_INET6
221 if(strchr(addr, ':'))
222 sip_family = AF_INET6;
223 #endif
224
225 if(eq(addr, ":::") || eq(addr, "::0") || eq(addr, "::*") || eq(addr, "*") || eq(addr, "0.0.0.0") || !*addr)
226 addr = NULL;
227
228 sip_iface = addr;
229 }
230
232 {
234 }
235
237 {
239 }
240
242 {
243 Parent = trunk;
244 if(Parent)
245 enlistTail(&trunk->Child);
246 }
247
250 {
251 assert(name != NULL && *name != 0);
252
253 root.setId((
char *)name);
254 root.setPointer(NULL);
255
257
258 if(!started) {
259 time(&started);
260 time(&periodic);
261 }
262 }
263
265 {
266 // we must zap the xml tree root node, lest it try to delete it's "id"
267 // or child nodes, since all was allocated from the "pager" heap.
268 reset_unsafe<keynode>(
root);
269 memalloc::purge();
270 }
271
273 {
274 Socket::address resolver;
275 const struct sockaddr *host;
276
277 if(!addr) {
278 memset(&peering, 0, sizeof(peering));
279 return;
280 }
281
282 int i = 0;
283 resolver.set(addr, i);
284 host = resolver.getAddr();
285 if(host) {
288 if(old)
289 free((char *)old);
290 Socket::store(&peering, host);
292 }
293 }
294
296 {
297 memcpy(peer, &peering, sizeof(peering));
298 }
299
301 {
302 time_t now;
303 time(&now);
304 if(!started)
305 return 0l;
306
307 return (long)(now - started);
308 }
309
311 {
312 assert(id != NULL && *id != 0);
313
315 return NULL;
316
318 }
319
321 {
322 assert(id != NULL && *id != 0);
323
325
327 return NULL;
328
330 if(node)
331 return node->getFirst();
332 return NULL;
333 }
334
336 {
337 assert(id != NULL && *id != 0);
339 linked_pointer<keymap>
map;
340
342 goto bail;
343
345
348
349 while(map) {
350 if(!stricmp(map->id, id))
351 return map->node;
352 map.next();
353 }
354
355 bail:
357 return NULL;
358 }
359
361 {
362 assert(id != NULL && *id != 0);
363
365
367 return NULL;
368
371 if(!node)
373 return node;
374 }
375
377 {
378 assert(path != NULL && *path != 0);
379
381 if(!base)
382 return NULL;
383
384 return base->getFirst();
385 }
386
388 {
392 }
393
395 {
396 if(node)
398 }
399
401 {
402 assert(id != NULL && *id != 0);
403
404 const char *np;
405 char buf[65];
406 char *ep;
408
409 while(id && *id && node) {
410 String::set(buf, sizeof(buf), id);
411 ep = strchr(buf, '.');
412 if(ep)
413 *ep = 0;
414 np = strchr(id, '.');
415 if(np)
416 id = ++np;
417 else
418 id = NULL;
419 child = node->getChild(buf);
420 if(!child)
421 child =
addNode(node, buf, NULL);
422 node = child;
423 }
424 return node;
425 }
426
428 {
429 assert(base != NULL);
430 assert(id != NULL && *id != 0);
431
432 void *mp;
434 char *cp;
435
436 mp = memalloc::alloc(
sizeof(
keynode));
437 cp = dup(id);
438 node =
new(mp)
keynode(base, cp);
439 if(value)
440 node->setPointer(dup(value));
441 else
442 node->setPointer(NULL);
443 return node;
444 }
445
447 {
448 assert(node != NULL);
449 assert(id != NULL && *id != 0);
450
451 node = node->getChild(id);
452 if(!node)
453 return NULL;
454
455 return node->getPointer();
456 }
457
459 {
460 assert(base != NULL);
461 assert(defs != NULL);
462
464 if(!node)
466
467 for(;;) {
468 ++defs;
470 return base;
471 if(node->getChild(defs->
key))
472 continue;
474 }
475 return node;
476 }
477
479 {
480 assert(base != NULL);
481 assert(id != NULL && *id != 0);
482 assert(attr != NULL);
483 assert(value != NULL);
484
485 linked_pointer<keynode> node = base->getFirst();
487 char *cp;
488
489 while(node) {
490 if(!strcmp(id, node->getId())) {
491 leaf = node->getLeaf(attr);
492 if(leaf) {
493 cp = leaf->getPointer();
494 if(cp && !stricmp(cp, value))
495 return *node;
496 }
497 }
498 node.next();
499 }
500 return NULL;
501 }
502
504 {
505 assert(base != NULL);
506 assert(id != NULL && *id != 0);
507 assert(text != NULL && *text != 0);
508
509 linked_pointer<keynode> node = base->getFirst();
510 char *cp;
511
512 while(node) {
513 if(!strcmp(id, node->getId())) {
514 cp = node->getPointer();
515 if(cp && !stricmp(cp, text))
516 return *node;
517 }
518 node.next();
519 }
520 return NULL;
521 }
522
524 {
525 assert(node != NULL);
526 assert(attr != NULL);
527
528 char *ep, *qt;
529 char *id;
530 int len;
531
532 while(attr && *attr && *attr != '>') {
533 while(isspace(*attr))
534 ++attr;
535
536 if(!*attr || *attr == '>')
537 return;
538
539 id = attr;
540 while(*attr && *attr != '=' && *attr != '>')
541 ++attr;
542
543 if(*attr != '=')
544 return;
545
546 *(attr++) = 0;
547 id = String::trim(id, " \t\r\n");
548 while(isspace(*attr))
549 ++attr;
550
551 qt = attr;
552 ep = strchr(++attr, *qt);
553 if(!ep)
554 return;
555
556 *(ep++) = 0;
557 len = strlen(attr);
558 qt = (char *)memalloc::alloc(len + 1);
559 xmldecode(qt, len + 1, attr);
561 attr = ep;
562 }
563 }
564
566 {
567 assert(fp != NULL);
568
569 char *cp, *ep, *bp, *id;
570 ssize_t len = 0;
571 bool rtn = false;
572 bool document = false, empty;
574
575 if(!node) {
577 top = NULL;
578 }
579 else
580 top = node->getParent();
581
582 if(!fp)
583 return false;
584
586
587 while(node != top) {
589 if(
buffer.len() < 1024 - 5) {
590 len = fread(cp, 1, 1024 -
buffer.len() - 1, fp);
591 }
592 else
593 len = 0;
594
595 if(len < 0)
596 goto exit;
597
598 cp[len] = 0;
600 goto exit;
601
603
604 while(node != top && cp && *cp)
605 {
606 cp = String::trim(cp, " \t\r\n");
607
608 if(cp && *cp && !node)
609 goto exit;
610
611 bp = strchr(cp, '<');
612 if(bp == cp && String::equal(bp, "<!--", 4)) {
613 ep = strstr(cp, "-->");
614 if(ep) {
615 cp = ep + 3;
616 continue; // obscure bug?...
617 }
618 }
619 else
620 ep = strchr(cp, '>');
621 if(!ep && bp == cp)
622 break;
623 if(!bp ) {
624 cp = cp + strlen(cp);
625 break;
626 }
627 if(bp > cp) {
628 if(node->getPointer() != NULL)
629 goto exit;
630
631 *bp = 0;
632 cp = String::chop(cp, " \r\n\t");
633 len = strlen(cp);
634 ep = (char *)memalloc::alloc(len + 1);
635 xmldecode(ep, len + 1, cp);
636 node->setPointer(ep);
637 *bp = '<';
638 cp = bp;
639 continue;
640 }
641
642 empty = false;
643 *ep = 0;
644 if(*(ep - 1) == '/') {
645 *(ep - 1) = 0;
646 empty = true;
647 }
648 cp = ++ep;
649
650 if(!strncmp(bp, "</", 2)) {
651 if(strcmp(bp + 2, node->getId())) {
652 shell::log(shell::ERR, "%s: %s\n",
653 _TEXT("No matching opening token found for"), node->getId());
654 goto exit;
655 }
656
657 node = node->getParent();
658 continue;
659 }
660
661 ++bp;
662
663 // if comment/control field...
664 if(!isalnum(*bp))
665 continue;
666
667 ep = bp;
668 while(isalnum(*ep))
669 ++ep;
670
671 id = NULL;
672 if(isspace(*ep))
673 id = ep;
674
675 while(id && *id && isspace(*id))
676 *(id++) = 0;
677
678 if(!document) {
679 if(strcmp(node->getId(), bp))
680 goto exit;
681 document = true;
682 continue;
683 }
684
685 node =
addNode(node, bp, NULL);
686 if(id)
688 if(empty)
689 node = node->getParent();
690 }
692 }
693 if(node == top)
694 rtn = true;
695 exit:
696 fclose(fp);
697 return rtn;
698 }
699
701 {
702 linked_pointer<callback> sp;
703
704 memset(&peering, 0, sizeof(peering));
705
706 shell::log(shell::NOTIFY, "startup");
707
709
712 while(sp) {
714 sp.next();
715 }
716 }
717 }
718
720 {
721 linked_pointer<callback> sp;
723
724 while(level--) {
726 while(sp) {
728 sp.next();
729 }
730 }
731
733 }
734
736 {
737 assert(fp != NULL);
738 assert(root != NULL);
739
740 unsigned offset;
741 const char *id, *
value;
743 linked_pointer<service::keynode> node =
root;
744 while(node) {
745 id = node->getId();
746 value = node->getPointer();
747 child = node->getFirst();
748 offset = level;
749 while(offset--)
750 fputc(' ', fp);
751 if(child && value && id)
752 fprintf(fp, "%s(%s):\n", id, value);
753 else if(child && id)
754 fprintf(fp, "%s:\n", id);
755 else if(value && id)
756 fprintf(fp, "%s=%s\n", id, value);
757 else if(id)
758 fprintf(fp, "%s\n", id);
759 if(child)
760 dump(fp, child, level + 2);
761 node.next();
762 }
763 }
764
766 {
767 assert(fp != NULL);
768
769 fprintf(fp, "Config ");
771 }
772
774 {
776
777 if(!fp) {
778 shell::log(shell::ERR, "%s\n",
779 _TEXT("dump cannot access file"));
780 return;
781 }
782
783 shell::log(
DEBUG1,
"%s\n",
784 _TEXT("dumping config"));
787 cfg->service::dump(fp);
789 fclose(fp);
790 }
791
793 {
796 unsigned short port;
797 const char *addr = (const char *)vaddr;
798
799 if(!addr)
801
802 if(!addr || eq(addr, "*")) {
803 #ifdef HAVE_GETHOSTNAME
804 static char hostbuf[256] = {0};
805 gethostname(hostbuf, sizeof(hostbuf));
806 if(hostbuf[0])
807 addr = hostbuf;
808 else
809 addr = "localhost";
810 #else
811 addr = "localhost";
812 #endif
813 }
814
816 if(port && port != 5060) {
817 if(strchr(addr, ':'))
818 uri = str("sip:[") + addr + "]:" + str(port);
819 else
820 uri = str("sip:") + addr + ":" + str(port);
821 }
822 else
823 uri = str("sip:") + addr;
824
825 return uri;
826 }
827
829 {
830 assert(slice > 0);
831
832 time_t now, next;
833
834 slice *= 60l; // convert to minute intervals...
835 time(&now);
836 next = ((periodic / slice) + 1l) * slice;
837 if(now < next)
838 return false;
839
840 next = (now / slice) * slice;
841
843
844 if(fp) {
845 DateTimeString dt(periodic);
846 fprintf(fp, "%s %ld\n", (const char *)dt, (long)(next - periodic));
847 }
848 periodic = next;
850 if(fp)
851 fclose(fp);
852
854 while(is(cb)) {
855 cb->period(slice);
856 cb.next();
857 }
858 return true;
859 }
860
862 {
863 linked_pointer<callback> cb;
864 unsigned rl = 0;
866
867 if(!fp) {
868 shell::log(shell::ERR, "%s\n",
869 _TEXT("snapshot; cannot access file"));
870 return;
871 }
872
873 shell::log(
DEBUG1,
"%s\n", _TEXT(
"snapshot started"));
874
877 while(cb) {
878 cb->snapshot(fp);
879 cb.next();
880 }
881 }
886 fclose(fp);
887 shell::log(
DEBUG1,
"%s\n", _TEXT(
"snapshot completed"));
888 }
889
891 {
892 }
893
895 {
896 linked_pointer<callback> cb;
897 unsigned rl = 0;
898 bool rtn = true;
899
902 while(rtn && is(cb)) {
903 rtn = cb->check();
904 cb.next();
905 }
906 }
907 return rtn;
908 }
909
911 {
913 linked_pointer<callback> cb;
914 unsigned rl = 0;
915
917
920 while(is(cb)) {
921 cb->reload(this);
922 cb.next();
923 }
924 }
925
927
934
935 rl = 0;
938 while(is(cb)) {
939 cb->publish(this);
940 cb.next();
941 }
942 }
943
944 // send any config related reload events...
946
947 // let short-term volatile references settle before we delete it...
948 if(orig) {
949 Thread::sleep(1000);
950 delete orig;
951 }
952 }
953
955 {
956 assert(digits != NULL);
957 assert(match != NULL);
958
959 unsigned len = strlen(match);
960 unsigned dlen = 0;
961 bool inc;
962 const char *d = digits;
963 char dbuf[32];
964
965 if(*d == '+')
966 ++d;
967
968 while(*d && dlen < sizeof(dbuf) - 1) {
969 if(isdigit(*d) || *d == '*' || *d == '#') {
970 dbuf[dlen++] = *(d++);
971 continue;
972 }
973
974 if(*d == ' ' || *d == ',') {
975 ++d;
976 continue;
977 }
978
979 if(*d == '!')
980 break;
981
982 if(!stricmp(digits, match))
983 return true;
984
985 return false;
986 }
987
988 if(*d && *d != '!')
989 return false;
990
991 digits = dbuf;
992 dbuf[dlen] = 0;
993
994 if(*match == '+') {
996 --len;
997 if(dlen < len)
998 return false;
999 digits += (len - dlen);
1000 }
1001
1002 while(*match && *digits) {
1003 inc = true;
1004 switch(*match) {
1005 case 'x':
1006 case 'X':
1007 if(!isdigit(*digits))
1008 return false;
1009 break;
1010 case 'N':
1011 case 'n':
1012 if(*digits < '2' || *digits > '9')
1013 return false;
1014 break;
1015 case 'O':
1016 case 'o':
1017 if(*digits && *digits != '1')
1018 inc = false;
1019 break;
1020 case 'Z':
1021 case 'z':
1022 if(*digits < '1' || *digits > '9')
1023 return false;
1024 break;
1025 case '?':
1026 if(!*digits)
1027 return false;
1028 break;
1029 default:
1030 if(*digits != *match)
1031 return false;
1032 }
1033 if(*digits && inc)
1034 ++digits;
1036 }
1037 if(*match && !*digits)
1038 return partial;
1039
1040 if(*match && *digits)
1041 return false;
1042
1043 return true;
1044 }
1045
1046 } // end namespace
Used for definitions of plugin modules.
Some convenience methods for manipulating SIP uri's.
static volatile char * sip_publish
virtual void snapshot(FILE *fp)
static void release(keynode *node)
void addAttributes(keynode *node, char *attrib)
Add attributes in a XML entity as child nodes of the xml node.
static voip::context_t udp_context
static volatile char * sip_contact
virtual void stop(service *cfg)
static voip::context_t tls_context
static volatile dialmode_t dialmode
static const char * getInterface(void)
static void start(void)
Start cdr subsystem and que dispatch thread.
static void startup(void)
void operator=(keynode *node)
virtual void start(service *cfg)
static keynode * getUser(const char *uid)
Used to splice new chains onto an existing xml tree.
keynode * getList(const char *path)
static unsigned sip_prefix
static void snapshot(void)
static void publish(const char *addr)
Set and publish public "appearing" address of the server.
service(const char *name, size_t s=0)
virtual void publish(service *cfg)
static void bind(unsigned short port)
static LinkedObject * getModules(void)
static void publish(const char *address)
Update publish address...
static void dumpfile(void)
static voip::context_t tcp_context
static const char * sip_iface
Keyword and value pair definition lists.
static void dump(FILE *fp, keynode *node, unsigned level)
static LinkedObject * runlevels[4]
static const char * sip_realm
bool load(FILE *file, keynode *node=NULL)
Load xml file into xml tree.
static void notice(const char *reason)
Send notice to user.
static void shutdown(void)
static condlock_t locking
keynode * addNode(keynode *base, define *defs)
Stream events to local clients.
static void reload(void)
Refresh clients with any config events...
static void published(struct sockaddr_storage *peer)
static bool static FILE * output(const char *id)
Used to open an output session for returning control data.
static char session_uuid[40]
static bool match(const char *digits, const char *pattern, bool partial)
static keynode * path(const char *p)
void splice(keyclone *trunk)
static unsigned short getPort(void)
static const char * sip_tlskey
static const char * sip_domain
static const char * sip_tlsdh
static string_t getContact(void)
static keynode * list(const char *p)
static void stop(void)
Stop cdr subsystem.
static const char * sip_tlsca
voip::context_t getContext(const char *uri)
virtual void errlog(shell::loglevel_t level, const char *text)
A pointer to a subtree in the xml configuration tree.
static bool period(long slice)
Service configuration and component callbacks.
static voip::context_t out_context
dialmode_t
Dialing mode supported.
keynode * getNode(keynode *base, const char *id, const char *value)
static const char * sip_tlsdev
static const char * sip_tlscert
System configuration instance and service functions.
Manage control interface.
static unsigned short sip_port
static keynode * get(void)
Interface class for call detail records.
static unsigned sip_range
static const char * getValue(keynode *base, const char *id)
static const char * sip_tlspwd
keynode * getPath(const char *path)
static const char * env(const char *id)
Return the value of a server environment variable.
treemap< char * > keynode
Definition of a xml node.
static keynode * getProtected(const char *path)
virtual void cdrlog(cdr *call)
virtual void confirm(void)
virtual void reload(service *cfg)