Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


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  *
6  * Mark Spencer <markster@digium.com>
7  * Matthew Brooks <mbrooks@digium.com>
8  * Terry Wilson <twilson@digium.com>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20 
21 /*! \file
22  *
23  * \brief Phone provisioning application for the asterisk internal http server
24  *
25  * \author Matthew Brooks <mbrooks@digium.com>
26  * \author Terry Wilson <twilson@digium.com>
27  */
28 
29 /*** MODULEINFO
30  <support_level>extended</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #ifdef SOLARIS
39 #include <sys/sockio.h>
40 #endif
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
42 
43 #include "asterisk/channel.h"
44 #include "asterisk/file.h"
45 #include "asterisk/paths.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/cli.h"
48 #include "asterisk/module.h"
49 #include "asterisk/http.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/app.h"
52 #include "asterisk/strings.h"
53 #include "asterisk/stringfields.h"
54 #include "asterisk/options.h"
55 #include "asterisk/config.h"
56 #include "asterisk/acl.h"
57 #include "asterisk/astobj2.h"
58 #include "asterisk/ast_version.h"
59 
60 #ifdef LOW_MEMORY
61 #define MAX_PROFILE_BUCKETS 1
62 #define MAX_ROUTE_BUCKETS 1
63 #define MAX_USER_BUCKETS 1
64 #else
65 #define MAX_PROFILE_BUCKETS 17
66 #define MAX_ROUTE_BUCKETS 563
67 #define MAX_USER_BUCKETS 563
68 #endif /* LOW_MEMORY */
69 
70 #define VAR_BUF_SIZE 4096
71 
72 /*** DOCUMENTATION
73  <function name="PP_EACH_EXTENSION" language="en_US">
74  <synopsis>
75  Execute specified template for each extension.
76  </synopsis>
77  <syntax>
78  <parameter name="mac" required="true" />
79  <parameter name="template" required="true" />
80  </syntax>
81  <description>
82  <para>Output the specified template for each extension associated with the specified MAC address.</para>
83  </description>
84  </function>
85  <function name="PP_EACH_USER" language="en_US">
86  <synopsis>
87  Generate a string for each phoneprov user.
88  </synopsis>
89  <syntax>
90  <parameter name="string" required="true" />
91  <parameter name="exclude_mac" required="true" />
92  </syntax>
93  <description>
94  <para>Pass in a string, with phoneprov variables you want substituted in the format of
95  %{VARNAME}, and you will get the string rendered for each user in phoneprov
96  excluding ones with MAC address <replaceable>exclude_mac</replaceable>. Probably not
97  useful outside of res_phoneprov.</para>
98  <para>Example: ${PP_EACH_USER(&lt;item&gt;&lt;fn&gt;%{DISPLAY_NAME}&lt;/fn&gt;&lt;/item&gt;|${MAC})</para>
99  </description>
100  </function>
101  ***/
102 
103 /*! \brief for use in lookup_iface */
104 static struct in_addr __ourip = { .s_addr = 0x00000000, };
105 
106 /* \note This enum and the pp_variable_list must be in the same order or
107  * bad things happen! */
118  PP_VAR_LIST_LENGTH, /* This entry must always be the last in the list */
119 };
120 
121 /*! \brief Lookup table to translate between users.conf property names and
122  * variables for use in phoneprov templates */
123 static const struct pp_variable_lookup {
125  const char * const user_var;
126  const char * const template_var;
127 } pp_variable_list[] = {
128  { PP_MACADDRESS, "macaddress", "MAC" },
129  { PP_USERNAME, "username", "USERNAME" },
130  { PP_FULLNAME, "fullname", "DISPLAY_NAME" },
131  { PP_SECRET, "secret", "SECRET" },
132  { PP_LABEL, "label", "LABEL" },
133  { PP_CALLERID, "cid_number", "CALLERID" },
134  { PP_TIMEZONE, "timezone", "TIMEZONE" },
135  { PP_LINENUMBER, "linenumber", "LINE" },
136  { PP_LINEKEYS, "linekeys", "LINEKEYS" },
137 };
138 
139 /*! \brief structure to hold file data */
142  AST_STRING_FIELD(format); /*!< After variable substitution, becomes route->uri */
143  AST_STRING_FIELD(template); /*!< Template/physical file location */
144  AST_STRING_FIELD(mime_type);/*!< Mime-type of the file */
145  );
147 };
148 
149 /*! \brief structure to hold phone profiles read from phoneprov.conf */
152  AST_STRING_FIELD(name); /*!< Name of phone profile */
153  AST_STRING_FIELD(default_mime_type); /*!< Default mime type if it isn't provided */
154  AST_STRING_FIELD(staticdir); /*!< Subdirectory that static files are stored in */
155  );
156  struct varshead *headp; /*!< List of variables set with 'setvar' in phoneprov.conf */
157  AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files; /*!< List of static files */
158  AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files; /*!< List of dynamic files */
159 };
160 
161 struct extension {
164  );
165  int index;
166  struct varshead *headp; /*!< List of variables to substitute into templates */
167  AST_LIST_ENTRY(extension) entry;
168 };
170 /*! \brief structure to hold users read from users.conf */
171 struct user {
173  AST_STRING_FIELD(macaddress); /*!< Mac address of user's phone */
174  );
175  struct phone_profile *profile; /*!< Profile the phone belongs to */
176  AST_LIST_HEAD_NOLOCK(, extension) extensions;
177 };
179 /*! \brief structure to hold http routes (valid URIs, and the files they link to) */
180 struct http_route {
182  AST_STRING_FIELD(uri); /*!< The URI requested */
183  );
184  struct phoneprov_file *file; /*!< The file that links to the URI */
185  struct user *user; /*!< The user that has variables to substitute into the file
186  * NULL in the case of a static route */
187 };
189 static struct ao2_container *profiles;
190 static struct ao2_container *http_routes;
191 static struct ao2_container *users;
193 static char global_server[80] = ""; /*!< Server to substitute into templates */
194 static char global_serverport[6] = ""; /*!< Server port to substitute into templates */
195 static char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */
197 /*! \brief List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH */
198 static struct varshead global_variables;
200 
201 /* iface is the interface (e.g. eth0); address is the return value */
202 static int lookup_iface(const char *iface, struct in_addr *address)
203 {
204  int mysock, res = 0;
205  struct ifreq ifr;
206  struct sockaddr_in *sin;
207 
208  memset(&ifr, 0, sizeof(ifr));
209  ast_copy_string(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
210 
211  mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
212  if (mysock < 0) {
213  ast_log(LOG_ERROR, "Failed to create socket: %s\n", strerror(errno));
214  return -1;
215  }
216 
217  res = ioctl(mysock, SIOCGIFADDR, &ifr);
218 
219  close(mysock);
220 
221  if (res < 0) {
222  ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
223  memcpy(address, &__ourip, sizeof(__ourip));
224  return -1;
225  } else {
226  sin = (struct sockaddr_in *)&ifr.ifr_addr;
227  memcpy(address, &sin->sin_addr, sizeof(*address));
228  return 0;
229  }
230 }
231 
232 static struct phone_profile *unref_profile(struct phone_profile *prof)
233 {
234  ao2_ref(prof, -1);
235 
236  return NULL;
237 }
238 
239 /*! \brief Return a phone profile looked up by name */
240 static struct phone_profile *find_profile(const char *name)
241 {
242  struct phone_profile tmp = {
243  .name = name,
244  };
245 
246  return ao2_find(profiles, &tmp, OBJ_POINTER);
247 }
248 
249 static int profile_hash_fn(const void *obj, const int flags)
250 {
251  const struct phone_profile *profile = obj;
252 
253  return ast_str_case_hash(profile->name);
254 }
255 
256 static int profile_cmp_fn(void *obj, void *arg, int flags)
257 {
258  const struct phone_profile *profile1 = obj, *profile2 = arg;
259 
260  return !strcasecmp(profile1->name, profile2->name) ? CMP_MATCH | CMP_STOP : 0;
261 }
262 
263 static void delete_file(struct phoneprov_file *file)
264 {
266  free(file);
267 }
268 
269 static void profile_destructor(void *obj)
270 {
271  struct phone_profile *profile = obj;
272  struct phoneprov_file *file;
273  struct ast_var_t *var;
274 
275  while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry)))
276  delete_file(file);
277 
278  while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry)))
279  delete_file(file);
280 
281  while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries)))
283 
284  ast_free(profile->headp);
286 }
287 
288 static struct http_route *unref_route(struct http_route *route)
289 {
290  ao2_ref(route, -1);
291 
292  return NULL;
293 }
294 
295 static int routes_hash_fn(const void *obj, const int flags)
296 {
297  const struct http_route *route = obj;
298 
299  return ast_str_case_hash(route->uri);
300 }
301 
302 static int routes_cmp_fn(void *obj, void *arg, int flags)
303 {
304  const struct http_route *route1 = obj, *route2 = arg;
305 
306  return !strcasecmp(route1->uri, route2->uri) ? CMP_MATCH | CMP_STOP : 0;
307 }
308 
309 static void route_destructor(void *obj)
310 {
311  struct http_route *route = obj;
312 
314 }
315 
316 /*! \brief Read a TEXT file into a string and return the length */
317 static int load_file(const char *filename, char **ret)
318 {
319  int len = 0;
320  FILE *f;
321 
322  if (!(f = fopen(filename, "r"))) {
323  *ret = NULL;
324  return -1;
325  }
326 
327  fseek(f, 0, SEEK_END);
328  len = ftell(f);
329  fseek(f, 0, SEEK_SET);
330  if (!(*ret = ast_malloc(len + 1)))
331  return -2;
332 
333  if (len != fread(*ret, sizeof(char), len, f)) {
334  free(*ret);
335  *ret = NULL;
336  return -3;
337  }
338 
339  fclose(f);
340 
341  (*ret)[len] = '\0';
342 
343  return len;
344 }
345 
346 /*! \brief Set all timezone-related variables based on a zone (i.e. America/New_York)
347  \param headp pointer to list of user variables
348  \param zone A time zone. NULL sets variables based on timezone of the machine
349 */
350 static void set_timezone_variables(struct varshead *headp, const char *zone)
351 {
352  time_t utc_time;
353  int dstenable;
354  time_t dststart;
355  time_t dstend;
356  struct ast_tm tm_info;
357  int tzoffset;
358  char buffer[21];
359  struct ast_var_t *var;
360  struct timeval when;
361 
362  time(&utc_time);
363  ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone);
364  snprintf(buffer, sizeof(buffer), "%d", tzoffset);
365  var = ast_var_assign("TZOFFSET", buffer);
366  if (var)
367  AST_LIST_INSERT_TAIL(headp, var, entries);
368 
369  if (!dstenable)
370  return;
371 
372  if ((var = ast_var_assign("DST_ENABLE", "1")))
373  AST_LIST_INSERT_TAIL(headp, var, entries);
374 
375  when.tv_sec = dststart;
376  ast_localtime(&when, &tm_info, zone);
377 
378  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1);
379  if ((var = ast_var_assign("DST_START_MONTH", buffer)))
380  AST_LIST_INSERT_TAIL(headp, var, entries);
381 
382  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
383  if ((var = ast_var_assign("DST_START_MDAY", buffer)))
384  AST_LIST_INSERT_TAIL(headp, var, entries);
385 
386  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
387  if ((var = ast_var_assign("DST_START_HOUR", buffer)))
388  AST_LIST_INSERT_TAIL(headp, var, entries);
389 
390  when.tv_sec = dstend;
391  ast_localtime(&when, &tm_info, zone);
392 
393  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1);
394  if ((var = ast_var_assign("DST_END_MONTH", buffer)))
395  AST_LIST_INSERT_TAIL(headp, var, entries);
396 
397  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
398  if ((var = ast_var_assign("DST_END_MDAY", buffer)))
399  AST_LIST_INSERT_TAIL(headp, var, entries);
400 
401  snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
402  if ((var = ast_var_assign("DST_END_HOUR", buffer)))
403  AST_LIST_INSERT_TAIL(headp, var, entries);
404 }
405 
406 /*! \brief Callback that is executed everytime an http request is received by this module */
407 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)
408 {
409  struct http_route *route;
410  struct http_route search_route = {
411  .uri = uri,
412  };
413  struct ast_str *result;
414  char path[PATH_MAX];
415  char *file = NULL;
416  int len;
417  int fd;
418  struct ast_str *http_header;
419 
420  if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
421  ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
422  return -1;
423  }
424 
425  if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
426  goto out404;
427  }
428 
429  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
430 
431  if (!route->user) { /* Static file */
432 
433  fd = open(path, O_RDONLY);
434  if (fd < 0) {
435  goto out500;
436  }
437 
438  len = lseek(fd, 0, SEEK_END);
439  lseek(fd, 0, SEEK_SET);
440  if (len < 0) {
441  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
442  close(fd);
443  goto out500;
444  }
445 
446  http_header = ast_str_create(80);
447  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
448  route->file->mime_type);
449 
450  ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
451 
452  close(fd);
453  route = unref_route(route);
454  return 0;
455  } else { /* Dynamic file */
456  struct ast_str *tmp;
457 
458  len = load_file(path, &file);
459  if (len < 0) {
460  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
461  if (file) {
462  ast_free(file);
463  }
464 
465  goto out500;
466  }
467 
468  if (!file) {
469  goto out500;
470  }
471 
472  if (!(tmp = ast_str_create(len))) {
473  if (file) {
474  ast_free(file);
475  }
476 
477  goto out500;
478  }
479 
480  /* Unless we are overridden by serveriface or serveraddr, we set the SERVER variable to
481  * the IP address we are listening on that the phone contacted for this config file */
482  if (ast_strlen_zero(global_server)) {
483  union {
484  struct sockaddr sa;
485  struct sockaddr_in sa_in;
486  } name;
487  socklen_t namelen = sizeof(name.sa);
488  int res;
489 
490  if ((res = getsockname(ser->fd, &name.sa, &namelen))) {
491  ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
492  } else {
493  struct ast_var_t *var;
494  struct extension *exten_iter;
495 
496  if ((var = ast_var_assign("SERVER", ast_inet_ntoa(name.sa_in.sin_addr)))) {
497  AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
498  AST_LIST_INSERT_TAIL(exten_iter->headp, var, entries);
499  }
500  }
501  }
502  }
503 
504  ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file);
505 
506  if (file) {
507  ast_free(file);
508  }
509 
510  http_header = ast_str_create(80);
511  ast_str_set(&http_header, 0, "Content-type: %s\r\n",
512  route->file->mime_type);
513 
514  if (!(result = ast_str_create(512))) {
515  ast_log(LOG_ERROR, "Could not create result string!\n");
516  if (tmp) {
517  ast_free(tmp);
518  }
519  ast_free(http_header);
520  goto out500;
521  }
522  ast_str_append(&result, 0, "%s", ast_str_buffer(tmp));
523 
524  ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
525  if (tmp) {
526  ast_free(tmp);
527  }
528 
529  route = unref_route(route);
530 
531  return 0;
532  }
533 
534 out404:
535  ast_http_error(ser, 404, "Not Found", "Nothing to see here. Move along.");
536  return -1;
537 
538 out500:
539  route = unref_route(route);
540  ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
541  return -1;
542 }
544 /*! \brief Build a route structure and add it to the list of available http routes
545  \param pp_file File to link to the route
546  \param user User to link to the route (NULL means static route)
547  \param uri URI of the route
548 */
549 static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
550 {
551  struct http_route *route;
552 
553  if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
554  return;
555  }
556 
557  if (ast_string_field_init(route, 32)) {
558  ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
559  route = unref_route(route);
560  return;
561  }
562 
563  ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
564  route->user = user;
565  route->file = pp_file;
566 
567  ao2_link(http_routes, route);
568 
569  route = unref_route(route);
570 }
571 
572 /*! \brief Build a phone profile and add it to the list of phone profiles
573  \param name the name of the profile
574  \param v ast_variable from parsing phoneprov.conf
575 */
576 static void build_profile(const char *name, struct ast_variable *v)
577 {
578  struct phone_profile *profile;
579  struct ast_var_t *var;
580 
581  if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
582  return;
583  }
584 
585  if (ast_string_field_init(profile, 32)) {
586  profile = unref_profile(profile);
587  return;
588  }
589 
590  if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
591  profile = unref_profile(profile);
592  return;
593  }
594 
597 
598  ast_string_field_set(profile, name, name);
599  for (; v; v = v->next) {
600  if (!strcasecmp(v->name, "mime_type")) {
601  ast_string_field_set(profile, default_mime_type, v->value);
602  } else if (!strcasecmp(v->name, "setvar")) {
603  struct ast_var_t *variable;
604  char *value_copy = ast_strdupa(v->value);
605 
607  AST_APP_ARG(varname);
608  AST_APP_ARG(varval);
609  );
610 
611  AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
612  do {
613  if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
614  break;
615  args.varname = ast_strip(args.varname);
616  args.varval = ast_strip(args.varval);
617  if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
618  break;
619  if ((variable = ast_var_assign(args.varname, args.varval)))
620  AST_LIST_INSERT_TAIL(profile->headp, variable, entries);
621  } while (0);
622  } else if (!strcasecmp(v->name, "staticdir")) {
623  ast_string_field_set(profile, staticdir, v->value);
624  } else {
625  struct phoneprov_file *pp_file;
626  char *file_extension;
627  char *value_copy = ast_strdupa(v->value);
628 
630  AST_APP_ARG(filename);
631  AST_APP_ARG(mimetype);
632  );
633 
634  if (!(pp_file = ast_calloc_with_stringfields(1, struct phoneprov_file, 32))) {
635  profile = unref_profile(profile);
636  return;
637  }
638 
639  if ((file_extension = strrchr(pp_file->format, '.')))
640  file_extension++;
641 
642  AST_STANDARD_APP_ARGS(args, value_copy);
643 
644  /* Mime type order of preference
645  * 1) Specific mime-type defined for file in profile
646  * 2) Mime determined by extension
647  * 3) Default mime type specified in profile
648  * 4) text/plain
649  */
650  ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype,
651  (S_OR(S_OR(ast_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
652 
653  if (!strcasecmp(v->name, "static_file")) {
654  ast_string_field_set(pp_file, format, args.filename);
655  ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
656  AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
657  /* Add a route for the static files, as their filenames won't change per-user */
658  build_route(pp_file, NULL, NULL);
659  } else {
660  ast_string_field_set(pp_file, format, v->name);
661  ast_string_field_set(pp_file, template, args.filename);
662  AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
663  }
664  }
665  }
666 
667  /* Append the global variables to the variables list for this profile.
668  * This is for convenience later, when we need to provide a single
669  * variable list for use in substitution. */
670  ast_mutex_lock(&globals_lock);
671  AST_LIST_TRAVERSE(&global_variables, var, entries) {
672  struct ast_var_t *new_var;
673  if ((new_var = ast_var_assign(var->name, var->value))) {
674  AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
675  }
676  }
677  ast_mutex_unlock(&globals_lock);
679  ao2_link(profiles, profile);
680 
681  profile = unref_profile(profile);
682 }
683 
684 static struct extension *delete_extension(struct extension *exten)
685 {
686  struct ast_var_t *var;
687  while ((var = AST_LIST_REMOVE_HEAD(exten->headp, entries))) {
688  ast_var_delete(var);
689  }
690  ast_free(exten->headp);
693  ast_free(exten);
694 
695  return NULL;
696 }
697 
698 static struct extension *build_extension(struct ast_config *cfg, const char *name)
699 {
700  struct extension *exten;
701  struct ast_var_t *var;
702  const char *tmp;
703  int i;
704 
705  if (!(exten = ast_calloc_with_stringfields(1, struct extension, 32))) {
706  return NULL;
707  }
708 
709  ast_string_field_set(exten, name, name);
710 
711  if (!(exten->headp = ast_calloc(1, sizeof(*exten->headp)))) {
712  ast_free(exten);
713  exten = NULL;
714  return NULL;
715  }
716 
717  for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
718  tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
719 
720  /* If we didn't get a USERNAME variable, set it to the user->name */
721  if (i == PP_USERNAME && !tmp) {
722  if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, exten->name))) {
723  AST_LIST_INSERT_TAIL(exten->headp, var, entries);
724  }
725  continue;
726  } else if (i == PP_TIMEZONE) {
727  /* perfectly ok if tmp is NULL, will set variables based on server's time zone */
728  set_timezone_variables(exten->headp, tmp);
729  } else if (i == PP_LINENUMBER) {
730  if (!tmp) {
731  tmp = "1";
732  }
733  exten->index = atoi(tmp);
734  } else if (i == PP_LINEKEYS) {
735  if (!tmp) {
736  tmp = "1";
737  }
738  }
739 
740  if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp))) {
741  AST_LIST_INSERT_TAIL(exten->headp, var, entries);
742  }
743  }
744 
745  if (!ast_strlen_zero(global_server)) {
746  if ((var = ast_var_assign("SERVER", global_server)))
747  AST_LIST_INSERT_TAIL(exten->headp, var, entries);
748  }
749 
750  if (!ast_strlen_zero(global_serverport)) {
751  if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
753  }
754 
755  return exten;
756 }
757 
758 static struct user *unref_user(struct user *user)
759 {
760  ao2_ref(user, -1);
761 
762  return NULL;
763 }
764 
765 /*! \brief Return a user looked up by name */
766 static struct user *find_user(const char *macaddress)
767 {
768  struct user tmp = {
770  };
771 
772  return ao2_find(users, &tmp, OBJ_POINTER);
773 }
774 
775 static int users_hash_fn(const void *obj, const int flags)
776 {
777  const struct user *user = obj;
778 
779  return ast_str_case_hash(user->macaddress);
780 }
781 
782 static int users_cmp_fn(void *obj, void *arg, int flags)
783 {
784  const struct user *user1 = obj, *user2 = arg;
785 
786  return !strcasecmp(user1->macaddress, user2->macaddress) ? CMP_MATCH | CMP_STOP : 0;
787 }
788 
789 /*! \brief Free all memory associated with a user */
790 static void user_destructor(void *obj)
791 {
792  struct user *user = obj;
793  struct extension *exten;
794 
795  while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
796  exten = delete_extension(exten);
797  }
798 
799  if (user->profile) {
800  user->profile = unref_profile(user->profile);
801  }
802 
804 }
805 
806 /*! \brief Delete all users */
807 static void delete_users(void)
808 {
809  struct ao2_iterator i;
810  struct user *user;
811 
812  i = ao2_iterator_init(users, 0);
813  while ((user = ao2_iterator_next(&i))) {
814  ao2_unlink(users, user);
815  user = unref_user(user);
816  }
818 }
819 
820 /*! \brief Build and return a user structure based on gathered config data */
821 static struct user *build_user(const char *mac, struct phone_profile *profile)
822 {
823  struct user *user;
824 
825  if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
826  profile = unref_profile(profile);
827  return NULL;
828  }
829 
830  if (ast_string_field_init(user, 32)) {
831  profile = unref_profile(profile);
832  user = unref_user(user);
833  return NULL;
834  }
835 
836  ast_string_field_set(user, macaddress, mac);
837  user->profile = profile; /* already ref counted by find_profile */
838 
839  return user;
840 }
841 
842 /*! \brief Add an extension to a user ordered by index/linenumber */
843 static int add_user_extension(struct user *user, struct extension *exten)
844 {
845  struct ast_var_t *var;
846  struct ast_str *str = ast_str_create(16);
847 
848  if (!str) {
849  return -1;
850  }
851 
852  /* Append profile variables here, and substitute variables on profile
853  * setvars, so that we can use user specific variables in them */
854  AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
855  struct ast_var_t *var2;
856 
857  ast_str_substitute_variables_varshead(&str, 0, exten->headp, var->value);
858  if ((var2 = ast_var_assign(var->name, ast_str_buffer(str)))) {
859  AST_LIST_INSERT_TAIL(exten->headp, var2, entries);
860  }
861  }
862 
863  ast_free(str);
864 
865  if (AST_LIST_EMPTY(&user->extensions)) {
866  AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
867  } else {
868  struct extension *exten_iter;
869 
870  AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
871  if (exten->index < exten_iter->index) {
872  AST_LIST_INSERT_BEFORE_CURRENT(exten, entry);
873  } else if (exten->index == exten_iter->index) {
874  ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
875  return -1;
876  } else if (!AST_LIST_NEXT(exten_iter, entry)) {
877  AST_LIST_INSERT_TAIL(&user->extensions, exten, entry);
878  }
879  }
881  }
882 
883  return 0;
884 }
885 
886 /*! \brief Add an http route for dynamic files attached to the profile of the user */
887 static int build_user_routes(struct user *user)
888 {
889  struct phoneprov_file *pp_file;
890  struct ast_str *str;
891 
892  if (!(str = ast_str_create(16))) {
893  return -1;
894  }
895 
896  AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
897  ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format);
898  build_route(pp_file, user, ast_str_buffer(str));
899  }
901  ast_free(str);
902  return 0;
903 }
904 
905 /* \brief Parse config files and create appropriate structures */
906 static int set_config(void)
907 {
908  struct ast_config *cfg, *phoneprov_cfg;
909  char *cat;
910  struct ast_variable *v;
911  struct ast_flags config_flags = { 0 };
912  struct ast_var_t *var;
913 
914  /* Try to grab the port from sip.conf. If we don't get it here, we'll set it
915  * to whatever is set in phoneprov.conf or default to 5060 */
916  if ((cfg = ast_config_load("sip.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
917  ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
918  ast_config_destroy(cfg);
919  }
920 
921  if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
922  ast_log(LOG_WARNING, "Unable to load users.conf\n");
923  return 0;
924  }
925 
926  /* Go ahead and load global variables from users.conf so we can append to profiles */
927  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
928  if (!strcasecmp(v->name, "vmexten")) {
929  if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value))) {
930  ast_mutex_lock(&globals_lock);
932  ast_mutex_unlock(&globals_lock);
933  }
934  }
935  if (!strcasecmp(v->name, "localextenlength")) {
936  if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
937  ast_mutex_lock(&globals_lock);
939  ast_mutex_unlock(&globals_lock);
940  }
941  }
942 
943  if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags)) || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
944  ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
945  ast_config_destroy(cfg);
946  return -1;
947  }
948 
949  cat = NULL;
950  while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
951  if (!strcasecmp(cat, "general")) {
952  for (v = ast_variable_browse(phoneprov_cfg, cat); v; v = v->next) {
953  if (!strcasecmp(v->name, "serveraddr"))
954  ast_copy_string(global_server, v->value, sizeof(global_server));
955  else if (!strcasecmp(v->name, "serveriface")) {
956  struct in_addr addr;
957  lookup_iface(v->value, &addr);
958  ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
959  } else if (!strcasecmp(v->name, "serverport"))
960  ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
961  else if (!strcasecmp(v->name, "default_profile"))
962  ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
963  }
964  } else
965  build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
966  }
967 
968  ast_config_destroy(phoneprov_cfg);
969 
970  cat = NULL;
971  while ((cat = ast_category_browse(cfg, cat))) {
972  const char *tmp, *mac;
973  struct user *user;
974  struct phone_profile *profile;
975  struct extension *exten;
976 
977  if (!strcasecmp(cat, "general")) {
978  continue;
979  }
980 
981  if (!strcasecmp(cat, "authentication"))
982  continue;
983 
984  if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))
985  continue;
986 
987  if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
988  ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
989  continue;
990  }
991 
992  tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
993  if (ast_strlen_zero(tmp)) {
994  ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
995  continue;
996  }
997 
998  if (!(user = find_user(mac))) {
999  if (!(profile = find_profile(tmp))) {
1000  ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
1001  continue;
1002  }
1003 
1004  if (!(user = build_user(mac, profile))) {
1005  ast_log(LOG_WARNING, "Could not create user for '%s' - skipping\n", user->macaddress);
1006  continue;
1007  }
1008 
1009  if (!(exten = build_extension(cfg, cat))) {
1010  ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
1011  user = unref_user(user);
1012  continue;
1013  }
1014 
1015  if (add_user_extension(user, exten)) {
1016  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1017  user = unref_user(user);
1018  exten = delete_extension(exten);
1019  continue;
1020  }
1021 
1022  if (build_user_routes(user)) {
1023  ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->macaddress);
1024  user = unref_user(user);
1025  continue;
1026  }
1027 
1028  ao2_link(users, user);
1029  user = unref_user(user);
1030  } else {
1031  if (!(exten = build_extension(cfg, cat))) {
1032  ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
1033  user = unref_user(user);
1034  continue;
1035  }
1036 
1037  if (add_user_extension(user, exten)) {
1038  ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
1039  user = unref_user(user);
1040  exten = delete_extension(exten);
1041  continue;
1042  }
1043 
1044  user = unref_user(user);
1045  }
1046  }
1047 
1049 
1050  return 0;
1051 }
1052 
1053 /*! \brief Delete all http routes, freeing their memory */
1054 static void delete_routes(void)
1055 {
1056  struct ao2_iterator i;
1057  struct http_route *route;
1058 
1059  i = ao2_iterator_init(http_routes, 0);
1060  while ((route = ao2_iterator_next(&i))) {
1061  ao2_unlink(http_routes, route);
1062  route = unref_route(route);
1063  }
1065 }
1066 
1067 /*! \brief Delete all phone profiles, freeing their memory */
1068 static void delete_profiles(void)
1069 {
1070  struct ao2_iterator i;
1071  struct phone_profile *profile;
1072 
1073  i = ao2_iterator_init(profiles, 0);
1074  while ((profile = ao2_iterator_next(&i))) {
1075  ao2_unlink(profiles, profile);
1076  profile = unref_profile(profile);
1077  }
1079 }
1080 
1081 /*! \brief A dialplan function that can be used to print a string for each phoneprov user */
1082 static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
1083 {
1084  char *tmp;
1085  struct ao2_iterator i;
1086  struct user *user;
1087  struct ast_str *str;
1089  AST_APP_ARG(string);
1090  AST_APP_ARG(exclude_mac);
1091  );
1092  AST_STANDARD_APP_ARGS(args, data);
1093 
1094  if (!(str = ast_str_create(16))) {
1095  return -1;
1096  }
1097 
1098  /* Fix data by turning %{ into ${ */
1099  while ((tmp = strstr(args.string, "%{")))
1100  *tmp = '$';
1101 
1102  i = ao2_iterator_init(users, 0);
1103  while ((user = ao2_iterator_next(&i))) {
1104  if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
1105  continue;
1106  }
1107  ast_str_substitute_variables_varshead(&str, len, AST_LIST_FIRST(&user->extensions)->headp, args.string);
1108  if (buf) {
1109  size_t slen = len;
1110  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1111  } else {
1112  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1113  }
1114  user = unref_user(user);
1115  }
1117 
1118  ast_free(str);
1119  return 0;
1120 }
1122 static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1123 {
1124  return pp_each_user_helper(chan, data, buf, NULL, len);
1125 }
1127 static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1128 {
1129  return pp_each_user_helper(chan, data, NULL, buf, len);
1130 }
1131 
1133  .name = "PP_EACH_USER",
1134  .read = pp_each_user_read,
1135  .read2 = pp_each_user_read2,
1136 };
1137 
1138 /*! \brief A dialplan function that can be used to output a template for each extension attached to a user */
1139 static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
1140 {
1141  struct user *user;
1142  struct extension *exten;
1143  char path[PATH_MAX];
1144  char *file;
1145  int filelen;
1146  struct ast_str *str;
1148  AST_APP_ARG(mac);
1149  AST_APP_ARG(template);
1150  );
1151 
1152  AST_STANDARD_APP_ARGS(args, data);
1153 
1154  if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
1155  ast_log(LOG_WARNING, "PP_EACH_EXTENSION requries both a macaddress and template filename.\n");
1156  return 0;
1157  }
1158 
1159  if (!(user = find_user(args.mac))) {
1160  ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
1161  return 0;
1162  }
1163 
1164  snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
1165  filelen = load_file(path, &file);
1166  if (filelen < 0) {
1167  ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
1168  if (file) {
1169  ast_free(file);
1170  }
1171  return 0;
1172  }
1173 
1174  if (!file) {
1175  return 0;
1176  }
1177 
1178  if (!(str = ast_str_create(filelen))) {
1179  return 0;
1180  }
1181 
1182  AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
1183  ast_str_substitute_variables_varshead(&str, 0, exten->headp, file);
1184  if (buf) {
1185  size_t slen = len;
1186  ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
1187  } else {
1188  ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
1189  }
1190  }
1191 
1192  ast_free(file);
1193  ast_free(str);
1195  user = unref_user(user);
1196 
1197  return 0;
1198 }
1200 static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1201 {
1202  return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
1203 }
1205 static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1206 {
1207  return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
1208 }
1209 
1211  .name = "PP_EACH_EXTENSION",
1212  .read = pp_each_extension_read,
1213  .read2 = pp_each_extension_read2,
1214 };
1215 
1216 /*! \brief CLI command to list static and dynamic routes */
1217 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1218 {
1219 #define FORMAT "%-40.40s %-30.30s\n"
1220  struct ao2_iterator i;
1221  struct http_route *route;
1222 
1223  switch(cmd) {
1224  case CLI_INIT:
1225  e->command = "phoneprov show routes";
1226  e->usage =
1227  "Usage: phoneprov show routes\n"
1228  " Lists all registered phoneprov http routes.\n";
1229  return NULL;
1230  case CLI_GENERATE:
1231  return NULL;
1232  }
1233 
1234  /* This currently iterates over routes twice, but it is the only place I've needed
1235  * to really separate static an dynamic routes, so I've just left it this way. */
1236  ast_cli(a->fd, "Static routes\n\n");
1237  ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
1238  i = ao2_iterator_init(http_routes, 0);
1239  while ((route = ao2_iterator_next(&i))) {
1240  if (!route->user)
1241  ast_cli(a->fd, FORMAT, route->uri, route->file->template);
1242  route = unref_route(route);
1243  }
1245 
1246  ast_cli(a->fd, "\nDynamic routes\n\n");
1247  ast_cli(a->fd, FORMAT, "Relative URI", "Template");
1248 
1249  i = ao2_iterator_init(http_routes, 0);
1250  while ((route = ao2_iterator_next(&i))) {
1251  if (route->user)
1252  ast_cli(a->fd, FORMAT, route->uri, route->file->template);
1253  route = unref_route(route);
1254  }
1256 
1257  return CLI_SUCCESS;
1259 
1260 static struct ast_cli_entry pp_cli[] = {
1261  AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
1262 };
1263 
1264 static struct ast_http_uri phoneprovuri = {
1266  .description = "Asterisk HTTP Phone Provisioning Tool",
1267  .uri = "phoneprov",
1268  .has_subtree = 1,
1269  .data = NULL,
1270  .key = __FILE__,
1271 };
1272 
1273 static int load_module(void)
1274 {
1276 
1278 
1280 
1282  ast_mutex_init(&globals_lock);
1283 
1284  ast_custom_function_register(&pp_each_user_function);
1285  ast_custom_function_register(&pp_each_extension_function);
1286  ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
1287 
1289  ast_http_uri_link(&phoneprovuri);
1290 
1291  return 0;
1292 }
1293 
1294 static int unload_module(void)
1295 {
1296  struct ast_var_t *var;
1297 
1298  ast_http_uri_unlink(&phoneprovuri);
1299  ast_custom_function_unregister(&pp_each_user_function);
1300  ast_custom_function_unregister(&pp_each_extension_function);
1301  ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
1302 
1303  delete_routes();
1304  delete_users();
1305  delete_profiles();
1306  ao2_ref(profiles, -1);
1307  ao2_ref(http_routes, -1);
1308  ao2_ref(users, -1);
1309 
1310  ast_mutex_lock(&globals_lock);
1311  while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
1312  ast_var_delete(var);
1313  }
1314  ast_mutex_unlock(&globals_lock);
1316  ast_mutex_destroy(&globals_lock);
1317 
1318  return 0;
1319 }
1320 
1321 static int reload(void)
1322 {
1323  struct ast_var_t *var;
1324 
1325  delete_routes();
1326  delete_users();
1327  delete_profiles();
1328 
1329  ast_mutex_lock(&globals_lock);
1330  while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
1331  ast_var_delete(var);
1332  }
1333  ast_mutex_unlock(&globals_lock);
1334 
1335  set_config();
1336 
1337  return 0;
1339 
1340 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP Phone Provisioning",
1341  .load = load_module,
1342  .unload = unload_module,
1343  .reload = reload,
1344  );
static char user[512]
const ast_string_field staticdir
static void route_destructor(void *obj)
struct phoneprov_file::@339 entry
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:109
Main Channel structure associated with a channel.
Definition: channel.h:742
static char global_server[80]
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
static struct user * build_user(const char *mac, struct phone_profile *profile)
Build and return a user structure based on gathered config data.
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
Definition: astobj2.h:785
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static void build_profile(const char *name, struct ast_variable *v)
Build a phone profile and add it to the list of phone profiles.
static struct pp_variable_lookup pp_variable_list[]
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:506
ast_http_callback callback
Definition: http.h:95
String manipulation functions.
Asterisk version information.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
struct ast_var_t * ast_var_assign(const char *name, const char *value)
Definition: chanvars.c:41
#define ao2_iterator_next(arg1)
Definition: astobj2.h:1126
const ast_string_field format
descriptor for a cli entry.
Definition: cli.h:165
#define LOG_WARNING
Definition: logger.h:144
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:544
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
const char * ast_http_ftype2mtype(const char *ftype) attribute_pure
Return mime type based on extension.
Definition: http.c:166
static struct ast_custom_function pp_each_user_function
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:1570
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static int lookup_iface(const char *iface, struct in_addr *address)
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
static void delete_profiles(void)
Delete all phone profiles, freeing their memory.
#define var
Definition: ast_expr2f.c:606
static void delete_routes(void)
Delete all http routes, freeing their memory.
static int users_hash_fn(const void *obj, const int flags)
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
const char *const template_var
static struct ao2_container * profiles
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:574
Definition: cli.h:146
Configuration File Parser.
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:275
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:900
enum pp_variables id
#define FORMAT
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:235
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#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 ast_mutex_lock(a)
Definition: lock.h:155
static int routes_hash_fn(const void *obj, const int flags)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
Create an iterator for a container.
Definition: astobj2.c:818
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
const ast_string_field uri
const char * str
Definition: app_jack.c:144
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
struct phoneprov_file * file
structure to hold file data
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
struct varshead * headp
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
void ast_var_delete(struct ast_var_t *var)
Definition: chanvars.c:63
static struct user * find_user(const char *macaddress)
Return a user looked up by name.
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
const ast_string_field default_mime_type
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
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)
String fields in structures.
Utility functions.
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, const int fd, unsigned int static_content)
Generic function for sending http/1.1 response.
Definition: http.c:393
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:874
Support for Private Asterisk HTTP Servers.
struct varshead * headp
#define MAX_PROFILE_BUCKETS
Definition: res_phoneprov.c:65
const char * value
Definition: config.h:79
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:1521
static int load_module(void)
pp_variables
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)
static int unload_module(void)
const int fd
Definition: cli.h:153
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:249
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
Access Control of various sorts.
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:155
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:220
int tm_mon
Definition: localtime.h:40
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
#define ao2_ref(o, delta)
Definition: astobj2.h:472
static int set_config(void)
static void profile_destructor(void *obj)
char * value
Definition: chanvars.h:30
const char * name
Definition: config.h:77
Lookup table to translate between users.conf property names and variables for use in phoneprov templa...
struct phone_profile * profile
char name[0]
Definition: chanvars.h:31
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:818
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.
const char * ast_config_AST_DATA_DIR
Definition: asterisk.c:262
#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:224
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.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static int profile_hash_fn(const void *obj, const int flags)
struct user * user
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
Definition: pbx.c:4474
#define free(a)
Definition: astmm.h:94
static struct @350 args
static int users_cmp_fn(void *obj, void *arg, int flags)
static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int routes_cmp_fn(void *obj, void *arg, int flags)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static struct http_route * unref_route(struct http_route *route)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static struct ast_cli_entry pp_cli[]
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
static struct ao2_container * users
#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:696
#define ao2_find(arg1, arg2, arg3)
Definition: astobj2.h:964
int errno
static const char name[]
struct ast_var_t::@158 entries
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: utils.c:564
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
static struct ast_format f[]
Definition: format_g726.c:181
structure to hold phone profiles read from phoneprov.conf
static struct extension * build_extension(struct ast_config *cfg, const char *name)
static void user_destructor(void *obj)
Free all memory associated with a user.
int tm_hour
Definition: localtime.h:38
static struct ast_custom_function pp_each_extension_function
structure to hold users read from users.conf
void ao2_iterator_destroy(struct ao2_iterator *i)
Destroy a container iterator.
Definition: astobj2.c:833
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:367
Structure used to handle boolean flags.
Definition: utils.h:200
const char * usage
Definition: cli.h:171
#define CLI_SUCCESS
Definition: cli.h:43
static int profile_cmp_fn(void *obj, void *arg, int flags)
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:666
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1053
const ast_string_field name
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
static char global_default_profile[80]
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
const char *const user_var
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
static struct ao2_container * http_routes
static struct extension * delete_extension(struct extension *exten)
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
Definition of a URI handler.
Definition: http.h:91
#define MAX_ROUTE_BUCKETS
Definition: res_phoneprov.c:66
static struct ast_http_uri phoneprovuri
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
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.
Options provided by main asterisk program.
const char * name
Definition: pbx.h:96
struct phone_profile::@340 static_files
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
struct ast_variable * next
Definition: config.h:82
#define ast_mutex_init(pmutex)
Definition: lock.h:152
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define MAX_USER_BUCKETS
Definition: res_phoneprov.c:67
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:56
#define ast_mutex_destroy(a)
Definition: lock.h:154
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:584
structure to hold http routes (valid URIs, and the files they link to)
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
Definition: app.h:619
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:1585
static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
Build a route structure and add it to the list of available http routes.
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:38
#define ast_malloc(a)
Definition: astmm.h:91
static int reload(void)
Asterisk module definitions.
struct phone_profile::@341 dynamic_files
static void delete_file(struct phoneprov_file *file)
static struct phone_profile * find_profile(const char *name)
Return a phone profile looked up by name.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:253
static ast_mutex_t globals_lock
static struct varshead global_variables
List of global variables currently available: VOICEMAIL_EXTEN struct ast_string_field_mgr __field_mgr...
#define ao2_unlink(arg1, arg2)
Definition: astobj2.h:817
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
struct user::@343 extensions
Structure for mutex and tracking information.
Definition: lock.h:121
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:989
static struct in_addr __ourip
for use in lookup_iface
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define ast_mutex_unlock(a)
Definition: lock.h:156
static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
static char global_serverport[6]
const ast_string_field mime_type
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
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.