SIP Witch 1.9.15
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cgiserver.cpp
Go to the documentation of this file.
1 // Copyright (C) 2008-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 <sipwitch/sipwitch.h>
19 #include <ctype.h>
20 #include <sys/stat.h>
21 
22 using namespace sipwitch;
23 
24 #define RPC_MAX_PARAMS 96
25 
26 #ifdef _MSWINDOWS_
27 typedef DWORD rpcint_t;
28 #else
29 typedef int32_t rpcint_t;
30 #endif
32 
33 typedef struct {
34  const char *method;
35  void (*exec)(void);
36  const char *help;
37  const char *signature;
38 } node_t;
39 
40 static char *cgi_version = NULL;
41 static char *cgi_remuser = NULL;
42 static char *cgi_method = NULL;
43 static char *cgi_query = NULL;
44 static char *cgi_content = NULL;
45 static unsigned cgi_length = 0;
46 static const char *save_file;
47 static const char *temp_file;
48 static const char *control_file;
49 static const char *snapshot_file;
50 static const char *dump_file;
51 
52 static void system_identity(void);
53 static void system_methods(void);
54 static void system_help(void);
55 static void system_signature(void);
56 static void system_status(void);
57 static void server_realm(void);
58 static void server_status(void);
59 static void server_control(void);
60 static void call_range(void);
61 static void call_instance(void);
62 static void stat_range(void);
63 static void stat_instance(void);
64 static void stat_periodic(void);
65 static void user_range(void);
66 static void user_instance(void);
67 
68 static node_t nodes[] = {
69  {"system.identity", &system_identity, "Identify server type and version", "string"},
70  {"system.listMethods", &system_methods, "List server methods", "array"},
71  {"system.methodHelp", &system_help, "Get help text for method", "string, string"},
72  {"system.methodSignature", &system_signature, "Get parameter signature for specified method", "array, string"},
73  {"system.status", &system_status, "Return server status information", "struct"},
74  {"server.status", &server_status, "Return server status string", "string"},
75  {"server.control", &server_control, "Return control request", "boolean, string"},
76  {"server.realm", &server_realm, "Return server realm", "string"},
77  {"call.range", &call_range, "Return list of active calls", "array"},
78  {"call.instance", &call_instance, "Return specific call instance", "struct, string"},
79  {"stat.range", &stat_range, "Return list of call stat nodes", "array"},
80  {"stat.instance", &stat_instance, "return specific statistic node", "struct, string"},
81  {"stat.periodic", &stat_periodic, "return periodic statistics of node", "struct, string"},
82  {"user.range", &user_range, "Return list of user registrations", "array"},
83  {"user.instance", &user_instance, "Return specific user registration", "struct, string"},
84  {NULL, NULL, NULL, NULL}
85 };
86 
87 static struct {
91  unsigned short param[RPC_MAX_PARAMS];
92  unsigned count;
93  short argc;
94 } params;
95 
96 static size_t xmlformat(char *dp, size_t max, const char *fmt, ...)
97 {
98  va_list args;
99 
100  if(max < 1)
101  return 0;
102 
103  va_start(args, fmt);
104  vsnprintf(dp, max, fmt, args);
105  va_end(args);
106  return strlen(dp);
107 }
108 
109 static const char *getIndexed(unsigned short param, unsigned short offset = 0)
110 {
111  unsigned count = 0;
112  unsigned member = 1;
113 
114  if(!offset)
115  offset = 1;
116 
117  while(count < params.count) {
118  if(params.param[count] > param)
119  break;
120 
121  if(params.param[count] == param)
122  if(member++ == offset)
123  return (const char *)params.value[count];
124 
125  ++count;
126  }
127  return NULL;
128 }
129 
130 /*
131 static const char *getNamed(unsigned short param, const char *member)
132 {
133  unsigned count = 0;
134 
135  while(count < params.count) {
136  if(params.param[count] > param)
137  break;
138 
139  if(params.param[count] == param)
140  if(!strcmp(params.name[count], member))
141  return (const char *)params.value[count];
142 
143  ++count;
144  }
145  return NULL;
146 }
147 
148 static const char *getMapped(const char *map, const char *member)
149 {
150  unsigned count = 0;
151 
152  while(count < params.count) {
153  if(!strcmp(params.map[count], map))
154  if(!strcmp(params.name[count], member))
155  return (const char *)params.value[count];
156  ++count;
157  }
158  return NULL;
159 }
160 
161 static const char *getParamId(unsigned short param, unsigned short offset)
162 {
163  unsigned count = 0;
164  unsigned member = 1;
165 
166  if(!offset)
167  offset = 1;
168 
169  while(count < params.count) {
170  if(params.param[count] > param)
171  break;
172 
173  if(params.param[count] == param)
174  if(member++ == offset)
175  return (const char *)params.name[count];
176 
177  ++count;
178  }
179  return NULL;
180 }
181 
182 */
183 
184 static size_t xmltext(char *dp, size_t max, const char *src)
185 {
186  unsigned count = 0;
187  while(*src && count < max) {
188  switch(*src) {
189  case '&':
190  snprintf(dp + count, max - count, "&amp;");
191  count += strlen(dp + count);
192  ++src;
193  break;
194  case '<':
195  snprintf(dp + count, max - count, "&lt;");
196  count += strlen(dp + count);
197  ++src;
198  break;
199  case '>':
200  snprintf(dp + count, max - count, "&gt;");
201  count += strlen(dp + count);
202  ++src;
203  break;
204  case '\"':
205  snprintf(dp + count, max - count, "&quot;");
206  count += strlen(dp + count);
207  ++src;
208  break;
209  case '\'':
210  snprintf(dp + count, max - count, "&apos;");
211  count = strlen(dp + count);
212  ++src;
213  break;
214  default:
215  dp[count++] = *(src++);
216  }
217  }
218  return count;
219 }
220 
221 static size_t b64encode(char *dest, const unsigned char *src, size_t size, size_t max)
222 {
223  static const unsigned char alphabet[65] =
224  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
225 
226  size_t count = 0;
227  unsigned bits;
228 
229  while(size >= 3 && max > 4) {
230  bits = (((unsigned)src[0])<<16) |
231  (((unsigned)src[1])<<8) | ((unsigned)src[2]);
232 
233  src += 3;
234  size -= 3;
235 
236  *(dest++) = alphabet[bits >> 18];
237  *(dest++) = alphabet[(bits >> 12) & 0x3f];
238  *(dest++) = alphabet[(bits >> 6) & 0x3f];
239  *(dest++) = alphabet[bits & 0x3f];
240  max -= 4;
241  count += 4;
242  }
243  *dest = 0;
244  if(!size || max < 5)
245  return count;
246 
247  bits = ((unsigned)src[0])<<16;
248  *(dest++) = alphabet[bits >> 18];
249  ++count;
250  if (size == 1) {
251  *(dest++) = alphabet[(bits >> 12) & 0x3f];
252  *(dest++) = '=';
253  count += 2;
254  }
255  else {
256  bits |= ((unsigned)src[1])<<8;
257  *(dest++) = alphabet[(bits >> 12) & 0x3f];
258  *(dest++) = alphabet[(bits >> 6) & 0x3f];
259  count += 2;
260  }
261  *(dest++) = '=';
262  ++count;
263  *(dest++) = 0;
264  return count;
265 }
266 
267 static char *parseText(char *cp)
268 {
269  char *dp = cp;
270  char *rp = cp;
271 
272  if(!cp)
273  return NULL;
274 
275  while(*cp) {
276  if(*cp != '&') {
277  *(dp++) = *(cp++);
278  continue;
279  }
280  if(!strncmp(cp, "&amp;", 5)) {
281  *(dp++) = '&';
282  cp += 5;
283  continue;
284  }
285  else if(!strncmp(cp, "&gt;", 4))
286  {
287  *(dp++) = '>';
288  cp += 4;
289  continue;
290  }
291  else if(!strncmp(cp, "&lt;", 4))
292  {
293  *(dp++) = '<';
294  *cp += 4;
295  continue;
296  }
297  else if(!strncmp(cp, "&quot;", 6))
298  {
299  *(dp++) = '\"';
300  *cp += 6;
301  continue;
302  }
303  else if(!strncmp(cp, "&apos;", 6))
304  {
305  *(dp++) = '\'';
306  *cp += 6;
307  continue;
308  }
309  *(dp++) = *(cp++);
310  }
311  *dp = 0;
312  return rp;
313 }
314 
315 static char *parseValue(char *cp, char **value, char **map)
316 {
317  *value = NULL;
318 
319  if(map)
320  *map = NULL;
321 
322  while(*cp) {
323  while(isspace(*cp))
324  ++cp;
325 
326  if(!strncmp(cp, "<base64>", 8)) {
327  cp += 8;
328  continue;
329  }
330 
331  if(!strncmp(cp, "<struct>", 8))
332  return cp + 8;
333  else if(!strncmp(cp, "<array>", 7))
334  return cp + 7;
335 
336  if(*cp == '<' && cp[1] != '/') {
337  if(map)
338  *map = ++cp;
339  while(*cp && *cp != '>')
340  ++cp;
341  if(*cp == '>')
342  *(cp++) = 0;
343  continue;
344  }
345 
346  *value = cp;
347  while(*cp && *cp != '<')
348  ++cp;
349 
350  if(*cp)
351  *(cp++) = 0;
352 
353  while(*cp && *cp != '>')
354  ++cp;
355  if(!*cp)
356  return cp;
357  ++cp;
358  parseText(*value);
359  return cp;
360  }
361  return cp;
362 }
363 
364 static char *parseName(char *cp, char **value)
365 {
366  char *t = NULL;
367 
368  while(isspace(*cp))
369  ++cp;
370 
371  if(isalnum(*cp))
372  t = cp;
373  while(*cp && !isspace(*cp) && *cp != '<')
374  ++cp;
375  while(isspace(*cp))
376  *(cp++) = 0;
377  if(*cp != '<')
378  t = NULL;
379  *(cp++) = 0;
380  *value = parseText(t);
381  return cp;
382 }
383 
384 static void version(void)
385 {
386  printf("sipwitch cgi 0.1.0\n"
387  "Copyright (C) 2008 David Sugar, Tycho Softworks\n"
388  "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
389  "This is free software: you are free to change and redistribute it.\n"
390  "There is NO WARRANTY, to the extent permitted by law.\n");
391  exit(0);
392 }
393 
394 static void error(unsigned err, const char *text)
395 {
396  printf(
397  "Status: %d %s\r\n"
398  "Content-Type: text/plain\r\n"
399  "\r\n"
400  "%s\r\n", err, text, text);
401  exit(0);
402 }
403 
404 #ifdef _MSWINDOWS_
405 
406 static void cgilock(void)
407 {
408 }
409 
410 static void cgiunlock(void)
411 {
412 }
413 
414 #else
415 
416 #include <fcntl.h>
417 
418 static pid_t pidfile(void)
419 {
420  struct stat ino;
421  time_t now;
422  fd_t fd;
423  pid_t pid;
424  char buf[65];
425 
426  fd = open(DEFAULT_VARPATH "/run/sipwitch/cgilock", O_RDONLY);
427  if(fd < 0 && errno == EPERM)
428  error(403, "Lock access forbidden");
429 
430  if(fd < 0)
431  return 0;
432 
433  if(read(fd, buf, 16) < 1) {
434  goto bydate;
435  }
436  buf[16] = 0;
437  pid = atoi(buf);
438  if(pid == 1)
439  goto bydate;
440 
441  close(fd);
442  if(kill(pid, 0) && errno == ESRCH)
443  return 0;
444 
445  return pid;
446 
447 bydate:
448  time(&now);
449  fstat(fd, &ino);
450  close(fd);
451  if(ino.st_mtime + 30 < now)
452  return 0;
453  return 1;
454 }
455 
456 static void cgiunlock(void)
457 {
458  remove(DEFAULT_VARPATH "/run/sipwitch/cgilock");
459 }
460 
461 static void cgilock(void)
462 {
463  unsigned count = 90;
464  pid_t opid;
465  fd_t fd;
466  char buf[65];
467 
468 retry:
469  fd = open(DEFAULT_VARPATH "/run/sipwitch/cgilock", O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, fsys::OWNER_PUBLIC);
470  if(fd < 0) {
471  opid = pidfile();
472  if(!opid || opid == 1) {
473  remove(DEFAULT_VARPATH "/run/sipwitch/cgilock");
474  goto retry;
475  }
476  if(count) {
477  --count;
478  ::sleep(1);
479  }
480  else
481  error(408, "Lock timed out");
482  }
483 
484  snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
485  if(write(fd, buf, strlen(buf)) < (ssize_t)strlen(buf))
486  error(500, "Failed Lock");
487  close(fd);
488 }
489 
490 #endif
491 
492 static void request(const char *fmt, ...)
493 {
494  char buf[512];
495  unsigned len = 0;
496  va_list args;
497  FILE *fp;
498 
499 #ifndef _MSWINDOWS_
500  int signo;
501  sigset_t sigs;
502  sigemptyset(&sigs);
503  sigaddset(&sigs, SIGUSR1);
504  sigaddset(&sigs, SIGUSR2);
505  sigaddset(&sigs, SIGALRM);
506  sigprocmask(SIG_BLOCK, &sigs, NULL);
507  snprintf(buf, sizeof(buf), "%ld ", (long)getpid());
508  len = strlen(buf);
509 #endif
510 
511  va_start(args, fmt);
512  vsnprintf(buf + len, sizeof(buf) - len, fmt, args);
513  va_end(args);
514  if(!strchr(buf, '\n'))
515  String::add(buf, sizeof(buf), "\n");
516 
517  fp = fopen(control_file, "w");
518  if(!fp)
519  error(405, "Server unavailable");
520 
521  fputs(buf, fp);
522  fclose(fp);
523 #ifndef _MSWINDOWS_
524  alarm(60);
525 #ifdef HAVE_SIGWAIT2
526  sigwait(&sigs, &signo);
527 #else
528  signo = sigwait(&sigs);
529 #endif
530  if(signo == SIGUSR2)
531  error(405, "Request failed");
532  if(signo == SIGALRM)
533  error(408, "Request timed out");
534 #endif
535 }
536 
537 static void dump(void)
538 {
539  char buf[512];
540 
541  cgilock();
542  request("dump");
543  FILE *fp = fopen(dump_file, "r");
544  cgiunlock();
545  if(!fp)
546  error(403, "Dump unavailable");
547 
548  printf(
549  "Status: 200 OK\r\n"
550  "Content-Type: text/plain\r\n"
551  "\r\n");
552 
553  while(!feof(fp)) {
554  if(fgets(buf, sizeof(buf) - 1, fp) != NULL)
555  fputs(buf, stdout);
556  }
557  fflush(stdout);
558  exit(0);
559 }
560 
561 static void snapshot(void)
562 {
563  char buf[512];
564 
565  cgilock();
566  request("snapshot");
567  FILE *fp = fopen(snapshot_file, "r");
568  cgiunlock();
569  if(!fp)
570  error(403, "Snapshot unavailable");
571 
572  printf(
573  "Status: 200 OK\r\n"
574  "Content-Type: text/plain\r\n"
575  "\r\n");
576 
577  while(!feof(fp)) {
578  if(fgets(buf, sizeof(buf) - 1, fp) != NULL)
579  fputs(buf, stdout);
580  }
581  fflush(stdout);
582  exit(0);
583 }
584 
585 static void config(void)
586 {
587  char buf[512];
588  FILE *fp = fopen(save_file, "r");
589  if(!fp)
590  error(403, "Config unavailable");
591 
592  printf(
593  "Status: 200 OK\r\n"
594  "Content-Type: text/xml\r\n"
595  "\r\n");
596 
597  while(!feof(fp)) {
598  if(fgets(buf, sizeof(buf) - 1, fp) != NULL)
599  fputs(buf, stdout);
600  }
601  fflush(stdout);
602  exit(0);
603 }
604 
605 static void response(char *buffer, unsigned max, const char *fmt, ...)
606 {
607  rpcint_t iv;
608  time_t tv;
609  struct tm *dt;
610  double dv;
611  const unsigned char *xp;
612  size_t xsize;
613  const char *sv;
614  const char *valtype = "string";
615  const char *name;
616  bool end_flag = false; // end of param...
617  bool map_flag = false;
618  bool struct_flag = false;
619  bool array_flag = false;
620  size_t count = strlen(buffer);
621  va_list args;
622  va_start(args, fmt);
623 
624  if(*fmt == '^') {
625  count = 0;
626  ++fmt;
627  }
628 
629  switch(*fmt) {
630  case '(':
631  case '[':
632  case '<':
633  case '{':
634  case 's':
635  case 'i':
636  case 'd':
637  case 't':
638  case 'b':
639  case 'x':
640  count += xmlformat(buffer + count, max - count,
641  "<?xml version=\"1.0\"?>\r\n"
642  "<methodResponse>\r\n"
643  " <params><param>\r\n");
644  break;
645  case '!':
646  array_flag = true;
647  break;
648  case '+':
649  map_flag = true;
650  case '.':
651  struct_flag = true;
652  }
653 
654  if(!*fmt)
655  end_flag = true;
656 
657  while(*fmt && *fmt != '$' && count < max - 1 && !end_flag) {
658  switch(*fmt) {
659  case '[':
660  count += xmlformat(buffer + count, max - count,
661  " <value><array><data>\r\n");
662  case ',':
663  array_flag = true;
664  break;
665  case ']':
666  array_flag = false;
667  count += xmlformat(buffer + count, max - count,
668  " </data></array></value>\r\n");
669  end_flag = true;
670  break;
671  case '}':
672  case '>':
673  map_flag = struct_flag = false;
674  count += xmlformat(buffer + count, max - count,
675  " </struct></value></member>\r\n"
676  " </struct></value>\r\n");
677  end_flag = true;
678  break;
679  case ';':
680  case ':':
681  name = va_arg(args, const char *);
682  count += xmlformat(buffer + count, max - count,
683  " </struct></value></member>\r\n"
684  " <member><name>%s<value><struct>\r\n", name);
685  break;
686  case '{':
687  case '<':
688  name = va_arg(args, const char *);
689  count += xmlformat(buffer + count, max - count,
690  " <value><struct>\r\n"
691  " <member><name>%s</name><value><struct>\r\n", name);
692  struct_flag = map_flag = true;
693  break;
694  case '(':
695  struct_flag = true;
696  count += xmlformat(buffer + count, max - count, " <value><struct>\r\n");
697  break;
698  case ')':
699  struct_flag = false;
700  if(!map_flag && !array_flag)
701  end_flag = true;
702  count += xmlformat(buffer + count, max - count,
703  " </struct></value>\r\n");
704  break;
705  case 's':
706  case 'i':
707  case 'b':
708  case 'd':
709  case 't':
710  case 'x':
711  case 'm':
712  switch(*fmt) {
713  case 'm':
714  valtype = "string";
715  break;
716  case 'd':
717  valtype = "double";
718  break;
719  case 'b':
720  valtype = "boolean";
721  break;
722  case 'i':
723  valtype = "i4";
724  break;
725  case 's':
726  valtype = "string";
727  break;
728  case 't':
729  valtype = "dateTime.iso8601";
730  break;
731  case 'x':
732  valtype = "base64";
733  }
734  if(struct_flag && *fmt == 'm') {
735  if(count > max - 60)
736  goto skip;
737  sv = va_arg(args, const char *);
738  while(sv && *sv) {
739  count += xmlformat(buffer + count, max - count,
740  " <member><name>");
741  while(*sv && *sv != ':' && *sv != '=' && count < max - 35) {
742  buffer[count++] = tolower(*sv);
743  ++sv;
744  }
745  buffer[count] = 0;
746  count += xmlformat(buffer + count, max - count,
747  "</name>\r\n"
748  " <value><%s>", valtype);
749  if(*sv == ':' || *sv == '=')
750  ++sv;
751  else
752  sv="";
753  while(*sv && *sv != ';' && count < max - 20) {
754  switch(*sv) {
755  case '<':
756  count += xmlformat(buffer + count, max - count, "&lt;");
757  break;
758  case '>':
759  count += xmlformat(buffer + count, max - count, "&gt;");
760  break;
761  case '&':
762  count += xmlformat(buffer + count, max - count, "&amp;");
763  break;
764  case '\"':
765  count += xmlformat(buffer + count, max - count, "&quot;");
766  break;
767  case '\'':
768  count += xmlformat(buffer + count, max - count, "&apos;");
769  break;
770  default:
771  buffer[count++] = *sv;
772  }
773  ++sv;
774  }
775  count += xmlformat(buffer + count, max - count,
776  "</%s></value></member>\r\n", valtype);
777  }
778  goto skip;
779  }
780  if(struct_flag) {
781  name = va_arg(args, const char *);
782  count += xmlformat(buffer + count, max - count,
783  " <member><name>%s</name>\r\n"
784  " <value><%s>", name, valtype);
785  }
786  else
787  count += xmlformat(buffer + count, max - count,
788  " <value><%s>", valtype);
789 
790  switch(*fmt) {
791  case 'x':
792  xp = va_arg(args, const unsigned char *);
793  xsize = va_arg(args, size_t);
794  count += b64encode(buffer + count, xp, xsize, max - count);
795  break;
796  case 's':
797  sv = va_arg(args, const char *);
798  if(!sv)
799  sv = "";
800  count += xmltext(buffer + count, max - count, sv);
801  break;
802  case 'd':
803  dv = va_arg(args, double);
804  count += xmlformat(buffer + count, max - count, "%f", dv);
805  break;
806  case 'i':
807  case 'b':
808  iv = va_arg(args, rpcint_t);
809  if(*fmt == 'b' && iv)
810  iv = 1;
811  count += xmlformat(buffer + count, max - count, "%ld", (long)iv);
812  break;
813  case 't':
814  tv = va_arg(args, time_t);
815  dt = DateTime::local(&tv);
816  if(dt->tm_year < 1800)
817  dt->tm_year += 1900;
818  count += xmlformat(buffer + count, max - count,
819  "%04d%02d%02dT%02d:%02d:%02d",
820  dt->tm_year, dt->tm_mon + 1, dt->tm_mday,
821  dt->tm_hour, dt->tm_min, dt->tm_sec);
822  DateTime::release(dt);
823  break;
824  }
825  if(struct_flag)
826  count += xmlformat(buffer + count, max - count,
827  "</%s></value></member>\r\n", valtype);
828  else
829  count += xmlformat(buffer + count, max - count,
830  "</%s></value>\r\n", valtype);
831 skip:
832  if(!struct_flag && !array_flag)
833  end_flag = true;
834  }
835  ++fmt;
836  }
837 
838  if(*fmt == '$' || end_flag)
839  xmlformat(buffer + count, max - count,
840  " </param></params>\r\n"
841  "</methodResponse>\r\n");
842 
843  va_end(args);
844 }
845 
846 static void reply(const char *buffer)
847 {
848  printf(
849  "Status: 200 OK\r\n"
850  "Content-Length: %ld\r\n"
851  "Content-Type: text/xml\r\n"
852  "\r\n%s", (long)strlen(buffer), buffer);
853  exit(0);
854 }
855 
856 /*
857 static void success(void)
858 {
859  char buffer[1024];
860 
861  xmlformat(buffer, sizeof(buffer),
862  "<?xml version=\"1.0\"?>\r\n"
863  "<methodResponse><params></params></methodResponse>\r\n");
864 
865  reply(buffer);
866 }
867 */
868 
869 static void fault(int code, const char *string)
870 {
871  char buffer[4096];
872 
873  size_t count = xmlformat(buffer, sizeof(buffer),
874  "<?xml version=\"1.0\"?>\r\n"
875  "<methodResponse>\r\n"
876  " <fault><value><struct>\r\n"
877  " <member><name>faultCode</name>\r\n"
878  " <value><int>%d</int></value></member>\r\n"
879  " <member><name>faultString</name>\r\n"
880  " <value><string>", code);
881  count += xmltext(buffer + count, sizeof(buffer) - count, string);
882  xmlformat(buffer + count, sizeof(buffer) - count,
883  "</string></value></member>\r\n"
884  " </struct></value></fault>\r\n"
885  "</methodResponse>\r\n");
886 
887  reply(buffer);
888 }
889 
890 static void system_methods(void)
891 {
892  char buffer[2048];
893  unsigned index = 0;
894 
895  if(params.argc)
896  fault(3, "Invalid Parameters");
897 
898  response(buffer, sizeof(buffer), "^[");
899 
900  while(nodes[index].method) {
901  response(buffer, sizeof(buffer), "!s", nodes[index].method);
902  ++index;
903  }
904 
905  response(buffer, sizeof(buffer), "]");
906  reply(buffer);
907 }
908 
909 static void system_help(void)
910 {
911  char buffer[1024];
912  unsigned index = 0;
913 
914  if(params.argc != 1)
915  fault(3, "Invalid Parameters");
916 
917  const char *method = getIndexed(1);
918  if(!method || !*method)
919  fault(4, "Invalid Method Argument");
920 
921  while(nodes[index].method && !String::equal(nodes[index].method, method))
922  ++index;
923 
924  if(!nodes[index].help)
925  fault(4, "Unknown Method");
926 
927  response(buffer, sizeof(buffer), "^s", nodes[index].help);
928  reply(buffer);
929 }
930 
931 static void system_signature(void)
932 {
933  char buffer[1024];
934  unsigned index = 0;
935 
936  if(params.argc != 1)
937  fault(3, "Invalid Parameters");
938 
939  const char *method = getIndexed(1);
940  if(!method || !*method)
941  fault(4, "Invalid Method Argument");
942 
943  while(nodes[index].method && !String::equal(nodes[index].method, method))
944  ++index;
945 
946  if(!nodes[index].signature)
947  fault(4, "Unknown Method");
948 
949  response(buffer, sizeof(buffer), "^[!s]", nodes[index].signature);
950  reply(buffer);
951 }
952 
953 static void system_identity(void)
954 {
955  char buffer[512];
956 
957  if(params.argc != 0)
958  fault(3, "Invalid Parameters");
959 
960  response(buffer, sizeof(buffer), "^s", "sipwitch/" VERSION);
961  reply(buffer);
962 }
963 
964 static bool iocontrol(const char *cmd)
965 {
966  char buffer[512];
967  FILE *fp;
968 #ifdef _MSWINDOWS_
969  snprintf(buffer, sizeof(buffer), "%s\n", cmd);
970 #else
971  snprintf(buffer, sizeof(buffer), "%ld %s\n", (long)getpid(), cmd);
972 #endif
973  char *ep = strchr(buffer, '\n');
974  if(ep)
975  *(++ep) = 0;
976 
977 #ifndef _MSWINDOWS_
978  int signo;
979  sigset_t sigs;
980  sigemptyset(&sigs);
981  sigaddset(&sigs, SIGUSR1);
982  sigaddset(&sigs, SIGUSR2);
983  sigaddset(&sigs, SIGALRM);
984  sigprocmask(SIG_BLOCK, &sigs, NULL);
985 #endif
986 
987  fp = fopen(control_file, "w");
988  if(!fp)
989  fault(2, "Server Offline");
990 
991  fputs(buffer, fp);
992  fclose(fp);
993 #ifndef _MSWINDOWS_
994  alarm(60);
995 #ifdef HAVE_SIGWAIT2
996  sigwait(&sigs, &signo);
997 #else
998  signo = sigwait(&sigs);
999 #endif
1000  if(signo == SIGUSR2)
1001  return false;
1002  if(signo == SIGALRM)
1003  fault(6, "Request Timed Out");
1004 #endif
1005  return true;
1006 }
1007 
1008 static void server_control(void)
1009 {
1010  char buffer[512];
1011 
1012  if(params.argc != 1)
1013  fault(3, "Invalid Parameters");
1014 
1015  const char *command = getIndexed(1);
1016  if(!command || !*command)
1017  fault(5, "Invalid Command Argument");
1018 
1019  response(buffer, sizeof(buffer), "^(b)", iocontrol(command));
1020  reply(buffer);
1021 }
1022 
1023 static void call_instance(void)
1024 {
1025  mapped_view<MappedCall> cr(CALL_MAP);
1026  unsigned index = 0;
1027  char id[32];
1028  MappedCall map;
1029  char buffer[1024];
1030  rpcint_t diff = 0;
1031  time_t now;
1032 
1033  if(params.argc != 1)
1034  fault(3, "Invalid Parameters");
1035 
1036  const char *cid = getIndexed(1);
1037  if(!cid || !*cid)
1038  fault(5, "Invalid Command Argument");
1039 
1040 
1041  unsigned count = cr.count();
1042  if(!count)
1043  fault(2, "Server Offline");
1044 
1045  time(&now);
1046  while(index < count) {
1047  cr.copy(index++, map);
1048  if(!map.created)
1049  continue;
1050 
1051  snprintf(id, sizeof(id), "%08x:%d", map.sequence, map.cid);
1052  if(!eq(id, cid))
1053  continue;
1054 
1055  if(map.active) {
1056  time(&now);
1057  diff = (rpcint_t)(now - map.active);
1058  }
1059 
1060  response(buffer, sizeof(buffer), "^(tsssssi)",
1061  map.created, map.state + 1, map.authorized,
1062  map.source, map.target, map.display, diff);
1063  reply(buffer);
1064  }
1065  fault(6, "Unknown Call");
1066  reset_unsafe<MappedCall>(map);
1067 }
1068 
1069 static void call_range(void)
1070 {
1071  mapped_view<MappedCall> cr(REGISTRY_MAP);
1072  unsigned size;
1073  unsigned index = 0;
1074  char id[32];
1075  MappedCall map;
1076 
1077  if(params.argc != 0)
1078  fault(3, "Invalid Parameters");
1079 
1080  unsigned count = cr.count();
1081  if(!count)
1082  fault(2, "Server Offline");
1083 
1084  size = count * 64 + 128;
1085  char *buffer = (char *)malloc(size);
1086  response(buffer, size, "^[");
1087 
1088  while(index < count) {
1089  cr.copy(index++, map);
1090 
1091  if(!map.created)
1092  continue;
1093 
1094  snprintf(id, sizeof(id), "%08x:%d", map.sequence, map.cid);
1095  response(buffer, size, "!s", buffer);
1096  }
1097  response(buffer, size, "]");
1098  reply(buffer);
1099  reset_unsafe<MappedCall>(map);
1100 }
1101 
1102 static void stat_periodic(void)
1103 {
1104  mapped_view<stats> sta(STAT_MAP);
1105  unsigned index = 0;
1106  stats map;
1107  char buffer[1024];
1108 
1109  if(params.argc != 1)
1110  fault(3, "Invalid Parameters");
1111 
1112  const char *cid = getIndexed(1);
1113  if(!cid || !*cid)
1114  fault(5, "Invalid Command Argument");
1115 
1116  unsigned count = sta.count();
1117  if(!count)
1118  fault(2, "Server Offline");
1119 
1120  while(index < count) {
1121  sta.copy(index++, map);
1122  if(!eq(map.id, cid))
1123  continue;
1124 
1125  response(buffer, sizeof(buffer), "^(itiiiiii)",
1126  (rpcint_t)map.limit, map.lastcall,
1127  (rpcint_t)map.data[0].pperiod, (rpcint_t)map.data[0].pmin, (rpcint_t)map.data[0].pmax,
1128  (rpcint_t)map.data[1].pperiod, (rpcint_t)map.data[1].pmin, (rpcint_t)map.data[1].pmax);
1129  reply(buffer);
1130  }
1131  fault(7, "Unknown Stat");
1132  reset_unsafe<stats>(map);
1133 }
1134 
1135 static void stat_instance(void)
1136 {
1137  mapped_view<stats> sta(STAT_MAP);
1138  unsigned index = 0;
1139  stats map;
1140  char buffer[1024];
1141 
1142  if(params.argc != 1)
1143  fault(3, "Invalid Parameters");
1144 
1145  const char *cid = getIndexed(1);
1146  if(!cid || !*cid)
1147  fault(5, "Invalid Command Argument");
1148 
1149  unsigned count = sta.count();
1150  if(!count)
1151  fault(2, "Server Offline");
1152 
1153  while(index < count) {
1154  sta.copy(index++, map);
1155  if(!eq(map.id, cid))
1156  continue;
1157 
1158  response(buffer, sizeof(buffer), "^(itiiiiii)",
1159  (rpcint_t)map.limit, map.lastcall,
1160  (rpcint_t)map.data[0].total, (rpcint_t)map.data[0].current, (rpcint_t)map.data[0].peak,
1161  (rpcint_t)map.data[1].total, (rpcint_t)map.data[1].current, (rpcint_t)map.data[1].peak);
1162  reply(buffer);
1163  }
1164  fault(7, "Unknown Stat");
1165  reset_unsafe<stats>(map);
1166 }
1167 
1168 static void stat_range(void)
1169 {
1170  mapped_view<stats> sta(STAT_MAP);
1171  unsigned size;
1172  unsigned index = 0;
1173  stats map;
1174 
1175  if(params.argc != 0)
1176  fault(3, "Invalid Parameters");
1177 
1178  unsigned count = sta.count();
1179  if(!count)
1180  fault(2, "Server Offline");
1181 
1182  size = count * 48 + 128;
1183  char *buffer = (char *)malloc(size);
1184  response(buffer, size, "^[");
1185 
1186  while(index < count) {
1187  sta.copy(index++, map);
1188  if(!map.id[0])
1189  continue;
1190 
1191  response(buffer, size, "!s", map.id);
1192  }
1193  response(buffer, size, "]");
1194  reply(buffer);
1195  reset_unsafe<stats>(map);
1196 }
1197 
1198 static void user_instance(void)
1199 {
1200  mapped_view<MappedRegistry> reg(REGISTRY_MAP);
1201  unsigned index = 0;
1202  char ext[48];
1204  char buffer[2048];
1205  time_t now;
1206  const char *status = "idle";
1207 
1208  if(params.argc != 1)
1209  fault(3, "Invalid Parameters");
1210 
1211  const char *id = getIndexed(1);
1212  if(!id || !*id)
1213  fault(5, "Invalid Command Argument");
1214 
1215  unsigned count = reg.count();
1216  if(!count)
1217  fault(2, "Server Offline");
1218 
1219  time(&now);
1220  while(index < count) {
1221  reg.copy(index++, map);
1222 
1224  continue;
1225 
1226  if(map.expires < now)
1227  continue;
1228 
1229  if(!eq(map.userid, id))
1230  continue;
1231 
1232  if(map.ext)
1233  snprintf(ext, sizeof(ext), "%u", map.ext);
1234  else
1235  String::set(ext, sizeof(ext), map.userid);
1236 
1237  if(map.inuse)
1238  status = "busy";
1239  else
1240  switch(map.status) {
1241  case MappedRegistry::AWAY:
1242  status = "away";
1243  break;
1244  case MappedRegistry::DND:
1245  status = "dnd";
1246  break;
1247  case MappedRegistry::BUSY:
1248  status = "busy";
1249  break;
1250  default:
1251  break;
1252  }
1253 
1254  response(buffer, sizeof(buffer), "^(ssssii)",
1255  ext, map.display, map.profile.id, status,
1256  (rpcint_t)map.inuse, (rpcint_t)map.profile.level);
1257  reply(buffer);
1258  }
1259  fault(8, "Unknown User");
1260  reset_unsafe<MappedRegistry>(map);
1261 }
1262 
1263 static void user_range(void)
1264 {
1265  mapped_view<MappedRegistry> reg(REGISTRY_MAP);
1266  unsigned size;
1267  unsigned index = 0;
1269 
1270  if(params.argc != 0)
1271  fault(3, "Invalid Parameters");
1272 
1273  unsigned count = reg.count();
1274  if(!count)
1275  fault(2, "Server Offline");
1276 
1277  size = count * 48 + 128;
1278  char *buffer = (char *)malloc(size);
1279  response(buffer, size, "^[");
1280  time_t now;
1281  time(&now);
1282 
1283  while(index < count) {
1284  reg.copy(index++, map);
1285 
1287  continue;
1288 
1289  if(map.expires < now)
1290  continue;
1291 
1292  response(buffer, size, "!s", map.userid);
1293  }
1294  response(buffer, size, "]");
1295  reply(buffer);
1296  reset_unsafe<MappedRegistry>(map);
1297 }
1298 
1299 static void server_realm(void)
1300 {
1301  fsys fd;
1302  char realm[128];
1303  char buffer[256];
1304 
1305  if(params.argc != 0)
1306  fault(3, "Invalid Parameters");
1307 
1308  fd.open(DEFAULT_CFGPATH "/siprealm", fsys::RDONLY);
1309  if(!is(fd))
1310  fd.open(DEFAULT_VARPATH "/lib/sipwitch/uuid", fsys::RDONLY);
1311 
1312  if(is(fd)) {
1313  memset(realm, 0, sizeof(realm));
1314  fd.read(realm, sizeof(realm) - 1);
1315  fd.close();
1316 
1317  char *cp = strchr(realm, '\n');
1318  if(cp)
1319  *cp = 0;
1320 
1321  cp = strchr(realm, ':');
1322  if(cp)
1323  *cp = 0;
1324  }
1325  else
1326  fault(2, "No Realm Available");
1327 
1328  response(buffer, sizeof(buffer), "^s", realm);
1329  reply(buffer);
1330 }
1331 
1332 static void server_status(void)
1333 {
1334  mapped_view<MappedCall> cr(REGISTRY_MAP);
1335  char *cp;
1336  unsigned index = 0;
1337  volatile const MappedCall *map;
1338 
1339  if(params.argc != 0)
1340  fault(3, "Invalid Parameters");
1341 
1342  unsigned count = cr.count();
1343  if(!count)
1344  fault(2, "Server Offline");
1345 
1346  cp = (char *)malloc(count + 1);
1347  cp[count] = 0;
1348  memset(cp, ' ', count);
1349  while(index < count) {
1350  map = (const volatile MappedCall*)(cr(index++));
1351  if(map->state[0])
1352  cp[index - 1] = map->state[0];
1353  }
1354  char *buffer = (char *)malloc(count + 512);
1355  response(buffer, count + 512, "^s", cp);
1356  reply(buffer);
1357 }
1358 
1359 static void system_status(void)
1360 {
1361  time_t now;
1362  struct stat ino;
1363  char buffer[512];
1364  unsigned count = 0;
1365 
1366 
1367  if(params.argc != 0)
1368  fault(3, "Invalid Parameters");
1369 
1370  if(stat(control_file, &ino))
1371  fault(2, "Server Offline");
1372 
1373  while(nodes[count].method)
1374  ++count;
1375 
1376  time(&now);
1377  response(buffer, sizeof(buffer), "^(titissi)",
1378  "date", now,
1379  "date_int", (rpcint_t)now,
1380  "started", ino.st_ctime,
1381  "started_int", ino.st_ctime,
1382  "name", "sipwitch",
1383  "version", VERSION,
1384  "methods_known", (rpcint_t)count);
1385 
1386  reply(buffer);
1387 }
1388 
1389 static void dispatch(const char *method)
1390 {
1391  unsigned index = 0;
1392  while(nodes[index].method && !String::equal(method, nodes[index].method))
1393  ++index;
1394  if(!nodes[index].method)
1395  fault(1, "Unknown Method");
1396  (*nodes[index].exec)();
1397 }
1398 
1399 static void post(FILE *inp = stdin)
1400 {
1401  FILE *fp;
1402  char *buf;
1403  char *cp;
1404  char *value;
1405  char *map = NULL;
1406  char *method = NULL;
1407  bool name_flag = false;
1408 
1409  params.argc = 0;
1410  params.count = 0;
1411 
1412  if(!cgi_length)
1413  error(411, "Length required");
1414  if(!cgi_content || stricmp(cgi_content, "text/xml"))
1415  error(415, "Unsupported media type");
1416 
1417  buf = new char[cgi_length + 1];
1418  if(fread(buf, cgi_length, 1, inp) < 1)
1419  error(400, "Invalid read of input");
1420 
1421  buf[cgi_length] = 0;
1422 
1423  cp = buf;
1424  while(*cp) {
1425  while(isspace(*cp))
1426  ++cp;
1427 
1428  if(!strncmp(cp, "<sipwitch>", 10)) {
1429  cgilock();
1430  remove(temp_file);
1431  fp = fopen(temp_file, "w");
1432  if(!fp) {
1433  cgiunlock();
1434  error(403, "Access forbidden");
1435  }
1436 
1437  if(fwrite(buf, cgi_length, 1, fp) < 1) {
1438  cgiunlock();
1439  error(500, "Invalid write of config");
1440  }
1441 
1442  fclose(fp);
1443  rename(temp_file, save_file);
1444  cgiunlock();
1445  request("reload");
1446  error(200, "ok");
1447  }
1448 
1449  if(!strncmp(cp, "<methodName>", 12)) {
1450  cp = parseName(cp + 12, &method);
1451  if(strncmp(cp, "/methodName>", 12) || !method)
1452  error(400, "Malformed request");
1453 
1454  cp += 12;
1455  break;
1456 
1457  }
1458 
1459  if(!strncmp(cp, "<methodCall>", 12)) {
1460  cp += 12;
1461  continue;
1462  }
1463 
1464  if(!strncmp(cp, "<?", 2) || !strncmp(cp, "<!", 2)) {
1465  while(*cp && *cp != '>')
1466  ++cp;
1467  if(*cp)
1468  ++cp;
1469  continue;
1470  }
1471 
1472  error(400, "Malformed request");
1473  }
1474 
1475  if(!method || !*cp)
1476  error(400, "Malformed request");
1477 
1478  while(*cp && params.count < RPC_MAX_PARAMS) {
1479  while(isspace(*cp))
1480  ++cp;
1481 
1482  if(!*cp)
1483  break;
1484 
1485  if(!strncmp(cp, "<name>", 6)) {
1486  name_flag = true;
1487  cp = parseName(cp + 6, &params.name[params.count]);
1488  params.map[params.count] = map;
1489  if(strncmp(cp, "/name>", 6))
1490  error(400, "Malformed request");
1491 
1492  cp += 6;
1493  continue;
1494  }
1495  if(!strncmp(cp, "</struct>", 9) && map && !name_flag) {
1496  map = NULL;
1497  cp += 9;
1498  continue;
1499  }
1500  if(!strncmp(cp, "<param>", 7)) {
1501  params.name[params.count] = params.value[params.count] = params.map[params.count] = NULL;
1502  ++params.argc;
1503  cp += 7;
1504  continue;
1505  }
1506  if(!strncmp(cp, "<value>", 7)) {
1507  params.param[params.count] = params.argc;
1508  cp = parseValue(cp, &value, NULL);
1509  if(value)
1510  params.value[params.count++] = value;
1511  else if(name_flag)
1512  map = params.name[params.count];
1513  name_flag = false;
1514  params.name[params.count] = params.map[params.count] = params.value[params.count] = NULL;
1515  continue;
1516  }
1517  if(!strncmp(cp, "</params>", 9))
1518  dispatch(method);
1519 
1520  if(*cp == '<') {
1521  while(*cp && *cp != '>')
1522  ++cp;
1523  if(*cp)
1524  ++cp;
1525  else
1526  error(400, "Malformed request");
1527  continue;
1528  }
1529  error(400, "Malformed request");
1530  }
1531  error(400, "Malformed request");
1532 }
1533 
1534 static void callfile(FILE *fp, const char *id)
1535 {
1536 #ifdef _MSWINDOWS_
1537  struct _stat ino;
1538 #else
1539  struct stat ino;
1540 #endif
1541  char buf[256];
1542  const char *cp = NULL;
1543  unsigned long date, line = 0;
1544  unsigned long last_date = 0, last_line = 0;
1545  struct tm *dt;
1546 
1547  if(!fp)
1548  return;
1549 
1550 #ifdef _MSWINDOWS_
1551  _fstat(_fileno(fp), &ino);
1552 #else
1553  fstat(fileno(fp), &ino);
1554 #endif
1555 
1556  dt = DateTime::local(&ino.st_ctime);
1557 
1558  if(dt->tm_year >= 2000)
1559  dt->tm_year -= 2000;
1560 
1561  date = dt->tm_hour + (32l * dt->tm_mday) + (1024l * dt->tm_mon) + (16384l * dt->tm_year);
1562  DateTime::release(dt);
1563 
1564  if(id) {
1565  last_date = atol(id);
1566  cp = strchr(id, '/');
1567  }
1568  if(cp)
1569  last_line = atol(++cp);
1570 
1571  if(date < last_date) {
1572  fclose(fp);
1573  return;
1574  }
1575 
1576  while(!feof(fp)) {
1577  if(fgets(buf, sizeof(buf), fp) == NULL)
1578  buf[0] = 0;
1579  if(!buf[0])
1580  continue;
1581  cp = buf;
1582  ++line;
1583  if(date == last_date && line <= last_line)
1584  continue;
1585  if(id && *id && strnicmp(id, cp, strlen(id) > 0))
1586  continue;
1587  printf("%07ld/%07ld %s", date, line, cp);
1588  }
1589  if(fp)
1590  fclose(fp);
1591 }
1592 
1593 static void calls(const char *id)
1594 {
1595  printf(
1596  "Status: 200 OK\r\n"
1597  "Content-Type: text/plain\r\n"
1598  "\r\n");
1599  callfile(fopen(DEFAULT_VARPATH "/log/sipwitch.calls.0", "r"), id);
1600  callfile(fopen(DEFAULT_VARPATH "/log/sipwitch.calls", "r"), id);
1601  exit(0);
1602 }
1603 
1604 static void info(void)
1605 {
1606  char buf[256];
1607  char *cp;
1608  printf(
1609  "Status: 200 OK\r\n"
1610  "Content-Type: text/xml\r\n"
1611  "\r\n");
1612 
1613  printf("<?xml version=\"1.0\"?>\n");
1614  printf("<serviceInfo>\n");
1615  printf(" <version>" VERSION "</version>\n");
1616  FILE *fp = fopen(DEFAULT_VARPATH "/run/sipwitch/state.def", "r");
1617  String::set(buf, sizeof(buf), "up");
1618  if(fp) {
1619  if(fgets(buf, sizeof(buf), fp) == NULL)
1620  buf[0] = 0;
1621  fclose(fp);
1622  }
1623  cp = strchr(buf, '\r');
1624  if(!cp)
1625  cp = strchr(buf, '\n');
1626  if(cp)
1627  *cp = 0;
1628  if(!stricmp(buf, "none") || !buf[0])
1629  String::set(buf, sizeof(buf), "up");
1630 #ifndef _MSWINDOWS_
1631  pid_t pid = 0;
1632  fp = fopen(DEFAULT_VARPATH "/run/sipwitch/pidfile", "r");
1633  if(fp) {
1634  if(fgets(buf, sizeof(buf), fp) == NULL)
1635  buf[0] = 0;
1636  fclose(fp);
1637  pid = atol(buf);
1638  if(pid) {
1639  if(kill(pid, 0) && errno == ESRCH)
1640  pid = 0;
1641  }
1642  }
1643  if(!pid)
1644  String::set(buf, sizeof(buf), "down");
1645 #endif
1646  printf(" <state>%s</state>\n", buf);
1647  printf("</serviceInfo>\n");
1648  exit(0);
1649 }
1650 
1651 static void dumpcalls(const char *id)
1652 {
1653  mapped_view<MappedCall> calls(CALL_MAP);
1654  unsigned count = calls.count();
1655  unsigned index = 0;
1656  MappedCall buffer;
1657  char idbuf[32];
1658  time_t now;
1659 
1660  if(!count)
1661  error(405, "Server unavailable");
1662 
1663  printf(
1664  "Status: 200 OK\r\n"
1665  "Content-Type: text/xml\r\n"
1666  "\r\n");
1667 
1668  printf("<?xml version=\"1.0\"?>\n");
1669  printf("<mappedCalls>\n");
1670  time(&now);
1671 
1672  while(index < count) {
1673  calls.copy(index++, buffer);
1674  if(!buffer.created)
1675  continue;
1676 
1677  snprintf(idbuf, sizeof(idbuf), "%08x:%u", buffer.sequence, buffer.cid);
1678  if(id && !String::equal(id, idbuf))
1679  continue;
1680  printf(" <call id=\"%s\">\n", idbuf);
1681  printf(" <source>%s</source>\n", buffer.source);
1682  printf(" <started>%ld</started>\n", (long)(now - buffer.created));
1683  if(buffer.target[0]) {
1684  printf(" <active>%ld</active>\n", (long)(now - buffer.active));
1685  printf(" <target>%s</target>\n", buffer.target);
1686  }
1687  printf(" </call>\n");
1688  }
1689  printf("</mappedCalls>\n");
1690  fflush(stdout);
1691  exit(0);
1692 }
1693 
1694 static void dumpstats(const char *id)
1695 {
1696  mapped_view<stats> sta(STAT_MAP);
1697  unsigned count = sta.count();
1698  unsigned index = 0;
1699  stats buffer;
1700  time_t now;
1701 
1702  if(!count)
1703  error(405, "Server unavailable");
1704 
1705  printf(
1706  "Status: 200 OK\r\n"
1707  "Content-Type: text/xml\r\n"
1708  "\r\n");
1709 
1710  printf("<?xml version=\"1.0\"?>\n");
1711  printf("<mappedStats>\n");
1712  time(&now);
1713 
1714  while(index < count) {
1715  sta.copy(index++, buffer);
1716  if(!buffer.id[0])
1717  continue;
1718  if(id && !eq(id, buffer.id))
1719  continue;
1720  printf(" <stat id=\"%s\">\n", buffer.id);
1721  printf(" <incoming>\n");
1722  printf(" <total>%lu</total>\n", buffer.data[0].total);
1723  printf(" <period>%lu</period>\n", buffer.data[0].period);
1724  printf(" <current>%hu</current>\n", buffer.data[0].current);
1725  printf(" <peak>%hu</peak>\n", buffer.data[0].peak);
1726  printf(" </incoming>\n");
1727  printf(" <outgoing>\n");
1728  printf(" <total>%lu</total>\n", buffer.data[1].total);
1729  printf(" <period>%lu</period>\n", buffer.data[1].period);
1730  printf(" <current>%hu</current>\n", buffer.data[1].current);
1731  printf(" <peak>%hu</peak>\n", buffer.data[1].peak);
1732  printf(" </outgoing>\n");
1733  printf(" </stat>\n");
1734  }
1735  printf("</mappedStats>\n");
1736  fflush(stdout);
1737  exit(0);
1738 }
1739 
1740 static void registry(const char *id)
1741 {
1742  mapped_view<MappedRegistry> reg(REGISTRY_MAP);
1743  unsigned count = reg.count();
1744  unsigned index = 0;
1745  MappedRegistry buffer;
1746  time_t now;
1747  struct tm *dt;
1748  char buf[64];
1749  const char *type;
1750  unsigned port;
1751 
1752  if(!count)
1753  error(405, "Server unavailable");
1754 
1755  printf(
1756  "Status: 200 OK\r\n"
1757  "Content-Type: text/xml\r\n"
1758  "\r\n");
1759 
1760  printf("<?xml version=\"1.0\"?>\n");
1761  printf("<mappedRegistry>\n");
1762  time(&now);
1763  while(index < count) {
1764  reg.copy(index++, buffer);
1765  if(buffer.type == MappedRegistry::EXPIRED)
1766  continue;
1767  else if(buffer.type == MappedRegistry::TEMPORARY && !buffer.inuse)
1768  continue;
1769  time(&now);
1770  if(buffer.expires && buffer.expires < now)
1771  continue;
1772  if(id && buffer.ext && (unsigned)atoi(id) == buffer.ext)
1773  goto use;
1774  if(id && stricmp(id, buffer.userid))
1775  continue;
1776 use:
1777  printf(" <registry id=\"%s\">\n", buffer.userid);
1778  if(buffer.ext)
1779  printf(" <extension>%d</extension>\n", buffer.ext);
1780  printf(" <used>%u</used>\n", buffer.inuse);
1781  if(buffer.expires && buffer.type != MappedRegistry::TEMPORARY)
1782  printf(" <expires>%ld</expires>\n", (long)(buffer.expires - now));
1783  switch(buffer.type) {
1785  type = "reject";
1786  break;
1787  case MappedRegistry::REFER:
1788  type = "refer";
1789  break;
1791  type = "gateway";
1792  break;
1794  type = "peer";
1795  break;
1797  type = "temp";
1798  break;
1799  default:
1800  type = "user";
1801  };
1802  printf(" <type>%s</type>\n", type);
1803  printf(" <class>%s</class>\n", buffer.profile.id);
1804 
1805  dt = DateTime::local(&buffer.created);
1806  if(dt->tm_year < 1000)
1807  dt->tm_year += 1900;
1808 
1809  printf(" <created>%04d%02d%02dT%02d%02d%02d</created>\n",
1810  dt->tm_year, dt->tm_mon + 1, dt->tm_mday,
1811  dt->tm_hour, dt->tm_min, dt->tm_sec);
1812 
1813  DateTime::release(dt);
1814  Socket::query((struct sockaddr *)&buffer.contact, buf, sizeof(buf));
1815  port = Socket::port((struct sockaddr *)&buffer.contact);
1816  printf(" <address>%s</address>\n", buf);
1817  printf(" <service>%u</service>\n", port);
1818  printf(" </registry>\n");
1819  fflush(stdout);
1820  }
1821  printf("</mappedRegistry>\n");
1822  fflush(stdout);
1823  exit(0);
1824 }
1825 
1827 {
1828  if(argc > 1) {
1829  if(String::equal(argv[1], "-version") || String::equal(argv[1], "--version"))
1830  version();
1831  }
1832 
1833 #ifdef _MSWINDOWS_
1834  char buf[256];
1835  GetEnvironmentVariable("APPDATA", buf, 192);
1836  unsigned len = strlen(buf);
1837  snprintf(buf + len, sizeof(buf) - len, "\\sipwitch\\config.xml");
1838  save_file = strdup(buf);
1839  snprintf(buf + len, sizeof(buf) - len, "\\sipwitch\\config.tmp");
1840  temp_file = strdup(buf);
1841  snprintf(buf + len, sizeof(buf) - len, "\\sipwitch\\snapshot.log");
1842  snapshot_file = strdup(buf);
1843  snprintf(buf + len, sizeof(buf) - len, "\\sipwitch\\dumpfile.log");
1844  dump_file = strdup(buf);
1845  if(GetEnvironmentVariable("GATEWAY_INTERFACE", buf, sizeof(buf)) > 0)
1846  cgi_version = strdup(buf);
1847  if(GetEnvironmentVariable("REMOTE_USER", buf, sizeof(buf)) > 0)
1848  cgi_remuser = strdup(buf);
1849  if(GetEnvironmentVariable("REQUEST_METHOD", buf, sizeof(buf)) > 0)
1850  cgi_method = strdup(buf);
1851  if(GetEnvironmentVariable("QUERY_STRING", buf, sizeof(buf)) > 0)
1852  cgi_query = strdup(buf);
1853  if(GetEnvironmentVariable("CONTENT_TYPE", buf, sizeof(buf)) > 0)
1854  cgi_content = strdup(buf);
1855  if(GetEnvironmentVariable("CONTENT_LENGTH", buf, sizeof(buf)) > 0)
1856  cgi_length = atol(buf);
1857  control_file = "\\\\.\\mailslot\\sipwitch_ctrl";
1858 #else
1859  save_file = DEFAULT_VARPATH "/run/sipwitch/config.xml";
1860  temp_file = DEFAULT_VARPATH "/run/sipwitch/config.tmp";
1861  control_file = DEFAULT_VARPATH "/run/sipwitch/control";
1862  dump_file = DEFAULT_VARPATH "/run/sipwitch/dumpfile";
1863  snapshot_file = DEFAULT_VARPATH "/run/sipwitch/snapshot";
1864  cgi_version = getenv("GATEWAY_INTERFACE");
1865  cgi_remuser = getenv("REMOTE_USER");
1866  cgi_method = getenv("REQUEST_METHOD");
1867  cgi_query = getenv("QUERY_STRING");
1868  cgi_content = getenv("CONTENT_LENGTH");
1869  if(cgi_content)
1870  cgi_length = atol(cgi_content);
1871  cgi_content = getenv("CONTENT_TYPE");
1872 #endif
1873 
1874  if(!cgi_version)
1875  shell::errexit(1, "*** sipwitch.cgi must execute from http server on password protected resource\n");
1876 
1877  if(!cgi_remuser || !*cgi_remuser)
1878  error(403, "Unauthorized to access sipwitch interface");
1879 
1880  if(cgi_method && !stricmp(cgi_method, "post"))
1881  post();
1882 
1883  if(cgi_query) {
1884  if(!strnicmp(cgi_query, "state-", 6) || !strnicmp(cgi_query, "state=", 6) || !strnicmp(cgi_query, "state_", 6)) {
1885  request("state %s", cgi_query + 6);
1886  error(200, "ok");
1887  }
1888 
1889  if(!stricmp(cgi_query, "reload")) {
1890  request("reload");
1891  error(200, "ok");
1892  }
1893 
1894  if(!stricmp(cgi_query, "restart")) {
1895  request("restart");
1896  error(200, "ok");
1897  }
1898 
1899  if(!stricmp(cgi_query, "check")) {
1900  request("check");
1901  error(200, "ok");
1902  }
1903 
1904  if(!stricmp(cgi_query, "snapshot"))
1905  snapshot();
1906 
1907  if(!stricmp(cgi_query, "dump"))
1908  dump();
1909 
1910  if(!stricmp(cgi_query, "info"))
1911  info();
1912 
1913  if(!stricmp(cgi_query, "registry"))
1914  registry(NULL);
1915 
1916  if(!stricmp(cgi_query, "stats"))
1917  dumpstats(NULL);
1918 
1919  if(!stricmp(cgi_query, "sessions"))
1920  dumpcalls(NULL);
1921 
1922  if(!stricmp(cgi_query, "calls"))
1923  calls(NULL);
1924 
1925  if(!strnicmp(cgi_query, "registry=", 9))
1926  registry(cgi_query + 9);
1927 
1928  if(!strnicmp(cgi_query, "stats=", 6))
1929  dumpstats(cgi_query + 6);
1930 
1931  if(!strnicmp(cgi_query, "calls=", 6))
1932  calls(cgi_query + 6);
1933 
1934  if(!strnicmp(cgi_query, "sessions=", 9))
1935  dumpcalls(cgi_query + 9);
1936  }
1937 
1938  config();
1939  PROGRAM_EXIT(0);
1940 }
1941 
1942 
unsigned long total
Definition: stats.h:69
A stat element of call traffic.
Definition: stats.h:57
char display[64]
Definition: mapped.h:155
unsigned count
Definition: cgiserver.cpp:92
unsigned short current
Definition: stats.h:70
unsigned short param[96]
Definition: cgiserver.cpp:91
Representation of a mapped active user record.
Definition: mapped.h:95
sockaddr_internet contact
Definition: mapped.h:111
uint32_t sequence
Definition: mapped.h:156
unsigned short limit
Definition: stats.h:74
void(* exec)(void)
Definition: cgiserver.cpp:35
char id[12]
Definition: stats.h:60
volatile time_t expires
Definition: mapped.h:113
char id[48]
Definition: mapped.h:84
#define RPC_MAX_PARAMS
Definition: cgiserver.cpp:24
unsigned short peak
Definition: stats.h:70
char state[16]
Definition: mapped.h:152
char target[(48+50)]
Definition: mapped.h:154
const char * method
Definition: cgiserver.cpp:34
unsigned short pmin
Definition: stats.h:70
char authorized[48]
Definition: mapped.h:153
#define REGISTRY_MAP
Definition: mapped.h:77
const char * signature
Definition: cgiserver.cpp:37
time_t lastcall
Definition: stats.h:73
unsigned short pmax
Definition: stats.h:70
Top level include directory for GNU Telephony SIP Witch Server.
int32_t rpcint_t
Definition: cgiserver.cpp:29
rpcint_t rpcbool_t
Definition: cgiserver.cpp:31
Representation of an active call record.
Definition: mapped.h:147
unsigned long period
Definition: stats.h:69
PROGRAM_MAIN(argc, argv)
Definition: cgiserver.cpp:1826
unsigned level
Definition: mapped.h:86
char * name[96]
Definition: cgiserver.cpp:88
char * value[96]
Definition: cgiserver.cpp:90
short argc
Definition: cgiserver.cpp:93
struct sipwitch::stats::@9 data[2]
We have stats for both incoming and outgoing traffic of various kinds.
char source[(48+50)]
Definition: mapped.h:154
char * map[96]
Definition: cgiserver.cpp:89
const char * help
Definition: cgiserver.cpp:36
#define STAT_MAP
Definition: stats.h:45
unsigned long pperiod
Definition: stats.h:69
volatile unsigned inuse
Definition: mapped.h:110
#define CALL_MAP
Definition: mapped.h:76
enum sipwitch::MappedRegistry::@5 type