Asterisk - The Open Source Telephony Project  21.4.1
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
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",
220  [AST_PHONEPROV_STD_SERVER] = NULL,
221  [AST_PHONEPROV_STD_SERVER_PORT] = NULL,
222  [AST_PHONEPROV_STD_SERVER_IFACE] = NULL,
223  [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = "vmexten",
224  [AST_PHONEPROV_STD_EXTENSION_LENGTH] = "localextenlength",
225  [AST_PHONEPROV_STD_TZOFFSET] = NULL,
226  [AST_PHONEPROV_STD_DST_ENABLE] = NULL,
227  [AST_PHONEPROV_STD_DST_START_MONTH] = NULL,
228  [AST_PHONEPROV_STD_DST_START_MDAY] = NULL,
229  [AST_PHONEPROV_STD_DST_START_HOUR] = NULL,
230  [AST_PHONEPROV_STD_DST_END_MONTH] = NULL,
231  [AST_PHONEPROV_STD_DST_END_MDAY] = NULL,
232  [AST_PHONEPROV_STD_DST_END_HOUR] = NULL,
233 };
234 
235 /* Translate the standard variables to their phoneprov.conf [general] equivalents. */
236 static const char *pp_general_lookup[] = {
237  [AST_PHONEPROV_STD_MAC] = NULL,
238  [AST_PHONEPROV_STD_PROFILE] = "default_profile",
239  [AST_PHONEPROV_STD_USERNAME] = NULL,
240  [AST_PHONEPROV_STD_DISPLAY_NAME] = NULL,
241  [AST_PHONEPROV_STD_SECRET] = NULL,
242  [AST_PHONEPROV_STD_LABEL] = NULL,
243  [AST_PHONEPROV_STD_CALLERID] = NULL,
244  [AST_PHONEPROV_STD_TIMEZONE] = NULL,
245  [AST_PHONEPROV_STD_LINENUMBER] = NULL,
246  [AST_PHONEPROV_STD_LINEKEYS] = NULL,
247  [AST_PHONEPROV_STD_SERVER] = "serveraddr",
248  [AST_PHONEPROV_STD_SERVER_PORT] = "serverport",
249  [AST_PHONEPROV_STD_SERVER_IFACE] = "serveriface",
250  [AST_PHONEPROV_STD_VOICEMAIL_EXTEN] = NULL,
251  [AST_PHONEPROV_STD_EXTENSION_LENGTH] = NULL,
252  [AST_PHONEPROV_STD_TZOFFSET] = NULL,
253  [AST_PHONEPROV_STD_DST_ENABLE] = NULL,
254  [AST_PHONEPROV_STD_DST_START_MONTH] = NULL,
255  [AST_PHONEPROV_STD_DST_START_MDAY] = NULL,
256  [AST_PHONEPROV_STD_DST_START_HOUR] = NULL,
257  [AST_PHONEPROV_STD_DST_END_MONTH] = NULL,
258  [AST_PHONEPROV_STD_DST_END_MDAY] = NULL,
259  [AST_PHONEPROV_STD_DST_END_HOUR] = NULL,
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 */
268  AST_STRING_FIELD(provider_name);
269  );
270  ast_phoneprov_load_users_cb load_users;
271 };
272 struct ao2_container *providers;
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 {
289  AST_STRING_FIELD(name);
290  );
291  int index;
292  struct varshead *headp; /*!< List of variables to substitute into templates */
293  AST_LIST_ENTRY(extension) entry;
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 };
307 struct ao2_container *profiles;
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 */
318  AST_LIST_HEAD_NOLOCK(, extension) extensions;
319 };
320 struct ao2_container *users;
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)
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 {
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  }
382 
383  ao2_callback(providers, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
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 
503  ao2_callback(http_routes, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
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 {
546  return ao2_find(profiles, name, OBJ_SEARCH_KEY);
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))) {
564  ast_var_delete(var);
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 
578  ao2_callback(profiles, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
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 
698  ast_string_field_set(exten, name, name);
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);
703  delete_extension(exten);
704  return NULL;
705  }
706 
707  tmp = ast_var_find(exten->headp, variable_lookup[AST_PHONEPROV_STD_LINENUMBER]);
708  if (!tmp) {
709  AST_VAR_LIST_INSERT_TAIL(exten->headp,
710  ast_var_assign(variable_lookup[AST_PHONEPROV_STD_LINENUMBER], "1"));
711  exten->index = 1;
712  } else {
713  sscanf(tmp, "%d", &exten->index);
714  }
715 
716  if (!ast_var_find(exten->headp, variable_lookup[AST_PHONEPROV_STD_LINEKEYS])) {
717  AST_VAR_LIST_INSERT_TAIL(exten->headp,
718  ast_var_assign(variable_lookup[AST_PHONEPROV_STD_LINEKEYS], "1"));
719  }
720 
722  ast_var_find(vars, variable_lookup[AST_PHONEPROV_STD_TIMEZONE]));
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 {
737  return ao2_find(users, macaddress, OBJ_SEARCH_KEY);
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 
758  while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
759  exten = delete_extension(exten);
760  }
761 
762  if (user->profile) {
763  user->profile = unref_profile(user->profile);
764  }
765 
766  if (http_routes) {
767  ao2_callback(http_routes, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, routes_delete_cb, (void *)user->macaddress);
768  }
769 
771 }
772 
773 /*! \brief Delete all users */
774 static void delete_users(void)
775 {
776  if (!users) {
777  return;
778  }
779 
780  ao2_callback(users, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
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 
797  ast_string_field_set(user, macaddress, mac);
798  ast_string_field_set(user, provider_name, provider_name);
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 */
817  AST_VAR_LIST_TRAVERSE(user->profile->headp, pvar) {
818  if (ast_var_find(exten->headp, pvar->name)) {
819  continue;
820  }
821 
822  ast_str_substitute_variables_varshead(&str, 0, exten->headp, pvar->value);
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)) {
830  AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
831  } else {
832  struct extension *exten_iter;
833 
834  AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
835  if (exten->index < exten_iter->index) {
836  AST_LIST_INSERT_BEFORE_CURRENT(exten, entry);
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)) {
841  AST_LIST_INSERT_TAIL(&user->extensions, exten, 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 
860  AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
861  ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format);
862  build_route(pp_file, user->profile, user, ast_str_buffer(str));
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  char *newserver = NULL;
878  struct extension *exten_iter;
879  int len;
880  int fd;
881  struct ast_str *http_header;
882 
883  if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
884  ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
885  return 0;
886  }
887 
888  if (!(route = ao2_find(http_routes, uri, OBJ_SEARCH_KEY))) {
889  goto out404;
890  }
891 
892  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
893 
894  if (!route->user) { /* Static file */
895 
896  fd = open(path, O_RDONLY);
897  if (fd < 0) {
898  goto out500;
899  }
900 
901  len = lseek(fd, 0, SEEK_END);
902  lseek(fd, 0, SEEK_SET);
903  if (len < 0) {
904  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
905  close(fd);
906  goto out500;
907  }
908 
909  http_header = ast_str_create(80);
910  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
911  route->file->mime_type);
912 
913  ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
914 
915  close(fd);
916  route = unref_route(route);
917  return 0;
918  } else { /* Dynamic file */
919  struct ast_str *tmp;
920 
921  len = load_file(path, &file);
922  if (len < 0) {
923  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
924  if (file) {
925  ast_free(file);
926  }
927 
928  goto out500;
929  }
930 
931  if (!file) {
932  goto out500;
933  }
934 
935  if (!(tmp = ast_str_create(len))) {
936  if (file) {
937  ast_free(file);
938  }
939 
940  goto out500;
941  }
942 
943  /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
944  * the IP address we are listening on that the phone contacted for this config file */
945 
946  server = ast_var_find(AST_LIST_FIRST(&route->user->extensions)->headp,
947  variable_lookup[AST_PHONEPROV_STD_SERVER]);
948 
949  if (!server) {
950  union {
951  struct sockaddr sa;
952  struct sockaddr_in sa_in;
953  } name;
954  socklen_t namelen = sizeof(name.sa);
955  int res;
956 
957  if ((res = getsockname(ast_iostream_get_fd(ser->stream), &name.sa, &namelen))) {
958  ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
959  } else {
960  newserver = ast_strdupa(ast_inet_ntoa(name.sa_in.sin_addr));
961 
962  AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
963  AST_VAR_LIST_INSERT_TAIL(exten_iter->headp,
964  ast_var_assign(variable_lookup[AST_PHONEPROV_STD_SERVER], newserver));
965  }
966  }
967  }
968 
969  ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file);
970 
971  /* Do not retain dynamic SERVER address because next request from the phone might arrive on
972  * different interface IP address eg. if this is a multi-homed server on multiple subnets */
973  if (newserver) {
974  struct ast_var_t *varns;
975  AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
976  AST_LIST_TRAVERSE_SAFE_BEGIN(exten_iter->headp, varns, entries) {
977  if (!strcmp(variable_lookup[AST_PHONEPROV_STD_SERVER], ast_var_name(varns))) {
978  AST_LIST_REMOVE_CURRENT(entries);
979  ast_var_delete(varns);
980  }
981  }
983  }
984  }
985 
986  ast_free(file);
987 
988  http_header = ast_str_create(80);
989  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
990  route->file->mime_type);
991 
992  if (!(result = ast_str_create(512))) {
993  ast_log(LOG_ERROR, "Could not create result string!\n");
994  if (tmp) {
995  ast_free(tmp);
996  }
997  ast_free(http_header);
998  goto out500;
999  }
1000  ast_str_append(&result, 0, "%s", ast_str_buffer(tmp));
1001 
1002  ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
1003  ast_free(tmp);
1004 
1005  route = unref_route(route);
1006 
1007  return 0;
1008  }
1009 
1010 out404:
1011  ast_http_error(ser, 404, "Not Found", uri);
1012  return 0;
1013 
1014 out500:
1015  route = unref_route(route);
1016  ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
1017  return 0;
1018 }
1019 
1020 /*! \brief A dialplan function that can be used to print a string for each phoneprov user */
1021 static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
1022 {
1023  char *tmp;
1024  struct ao2_iterator i;
1025  struct user *user;
1026  struct ast_str *str;
1027  AST_DECLARE_APP_ARGS(args,
1028  AST_APP_ARG(string);
1029  AST_APP_ARG(exclude_mac);
1030  );
1031  AST_STANDARD_APP_ARGS(args, data);
1032 
1033  if (!(str = ast_str_create(16))) {
1034  return -1;
1035  }
1036 
1037  /* Fix data by turning %{ into ${ */
1038  while ((tmp = strstr(args.string, "%{")))
1039  *tmp = '$';
1040 
1041  i = ao2_iterator_init(users, 0);
1042  while ((user = ao2_iterator_next(&i))) {
1043  if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
1044  continue;
1045  }
1046  ast_str_substitute_variables_varshead(&str, len, AST_LIST_FIRST(&user->extensions)->headp, args.string);
1047  if (buf) {
1048  size_t slen = len;
1049  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1050  } else {
1051  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1052  }
1053  user = unref_user(user);
1054  }
1056 
1057  ast_free(str);
1058  return 0;
1059 }
1060 
1061 static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1062 {
1063  return pp_each_user_helper(chan, data, buf, NULL, len);
1064 }
1065 
1066 static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1067 {
1068  return pp_each_user_helper(chan, data, NULL, buf, len);
1069 }
1070 
1071 static struct ast_custom_function pp_each_user_function = {
1072  .name = "PP_EACH_USER",
1073  .read = pp_each_user_read,
1074  .read2 = pp_each_user_read2,
1075 };
1076 
1077 /*! \brief A dialplan function that can be used to output a template for each extension attached to a user */
1078 static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
1079 {
1080  struct user *user;
1081  struct extension *exten;
1082  char path[PATH_MAX];
1083  char *file;
1084  int filelen;
1085  struct ast_str *str;
1086  AST_DECLARE_APP_ARGS(args,
1087  AST_APP_ARG(mac);
1088  AST_APP_ARG(template);
1089  );
1090 
1091  AST_STANDARD_APP_ARGS(args, data);
1092 
1093  if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
1094  ast_log(LOG_WARNING, "PP_EACH_EXTENSION requires both a macaddress and template filename.\n");
1095  return 0;
1096  }
1097 
1098  if (!(user = find_user(args.mac))) {
1099  ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
1100  return 0;
1101  }
1102 
1103  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
1104  filelen = load_file(path, &file);
1105  if (filelen < 0) {
1106  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
1107  if (file) {
1108  ast_free(file);
1109  }
1110  return 0;
1111  }
1112 
1113  if (!file) {
1114  return 0;
1115  }
1116 
1117  if (!(str = ast_str_create(filelen))) {
1118  return 0;
1119  }
1120 
1121  AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
1122  ast_str_substitute_variables_varshead(&str, 0, exten->headp, file);
1123  if (buf) {
1124  size_t slen = len;
1125  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1126  } else {
1127  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1128  }
1129  }
1130 
1131  ast_free(file);
1132  ast_free(str);
1133 
1134  user = unref_user(user);
1135 
1136  return 0;
1137 }
1138 
1139 static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1140 {
1141  return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
1142 }
1143 
1144 static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1145 {
1146  return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
1147 }
1148 
1149 static struct ast_custom_function pp_each_extension_function = {
1150  .name = "PP_EACH_EXTENSION",
1151  .read = pp_each_extension_read,
1152  .read2 = pp_each_extension_read2,
1153 };
1154 
1155 #define FORMATS "%-20.20s %-40.40s %-30.30s\n"
1156 #define FORMATD "%-20.20s %-20.20s %-40.40s %-30.30s\n"
1157 static int route_list_cb(void *obj, void *arg, void *data, int flags)
1158 {
1159  int fd = *(int *)arg;
1160  struct http_route *route = obj;
1161 
1162  if (data && route->user) {
1163  ast_cli(fd, FORMATD, route->user->provider_name, route->profile->name, route->uri, route->file->template);
1164  }
1165  if (!data && !route->user) {
1166  ast_cli(fd, FORMATS, route->profile->name, route->uri, route->file->template);
1167  }
1168 
1169  return CMP_MATCH;
1170 }
1171 
1172 /*! \brief CLI command to list static and dynamic routes */
1173 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1174 {
1175  int fd = a->fd;
1176  switch(cmd) {
1177  case CLI_INIT:
1178  e->command = "phoneprov show routes";
1179  e->usage =
1180  "Usage: phoneprov show routes\n"
1181  " Lists all registered phoneprov http routes.\n";
1182  return NULL;
1183  case CLI_GENERATE:
1184  return NULL;
1185  }
1186 
1187  /* This currently iterates over routes twice, but it is the only place I've needed
1188  * to really separate static an dynamic routes, so I've just left it this way. */
1189  ast_cli(a->fd, "Static routes\n\n");
1190  ast_cli(a->fd, FORMATS, "Profile", "Relative URI", "Physical location");
1191 
1192  ao2_callback_data(http_routes, OBJ_NODATA | OBJ_MULTIPLE, route_list_cb, &fd, NULL);
1193 
1194  ast_cli(a->fd, "\nDynamic routes\n\n");
1195  ast_cli(a->fd, FORMATD, "Provider", "Profile", "Relative URI", "Template");
1196 
1197  ao2_callback_data(http_routes, OBJ_NODATA | OBJ_MULTIPLE, route_list_cb, &fd, (void *)1);
1198 
1199  return CLI_SUCCESS;
1200 }
1201 
1202 static struct ast_cli_entry pp_cli[] = {
1203  AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
1204 };
1205 
1206 static struct ast_http_uri phoneprovuri = {
1207  .callback = phoneprov_callback,
1208  .description = "Asterisk HTTP Phone Provisioning Tool",
1209  .uri = "phoneprov",
1210  .has_subtree = 1,
1211  .data = NULL,
1212  .key = __FILE__,
1213 };
1214 
1215 static struct varshead *get_defaults(void)
1216 {
1217  struct ast_config *phoneprov_cfg, *cfg = CONFIG_STATUS_FILEINVALID;
1218  const char *value;
1219  struct ast_variable *v;
1220  struct ast_var_t *var;
1221  struct ast_flags config_flags = { 0 };
1222  struct varshead *defaults = ast_var_list_create();
1223 
1224  if (!defaults) {
1225  ast_log(LOG_ERROR, "Unable to create default var list.\n");
1226  return NULL;
1227  }
1228 
1229  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))
1230  || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
1231  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
1232  ast_var_list_destroy(defaults);
1233  return NULL;
1234  }
1235 
1236  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_SERVER]);
1237  if (!value) {
1238  struct in_addr addr;
1239  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_SERVER_IFACE]);
1240  if (value) {
1241  lookup_iface(value, &addr);
1242  value = ast_inet_ntoa(addr);
1243  }
1244  }
1245  if (value) {
1246  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_SERVER], value);
1247  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1248  } else {
1249  ast_log(LOG_WARNING, "Unable to find a valid server address or name.\n");
1250  }
1251 
1252  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_SERVER_PORT]);
1253  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_SERVER_PORT], S_OR(value, "5060"));
1254  if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
1255  ast_config_destroy(cfg);
1256  }
1257  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1258 
1259  value = ast_variable_retrieve(phoneprov_cfg, "general", pp_general_lookup[AST_PHONEPROV_STD_PROFILE]);
1260  if (!value) {
1261  ast_log(LOG_ERROR, "Unable to load default profile.\n");
1262  ast_config_destroy(phoneprov_cfg);
1263  ast_var_list_destroy(defaults);
1264  return NULL;
1265  }
1266  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_PROFILE], value);
1267  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1268  ast_config_destroy(phoneprov_cfg);
1269 
1270  if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
1271  ast_log(LOG_ERROR, "Unable to load users.conf\n");
1272  ast_var_list_destroy(defaults);
1273  return NULL;
1274  }
1275 
1276  /* Go ahead and load global variables from users.conf so we can append to profiles */
1277  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
1278  if (!strcasecmp(v->name, pp_user_lookup[AST_PHONEPROV_STD_VOICEMAIL_EXTEN])) {
1279  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_VOICEMAIL_EXTEN], v->value);
1280  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1281  }
1282  if (!strcasecmp(v->name, pp_user_lookup[AST_PHONEPROV_STD_EXTENSION_LENGTH])) {
1283  var = ast_var_assign(variable_lookup[AST_PHONEPROV_STD_EXTENSION_LENGTH], v->value);
1284  AST_VAR_LIST_INSERT_TAIL(defaults, var);
1285  }
1286  }
1287  ast_config_destroy(cfg);
1288 
1289  return defaults;
1290 }
1291 
1292 static int load_users(void)
1293 {
1294  struct ast_config *cfg;
1295  char *cat;
1296  const char *value;
1297  struct ast_flags config_flags = { 0 };
1298  struct varshead *defaults = get_defaults();
1299 
1300  if (!defaults) {
1301  ast_log(LOG_WARNING, "Unable to load default variables.\n");
1302  return -1;
1303  }
1304 
1305  if (!(cfg = ast_config_load("users.conf", config_flags))
1306  || cfg == CONFIG_STATUS_FILEINVALID) {
1307  ast_log(LOG_WARNING, "Unable to load users.conf\n");
1308  ast_var_list_destroy(defaults);
1309  return -1;
1310  }
1311 
1312  cat = NULL;
1313  while ((cat = ast_category_browse(cfg, cat))) {
1314  const char *tmp;
1315  int i;
1316  struct ast_var_t *varx;
1317  struct ast_var_t *vard;
1318 
1319  if (strcasecmp(cat, "general") && strcasecmp(cat, "authentication")) {
1320  struct varshead *variables = ast_var_list_create();
1321 
1322  if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp))) {
1323  ast_var_list_destroy(variables);
1324  continue;
1325  }
1326 
1327  /* Transfer the standard variables */
1328  for (i = 0; i < AST_PHONEPROV_STD_VAR_LIST_LENGTH; i++) {
1329  if (pp_user_lookup[i]) {
1330  value = ast_variable_retrieve(cfg, cat, pp_user_lookup[i]);
1331  if (value) {
1332  varx = ast_var_assign(variable_lookup[i],
1333  value);
1334  AST_VAR_LIST_INSERT_TAIL(variables, varx);
1335  }
1336  }
1337  }
1338 
1339  if (!ast_var_find(variables, variable_lookup[AST_PHONEPROV_STD_MAC])) {
1340  ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
1341  ast_var_list_destroy(variables);
1342  continue;
1343  }
1344 
1345  /* Apply defaults */
1346  AST_VAR_LIST_TRAVERSE(defaults, vard) {
1347  if (ast_var_find(variables, vard->name)) {
1348  continue;
1349  }
1350  varx = ast_var_assign(vard->name, vard->value);
1351  AST_VAR_LIST_INSERT_TAIL(variables, varx);
1352  }
1353 
1354  ast_phoneprov_add_extension(SIPUSERS_PROVIDER_NAME, variables);
1355  }
1356  }
1357  ast_config_destroy(cfg);
1358  ast_var_list_destroy(defaults);
1359  return 0;
1360 }
1361 
1362 static int load_common(void)
1363 {
1364  struct ast_config *phoneprov_cfg;
1365  struct ast_flags config_flags = { 0 };
1366  char *cat;
1367 
1368  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))
1369  || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
1370  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
1371  return -1;
1372  }
1373 
1374  cat = NULL;
1375  while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
1376  if (!strcasecmp(cat, "general")) {
1377  continue;
1378  }
1379  build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
1380  }
1381  ast_config_destroy(phoneprov_cfg);
1382 
1383  if (!ao2_container_count(profiles)) {
1384  ast_log(LOG_ERROR, "There are no provisioning profiles in phoneprov.conf.\n");
1385  return -1;
1386  }
1387 
1388  return 0;
1389 }
1390 
1391 static int unload_module(void)
1392 {
1393  ast_http_uri_unlink(&phoneprovuri);
1394  ast_custom_function_unregister(&pp_each_user_function);
1395  ast_custom_function_unregister(&pp_each_extension_function);
1396  ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
1397 
1398  /* This cleans up the users.conf provider (called specifically for clarity) */
1399  ast_phoneprov_provider_unregister(SIPUSERS_PROVIDER_NAME);
1400 
1401  /* This cleans up the framework which also cleans up the providers. */
1402  delete_profiles();
1403  ao2_cleanup(profiles);
1404  profiles = NULL;
1405  delete_routes();
1406  delete_users();
1407  ao2_cleanup(http_routes);
1408  http_routes = NULL;
1409  ao2_cleanup(users);
1410  users = NULL;
1411  delete_providers();
1412  ao2_cleanup(providers);
1413  providers = NULL;
1414 
1415  return 0;
1416 }
1417 
1418 /*!
1419  * \brief Load the module
1420  *
1421  * Module loading including tests for configuration or dependencies.
1422  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
1423  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
1424  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
1425  * configuration file or other non-critical problem return
1426  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
1427  */
1428 static int load_module(void)
1429 {
1430  profiles = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAX_PROFILE_BUCKETS,
1431  phone_profile_hash_fn, NULL, phone_profile_cmp_fn);
1432  if (!profiles) {
1433  ast_log(LOG_ERROR, "Unable to allocate profiles container.\n");
1434  return AST_MODULE_LOAD_DECLINE;
1435  }
1436 
1437  http_routes = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAX_ROUTE_BUCKETS,
1438  http_route_hash_fn, NULL, http_route_cmp_fn);
1439  if (!http_routes) {
1440  ast_log(LOG_ERROR, "Unable to allocate routes container.\n");
1441  goto error;
1442  }
1443 
1444  if (load_common()) {
1445  ast_log(LOG_ERROR, "Unable to load provisioning profiles.\n");
1446  goto error;
1447  }
1448 
1449  users = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAX_USER_BUCKETS,
1450  user_hash_fn, NULL, user_cmp_fn);
1451  if (!users) {
1452  ast_log(LOG_ERROR, "Unable to allocate users container.\n");
1453  goto error;
1454  }
1455 
1457  MAX_PROVIDER_BUCKETS, phoneprov_provider_hash_fn, NULL, phoneprov_provider_cmp_fn);
1458  if (!providers) {
1459  ast_log(LOG_ERROR, "Unable to allocate providers container.\n");
1460  goto error;
1461  }
1462 
1463  /* Register ourselves as the provider for users.conf */
1464  if (ast_phoneprov_provider_register(SIPUSERS_PROVIDER_NAME, load_users)) {
1465  ast_log(LOG_WARNING, "Unable register users config provider. Others may succeed.\n");
1466  }
1467 
1468  ast_http_uri_link(&phoneprovuri);
1469 
1470  ast_custom_function_register(&pp_each_user_function);
1471  ast_custom_function_register(&pp_each_extension_function);
1472  ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
1473 
1474  return AST_MODULE_LOAD_SUCCESS;
1475 
1476 error:
1477  unload_module();
1478  return AST_MODULE_LOAD_DECLINE;
1479 }
1480 
1481 static int reload(void)
1482 {
1483  struct ao2_iterator i;
1484  struct phoneprov_provider *provider;
1485 
1486  /* Clean everything except the providers */
1487  delete_routes();
1488  delete_users();
1489  delete_profiles();
1490 
1491  /* Reload the profiles */
1492  if (load_common()) {
1493  ast_log(LOG_ERROR, "Unable to reload provisioning profiles.\n");
1494  unload_module();
1495  return AST_MODULE_LOAD_DECLINE;
1496  }
1497 
1498  /* For each provider, reload the users */
1499  ao2_lock(providers);
1500  i = ao2_iterator_init(providers, 0);
1501  for(; (provider = ao2_iterator_next(&i)); ao2_ref(provider, -1)) {
1502  if (provider->load_users()) {
1503  ast_log(LOG_ERROR, "Unable to load provider '%s' users. Reload aborted.\n", provider->provider_name);
1504  continue;
1505  }
1506  }
1508  ao2_unlock(providers);
1509 
1510  return AST_MODULE_LOAD_SUCCESS;
1511 }
1512 
1513 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",
1514  .support_level = AST_MODULE_SUPPORT_EXTENDED,
1515  .load = load_module,
1516  .unload = unload_module,
1517  .reload = reload,
1518  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1519  .requires = "http",
1520 );
1521 
1522 /**** Public API for register/unregister, set defaults, and add extension. ****/
1523 
1524 const char *ast_phoneprov_std_variable_lookup(enum ast_phoneprov_std_variables var)
1525 {
1526  if (var >= AST_PHONEPROV_STD_VAR_LIST_LENGTH) {
1527  return NULL;
1528  }
1529 
1530  return variable_lookup[var];
1531 }
1532 
1533 int ast_phoneprov_provider_register(char *provider_name,
1534  ast_phoneprov_load_users_cb load_users)
1535 {
1536  struct phoneprov_provider *provider;
1537 
1538  if (ast_strlen_zero(provider_name)) {
1539  ast_log(LOG_ERROR, "Provider name can't be empty.\n");
1540  return -1;
1541  }
1542 
1543  if (!providers) {
1544  ast_log(LOG_WARNING, "Provider '%s' cannot be registered: res_phoneprov not loaded.\n", provider_name);
1545  return -1;
1546  }
1547 
1548  provider = find_provider(provider_name);
1549  if (provider) {
1550  ast_log(LOG_ERROR, "There is already a provider registered named '%s'.\n", provider_name);
1551  ao2_ref(provider, -1);
1552  return -1;
1553  }
1554 
1555  provider = ao2_alloc(sizeof(struct phoneprov_provider), provider_destructor);
1556  if (!provider) {
1557  ast_log(LOG_ERROR, "Unable to allocate sufficient memory for provider '%s'.\n", provider_name);
1558  return -1;
1559  }
1560 
1561  if (ast_string_field_init(provider, 32)) {
1562  ao2_ref(provider, -1);
1563  ast_log(LOG_ERROR, "Unable to allocate sufficient memory for provider '%s' stringfields.\n", provider_name);
1564  return -1;
1565  }
1566 
1567  ast_string_field_set(provider, provider_name, provider_name);
1568  provider->load_users = load_users;
1569 
1570  ao2_link(providers, provider);
1571  ao2_ref(provider, -1);
1572 
1573  if (provider->load_users()) {
1574  ast_log(LOG_ERROR, "Unable to load provider '%s' users. Register aborted.\n", provider_name);
1575  ast_phoneprov_provider_unregister(provider_name);
1576  return -1;
1577  }
1578 
1579  return 0;
1580 }
1581 
1582 static int extensions_delete_cb(void *obj, void *arg, int flags)
1583 {
1584  char *provider_name = arg;
1585  struct user *user = obj;
1586  if (strcmp(user->provider_name, provider_name)) {
1587  return 0;
1588  }
1589  return CMP_MATCH;
1590 }
1591 
1592 static int extension_delete_cb(void *obj, void *arg, void *data, int flags)
1593 {
1594  struct user *user = obj;
1595  char *provider_name = data;
1596  char *macaddress = arg;
1598  if (!strcmp(user->provider_name, provider_name) && !strcasecmp(user->macaddress, macaddress)) {
1599  return CMP_MATCH;
1600  }
1601  return 0;
1602 }
1603 
1604 void ast_phoneprov_delete_extension(char *provider_name, char *macaddress)
1605 {
1606  if (!users) {
1607  return;
1608  }
1609 
1610  ao2_callback_data(users, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
1611  extension_delete_cb, macaddress, provider_name);
1612 }
1613 
1614 void ast_phoneprov_delete_extensions(char *provider_name)
1615 {
1616  if (!users) {
1617  return;
1618  }
1619 
1620  ao2_callback(users, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, extensions_delete_cb, provider_name);
1621 }
1622 
1623 void ast_phoneprov_provider_unregister(char *provider_name)
1624 {
1625  if (!providers) {
1626  return;
1627  }
1628 
1629  ast_phoneprov_delete_extensions(provider_name);
1630  ao2_find(providers, provider_name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
1631 }
1632 
1633 int ast_phoneprov_add_extension(char *provider_name, struct varshead *vars)
1634 {
1635  RAII_VAR(struct phoneprov_provider *, provider, NULL, ao2_cleanup);
1636  RAII_VAR(struct user *, user, NULL, ao2_cleanup);
1637  RAII_VAR(struct phone_profile *, profile, NULL, ao2_cleanup);
1638  struct extension *exten;
1639  char *profile_name;
1640  char *mac;
1641  char *username;
1642 
1643  if (ast_strlen_zero(provider_name)) {
1644  ast_log(LOG_ERROR, "Provider name can't be empty.\n");
1645  return -1;
1646  }
1647  if (!vars) {
1648  ast_log(LOG_ERROR, "Variable list can't be empty.\n");
1649  return -1;
1650  }
1651 
1652  username = ast_var_find(vars, variable_lookup[AST_PHONEPROV_STD_USERNAME]);
1653  if (!username) {
1654  ast_log(LOG_ERROR, "Extension name can't be empty.\n");
1655  return -1;
1656  }
1657 
1658  mac = ast_var_find(vars, variable_lookup[AST_PHONEPROV_STD_MAC]);
1659  if (!mac) {
1660  ast_log(LOG_ERROR, "MAC Address can't be empty.\n");
1661  return -1;
1662  }
1663 
1664  provider = find_provider(provider_name);
1665  if (!provider) {
1666  ast_log(LOG_ERROR, "Provider '%s' wasn't found in the registry.\n", provider_name);
1667  return -1;
1668  }
1669 
1670  profile_name = ast_var_find(vars,
1671  variable_lookup[AST_PHONEPROV_STD_PROFILE]);
1672  if (!profile_name) {
1673  ast_log(LOG_ERROR, "No profile could be found for user '%s' - skipping.\n", username);
1674  return -1;
1675  }
1676  if (!(profile = find_profile(profile_name))) {
1677  ast_log(LOG_ERROR, "Could not look up profile '%s' - skipping.\n", profile_name);
1678  return -1;
1679  }
1680 
1681  if (!(user = find_user(mac))) {
1682 
1683  if (!(user = build_user(mac, profile, provider_name))) {
1684  ast_log(LOG_ERROR, "Could not create user for '%s' - skipping\n", mac);
1685  return -1;
1686  }
1687 
1688  if (!(exten = build_extension(username, vars))) {
1689  ast_log(LOG_ERROR, "Could not create extension for '%s' - skipping\n", user->macaddress);
1690  return -1;
1691  }
1692 
1693  if (add_user_extension(user, exten)) {
1694  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1695  exten = delete_extension(exten);
1696  return -1;
1697  }
1698 
1699  if (build_user_routes(user)) {
1700  ast_log(LOG_WARNING, "Could not create http routes for '%s' - skipping\n", user->macaddress);
1701  return -1;
1702  }
1703  ao2_link(users, user);
1704 
1705  } else {
1706  if (strcmp(provider_name, user->provider_name)) {
1707  ast_log(LOG_ERROR, "MAC address '%s' was already added by provider '%s' - skipping\n", user->macaddress, user->provider_name);
1708  return -1;
1709  }
1710 
1711  if (!(exten = build_extension(username, vars))) {
1712  ast_log(LOG_ERROR, "Could not create extension for '%s' - skipping\n", user->macaddress);
1713  return -1;
1714  }
1715 
1716  if (add_user_extension(user, exten)) {
1717  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1718  exten = delete_extension(exten);
1719  return -1;
1720  }
1721  }
1722 
1723  return 0;
1724 }
const ast_string_field staticdir
const char * name
Definition: pbx.h:119
struct ast_variable * next
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.
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:421
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
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:651
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:1101
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
const ast_string_field format
#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
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:676
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
const char * ast_http_ftype2mtype(const char *ftype) attribute_pure
Return mime type based on extension.
Definition: http.c:206
const ast_string_field template
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
Structure for variables, used for configurations and for channel variables.
static void delete_profiles(void)
Delete all phone profiles, freeing their memory.
static void delete_routes(void)
Delete all http routes, freeing their memory.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:708
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:432
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:1139
#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's file descriptor.
Definition: iostream.c:85
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
static int build_user_routes(struct user *user)
Add an http route for dynamic files attached to the profile of the user.
static void delete_providers(void)
Delete all providers.
const ast_string_field uri
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:3326
struct phoneprov_file * file
struct phone_profile::@454 static_files
structure to hold file data
list of users found in the config file
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:459
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
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.
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.
#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:1113
Configuration File Parser.
Support for Private Asterisk HTTP Servers.
struct varshead * headp
structure to hold config providers
#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: utils.c:2167
static int load_module(void)
Load the module.
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
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:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
Access Control of various sorts.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
int tm_mon
Definition: localtime.h:40
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
structure to hold extensions
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
struct phone_profile * profile
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
int tm_mday
Definition: localtime.h:39
const ast_string_field name
static int load_file(const char *filename, char **ret)
Read a TEXT file into a string and return the length.
Core PBX routines and definitions.
describes a server instance
Definition: tcptls.h:150
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
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
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#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
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: utils.c:2199
Support for dynamic strings.
Definition: strings.h:623
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
const ast_string_field provider_name
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: utils.c:928
char * command
Definition: cli.h:186
structure to hold phone profiles read from phoneprov.conf
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.
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:555
Structure used to handle boolean flags.
Definition: utils.h:199
struct ast_iostream * stream
Definition: tcptls.h:161
const char * usage
Definition: cli.h:177
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
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:425
#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
Definition of a URI handler.
Definition: http.h:102
void ast_phoneprov_provider_unregister(char *provider_name)
Unegisters a config provider from phoneprov and frees its resources.
static int load_users(void)
Callback that loads the users from phoneprov sections.
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.
Options provided by main asterisk program.
Definition: search.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
Generic container type.
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:58
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
structure to hold http routes (valid URIs, and the files they link to)
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.
Asterisk module definitions.
#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:941
static struct phone_profile * find_profile(const char *name)
Return a phone profile looked up by name.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct phone_profile::@455 dynamic_files
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
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'ed dynamic length string.
Definition: strings.h:659
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:521
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.
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532