Asterisk - The Open Source Telephony Project  GIT-master-8beac82
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  );
283  AST_LIST_ENTRY(phoneprov_file) entry;
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 };
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)
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 */
332  struct phone_profile *profile;
333 };
334 struct ao2_container *http_routes;
335 SIMPLE_HASH_FN(http_route_hash_fn, http_route, uri)
336 SIMPLE_CMP_FN(http_route_cmp_fn, http_route, uri)
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 {
373  return ao2_find(providers, name, OBJ_SEARCH_KEY);
374 }
375 
376 /*! \brief Delete all providers */
377 static void delete_providers(void)
378 {
379  if (!providers) {
380  return;
381  }
384 }
385 
386 static void provider_destructor(void *obj)
387 {
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);
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 user User to link to the route (NULL means static route)
509  \param uri URI of the route
510 */
511 static void build_route(struct phoneprov_file *pp_file, struct phone_profile *profile, struct user *user, char *uri)
512 {
513  struct http_route *route;
514 
515  if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
516  return;
517  }
518 
519  if (ast_string_field_init(route, 32)) {
520  ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
521  route = unref_route(route);
522  return;
523  }
524 
525  ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
526  route->user = user;
527  route->file = pp_file;
528  route->profile = profile;
529 
530  ao2_link(http_routes, route);
532  route = unref_route(route);
533 }
534 
535 static struct phone_profile *unref_profile(struct phone_profile *prof)
536 {
537  ao2_cleanup(prof);
538 
539  return NULL;
540 }
541 
542 /*! \brief Return a phone profile looked up by name */
543 static struct phone_profile *find_profile(const char *name)
544 {
545  return ao2_find(profiles, name, OBJ_SEARCH_KEY);
546 }
547 
548 static void profile_destructor(void *obj)
549 {
550  struct phone_profile *profile = obj;
551  struct phoneprov_file *file;
552  struct ast_var_t *var;
553 
554  while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry))) {
555  delete_file(file);
556  }
557 
558  while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry))) {
559  delete_file(file);
560  }
561 
562  while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries))) {
563  ast_var_delete(var);
564  }
565 
566  ast_free(profile->headp);
568 }
569 
570 /*! \brief Delete all phone profiles, freeing their memory */
571 static void delete_profiles(void)
572 {
573  if (!profiles) {
574  return;
575  }
576 
578 }
579 
580 /*! \brief Build a phone profile and add it to the list of phone profiles
581  \param name the name of the profile
582  \param v ast_variable from parsing phoneprov.conf
583 */
584 static void build_profile(const char *name, struct ast_variable *v)
585 {
586  struct phone_profile *profile;
587 
588  if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
589  return;
590  }
591 
592  if (ast_string_field_init(profile, 32)) {
593  profile = unref_profile(profile);
594  return;
595  }
596 
597  if (!(profile->headp = ast_var_list_create())) {
598  profile = unref_profile(profile);
599  return;
600  }
601 
604 
605  ast_string_field_set(profile, name, name);
606  for (; v; v = v->next) {
607  if (!strcasecmp(v->name, "mime_type")) {
609  } else if (!strcasecmp(v->name, "setvar")) {
610  char value_copy[strlen(v->value) + 1];
611 
613  AST_APP_ARG(varname);
614  AST_APP_ARG(varval);
615  );
616 
617  strcpy(value_copy, v->value); /* safe */
618  AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
619  do {
620  if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
621  break;
622  args.varname = ast_strip(args.varname);
623  args.varval = ast_strip(args.varval);
624  if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
625  break;
626  AST_VAR_LIST_INSERT_TAIL(profile->headp, ast_var_assign(args.varname, args.varval));
627  } while (0);
628  } else if (!strcasecmp(v->name, "staticdir")) {
629  ast_string_field_set(profile, staticdir, v->value);
630  } else {
631  struct phoneprov_file *pp_file;
632  char *file_extension;
633  char value_copy[strlen(v->value) + 1];
634 
636  AST_APP_ARG(filename);
637  AST_APP_ARG(mimetype);
638  );
639 
640  if (!(pp_file = ast_calloc_with_stringfields(1, struct phoneprov_file, 32))) {
641  profile = unref_profile(profile);
642  return;
643  }
644 
645  if ((file_extension = strrchr(pp_file->format, '.')))
646  file_extension++;
647 
648  strcpy(value_copy, v->value); /* safe */
649  AST_STANDARD_APP_ARGS(args, value_copy);
650 
651  /* Mime type order of preference
652  * 1) Specific mime-type defined for file in profile
653  * 2) Mime determined by extension
654  * 3) Default mime type specified in profile
655  * 4) text/plain
656  */
657  ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype,
658  (S_OR(S_OR(ast_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
659 
660  if (!strcasecmp(v->name, "static_file")) {
661  ast_string_field_set(pp_file, format, args.filename);
662  ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
663  AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
664  /* Add a route for the static files, as their filenames won't change per-user */
665  build_route(pp_file, profile, NULL, NULL);
666  } else {
667  ast_string_field_set(pp_file, format, v->name);
668  ast_string_field_set(pp_file, template, args.filename);
669  AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
670  }
671  }
672  }
673 
674  ao2_link(profiles, profile);
675 
676  profile = unref_profile(profile);
677 }
678 
679 static struct extension *delete_extension(struct extension *exten)
680 {
683  ast_free(exten);
684 
685  return NULL;
686 }
687 
688 static struct extension *build_extension(const char *name, struct varshead *vars)
689 {
690  struct extension *exten;
691  const char *tmp;
692 
693  if (!(exten = ast_calloc_with_stringfields(1, struct extension, 32))) {
694  return NULL;
695  }
696 
697  ast_string_field_set(exten, name, name);
698 
699  exten->headp = ast_var_list_clone(vars);
700  if (!exten->headp) {
701  ast_log(LOG_ERROR, "Unable to clone variables for extension '%s'\n", name);
702  delete_extension(exten);
703  return NULL;
704  }
705 
707  if (!tmp) {
710  exten->index = 1;
711  } else {
712  sscanf(tmp, "%d", &exten->index);
713  }
714 
717  ast_var_assign(variable_lookup[AST_PHONEPROV_STD_LINEKEYS], "1"));
718  }
722 
723  return exten;
724 }
725 
726 static struct user *unref_user(struct user *user)
727 {
728  ao2_cleanup(user);
729 
730  return NULL;
731 }
733 /*! \brief Return a user looked up by name */
734 static struct user *find_user(const char *macaddress)
735 {
736  return ao2_find(users, macaddress, OBJ_SEARCH_KEY);
737 }
738 
739 static int routes_delete_cb(void *obj, void *arg, int flags)
740 {
741  struct http_route *route = obj;
742  struct user *user = route->user;
743  char *macaddress = arg;
744 
745  if (user && !strcmp(user->macaddress, macaddress)) {
746  return CMP_MATCH;
747  }
748  return 0;
749 }
750 
751 /*! \brief Free all memory associated with a user */
752 static void user_destructor(void *obj)
753 {
754  struct user *user = obj;
755  struct extension *exten;
756 
757  while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
758  exten = delete_extension(exten);
759  }
760 
761  if (user->profile) {
762  user->profile = unref_profile(user->profile);
763  }
764 
765  if (http_routes) {
767  }
768 
770 }
771 
772 /*! \brief Delete all users */
773 static void delete_users(void)
774 {
775  if (!users) {
776  return;
777  }
778 
780 }
781 
782 /*! \brief Build and return a user structure based on gathered config data */
783 static struct user *build_user(const char *mac, struct phone_profile *profile, char *provider_name)
784 {
785  struct user *user;
786 
787  if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
788  return NULL;
789  }
790 
791  if (ast_string_field_init(user, 64)) {
792  user = unref_user(user);
793  return NULL;
794  }
795 
796  ast_string_field_set(user, macaddress, mac);
797  ast_string_field_set(user, provider_name, provider_name);
798  user->profile = profile;
799  ao2_ref(profile, 1);
800 
801  return user;
802 }
803 
804 /*! \brief Add an extension to a user ordered by index/linenumber */
805 static int add_user_extension(struct user *user, struct extension *exten)
806 {
807  struct ast_var_t *pvar, *var2;
808  struct ast_str *str = ast_str_create(16);
809 
810  if (!str) {
811  return -1;
812  }
813 
814  /* Append profile variables here, and substitute variables on profile
815  * setvars, so that we can use user specific variables in them */
816  AST_VAR_LIST_TRAVERSE(user->profile->headp, pvar) {
817  if (ast_var_find(exten->headp, pvar->name)) {
818  continue;
819  }
820 
821  ast_str_substitute_variables_varshead(&str, 0, exten->headp, pvar->value);
822  if ((var2 = ast_var_assign(pvar->name, ast_str_buffer(str)))) {
823  AST_VAR_LIST_INSERT_TAIL(exten->headp, var2);
824  }
825  }
826  ast_free(str);
827 
828  if (AST_LIST_EMPTY(&user->extensions)) {
829  AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
830  } else {
831  struct extension *exten_iter;
832 
833  AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
834  if (exten->index < exten_iter->index) {
836  } else if (exten->index == exten_iter->index) {
837  ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
838  return -1;
839  } else if (!AST_LIST_NEXT(exten_iter, entry)) {
840  AST_LIST_INSERT_TAIL(&user->extensions, exten, entry);
841  }
842  }
844  }
845 
846  return 0;
847 }
848 
849 /*! \brief Add an http route for dynamic files attached to the profile of the user */
850 static int build_user_routes(struct user *user)
851 {
852  struct phoneprov_file *pp_file;
853  struct ast_str *str;
854 
855  if (!(str = ast_str_create(16))) {
856  return -1;
857  }
858 
859  AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
860  ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format);
861  build_route(pp_file, user->profile, user, ast_str_buffer(str));
862  }
863 
864  ast_free(str);
865  return 0;
866 }
867 
868 /*! \brief Callback that is executed everytime an http request is received by this module */
869 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)
870 {
871  struct http_route *route;
872  struct ast_str *result;
873  char path[PATH_MAX];
874  char *file = NULL;
875  char *server;
876  int len;
877  int fd;
878  struct ast_str *http_header;
879 
880  if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
881  ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
882  return 0;
883  }
884 
885  if (!(route = ao2_find(http_routes, uri, OBJ_SEARCH_KEY))) {
886  goto out404;
887  }
888 
889  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
890 
891  if (!route->user) { /* Static file */
892 
893  fd = open(path, O_RDONLY);
894  if (fd < 0) {
895  goto out500;
896  }
897 
898  len = lseek(fd, 0, SEEK_END);
899  lseek(fd, 0, SEEK_SET);
900  if (len < 0) {
901  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
902  close(fd);
903  goto out500;
904  }
905 
906  http_header = ast_str_create(80);
907  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
908  route->file->mime_type);
909 
910  ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
911 
912  close(fd);
913  route = unref_route(route);
914  return 0;
915  } else { /* Dynamic file */
916  struct ast_str *tmp;
917 
918  len = load_file(path, &file);
919  if (len < 0) {
920  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
921  if (file) {
922  ast_free(file);
923  }
924 
925  goto out500;
926  }
927 
928  if (!file) {
929  goto out500;
930  }
931 
932  if (!(tmp = ast_str_create(len))) {
933  if (file) {
934  ast_free(file);
935  }
936 
937  goto out500;
938  }
939 
940  /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
941  * the IP address we are listening on that the phone contacted for this config file */
942 
943  server = ast_var_find(AST_LIST_FIRST(&route->user->extensions)->headp,
945 
946  if (!server) {
947  union {
948  struct sockaddr sa;
949  struct sockaddr_in sa_in;
950  } name;
951  socklen_t namelen = sizeof(name.sa);
952  int res;
953 
954  if ((res = getsockname(ast_iostream_get_fd(ser->stream), &name.sa, &namelen))) {
955  ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
956  } else {
957  struct extension *exten_iter;
958  const char *newserver = ast_inet_ntoa(name.sa_in.sin_addr);
959 
960  AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
961  AST_VAR_LIST_INSERT_TAIL(exten_iter->headp,
963  }
964  }
965  }
966 
967  ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file);
968 
969  ast_free(file);
970 
971  http_header = ast_str_create(80);
972  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
973  route->file->mime_type);
974 
975  if (!(result = ast_str_create(512))) {
976  ast_log(LOG_ERROR, "Could not create result string!\n");
977  if (tmp) {
978  ast_free(tmp);
979  }
980  ast_free(http_header);
981  goto out500;
982  }
983  ast_str_append(&result, 0, "%s", ast_str_buffer(tmp));
984 
985  ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
986  ast_free(tmp);
987 
988  route = unref_route(route);
989 
990  return 0;
991  }
992 
993 out404:
994  ast_http_error(ser, 404, "Not Found", uri);
995  return 0;
996 
997 out500:
998  route = unref_route(route);
999  ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
1000  return 0;
1001 }
1002 
1003 /*! \brief A dialplan function that can be used to print a string for each phoneprov user */
1004 static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
1005 {
1006  char *tmp;
1007  struct ao2_iterator i;
1008  struct user *user;
1009  struct ast_str *str;
1011  AST_APP_ARG(string);
1012  AST_APP_ARG(exclude_mac);
1013  );
1014  AST_STANDARD_APP_ARGS(args, data);
1015 
1016  if (!(str = ast_str_create(16))) {
1017  return -1;
1018  }
1019 
1020  /* Fix data by turning %{ into ${ */
1021  while ((tmp = strstr(args.string, "%{")))
1022  *tmp = '$';
1023 
1024  i = ao2_iterator_init(users, 0);
1025  while ((user = ao2_iterator_next(&i))) {
1026  if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
1027  continue;
1028  }
1029  ast_str_substitute_variables_varshead(&str, len, AST_LIST_FIRST(&user->extensions)->headp, args.string);
1030  if (buf) {
1031  size_t slen = len;
1032  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1033  } else {
1034  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1035  }
1036  user = unref_user(user);
1037  }
1039 
1040  ast_free(str);
1041  return 0;
1043 
1044 static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1045 {
1046  return pp_each_user_helper(chan, data, buf, NULL, len);
1048 
1049 static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1050 {
1051  return pp_each_user_helper(chan, data, NULL, buf, len);
1052 }
1053 
1055  .name = "PP_EACH_USER",
1056  .read = pp_each_user_read,
1057  .read2 = pp_each_user_read2,
1058 };
1059 
1060 /*! \brief A dialplan function that can be used to output a template for each extension attached to a user */
1061 static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
1062 {
1063  struct user *user;
1064  struct extension *exten;
1065  char path[PATH_MAX];
1066  char *file;
1067  int filelen;
1068  struct ast_str *str;
1070  AST_APP_ARG(mac);
1071  AST_APP_ARG(template);
1072  );
1073 
1074  AST_STANDARD_APP_ARGS(args, data);
1075 
1076  if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
1077  ast_log(LOG_WARNING, "PP_EACH_EXTENSION requries both a macaddress and template filename.\n");
1078  return 0;
1079  }
1080 
1081  if (!(user = find_user(args.mac))) {
1082  ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
1083  return 0;
1084  }
1085 
1086  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
1087  filelen = load_file(path, &file);
1088  if (filelen < 0) {
1089  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
1090  if (file) {
1091  ast_free(file);
1092  }
1093  return 0;
1094  }
1095 
1096  if (!file) {
1097  return 0;
1098  }
1099 
1100  if (!(str = ast_str_create(filelen))) {
1101  return 0;
1102  }
1103 
1104  AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
1105  ast_str_substitute_variables_varshead(&str, 0, exten->headp, file);
1106  if (buf) {
1107  size_t slen = len;
1108  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1109  } else {
1110  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1111  }
1112  }
1113 
1114  ast_free(file);
1115  ast_free(str);
1116 
1117  user = unref_user(user);
1118 
1119  return 0;
1121 
1122 static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1123 {
1124  return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
1126 
1127 static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1128 {
1129  return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
1130 }
1131 
1132 static struct ast_custom_function pp_each_extension_function = {
1133  .name = "PP_EACH_EXTENSION",
1134  .read = pp_each_extension_read,
1135  .read2 = pp_each_extension_read2,
1136 };
1137 
1138 #define FORMATS "%-20.20s %-40.40s %-30.30s\n"
1139 #define FORMATD "%-20.20s %-20.20s %-40.40s %-30.30s\n"
1140 static int route_list_cb(void *obj, void *arg, void *data, int flags)
1141 {
1142  int fd = *(int *)arg;
1143  struct http_route *route = obj;
1144 
1145  if (data && route->user) {
1146  ast_cli(fd, FORMATD, route->user->provider_name, route->profile->name, route->uri, route->file->template);
1147  }
1148  if (!data && !route->user) {
1149  ast_cli(fd, FORMATS, route->profile->name, route->uri, route->file->template);
1150  }
1151 
1152  return CMP_MATCH;
1153 }
1154 
1155 /*! \brief CLI command to list static and dynamic routes */
1156 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1157 {
1158  int fd = a->fd;
1159  switch(cmd) {
1160  case CLI_INIT:
1161  e->command = "phoneprov show routes";
1162  e->usage =
1163  "Usage: phoneprov show routes\n"
1164  " Lists all registered phoneprov http routes.\n";
1165  return NULL;
1166  case CLI_GENERATE:
1167  return NULL;
1168  }
1169 
1170  /* This currently iterates over routes twice, but it is the only place I've needed
1171  * to really separate static an dynamic routes, so I've just left it this way. */
1172  ast_cli(a->fd, "Static routes\n\n");
1173  ast_cli(a->fd, FORMATS, "Profile", "Relative URI", "Physical location");
1174 
1176 
1177  ast_cli(a->fd, "\nDynamic routes\n\n");
1178  ast_cli(a->fd, FORMATD, "Provider", "Profile", "Relative URI", "Template");
1179 
1180  ao2_callback_data(http_routes, OBJ_NODATA | OBJ_MULTIPLE, route_list_cb, &fd, (void *)1);
1181 
1182  return CLI_SUCCESS;
1183 }
1184 
1185 static struct ast_cli_entry pp_cli[] = {
1186  AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
1187 };
1188 
1189 static struct ast_http_uri phoneprovuri = {
1191  .description = "Asterisk HTTP Phone Provisioning Tool",
1192  .uri = "phoneprov",
1193  .has_subtree = 1,
1194  .data = NULL,
1195  .key = __FILE__,
1196 };
1197 
1198 static struct varshead *get_defaults(void)
1199 {
1200  struct ast_config *phoneprov_cfg, *cfg = CONFIG_STATUS_FILEINVALID;
1201  const char *value;
1202  struct ast_variable *v;
1203  struct ast_var_t *var;
1204  struct ast_flags config_flags = { 0 };
1205  struct varshead *defaults = ast_var_list_create();
1206 
1207  if (!defaults) {
1208  ast_log(LOG_ERROR, "Unable to create default var list.\n");
1209  return NULL;
1210  }
1211 
1212  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))
1213  || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
1214  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
1215  ast_var_list_destroy(defaults);
1216  return NULL;
1217  }
1218 
1219  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_SERVER]);
1220  if (!value) {
1221  struct in_addr addr;
1222  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_SERVER_IFACE]);
1223  if (value) {
1224  lookup_iface(value, &addr);
1225  value = ast_inet_ntoa(addr);
1226  }
1227  }
1228  if (value) {
1229  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_SERVER], value);
1230  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1231  } else {
1232  ast_log(LOG_WARNING, "Unable to find a valid server address or name.\n");
1233  }
1234 
1235  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_SERVER_PORT]);
1236  if (!value) {
1237  if ((cfg = ast_config_load("sip.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1238  value = ast_variable_retrieve(cfg, "general", "bindport");
1239  }
1240  }
1241  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_SERVER_PORT], S_OR(value, "5060"));
1242  if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
1243  ast_config_destroy(cfg);
1244  }
1245  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1246 
1247  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_PROFILE]);
1248  if (!value) {
1249  ast_log(LOG_ERROR, "Unable to load default profile.\n");
1250  ast_config_destroy(phoneprov_cfg);
1251  ast_var_list_destroy(defaults);
1252  return NULL;
1253  }
1254  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_PROFILE], value);
1255  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1256  ast_config_destroy(phoneprov_cfg);
1257 
1258  if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
1259  ast_log(LOG_ERROR, "Unable to load users.conf\n");
1260  ast_var_list_destroy(defaults);
1261  return NULL;
1262  }
1263 
1264  /* Go ahead and load global variables from users.conf so we can append to profiles */
1265  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
1266  if (!strcasecmp(v->name, pp_user_lookup[AST_PHONEPROV_STD_VOICEMAIL_EXTEN])) {
1267  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_VOICEMAIL_EXTEN], v->value);
1268  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1269  }
1270  if (!strcasecmp(v->name, pp_user_lookup[AST_PHONEPROV_STD_EXTENSION_LENGTH])) {
1271  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_EXTENSION_LENGTH], v->value);
1272  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1273  }
1274  }
1275  ast_config_destroy(cfg);
1276 
1277  return defaults;
1278 }
1279 
1280 static int load_users(void)
1281 {
1282  struct ast_config *cfg;
1283  char *cat;
1284  const char *value;
1285  struct ast_flags config_flags = { 0 };
1286  struct varshead *defaults = get_defaults();
1287 
1288  if (!defaults) {
1289  ast_log(LOG_WARNING, "Unable to load default variables.\n");
1290  return -1;
1291  }
1292 
1293  if (!(cfg = ast_config_load("users.conf", config_flags))
1294  || cfg == CONFIG_STATUS_FILEINVALID) {
1295  ast_log(LOG_WARNING, "Unable to load users.conf\n");
1296  ast_var_list_destroy(defaults);
1297  return -1;
1298  }
1299 
1300  cat = NULL;
1301  while ((cat = ast_category_browse(cfg, cat))) {
1302  const char *tmp;
1303  int i;
1304  struct ast_var_t *varx;
1305  struct ast_var_t *vard;
1306 
1307  if (strcasecmp(cat, "general") && strcasecmp(cat, "authentication")) {
1308  struct varshead *variables = ast_var_list_create();
1309 
1310  if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp))) {
1311  ast_var_list_destroy(variables);
1312  continue;
1313  }
1314 
1315  /* Transfer the standard variables */
1316  for (i = 0; i < AST_PHONEPROV_STD_VAR_LIST_LENGTH; i++) {
1317  if (pp_user_lookup[i]) {
1318  value = ast_variable_retrieve(cfg, cat, pp_user_lookup[i]);
1319  if (value) {
1320  varx = ast_var_assign(variable_lookup[i],
1321  value);
1322  AST_VAR_LIST_INSERT_TAIL(variables, varx);
1323  }
1324  }
1325  }
1326 
1328  ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
1329  ast_var_list_destroy(variables);
1330  continue;
1331  }
1332 
1333  /* Apply defaults */
1334  AST_VAR_LIST_TRAVERSE(defaults, vard) {
1335  if (ast_var_find(variables, vard->name)) {
1336  continue;
1337  }
1338  varx = ast_var_assign(vard->name, vard->value);
1339  AST_VAR_LIST_INSERT_TAIL(variables, varx);
1340  }
1341 
1342  ast_phoneprov_add_extension(SIPUSERS_PROVIDER_NAME, variables);
1343  }
1344  }
1345  ast_config_destroy(cfg);
1346  ast_var_list_destroy(defaults);
1347  return 0;
1348 }
1349 
1350 static int load_common(void)
1351 {
1352  struct ast_config *phoneprov_cfg;
1353  struct ast_flags config_flags = { 0 };
1354  char *cat;
1355 
1356  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))
1357  || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
1358  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
1359  return -1;
1360  }
1361 
1362  cat = NULL;
1363  while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
1364  if (!strcasecmp(cat, "general")) {
1365  continue;
1366  }
1367  build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
1368  }
1369  ast_config_destroy(phoneprov_cfg);
1370 
1371  if (!ao2_container_count(profiles)) {
1372  ast_log(LOG_ERROR, "There are no provisioning profiles in phoneprov.conf.\n");
1373  return -1;
1374  }
1375 
1376  return 0;
1377 }
1378 
1379 static int unload_module(void)
1380 {
1381  ast_http_uri_unlink(&phoneprovuri);
1382  ast_custom_function_unregister(&pp_each_user_function);
1383  ast_custom_function_unregister(&pp_each_extension_function);
1384  ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
1385 
1386  /* This cleans up the sip.conf/users.conf provider (called specifically for clarity) */
1387  ast_phoneprov_provider_unregister(SIPUSERS_PROVIDER_NAME);
1388 
1389  /* This cleans up the framework which also cleans up the providers. */
1390  delete_profiles();
1391  ao2_cleanup(profiles);
1392  profiles = NULL;
1393  delete_routes();
1394  delete_users();
1395  ao2_cleanup(http_routes);
1396  http_routes = NULL;
1397  ao2_cleanup(users);
1398  users = NULL;
1399  delete_providers();
1400  ao2_cleanup(providers);
1401  providers = NULL;
1402 
1403  return 0;
1404 }
1405 
1406 /*!
1407  * \brief Load the module
1408  *
1409  * Module loading including tests for configuration or dependencies.
1410  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
1411  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
1412  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
1413  * configuration file or other non-critical problem return
1414  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
1415  */
1416 static int load_module(void)
1417 {
1419  phone_profile_hash_fn, NULL, phone_profile_cmp_fn);
1420  if (!profiles) {
1421  ast_log(LOG_ERROR, "Unable to allocate profiles container.\n");
1422  return AST_MODULE_LOAD_DECLINE;
1423  }
1424 
1427  if (!http_routes) {
1428  ast_log(LOG_ERROR, "Unable to allocate routes container.\n");
1429  goto error;
1430  }
1431 
1432  if (load_common()) {
1433  ast_log(LOG_ERROR, "Unable to load provisioning profiles.\n");
1434  goto error;
1435  }
1436 
1439  if (!users) {
1440  ast_log(LOG_ERROR, "Unable to allocate users container.\n");
1441  goto error;
1442  }
1443 
1445  MAX_PROVIDER_BUCKETS, phoneprov_provider_hash_fn, NULL, phoneprov_provider_cmp_fn);
1446  if (!providers) {
1447  ast_log(LOG_ERROR, "Unable to allocate providers container.\n");
1448  goto error;
1449  }
1450 
1451  /* Register ourselves as the provider for sip.conf/users.conf */
1452  if (ast_phoneprov_provider_register(SIPUSERS_PROVIDER_NAME, load_users)) {
1453  ast_log(LOG_WARNING, "Unable register sip/users config provider. Others may succeed.\n");
1454  }
1455 
1456  ast_http_uri_link(&phoneprovuri);
1457 
1458  ast_custom_function_register(&pp_each_user_function);
1459  ast_custom_function_register(&pp_each_extension_function);
1460  ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
1461 
1463 
1464 error:
1465  unload_module();
1466  return AST_MODULE_LOAD_DECLINE;
1467 }
1468 
1469 static int reload(void)
1470 {
1471  struct ao2_iterator i;
1472  struct phoneprov_provider *provider;
1473 
1474  /* Clean everything except the providers */
1475  delete_routes();
1476  delete_users();
1477  delete_profiles();
1478 
1479  /* Reload the profiles */
1480  if (load_common()) {
1481  ast_log(LOG_ERROR, "Unable to reload provisioning profiles.\n");
1482  unload_module();
1483  return AST_MODULE_LOAD_DECLINE;
1484  }
1485 
1486  /* For each provider, reload the users */
1487  ao2_lock(providers);
1488  i = ao2_iterator_init(providers, 0);
1489  for(; (provider = ao2_iterator_next(&i)); ao2_ref(provider, -1)) {
1490  if (provider->load_users()) {
1491  ast_log(LOG_ERROR, "Unable to load provider '%s' users. Reload aborted.\n", provider->provider_name);
1492  continue;
1493  }
1494  }
1496  ao2_unlock(providers);
1497 
1498  return AST_MODULE_LOAD_SUCCESS;
1499 }
1500 
1502  .support_level = AST_MODULE_SUPPORT_EXTENDED,
1503  .load = load_module,
1504  .unload = unload_module,
1506  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1507  .requires = "http",
1508 );
1509 
1510 /**** Public API for register/unregister, set defaults, and add extension. ****/
1511 
1513 {
1514  if (var >= AST_PHONEPROV_STD_VAR_LIST_LENGTH) {
1515  return NULL;
1516  }
1517 
1518  return variable_lookup[var];
1519 }
1520 
1521 int ast_phoneprov_provider_register(char *provider_name,
1523 {
1524  struct phoneprov_provider *provider;
1525 
1526  if (ast_strlen_zero(provider_name)) {
1527  ast_log(LOG_ERROR, "Provider name can't be empty.\n");
1528  return -1;
1529  }
1530 
1531  if (!providers) {
1532  ast_log(LOG_WARNING, "Provider '%s' cannot be registered: res_phoneprov not loaded.\n", provider_name);
1533  return -1;
1534  }
1535 
1536  provider = find_provider(provider_name);
1537  if (provider) {
1538  ast_log(LOG_ERROR, "There is already a provider registered named '%s'.\n", provider_name);
1539  ao2_ref(provider, -1);
1540  return -1;
1541  }
1542 
1543  provider = ao2_alloc(sizeof(struct phoneprov_provider), provider_destructor);
1544  if (!provider) {
1545  ast_log(LOG_ERROR, "Unable to allocate sufficient memory for provider '%s'.\n", provider_name);
1546  return -1;
1547  }
1548 
1549  if (ast_string_field_init(provider, 32)) {
1550  ao2_ref(provider, -1);
1551  ast_log(LOG_ERROR, "Unable to allocate sufficient memory for provider '%s' stringfields.\n", provider_name);
1552  return -1;
1553  }
1554 
1555  ast_string_field_set(provider, provider_name, provider_name);
1556  provider->load_users = load_users;
1557 
1558  ao2_link(providers, provider);
1559  ao2_ref(provider, -1);
1560 
1561  if (provider->load_users()) {
1562  ast_log(LOG_ERROR, "Unable to load provider '%s' users. Register aborted.\n", provider_name);
1564  return -1;
1565  }
1566 
1567  return 0;
1568 }
1569 
1570 static int extensions_delete_cb(void *obj, void *arg, int flags)
1571 {
1572  char *provider_name = arg;
1573  struct user *user = obj;
1574  if (strcmp(user->provider_name, provider_name)) {
1575  return 0;
1576  }
1577  return CMP_MATCH;
1578 }
1579 
1580 static int extension_delete_cb(void *obj, void *arg, void *data, int flags)
1581 {
1582  struct user *user = obj;
1583  char *provider_name = data;
1584  char *macaddress = arg;
1586  if (!strcmp(user->provider_name, provider_name) && !strcasecmp(user->macaddress, macaddress)) {
1587  return CMP_MATCH;
1588  }
1589  return 0;
1590 }
1591 
1592 void ast_phoneprov_delete_extension(char *provider_name, char *macaddress)
1593 {
1594  if (!users) {
1595  return;
1596  }
1597 
1599  extension_delete_cb, macaddress, provider_name);
1600 }
1601 
1602 void ast_phoneprov_delete_extensions(char *provider_name)
1603 {
1604  if (!users) {
1605  return;
1606  }
1607 
1609 }
1610 
1611 void ast_phoneprov_provider_unregister(char *provider_name)
1612 {
1613  if (!providers) {
1614  return;
1615  }
1616 
1617  ast_phoneprov_delete_extensions(provider_name);
1618  ao2_find(providers, provider_name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
1619 }
1620 
1621 int ast_phoneprov_add_extension(char *provider_name, struct varshead *vars)
1622 {
1623  RAII_VAR(struct phoneprov_provider *, provider, NULL, ao2_cleanup);
1624  RAII_VAR(struct user *, user, NULL, ao2_cleanup);
1625  RAII_VAR(struct phone_profile *, profile, NULL, ao2_cleanup);
1626  struct extension *exten;
1627  char *profile_name;
1628  char *mac;
1629  char *username;
1630 
1631  if (ast_strlen_zero(provider_name)) {
1632  ast_log(LOG_ERROR, "Provider name can't be empty.\n");
1633  return -1;
1634  }
1635  if (!vars) {
1636  ast_log(LOG_ERROR, "Variable list can't be empty.\n");
1637  return -1;
1638  }
1639 
1641  if (!username) {
1642  ast_log(LOG_ERROR, "Extension name can't be empty.\n");
1643  return -1;
1644  }
1645 
1647  if (!mac) {
1648  ast_log(LOG_ERROR, "MAC Address can't be empty.\n");
1649  return -1;
1650  }
1651 
1652  provider = find_provider(provider_name);
1653  if (!provider) {
1654  ast_log(LOG_ERROR, "Provider '%s' wasn't found in the registry.\n", provider_name);
1655  return -1;
1656  }
1657 
1658  profile_name = ast_var_find(vars,
1660  if (!profile_name) {
1661  ast_log(LOG_ERROR, "No profile could be found for user '%s' - skipping.\n", username);
1662  return -1;
1663  }
1664  if (!(profile = find_profile(profile_name))) {
1665  ast_log(LOG_ERROR, "Could not look up profile '%s' - skipping.\n", profile_name);
1666  return -1;
1667  }
1668 
1669  if (!(user = find_user(mac))) {
1670 
1671  if (!(user = build_user(mac, profile, provider_name))) {
1672  ast_log(LOG_ERROR, "Could not create user for '%s' - skipping\n", mac);
1673  return -1;
1674  }
1675 
1676  if (!(exten = build_extension(username, vars))) {
1677  ast_log(LOG_ERROR, "Could not create extension for '%s' - skipping\n", user->macaddress);
1678  return -1;
1679  }
1680 
1681  if (add_user_extension(user, exten)) {
1682  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1683  exten = delete_extension(exten);
1684  return -1;
1685  }
1686 
1687  if (build_user_routes(user)) {
1688  ast_log(LOG_WARNING, "Could not create http routes for '%s' - skipping\n", user->macaddress);
1689  return -1;
1690  }
1691  ao2_link(users, user);
1692 
1693  } else {
1694  if (strcmp(provider_name, user->provider_name)) {
1695  ast_log(LOG_ERROR, "MAC address '%s' was already added by provider '%s' - skipping\n", user->macaddress, user->provider_name);
1696  return -1;
1697  }
1698 
1699  if (!(exten = build_extension(username, vars))) {
1700  ast_log(LOG_ERROR, "Could not create extension for '%s' - skipping\n", user->macaddress);
1701  return -1;
1702  }
1703 
1704  if (add_user_extension(user, exten)) {
1705  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1706  exten = delete_extension(exten);
1707  return -1;
1708  }
1709  }
1710 
1711  return 0;
1712 }
static char user[512]
const ast_string_field staticdir
const char * name
Definition: pbx.h:119
struct ast_variable * next
static void route_destructor(void *obj)
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:122
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.
Main Channel structure associated with a channel.
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
static const char name[]
Definition: format_mp3.c:68
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
static const char * pp_general_lookup[]
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.
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:648
ast_http_callback callback
Definition: http.h:105
static const char * pp_user_lookup[]
String manipulation functions.
Asterisk version information.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
ast_phoneprov_std_variables
Definition: phoneprov.h:29
static int extensions_delete_cb(void *obj, void *arg, int flags)
char * address
Definition: f2c.h:59
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
const ast_string_field format
struct user::@459 extensions
#define SIMPLE_HASH_FN(fname, stype, field)
Creates a hash function for a structure string field.
descriptor for a cli entry.
Definition: cli.h:171
#define LOG_WARNING
Definition: logger.h:274
void ast_phoneprov_delete_extensions(char *provider_name)
Deletes all extensions for this provider.
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:673
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
static int user_hash_fn(const void *obj, const int flags)
#define CONFIG_STATUS_FILEINVALID
const char * ast_http_ftype2mtype(const char *ftype) attribute_pure
Return mime type based on extension.
Definition: http.c:203
static struct ast_custom_function pp_each_user_function
const ast_string_field template
static int tmp()
Definition: bt_open.c:389
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...
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
static int lookup_iface(const char *iface, struct in_addr *address)
Structure for variables, used for configurations and for channel variables.
static void delete_profiles(void)
Delete all phone profiles, freeing their memory.
#define var
Definition: ast_expr2f.c:614
static void delete_routes(void)
Delete all http routes, freeing their memory.
static int routes_delete_cb(void *obj, void *arg, int flags)
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
struct ao2_container * profiles
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:705
Definition: cli.h:152
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:426
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:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream&#39;s file descriptor.
Definition: iostream.c:84
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
static int build_user_routes(struct user *user)
Add an http route for dynamic files attached to the profile of the user.
#define ao2_unlock(a)
Definition: astobj2.h:730
static void delete_providers(void)
Delete all providers.
const ast_string_field uri
const char * str
Definition: app_jack.c:147
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
const char * args
struct phoneprov_file * file
#define NULL
Definition: resample.c:96
structure to hold file data
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
struct varshead * headp
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:456
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
static struct user * find_user(const char *macaddress)
Return a user looked up by name.
const ast_string_field default_mime_type
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
struct varshead * ast_var_list_clone(struct varshead *head)
Definition: chanvars.c:124
int(* ast_phoneprov_load_users_cb)(void)
Causes the provider to load its users.
Definition: phoneprov.h:75
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)
Utility functions.
static struct phoneprov_provider * find_provider(char *name)
static int http_route_cmp_fn(void *obj, void *arg, int flags)
#define SIMPLE_CMP_FN(fname, stype, field)
Creates a compare function for a structure string field.
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:1065
static int load_common(void)
Configuration File Parser.
Support for Private Asterisk HTTP Servers.
struct varshead * headp
structure to hold config providers
#define MAX_PROFILE_BUCKETS
Definition: res_phoneprov.c:81
#define ast_log
Definition: astobj2.c:42
#define ast_config_load(filename, flags)
Load a config file.
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:1919
static int load_module(void)
Load the module.
static int user_cmp_fn(void *obj, void *arg, int flags)
static struct extension * build_extension(const char *name, struct varshead *vars)
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
#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:911
static int unload_module(void)
const int fd
Definition: cli.h:159
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.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
Access Control of various sorts.
static int phone_profile_cmp_fn(void *obj, void *arg, int flags)
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
int tm_mon
Definition: localtime.h:40
structure to hold extensions
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ao2_lock(a)
Definition: astobj2.h:718
struct ao2_container * providers
static void profile_destructor(void *obj)
const char * method
Definition: res_pjsip.c:4372
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
struct phone_profile * profile
static struct varshead * get_defaults(void)
static struct phone_profile * unref_profile(struct phone_profile *prof)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
int tm_mday
Definition: localtime.h:39
const ast_string_field name
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
static int load_file(const char *filename, char **ret)
Read a TEXT file into a string and return the length.
#define ARRAY_LEN(a)
Definition: utils.h:639
Core PBX routines and definitions.
describes a server instance
Definition: tcptls.h:149
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
static int http_route_hash_fn(const void *obj, const int flags)
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.
struct user * user
struct phone_profile * profile
char name[0]
Definition: chanvars.h:31
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
static int route_list_cb(void *obj, void *arg, void *data, int flags)
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
const ast_string_field provider_name
void ast_var_delete(struct ast_var_t *var)
Definition: extconf.c:2473
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
char * value
Definition: chanvars.h:30
int errno
static struct http_route * unref_route(struct http_route *route)
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1743
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
static struct ast_cli_entry pp_cli[]
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
struct ao2_container * users
struct phone_profile::@458 dynamic_files
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
static void provider_destructor(void *obj)
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: main/utils.c:782
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
char * ast_var_find(const struct varshead *head, const char *name)
Definition: chanvars.c:85
#define ast_var_assign(name, value)
Definition: chanvars.h:40
static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
structure to hold phone profiles read from phoneprov.conf
static const char * variable_lookup[]
static void user_destructor(void *obj)
Free all memory associated with a user.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int ast_phoneprov_add_extension(char *provider_name, struct varshead *vars)
Adds an extension.
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
int tm_hour
Definition: localtime.h:38
structure to hold users read from users.conf
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:550
Structure used to handle boolean flags.
Definition: utils.h:199
#define MAX_PROVIDER_BUCKETS
Definition: res_phoneprov.c:80
struct ast_iostream * stream
Definition: tcptls.h:160
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
const char * usage
Definition: cli.h:177
struct phone_profile::@457 static_files
struct varshead * ast_var_list_create(void)
Definition: chanvars.c:97
#define CLI_SUCCESS
Definition: cli.h:44
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:680
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
const ast_string_field name
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static int phone_profile_hash_fn(const void *obj, const int flags)
struct ao2_container * http_routes
static struct extension * delete_extension(struct extension *exten)
Definition of a URI handler.
Definition: http.h:100
#define MAX_ROUTE_BUCKETS
Definition: res_phoneprov.c:82
static PGresult * result
Definition: cel_pgsql.c:88
void ast_phoneprov_provider_unregister(char *provider_name)
Unegisters a config provider from phoneprov and frees its resources.
static int extension_delete_cb(void *obj, void *arg, void *data, int flags)
static struct user * unref_user(struct user *user)
const ast_string_field macaddress
static void delete_users(void)
Delete all users.
static int add_user_extension(struct user *user, struct extension *exten)
Add an extension to a user ordered by index/linenumber.
ast_phoneprov_load_users_cb load_users
Options provided by main asterisk program.
Definition: search.h:40
int error(const char *format,...)
Definition: utils/frame.c:999
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
#define PATH_MAX
Definition: asterisk.h:40
Generic container type.
#define MAX_USER_BUCKETS
Definition: res_phoneprov.c:83
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:56
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:598
structure to hold http routes (valid URIs, and the files they link to)
#define AST_VAR_LIST_TRAVERSE(head, var)
Definition: chanvars.h:49
const ast_string_field provider_name
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
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 ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
const char * ast_phoneprov_std_variable_lookup(enum ast_phoneprov_std_variables var)
Returns the string respresentation of a phoneprov standard variable.
static int reload(void)
Asterisk module definitions.
static void delete_file(struct phoneprov_file *file)
static snd_pcm_format_t format
Definition: chan_alsa.c:106
void ast_var_list_destroy(struct varshead *head)
Definition: chanvars.c:109
static void AST_VAR_LIST_INSERT_TAIL(struct varshead *head, struct ast_var_t *var)
Definition: chanvars.h:51
static struct phone_profile * find_profile(const char *name)
Return a phone profile looked up by name.
static struct prometheus_metrics_provider provider
Definition: bridges.c:178
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ast_var_t::@236 entries
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
void ast_phoneprov_delete_extension(char *provider_name, char *macaddress)
Deletes an extension.
static struct in_addr __ourip
for use in lookup_iface
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
const ast_string_field mime_type
#define AST_APP_ARG(name)
Define an application argument.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
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.
static struct test_val a
#define ao2_link(container, obj)
Definition: astobj2.h:1549