Asterisk - The Open Source Telephony Project  GIT-master-a24979a
res_phoneprov.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2008, Digium, Inc.
5  * Copyright (C) 2014, Fairview 5 Engineering, LLC
6  *
7  * Mark Spencer <markster@digium.com>
8  * Matthew Brooks <mbrooks@digium.com>
9  * Terry Wilson <twilson@digium.com>
10  * George Joseph <george.joseph@fairview5.com>
11  *
12  * See http://www.asterisk.org for more information about
13  * the Asterisk project. Please do not directly contact
14  * any of the maintainers of this project for assistance;
15  * the project provides a web site, mailing lists and IRC
16  * channels for your use.
17  *
18  * This program is free software, distributed under the terms of
19  * the GNU General Public License Version 2. See the LICENSE file
20  * at the top of the source tree.
21  */
22 
23 /*! \file
24  *
25  * \brief Phone provisioning application for the asterisk internal http server
26  *
27  * \author Matthew Brooks <mbrooks@digium.com>
28  * \author Terry Wilson <twilson@digium.com>
29  * \author George Joseph <george.joseph@fairview5.com>
30  */
31 
32 /*! \li \ref res_phoneprov.c uses the configuration file \ref phoneprov.conf and \ref users.conf and \ref sip.conf
33  * \addtogroup configuration_file Configuration Files
34  */
35 
36 /*!
37  * \page phoneprov.conf phoneprov.conf
38  * \verbinclude phoneprov.conf.sample
39  */
40 
41 /*** MODULEINFO
42  <support_level>extended</support_level>
43  ***/
44 
45 #define AST_API_MODULE
46 
47 #include "asterisk.h"
48 
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <net/if.h>
52 #ifdef SOLARIS
53 #include <sys/sockio.h>
54 #endif
55 
56 #include "asterisk/channel.h"
57 #include "asterisk/file.h"
58 #include "asterisk/paths.h"
59 #include "asterisk/pbx.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/module.h"
62 #include "asterisk/http.h"
63 #include "asterisk/utils.h"
64 #include "asterisk/app.h"
65 #include "asterisk/strings.h"
66 #include "asterisk/stringfields.h"
67 #include "asterisk/options.h"
68 #include "asterisk/config.h"
69 #include "asterisk/acl.h"
70 #include "asterisk/astobj2.h"
71 #include "asterisk/ast_version.h"
72 #include "asterisk/phoneprov.h"
73 
74 #ifdef LOW_MEMORY
75 #define MAX_PROVIDER_BUCKETS 1
76 #define MAX_PROFILE_BUCKETS 1
77 #define MAX_ROUTE_BUCKETS 1
78 #define MAX_USER_BUCKETS 1
79 #else
80 #define MAX_PROVIDER_BUCKETS 17
81 #define MAX_PROFILE_BUCKETS 17
82 #define MAX_ROUTE_BUCKETS 563
83 #define MAX_USER_BUCKETS 563
84 #endif /* LOW_MEMORY */
85 
86 #define VAR_BUF_SIZE 4096
87 
88 /*** DOCUMENTATION
89  <function name="PP_EACH_EXTENSION" language="en_US">
90  <synopsis>
91  Execute specified template for each extension.
92  </synopsis>
93  <syntax>
94  <parameter name="mac" required="true" />
95  <parameter name="template_file" required="true" />
96  </syntax>
97  <description>
98  <para>Output the specified template for each extension associated with the specified MAC address.</para>
99  </description>
100  </function>
101  <function name="PP_EACH_USER" language="en_US">
102  <synopsis>
103  Generate a string for each phoneprov user.
104  </synopsis>
105  <syntax>
106  <parameter name="string" required="true" />
107  <parameter name="exclude_mac" required="true" />
108  </syntax>
109  <description>
110  <para>Pass in a string, with phoneprov variables you want substituted in the format of
111  %{VARNAME}, and you will get the string rendered for each user in phoneprov
112  excluding ones with MAC address <replaceable>exclude_mac</replaceable>. Probably not
113  useful outside of res_phoneprov.</para>
114  <para>Example: ${PP_EACH_USER(&lt;item&gt;&lt;fn&gt;%{DISPLAY_NAME}&lt;/fn&gt;&lt;/item&gt;|${MAC})</para>
115  </description>
116  </function>
117  ***/
118 
119 /*!
120  * \brief Creates a hash function for a structure string field.
121  * \param fname The name to use for the function
122  * \param stype The structure type
123  * \param field The field in the structure to hash
124  *
125  * SIMPLE_HASH_FN(mystruct, myfield) will produce a function
126  * named mystruct_hash_fn which hashes mystruct->myfield.
127  */
128 #define SIMPLE_HASH_FN(fname, stype, field) \
129 static int fname(const void *obj, const int flags) \
130 { \
131  const struct stype *provider = obj; \
132  const char *key; \
133  switch (flags & OBJ_SEARCH_MASK) { \
134  case OBJ_SEARCH_KEY: \
135  key = obj; \
136  break; \
137  case OBJ_SEARCH_OBJECT: \
138  provider = obj; \
139  key = provider->field; \
140  break; \
141  default: \
142  ast_assert(0); \
143  return 0; \
144  } \
145  return ast_str_hash(key); \
146 }
147 
148 /*!
149  * \brief Creates a compare function for a structure string field.
150  * \param fname The name to use for the function
151  * \param stype The structure type
152  * \param field The field in the structure to compare
153  *
154  * SIMPLE_CMP_FN(mystruct, myfield) will produce a function
155  * named mystruct_cmp_fn which compares mystruct->myfield.
156  */
157 #define SIMPLE_CMP_FN(fname, stype, field) \
158 static int fname(void *obj, void *arg, int flags) \
159 { \
160  const struct stype *object_left = obj, *object_right = arg; \
161  const char *right_key = arg; \
162  int cmp; \
163  switch (flags & OBJ_SEARCH_MASK) { \
164  case OBJ_SEARCH_OBJECT: \
165  right_key = object_right->field; \
166  case OBJ_SEARCH_KEY: \
167  cmp = strcmp(object_left->field, right_key); \
168  break; \
169  case OBJ_SEARCH_PARTIAL_KEY: \
170  cmp = strncmp(object_left->field, right_key, strlen(right_key)); \
171  break; \
172  default: \
173  cmp = 0; \
174  break; \
175  } \
176  if (cmp) { \
177  return 0; \
178  } \
179  return CMP_MATCH; \
180 }
181 
182 static const char *variable_lookup[] = {
183  [AST_PHONEPROV_STD_MAC] = "MAC",
184  [AST_PHONEPROV_STD_PROFILE] = "PROFILE",
185  [AST_PHONEPROV_STD_USERNAME] = "USERNAME",
186  [AST_PHONEPROV_STD_DISPLAY_NAME] = "DISPLAY_NAME",
187  [AST_PHONEPROV_STD_SECRET] = "SECRET",
188  [AST_PHONEPROV_STD_LABEL] = "LABEL",
189  [AST_PHONEPROV_STD_CALLERID] = "CALLERID",
190  [AST_PHONEPROV_STD_TIMEZONE] = "TIMEZONE",
191  [AST_PHONEPROV_STD_LINENUMBER] = "LINE",
192  [AST_PHONEPROV_STD_LINEKEYS] = "LINEKEYS",
193  [AST_PHONEPROV_STD_SERVER] = "SERVER",
194  [AST_PHONEPROV_STD_SERVER_PORT] = "SERVER_PORT",
195  [AST_PHONEPROV_STD_SERVER_IFACE] = "SERVER_IFACE",
196  [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = "VOICEMAIL_EXTEN",
197  [AST_PHONEPROV_STD_EXTENSION_LENGTH] = "EXTENSION_LENGTH",
198  [AST_PHONEPROV_STD_TZOFFSET] = "TZOFFSET",
199  [AST_PHONEPROV_STD_DST_ENABLE] = "DST_ENABLE",
200  [AST_PHONEPROV_STD_DST_START_MONTH] = "DST_START_MONTH",
201  [AST_PHONEPROV_STD_DST_START_MDAY] = "DST_START_MDAY",
202  [AST_PHONEPROV_STD_DST_START_HOUR] = "DST_START_HOUR",
203  [AST_PHONEPROV_STD_DST_END_MONTH] = "DST_END_MONTH",
204  [AST_PHONEPROV_STD_DST_END_MDAY] = "DST_END_MDAY",
205  [AST_PHONEPROV_STD_DST_END_HOUR] = "DST_END_HOUR",
206 };
207 
208 /* Translate the standard variables to their users.conf equivalents. */
209 static const char *pp_user_lookup[] = {
210  [AST_PHONEPROV_STD_MAC] = "macaddress",
211  [AST_PHONEPROV_STD_PROFILE] = "profile",
212  [AST_PHONEPROV_STD_USERNAME] = "username",
213  [AST_PHONEPROV_STD_DISPLAY_NAME] = "fullname",
214  [AST_PHONEPROV_STD_SECRET] = "secret",
215  [AST_PHONEPROV_STD_LABEL] = "label",
216  [AST_PHONEPROV_STD_CALLERID] = "cid_number",
217  [AST_PHONEPROV_STD_TIMEZONE] = "timezone",
218  [AST_PHONEPROV_STD_LINENUMBER] = "linenumber",
219  [AST_PHONEPROV_STD_LINEKEYS] = "linekeys",
223  [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = "vmexten",
224  [AST_PHONEPROV_STD_EXTENSION_LENGTH] = "localextenlength",
233 };
234 
235 /* Translate the standard variables to their phoneprov.conf [general] equivalents. */
236 static const char *pp_general_lookup[] = {
238  [AST_PHONEPROV_STD_PROFILE] = "default_profile",
247  [AST_PHONEPROV_STD_SERVER] = "serveraddr",
248  [AST_PHONEPROV_STD_SERVER_PORT] = "serverport",
249  [AST_PHONEPROV_STD_SERVER_IFACE] = "serveriface",
260 };
261 
262 /*! \brief for use in lookup_iface */
263 static struct in_addr __ourip = { .s_addr = 0x00000000, };
264 
265 /*! \brief structure to hold config providers */
269  );
271 };
273 SIMPLE_HASH_FN(phoneprov_provider_hash_fn, phoneprov_provider, provider_name)
274 SIMPLE_CMP_FN(phoneprov_provider_cmp_fn, phoneprov_provider, provider_name)
275 
276 /*! \brief structure to hold file data */
279  AST_STRING_FIELD(format); /*!< After variable substitution, becomes route->uri */
280  AST_STRING_FIELD(template); /*!< Template/physical file location */
281  AST_STRING_FIELD(mime_type);/*!< Mime-type of the file */
282  );
284 };
285 
286 /*! \brief structure to hold extensions */
287 struct extension {
290  );
291  int index;
292  struct varshead *headp; /*!< List of variables to substitute into templates */
294 };
295 
296 /*! \brief structure to hold phone profiles read from phoneprov.conf */
299  AST_STRING_FIELD(name); /*!< Name of phone profile */
300  AST_STRING_FIELD(default_mime_type); /*!< Default mime type if it isn't provided */
301  AST_STRING_FIELD(staticdir); /*!< Subdirectory that static files are stored in */
302  );
303  struct varshead *headp; /*!< List of variables set with 'setvar' in phoneprov.conf */
304  AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files; /*!< List of static files */
305  AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files; /*!< List of dynamic files */
306 };
308 SIMPLE_HASH_FN(phone_profile_hash_fn, phone_profile, name)
309 SIMPLE_CMP_FN(phone_profile_cmp_fn, phone_profile, name)
310 
311 /*! \brief structure to hold users read from users.conf */
312 struct user {
314  AST_STRING_FIELD(macaddress); /*!< Mac address of user's phone */
315  AST_STRING_FIELD(provider_name); /*!< Name of the provider who registered this mac */
316  );
317  struct phone_profile *profile; /*!< Profile the phone belongs to */
319 };
321 SIMPLE_HASH_FN(user_hash_fn, user, macaddress)
322 SIMPLE_CMP_FN(user_cmp_fn, user, macaddress)
323 
324 /*! \brief structure to hold http routes (valid URIs, and the files they link to) */
325 struct http_route {
327  AST_STRING_FIELD(uri); /*!< The URI requested */
328  );
329  struct phoneprov_file *file; /*!< The file that links to the URI */
330  struct user *user; /*!< The user that has variables to substitute into the file
331  * NULL in the case of a static route */
333 };
335 SIMPLE_HASH_FN(http_route_hash_fn, http_route, uri)
336 SIMPLE_CMP_FN(http_route_cmp_fn, http_route, uri)
337 
338 #define SIPUSERS_PROVIDER_NAME "sipusers"
339 
340 /* iface is the interface (e.g. eth0); address is the return value */
341 static int lookup_iface(const char *iface, struct in_addr *address)
342 {
343  int mysock, res = 0;
344  struct ifreq ifr;
345  struct sockaddr_in *sin;
346 
347  memset(&ifr, 0, sizeof(ifr));
348  ast_copy_string(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
349 
350  mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
351  if (mysock < 0) {
352  ast_log(LOG_ERROR, "Failed to create socket: %s\n", strerror(errno));
353  return -1;
354  }
355 
356  res = ioctl(mysock, SIOCGIFADDR, &ifr);
357 
358  close(mysock);
359 
360  if (res < 0) {
361  ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
362  memcpy(address, &__ourip, sizeof(__ourip));
363  return -1;
364  } else {
365  sin = (struct sockaddr_in *)&ifr.ifr_addr;
366  memcpy(address, &sin->sin_addr, sizeof(*address));
367  return 0;
368  }
369 }
370 
371 static struct phoneprov_provider *find_provider(char *name)
372 {
374 }
375 
376 /*! \brief Delete all providers */
377 static void delete_providers(void)
378 {
379  if (!providers) {
380  return;
381  }
382 
384 }
385 
386 static void provider_destructor(void *obj)
387 {
388  struct phoneprov_provider *provider = obj;
390 }
391 
392 static void delete_file(struct phoneprov_file *file)
393 {
395  ast_free(file);
396 }
397 
398 /*! \brief Read a TEXT file into a string and return the length */
399 static int load_file(const char *filename, char **ret)
400 {
401  int len = 0;
402  FILE *f;
403 
404  if (!(f = fopen(filename, "r"))) {
405  *ret = NULL;
406  return -1;
407  }
408 
409  fseek(f, 0, SEEK_END);
410  len = ftell(f);
411  fseek(f, 0, SEEK_SET);
412  if (!(*ret = ast_malloc(len + 1))) {
413  fclose(f);
414  return -2;
415  }
416 
417  if (len != fread(*ret, sizeof(char), len, f)) {
418  fclose(f);
419  ast_free(*ret);
420  *ret = NULL;
421  return -3;
422  }
423 
424  fclose(f);
425 
426  (*ret)[len] = '\0';
427 
428  return len;
429 }
430 
431 /*! \brief Set all timezone-related variables based on a zone (i.e. America/New_York)
432  \param headp pointer to list of user variables
433  \param zone A time zone. NULL sets variables based on timezone of the machine
434 */
435 static void set_timezone_variables(struct varshead *headp, const char *zone)
436 {
437  time_t utc_time;
438  int dstenable;
439  time_t dststart;
440  time_t dstend;
441  struct ast_tm tm_info;
442  int tzoffset;
443  char buffer[21];
444  struct timeval when;
445 
446  time(&utc_time);
447  ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone);
448  snprintf(buffer, sizeof(buffer), "%d", tzoffset);
449  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("TZOFFSET", buffer));
450 
451  if (!dstenable) {
452  return;
453  }
454 
455  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_ENABLE", "1"));
456 
457  when.tv_sec = dststart;
458  ast_localtime(&when, &tm_info, zone);
459 
460  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1);
461  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_START_MONTH", buffer));
462 
463  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
464  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_START_MDAY", buffer));
465 
466  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
467  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_START_HOUR", buffer));
468 
469  when.tv_sec = dstend;
470  ast_localtime(&when, &tm_info, zone);
471 
472  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1);
473  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_END_MONTH", buffer));
474 
475  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
476  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_END_MDAY", buffer));
477 
478  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
479  AST_VAR_LIST_INSERT_TAIL(headp, ast_var_assign("DST_END_HOUR", buffer));
480 }
481 
482 static struct http_route *unref_route(struct http_route *route)
483 {
484  ao2_cleanup(route);
485 
486  return NULL;
487 }
488 
489 static void route_destructor(void *obj)
490 {
491  struct http_route *route = obj;
492 
494 }
495 
496 /*! \brief Delete all http routes, freeing their memory */
497 static void delete_routes(void)
498 {
499  if (!http_routes) {
500  return;
501  }
502 
504 }
505 
506 /*! \brief Build a route structure and add it to the list of available http routes
507  \param pp_file File to link to the route
508  \param profile
509  \param user User to link to the route (NULL means static route)
510  \param uri URI of the route
511 */
512 static void build_route(struct phoneprov_file *pp_file, struct phone_profile *profile, struct user *user, char *uri)
513 {
514  struct http_route *route;
515 
516  if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
517  return;
518  }
519 
520  if (ast_string_field_init(route, 32)) {
521  ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
522  route = unref_route(route);
523  return;
524  }
525 
526  ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
527  route->user = user;
528  route->file = pp_file;
529  route->profile = profile;
530 
531  ao2_link(http_routes, route);
532 
533  route = unref_route(route);
534 }
535 
536 static struct phone_profile *unref_profile(struct phone_profile *prof)
537 {
538  ao2_cleanup(prof);
539 
540  return NULL;
541 }
542 
543 /*! \brief Return a phone profile looked up by name */
544 static struct phone_profile *find_profile(const char *name)
545 {
547 }
548 
549 static void profile_destructor(void *obj)
550 {
551  struct phone_profile *profile = obj;
552  struct phoneprov_file *file;
553  struct ast_var_t *var;
554 
555  while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry))) {
556  delete_file(file);
557  }
558 
559  while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry))) {
560  delete_file(file);
561  }
562 
563  while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries))) {
565  }
566 
567  ast_free(profile->headp);
569 }
570 
571 /*! \brief Delete all phone profiles, freeing their memory */
572 static void delete_profiles(void)
573 {
574  if (!profiles) {
575  return;
576  }
577 
579 }
580 
581 /*! \brief Build a phone profile and add it to the list of phone profiles
582  \param name the name of the profile
583  \param v ast_variable from parsing phoneprov.conf
584 */
585 static void build_profile(const char *name, struct ast_variable *v)
586 {
587  struct phone_profile *profile;
588 
589  if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
590  return;
591  }
592 
593  if (ast_string_field_init(profile, 32)) {
594  profile = unref_profile(profile);
595  return;
596  }
597 
598  if (!(profile->headp = ast_var_list_create())) {
599  profile = unref_profile(profile);
600  return;
601  }
602 
605 
606  ast_string_field_set(profile, name, name);
607  for (; v; v = v->next) {
608  if (!strcasecmp(v->name, "mime_type")) {
610  } else if (!strcasecmp(v->name, "setvar")) {
611  char value_copy[strlen(v->value) + 1];
612 
614  AST_APP_ARG(varname);
615  AST_APP_ARG(varval);
616  );
617 
618  strcpy(value_copy, v->value); /* safe */
619  AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
620  do {
621  if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
622  break;
623  args.varname = ast_strip(args.varname);
624  args.varval = ast_strip(args.varval);
625  if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
626  break;
627  AST_VAR_LIST_INSERT_TAIL(profile->headp, ast_var_assign(args.varname, args.varval));
628  } while (0);
629  } else if (!strcasecmp(v->name, "staticdir")) {
630  ast_string_field_set(profile, staticdir, v->value);
631  } else {
632  struct phoneprov_file *pp_file;
633  char *file_extension;
634  char value_copy[strlen(v->value) + 1];
635 
637  AST_APP_ARG(filename);
638  AST_APP_ARG(mimetype);
639  );
640 
641  if (!(pp_file = ast_calloc_with_stringfields(1, struct phoneprov_file, 32))) {
642  profile = unref_profile(profile);
643  return;
644  }
645 
646  if ((file_extension = strrchr(pp_file->format, '.')))
647  file_extension++;
648 
649  strcpy(value_copy, v->value); /* safe */
650  AST_STANDARD_APP_ARGS(args, value_copy);
651 
652  /* Mime type order of preference
653  * 1) Specific mime-type defined for file in profile
654  * 2) Mime determined by extension
655  * 3) Default mime type specified in profile
656  * 4) text/plain
657  */
658  ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype,
659  (S_OR(S_OR(ast_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
660 
661  if (!strcasecmp(v->name, "static_file")) {
662  ast_string_field_set(pp_file, format, args.filename);
663  ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
664  AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
665  /* Add a route for the static files, as their filenames won't change per-user */
666  build_route(pp_file, profile, NULL, NULL);
667  } else {
668  ast_string_field_set(pp_file, format, v->name);
669  ast_string_field_set(pp_file, template, args.filename);
670  AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
671  }
672  }
673  }
674 
675  ao2_link(profiles, profile);
676 
677  profile = unref_profile(profile);
678 }
679 
680 static struct extension *delete_extension(struct extension *exten)
681 {
682  ast_var_list_destroy(exten->headp);
684  ast_free(exten);
685 
686  return NULL;
687 }
688 
689 static struct extension *build_extension(const char *name, struct varshead *vars)
690 {
691  struct extension *exten;
692  const char *tmp;
693 
694  if (!(exten = ast_calloc_with_stringfields(1, struct extension, 32))) {
695  return NULL;
696  }
697 
699 
700  exten->headp = ast_var_list_clone(vars);
701  if (!exten->headp) {
702  ast_log(LOG_ERROR, "Unable to clone variables for extension '%s'\n", name);
704  return NULL;
705  }
706 
708  if (!tmp) {
711  exten->index = 1;
712  } else {
713  sscanf(tmp, "%d", &exten->index);
714  }
715 
719  }
720 
723 
724  return exten;
725 }
726 
727 static struct user *unref_user(struct user *user)
728 {
729  ao2_cleanup(user);
730 
731  return NULL;
732 }
733 
734 /*! \brief Return a user looked up by name */
735 static struct user *find_user(const char *macaddress)
736 {
738 }
739 
740 static int routes_delete_cb(void *obj, void *arg, int flags)
741 {
742  struct http_route *route = obj;
743  struct user *user = route->user;
744  char *macaddress = arg;
745 
746  if (user && !strcmp(user->macaddress, macaddress)) {
747  return CMP_MATCH;
748  }
749  return 0;
750 }
751 
752 /*! \brief Free all memory associated with a user */
753 static void user_destructor(void *obj)
754 {
755  struct user *user = obj;
756  struct extension *exten;
757 
760  }
761 
762  if (user->profile) {
764  }
765 
766  if (http_routes) {
768  }
769 
771 }
772 
773 /*! \brief Delete all users */
774 static void delete_users(void)
775 {
776  if (!users) {
777  return;
778  }
779 
781 }
782 
783 /*! \brief Build and return a user structure based on gathered config data */
784 static struct user *build_user(const char *mac, struct phone_profile *profile, char *provider_name)
785 {
786  struct user *user;
787 
788  if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
789  return NULL;
790  }
791 
792  if (ast_string_field_init(user, 64)) {
793  user = unref_user(user);
794  return NULL;
795  }
796 
799  user->profile = profile;
800  ao2_ref(profile, 1);
801 
802  return user;
803 }
804 
805 /*! \brief Add an extension to a user ordered by index/linenumber */
806 static int add_user_extension(struct user *user, struct extension *exten)
807 {
808  struct ast_var_t *pvar, *var2;
809  struct ast_str *str = ast_str_create(16);
810 
811  if (!str) {
812  return -1;
813  }
814 
815  /* Append profile variables here, and substitute variables on profile
816  * setvars, so that we can use user specific variables in them */
818  if (ast_var_find(exten->headp, pvar->name)) {
819  continue;
820  }
821 
823  if ((var2 = ast_var_assign(pvar->name, ast_str_buffer(str)))) {
824  AST_VAR_LIST_INSERT_TAIL(exten->headp, var2);
825  }
826  }
827  ast_free(str);
828 
829  if (AST_LIST_EMPTY(&user->extensions)) {
831  } else {
832  struct extension *exten_iter;
833 
835  if (exten->index < exten_iter->index) {
837  } else if (exten->index == exten_iter->index) {
838  ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
839  return -1;
840  } else if (!AST_LIST_NEXT(exten_iter, entry)) {
842  }
843  }
845  }
846 
847  return 0;
848 }
849 
850 /*! \brief Add an http route for dynamic files attached to the profile of the user */
851 static int build_user_routes(struct user *user)
852 {
853  struct phoneprov_file *pp_file;
854  struct ast_str *str;
855 
856  if (!(str = ast_str_create(16))) {
857  return -1;
858  }
859 
863  }
864 
865  ast_free(str);
866  return 0;
867 }
868 
869 /*! \brief Callback that is executed everytime an http request is received by this module */
870 static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
871 {
872  struct http_route *route;
873  struct ast_str *result;
874  char path[PATH_MAX];
875  char *file = NULL;
876  char *server;
877  int len;
878  int fd;
879  struct ast_str *http_header;
880 
881  if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
882  ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
883  return 0;
884  }
885 
886  if (!(route = ao2_find(http_routes, uri, OBJ_SEARCH_KEY))) {
887  goto out404;
888  }
889 
890  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
891 
892  if (!route->user) { /* Static file */
893 
894  fd = open(path, O_RDONLY);
895  if (fd < 0) {
896  goto out500;
897  }
898 
899  len = lseek(fd, 0, SEEK_END);
900  lseek(fd, 0, SEEK_SET);
901  if (len < 0) {
902  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
903  close(fd);
904  goto out500;
905  }
906 
907  http_header = ast_str_create(80);
908  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
909  route->file->mime_type);
910 
911  ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
912 
913  close(fd);
914  route = unref_route(route);
915  return 0;
916  } else { /* Dynamic file */
917  struct ast_str *tmp;
918 
919  len = load_file(path, &file);
920  if (len < 0) {
921  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
922  if (file) {
923  ast_free(file);
924  }
925 
926  goto out500;
927  }
928 
929  if (!file) {
930  goto out500;
931  }
932 
933  if (!(tmp = ast_str_create(len))) {
934  if (file) {
935  ast_free(file);
936  }
937 
938  goto out500;
939  }
940 
941  /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
942  * the IP address we are listening on that the phone contacted for this config file */
943 
944  server = ast_var_find(AST_LIST_FIRST(&route->user->extensions)->headp,
946 
947  if (!server) {
948  union {
949  struct sockaddr sa;
950  struct sockaddr_in sa_in;
951  } name;
952  socklen_t namelen = sizeof(name.sa);
953  int res;
954 
955  if ((res = getsockname(ast_iostream_get_fd(ser->stream), &name.sa, &namelen))) {
956  ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
957  } else {
958  struct extension *exten_iter;
959  const char *newserver = ast_inet_ntoa(name.sa_in.sin_addr);
960 
961  AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
962  AST_VAR_LIST_INSERT_TAIL(exten_iter->headp,
964  }
965  }
966  }
967 
969 
970  ast_free(file);
971 
972  http_header = ast_str_create(80);
973  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
974  route->file->mime_type);
975 
976  if (!(result = ast_str_create(512))) {
977  ast_log(LOG_ERROR, "Could not create result string!\n");
978  if (tmp) {
979  ast_free(tmp);
980  }
981  ast_free(http_header);
982  goto out500;
983  }
985 
986  ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
987  ast_free(tmp);
988 
989  route = unref_route(route);
990 
991  return 0;
992  }
993 
994 out404:
995  ast_http_error(ser, 404, "Not Found", uri);
996  return 0;
997 
998 out500:
999  route = unref_route(route);
1000  ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
1001  return 0;
1002 }
1003 
1004 /*! \brief A dialplan function that can be used to print a string for each phoneprov user */
1005 static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
1006 {
1007  char *tmp;
1008  struct ao2_iterator i;
1009  struct user *user;
1010  struct ast_str *str;
1012  AST_APP_ARG(string);
1013  AST_APP_ARG(exclude_mac);
1014  );
1015  AST_STANDARD_APP_ARGS(args, data);
1016 
1017  if (!(str = ast_str_create(16))) {
1018  return -1;
1019  }
1020 
1021  /* Fix data by turning %{ into ${ */
1022  while ((tmp = strstr(args.string, "%{")))
1023  *tmp = '$';
1024 
1025  i = ao2_iterator_init(users, 0);
1026  while ((user = ao2_iterator_next(&i))) {
1027  if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
1028  continue;
1029  }
1031  if (buf) {
1032  size_t slen = len;
1033  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1034  } else {
1035  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1036  }
1037  user = unref_user(user);
1038  }
1040 
1041  ast_free(str);
1042  return 0;
1043 }
1044 
1045 static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1046 {
1047  return pp_each_user_helper(chan, data, buf, NULL, len);
1048 }
1049 
1050 static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1051 {
1052  return pp_each_user_helper(chan, data, NULL, buf, len);
1053 }
1054 
1056  .name = "PP_EACH_USER",
1057  .read = pp_each_user_read,
1058  .read2 = pp_each_user_read2,
1059 };
1060 
1061 /*! \brief A dialplan function that can be used to output a template for each extension attached to a user */
1062 static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
1063 {
1064  struct user *user;
1065  struct extension *exten;
1066  char path[PATH_MAX];
1067  char *file;
1068  int filelen;
1069  struct ast_str *str;
1071  AST_APP_ARG(mac);
1072  AST_APP_ARG(template);
1073  );
1074 
1075  AST_STANDARD_APP_ARGS(args, data);
1076 
1077  if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
1078  ast_log(LOG_WARNING, "PP_EACH_EXTENSION requires both a macaddress and template filename.\n");
1079  return 0;
1080  }
1081 
1082  if (!(user = find_user(args.mac))) {
1083  ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
1084  return 0;
1085  }
1086 
1087  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
1088  filelen = load_file(path, &file);
1089  if (filelen < 0) {
1090  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
1091  if (file) {
1092  ast_free(file);
1093  }
1094  return 0;
1095  }
1096 
1097  if (!file) {
1098  return 0;
1099  }
1100 
1101  if (!(str = ast_str_create(filelen))) {
1102  return 0;
1103  }
1104 
1107  if (buf) {
1108  size_t slen = len;
1109  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1110  } else {
1111  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1112  }
1113  }
1114 
1115  ast_free(file);
1116  ast_free(str);
1117 
1118  user = unref_user(user);
1119 
1120  return 0;
1121 }
1122 
1123 static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1124 {
1125  return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
1126 }
1127 
1128 static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1129 {
1130  return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
1131 }
1132 
1134  .name = "PP_EACH_EXTENSION",
1135  .read = pp_each_extension_read,
1136  .read2 = pp_each_extension_read2,
1137 };
1138 
1139 #define FORMATS "%-20.20s %-40.40s %-30.30s\n"
1140 #define FORMATD "%-20.20s %-20.20s %-40.40s %-30.30s\n"
1141 static int route_list_cb(void *obj, void *arg, void *data, int flags)
1142 {
1143  int fd = *(int *)arg;
1144  struct http_route *route = obj;
1145 
1146  if (data && route->user) {
1147  ast_cli(fd, FORMATD, route->user->provider_name, route->profile->name, route->uri, route->file->template);
1148  }
1149  if (!data && !route->user) {
1150  ast_cli(fd, FORMATS, route->profile->name, route->uri, route->file->template);
1151  }
1152 
1153  return CMP_MATCH;
1154 }
1155 
1156 /*! \brief CLI command to list static and dynamic routes */
1157 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1158 {
1159  int fd = a->fd;
1160  switch(cmd) {
1161  case CLI_INIT:
1162  e->command = "phoneprov show routes";
1163  e->usage =
1164  "Usage: phoneprov show routes\n"
1165  " Lists all registered phoneprov http routes.\n";
1166  return NULL;
1167  case CLI_GENERATE:
1168  return NULL;
1169  }
1170 
1171  /* This currently iterates over routes twice, but it is the only place I've needed
1172  * to really separate static an dynamic routes, so I've just left it this way. */
1173  ast_cli(a->fd, "Static routes\n\n");
1174  ast_cli(a->fd, FORMATS, "Profile", "Relative URI", "Physical location");
1175 
1177 
1178  ast_cli(a->fd, "\nDynamic routes\n\n");
1179  ast_cli(a->fd, FORMATD, "Provider", "Profile", "Relative URI", "Template");
1180 
1182 
1183  return CLI_SUCCESS;
1184 }
1185 
1186 static struct ast_cli_entry pp_cli[] = {
1187  AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
1188 };
1189 
1190 static struct ast_http_uri phoneprovuri = {
1192  .description = "Asterisk HTTP Phone Provisioning Tool",
1193  .uri = "phoneprov",
1194  .has_subtree = 1,
1195  .data = NULL,
1196  .key = __FILE__,
1197 };
1198 
1199 static struct varshead *get_defaults(void)
1200 {
1201  struct ast_config *phoneprov_cfg, *cfg = CONFIG_STATUS_FILEINVALID;
1202  const char *value;
1203  struct ast_variable *v;
1204  struct ast_var_t *var;
1205  struct ast_flags config_flags = { 0 };
1206  struct varshead *defaults = ast_var_list_create();
1207 
1208  if (!defaults) {
1209  ast_log(LOG_ERROR, "Unable to create default var list.\n");
1210  return NULL;
1211  }
1212 
1213  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))
1214  || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
1215  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
1216  ast_var_list_destroy(defaults);
1217  return NULL;
1218  }
1219 
1221  if (!value) {
1222  struct in_addr addr;
1224  if (value) {
1225  lookup_iface(value, &addr);
1226  value = ast_inet_ntoa(addr);
1227  }
1228  }
1229  if (value) {
1231  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1232  } else {
1233  ast_log(LOG_WARNING, "Unable to find a valid server address or name.\n");
1234  }
1235 
1237  if (!value) {
1238  if ((cfg = ast_config_load("sip.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1239  value = ast_variable_retrieve(cfg, "general", "bindport");
1240  }
1241  }
1243  if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
1244  ast_config_destroy(cfg);
1245  }
1246  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1247 
1249  if (!value) {
1250  ast_log(LOG_ERROR, "Unable to load default profile.\n");
1251  ast_config_destroy(phoneprov_cfg);
1252  ast_var_list_destroy(defaults);
1253  return NULL;
1254  }
1256  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1257  ast_config_destroy(phoneprov_cfg);
1258 
1259  if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
1260  ast_log(LOG_ERROR, "Unable to load users.conf\n");
1261  ast_var_list_destroy(defaults);
1262  return NULL;
1263  }
1264 
1265  /* Go ahead and load global variables from users.conf so we can append to profiles */
1266  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
1267  if (!strcasecmp(v->name, pp_user_lookup[AST_PHONEPROV_STD_VOICEMAIL_EXTEN])) {
1269  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1270  }
1271  if (!strcasecmp(v->name, pp_user_lookup[AST_PHONEPROV_STD_EXTENSION_LENGTH])) {
1273  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1274  }
1275  }
1276  ast_config_destroy(cfg);
1277 
1278  return defaults;
1279 }
1280 
1281 static int load_users(void)
1282 {
1283  struct ast_config *cfg;
1284  char *cat;
1285  const char *value;
1286  struct ast_flags config_flags = { 0 };
1287  struct varshead *defaults = get_defaults();
1288 
1289  if (!defaults) {
1290  ast_log(LOG_WARNING, "Unable to load default variables.\n");
1291  return -1;
1292  }
1293 
1294  if (!(cfg = ast_config_load("users.conf", config_flags))
1295  || cfg == CONFIG_STATUS_FILEINVALID) {
1296  ast_log(LOG_WARNING, "Unable to load users.conf\n");
1297  ast_var_list_destroy(defaults);
1298  return -1;
1299  }
1300 
1301  cat = NULL;
1302  while ((cat = ast_category_browse(cfg, cat))) {
1303  const char *tmp;
1304  int i;
1305  struct ast_var_t *varx;
1306  struct ast_var_t *vard;
1307 
1308  if (strcasecmp(cat, "general") && strcasecmp(cat, "authentication")) {
1309  struct varshead *variables = ast_var_list_create();
1310 
1311  if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp))) {
1312  ast_var_list_destroy(variables);
1313  continue;
1314  }
1315 
1316  /* Transfer the standard variables */
1317  for (i = 0; i < AST_PHONEPROV_STD_VAR_LIST_LENGTH; i++) {
1318  if (pp_user_lookup[i]) {
1319  value = ast_variable_retrieve(cfg, cat, pp_user_lookup[i]);
1320  if (value) {
1321  varx = ast_var_assign(variable_lookup[i],
1322  value);
1323  AST_VAR_LIST_INSERT_TAIL(variables, varx);
1324  }
1325  }
1326  }
1327 
1329  ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
1330  ast_var_list_destroy(variables);
1331  continue;
1332  }
1333 
1334  /* Apply defaults */
1335  AST_VAR_LIST_TRAVERSE(defaults, vard) {
1336  if (ast_var_find(variables, vard->name)) {
1337  continue;
1338  }
1339  varx = ast_var_assign(vard->name, vard->value);
1340  AST_VAR_LIST_INSERT_TAIL(variables, varx);
1341  }
1342 
1344  }
1345  }
1346  ast_config_destroy(cfg);
1347  ast_var_list_destroy(defaults);
1348  return 0;
1349 }
1350 
1351 static int load_common(void)
1352 {
1353  struct ast_config *phoneprov_cfg;
1354  struct ast_flags config_flags = { 0 };
1355  char *cat;
1356 
1357  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))
1358  || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
1359  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
1360  return -1;
1361  }
1362 
1363  cat = NULL;
1364  while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
1365  if (!strcasecmp(cat, "general")) {
1366  continue;
1367  }
1368  build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
1369  }
1370  ast_config_destroy(phoneprov_cfg);
1371 
1372  if (!ao2_container_count(profiles)) {
1373  ast_log(LOG_ERROR, "There are no provisioning profiles in phoneprov.conf.\n");
1374  return -1;
1375  }
1376 
1377  return 0;
1378 }
1379 
1380 static int unload_module(void)
1381 {
1386 
1387  /* This cleans up the sip.conf/users.conf provider (called specifically for clarity) */
1389 
1390  /* This cleans up the framework which also cleans up the providers. */
1391  delete_profiles();
1393  profiles = NULL;
1394  delete_routes();
1395  delete_users();
1397  http_routes = NULL;
1398  ao2_cleanup(users);
1399  users = NULL;
1400  delete_providers();
1402  providers = NULL;
1403 
1404  return 0;
1405 }
1406 
1407 /*!
1408  * \brief Load the module
1409  *
1410  * Module loading including tests for configuration or dependencies.
1411  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
1412  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
1413  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
1414  * configuration file or other non-critical problem return
1415  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
1416  */
1417 static int load_module(void)
1418 {
1420  phone_profile_hash_fn, NULL, phone_profile_cmp_fn);
1421  if (!profiles) {
1422  ast_log(LOG_ERROR, "Unable to allocate profiles container.\n");
1423  return AST_MODULE_LOAD_DECLINE;
1424  }
1425 
1427  http_route_hash_fn, NULL, http_route_cmp_fn);
1428  if (!http_routes) {
1429  ast_log(LOG_ERROR, "Unable to allocate routes container.\n");
1430  goto error;
1431  }
1432 
1433  if (load_common()) {
1434  ast_log(LOG_ERROR, "Unable to load provisioning profiles.\n");
1435  goto error;
1436  }
1437 
1439  user_hash_fn, NULL, user_cmp_fn);
1440  if (!users) {
1441  ast_log(LOG_ERROR, "Unable to allocate users container.\n");
1442  goto error;
1443  }
1444 
1446  MAX_PROVIDER_BUCKETS, phoneprov_provider_hash_fn, NULL, phoneprov_provider_cmp_fn);
1447  if (!providers) {
1448  ast_log(LOG_ERROR, "Unable to allocate providers container.\n");
1449  goto error;
1450  }
1451 
1452  /* Register ourselves as the provider for sip.conf/users.conf */
1454  ast_log(LOG_WARNING, "Unable register sip/users config provider. Others may succeed.\n");
1455  }
1456 
1458 
1462 
1463  return AST_MODULE_LOAD_SUCCESS;
1464 
1465 error:
1466  unload_module();
1467  return AST_MODULE_LOAD_DECLINE;
1468 }
1469 
1470 static int reload(void)
1471 {
1472  struct ao2_iterator i;
1473  struct phoneprov_provider *provider;
1474 
1475  /* Clean everything except the providers */
1476  delete_routes();
1477  delete_users();
1478  delete_profiles();
1479 
1480  /* Reload the profiles */
1481  if (load_common()) {
1482  ast_log(LOG_ERROR, "Unable to reload provisioning profiles.\n");
1483  unload_module();
1484  return AST_MODULE_LOAD_DECLINE;
1485  }
1486 
1487  /* For each provider, reload the users */
1489  i = ao2_iterator_init(providers, 0);
1490  for(; (provider = ao2_iterator_next(&i)); ao2_ref(provider, -1)) {
1491  if (provider->load_users()) {
1492  ast_log(LOG_ERROR, "Unable to load provider '%s' users. Reload aborted.\n", provider->provider_name);
1493  continue;
1494  }
1495  }
1498 
1499  return AST_MODULE_LOAD_SUCCESS;
1500 }
1501 
1503  .support_level = AST_MODULE_SUPPORT_EXTENDED,
1504  .load = load_module,
1505  .unload = unload_module,
1506  .reload = reload,
1507  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1508  .requires = "http",
1509 );
1510 
1511 /**** Public API for register/unregister, set defaults, and add extension. ****/
1512 
1514 {
1516  return NULL;
1517  }
1518 
1519  return variable_lookup[var];
1520 }
1521 
1524 {
1525  struct phoneprov_provider *provider;
1526 
1528  ast_log(LOG_ERROR, "Provider name can't be empty.\n");
1529  return -1;
1530  }
1531 
1532  if (!providers) {
1533  ast_log(LOG_WARNING, "Provider '%s' cannot be registered: res_phoneprov not loaded.\n", provider_name);
1534  return -1;
1535  }
1536 
1538  if (provider) {
1539  ast_log(LOG_ERROR, "There is already a provider registered named '%s'.\n", provider_name);
1540  ao2_ref(provider, -1);
1541  return -1;
1542  }
1543 
1545  if (!provider) {
1546  ast_log(LOG_ERROR, "Unable to allocate sufficient memory for provider '%s'.\n", provider_name);
1547  return -1;
1548  }
1549 
1550  if (ast_string_field_init(provider, 32)) {
1551  ao2_ref(provider, -1);
1552  ast_log(LOG_ERROR, "Unable to allocate sufficient memory for provider '%s' stringfields.\n", provider_name);
1553  return -1;
1554  }
1555 
1557  provider->load_users = load_users;
1558 
1560  ao2_ref(provider, -1);
1561 
1562  if (provider->load_users()) {
1563  ast_log(LOG_ERROR, "Unable to load provider '%s' users. Register aborted.\n", provider_name);
1565  return -1;
1566  }
1567 
1568  return 0;
1569 }
1570 
1571 static int extensions_delete_cb(void *obj, void *arg, int flags)
1572 {
1573  char *provider_name = arg;
1574  struct user *user = obj;
1575  if (strcmp(user->provider_name, provider_name)) {
1576  return 0;
1577  }
1578  return CMP_MATCH;
1579 }
1580 
1581 static int extension_delete_cb(void *obj, void *arg, void *data, int flags)
1582 {
1583  struct user *user = obj;
1584  char *provider_name = data;
1585  char *macaddress = arg;
1586 
1587  if (!strcmp(user->provider_name, provider_name) && !strcasecmp(user->macaddress, macaddress)) {
1588  return CMP_MATCH;
1589  }
1590  return 0;
1591 }
1592 
1594 {
1595  if (!users) {
1596  return;
1597  }
1598 
1601 }
1602 
1604 {
1605  if (!users) {
1606  return;
1607  }
1608 
1610 }
1611 
1613 {
1614  if (!providers) {
1615  return;
1616  }
1617 
1620 }
1621 
1623 {
1625  RAII_VAR(struct user *, user, NULL, ao2_cleanup);
1627  struct extension *exten;
1628  char *profile_name;
1629  char *mac;
1630  char *username;
1631 
1632  if (ast_strlen_zero(provider_name)) {
1633  ast_log(LOG_ERROR, "Provider name can't be empty.\n");
1634  return -1;
1635  }
1636  if (!vars) {
1637  ast_log(LOG_ERROR, "Variable list can't be empty.\n");
1638  return -1;
1639  }
1640 
1642  if (!username) {
1643  ast_log(LOG_ERROR, "Extension name can't be empty.\n");
1644  return -1;
1645  }
1646 
1648  if (!mac) {
1649  ast_log(LOG_ERROR, "MAC Address can't be empty.\n");
1650  return -1;
1651  }
1652 
1653  provider = find_provider(provider_name);
1654  if (!provider) {
1655  ast_log(LOG_ERROR, "Provider '%s' wasn't found in the registry.\n", provider_name);
1656  return -1;
1657  }
1658 
1659  profile_name = ast_var_find(vars,
1661  if (!profile_name) {
1662  ast_log(LOG_ERROR, "No profile could be found for user '%s' - skipping.\n", username);
1663  return -1;
1664  }
1665  if (!(profile = find_profile(profile_name))) {
1666  ast_log(LOG_ERROR, "Could not look up profile '%s' - skipping.\n", profile_name);
1667  return -1;
1668  }
1669 
1670  if (!(user = find_user(mac))) {
1671 
1672  if (!(user = build_user(mac, profile, provider_name))) {
1673  ast_log(LOG_ERROR, "Could not create user for '%s' - skipping\n", mac);
1674  return -1;
1675  }
1676 
1677  if (!(exten = build_extension(username, vars))) {
1678  ast_log(LOG_ERROR, "Could not create extension for '%s' - skipping\n", user->macaddress);
1679  return -1;
1680  }
1681 
1682  if (add_user_extension(user, exten)) {
1683  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1685  return -1;
1686  }
1687 
1688  if (build_user_routes(user)) {
1689  ast_log(LOG_WARNING, "Could not create http routes for '%s' - skipping\n", user->macaddress);
1690  return -1;
1691  }
1692  ao2_link(users, user);
1693 
1694  } else {
1695  if (strcmp(provider_name, user->provider_name)) {
1696  ast_log(LOG_ERROR, "MAC address '%s' was already added by provider '%s' - skipping\n", user->macaddress, user->provider_name);
1697  return -1;
1698  }
1699 
1700  if (!(exten = build_extension(username, vars))) {
1701  ast_log(LOG_ERROR, "Could not create extension for '%s' - skipping\n", user->macaddress);
1702  return -1;
1703  }
1704 
1705  if (add_user_extension(user, exten)) {
1706  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1708  return -1;
1709  }
1710  }
1711 
1712  return 0;
1713 }
Access Control of various sorts.
const char * str
Definition: app_jack.c:147
#define var
Definition: ast_expr2f.c:614
Asterisk version information.
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition: asterisk.h:40
#define ast_free(a)
Definition: astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1723
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
static struct prometheus_metrics_provider provider
Definition: bridges.c:178
static int tmp()
Definition: bt_open.c:389
static PGresult * result
Definition: cel_pgsql.c:84
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:122
static snd_pcm_format_t format
Definition: chan_alsa.c:106
General Asterisk PBX channel definitions.
struct varshead * ast_var_list_clone(struct varshead *head)
Definition: chanvars.c:124
void ast_var_list_destroy(struct varshead *head)
Definition: chanvars.c:109
char * ast_var_find(const struct varshead *head, const char *name)
Definition: chanvars.c:85
struct varshead * ast_var_list_create(void)
Definition: chanvars.c:97
static void AST_VAR_LIST_INSERT_TAIL(struct varshead *head, struct ast_var_t *var)
Definition: chanvars.h:51
#define AST_VAR_LIST_TRAVERSE(head, var)
Definition: chanvars.h:49
#define ast_var_assign(name, value)
Definition: chanvars.h:40
void ast_var_delete(struct ast_var_t *var)
Definition: extconf.c:2472
Standard Command Line Interface.
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
char * address
Definition: f2c.h:59
Generic File Format Support. Should be included by clients of the file handling routines....
static const char name[]
Definition: format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Support for Private Asterisk HTTP Servers.
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition: http.c:459
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:58
@ AST_HTTP_GET
Definition: http.h:60
@ AST_HTTP_HEAD
Definition: http.h:62
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:708
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:651
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:676
const char * ast_http_ftype2mtype(const char *ftype) attribute_pure
Return mime type based on extension.
Definition: http.c:206
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3327
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:768
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define LOG_ERROR
#define LOG_WARNING
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition: iostream.c:84
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
void ast_get_dst_info(const time_t *const timep, int *dst_enabled, time_t *dst_start, time_t *dst_end, int *gmt_off, const char *const zone)
Definition: localtime.c:1754
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: main/utils.c:928
Options provided by main asterisk program.
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
Core PBX routines and definitions.
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1543
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
ast_phoneprov_std_variables
Definition: phoneprov.h:29
@ AST_PHONEPROV_STD_DST_END_MDAY
Definition: phoneprov.h:51
@ AST_PHONEPROV_STD_SERVER_IFACE
Definition: phoneprov.h:42
@ AST_PHONEPROV_STD_VAR_LIST_LENGTH
Definition: phoneprov.h:53
@ AST_PHONEPROV_STD_SERVER_PORT
Definition: phoneprov.h:41
@ AST_PHONEPROV_STD_DST_START_MDAY
Definition: phoneprov.h:48
@ AST_PHONEPROV_STD_MAC
Definition: phoneprov.h:30
@ AST_PHONEPROV_STD_CALLERID
Definition: phoneprov.h:36
@ AST_PHONEPROV_STD_DST_START_MONTH
Definition: phoneprov.h:47
@ AST_PHONEPROV_STD_TZOFFSET
Definition: phoneprov.h:45
@ AST_PHONEPROV_STD_USERNAME
Definition: phoneprov.h:32
@ AST_PHONEPROV_STD_DST_END_MONTH
Definition: phoneprov.h:50
@ AST_PHONEPROV_STD_LINENUMBER
Definition: phoneprov.h:38
@ AST_PHONEPROV_STD_TIMEZONE
Definition: phoneprov.h:37
@ AST_PHONEPROV_STD_SERVER
Definition: phoneprov.h:40
@ AST_PHONEPROV_STD_LABEL
Definition: phoneprov.h:35
@ AST_PHONEPROV_STD_PROFILE
Definition: phoneprov.h:31
@ AST_PHONEPROV_STD_EXTENSION_LENGTH
Definition: phoneprov.h:44
@ AST_PHONEPROV_STD_DST_END_HOUR
Definition: phoneprov.h:52
@ AST_PHONEPROV_STD_DST_ENABLE
Definition: phoneprov.h:46
@ AST_PHONEPROV_STD_DISPLAY_NAME
Definition: phoneprov.h:33
@ AST_PHONEPROV_STD_SECRET
Definition: phoneprov.h:34
@ AST_PHONEPROV_STD_DST_START_HOUR
Definition: phoneprov.h:49
@ AST_PHONEPROV_STD_VOICEMAIL_EXTEN
Definition: phoneprov.h:43
@ AST_PHONEPROV_STD_LINEKEYS
Definition: phoneprov.h:39
int(* ast_phoneprov_load_users_cb)(void)
Causes the provider to load its users.
Definition: phoneprov.h:75
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
static char user[512]
#define MAX_USER_BUCKETS
Definition: res_phoneprov.c:83
#define FORMATS
static int load_file(const char *filename, char **ret)
Read a TEXT file into a string and return the length.
static void profile_destructor(void *obj)
#define MAX_ROUTE_BUCKETS
Definition: res_phoneprov.c:82
#define MAX_PROFILE_BUCKETS
Definition: res_phoneprov.c:81
static void delete_providers(void)
Delete all providers.
static struct in_addr __ourip
for use in lookup_iface
static struct phone_profile * find_profile(const char *name)
Return a phone profile looked up by name.
static void provider_destructor(void *obj)
#define MAX_PROVIDER_BUCKETS
Definition: res_phoneprov.c:80
void ast_phoneprov_delete_extensions(char *provider_name)
Deletes all extensions for this provider.
void ast_phoneprov_provider_unregister(char *provider_name)
Unegisters a config provider from phoneprov and frees its resources.
static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static struct phone_profile * unref_profile(struct phone_profile *prof)
static struct ast_custom_function pp_each_extension_function
static struct varshead * get_defaults(void)
static struct extension * delete_extension(struct extension *exten)
static int load_common(void)
static char * handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command to list static and dynamic routes.
static struct ast_cli_entry pp_cli[]
static struct ast_custom_function pp_each_user_function
static void set_timezone_variables(struct varshead *headp, const char *zone)
Set all timezone-related variables based on a zone (i.e. America/New_York)
#define SIMPLE_CMP_FN(fname, stype, field)
Creates a compare function for a structure string field.
int ast_phoneprov_add_extension(char *provider_name, struct varshead *vars)
Adds an extension.
static struct http_route * unref_route(struct http_route *route)
static struct user * unref_user(struct user *user)
static int build_user_routes(struct user *user)
Add an http route for dynamic files attached to the profile of the user.
static struct user * build_user(const char *mac, struct phone_profile *profile, char *provider_name)
Build and return a user structure based on gathered config data.
static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static const char * pp_general_lookup[]
static void user_destructor(void *obj)
Free all memory associated with a user.
static int route_list_cb(void *obj, void *arg, void *data, int flags)
static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
const char * ast_phoneprov_std_variable_lookup(enum ast_phoneprov_std_variables var)
Returns the string respresentation of a phoneprov standard variable.
static int routes_delete_cb(void *obj, void *arg, int flags)
static int extensions_delete_cb(void *obj, void *arg, int flags)
struct ao2_container * users
static const char * pp_user_lookup[]
static struct phoneprov_provider * find_provider(char *name)
struct ao2_container * http_routes
static int lookup_iface(const char *iface, struct in_addr *address)
static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
A dialplan function that can be used to print a string for each phoneprov user.
#define FORMATD
static const char * variable_lookup[]
static void delete_profiles(void)
Delete all phone profiles, freeing their memory.
#define SIMPLE_HASH_FN(fname, stype, field)
Creates a hash function for a structure string field.
static void delete_file(struct phoneprov_file *file)
static struct ast_http_uri phoneprovuri
static struct user * find_user(const char *macaddress)
Return a user looked up by name.
static struct extension * build_extension(const char *name, struct varshead *vars)
struct ao2_container * profiles
static void delete_routes(void)
Delete all http routes, freeing their memory.
static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
static int extension_delete_cb(void *obj, void *arg, void *data, int flags)
void ast_phoneprov_delete_extension(char *provider_name, char *macaddress)
Deletes an extension.
static int load_module(void)
Load the module.
static void delete_users(void)
Delete all users.
static int load_users(void)
#define SIPUSERS_PROVIDER_NAME
static int unload_module(void)
static int reload(void)
static int add_user_extension(struct user *user, struct extension *exten)
Add an extension to a user ordered by index/linenumber.
static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
Callback that is executed everytime an http request is received by this module.
struct ao2_container * providers
static void route_destructor(void *obj)
static void build_profile(const char *name, struct ast_variable *v)
Build a phone profile and add it to the list of phone profiles.
int ast_phoneprov_provider_register(char *provider_name, ast_phoneprov_load_users_cb load_users)
Registers a config provider to phoneprov.
static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
A dialplan function that can be used to output a template for each extension attached to a user.
static void build_route(struct phoneprov_file *pp_file, struct phone_profile *profile, struct user *user, char *uri)
Build a route structure and add it to the list of available http routes.
const char * method
Definition: res_pjsip.c:1130
#define NULL
Definition: resample.c:96
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:432
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1117
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:739
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: main/utils.c:2097
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: main/utils.c:2065
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:640
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1091
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:406
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure used to handle boolean flags.
Definition: utils.h:199
Definition of a URI handler.
Definition: http.h:102
ast_http_callback callback
Definition: http.h:107
Support for dynamic strings.
Definition: strings.h:604
describes a server instance
Definition: tcptls.h:150
struct ast_iostream * stream
Definition: tcptls.h:161
int tm_mday
Definition: localtime.h:39
int tm_hour
Definition: localtime.h:38
int tm_mon
Definition: localtime.h:40
char name[0]
Definition: chanvars.h:31
char * value
Definition: chanvars.h:30
struct ast_var_t::@239 entries
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Definition: search.h:40
structure to hold extensions
struct extension::@475 entry
struct varshead * headp
const ast_string_field name
structure to hold http routes (valid URIs, and the files they link to)
const ast_string_field uri
struct phone_profile * profile
struct phoneprov_file * file
struct user * user
structure to hold phone profiles read from phoneprov.conf
struct phone_profile::@476 static_files
const ast_string_field staticdir
const ast_string_field default_mime_type
struct phone_profile::@477 dynamic_files
struct varshead * headp
const ast_string_field name
structure to hold file data
const ast_string_field mime_type
struct phoneprov_file::@474 entry
const ast_string_field template
const ast_string_field format
structure to hold config providers
ast_phoneprov_load_users_cb load_users
const ast_string_field provider_name
structure to hold users read from users.conf
const ast_string_field macaddress
struct phone_profile * profile
const ast_string_field provider_name
struct user::@478 extensions
list of users found in the config file
int value
Definition: syslog.c:37
const char * args
static struct test_val a
int error(const char *format,...)
Definition: utils/frame.c:999
Utility functions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:936
#define ARRAY_LEN(a)
Definition: utils.h:661