00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 #include <sys/ioctl.h>
00032 #include <sys/socket.h>
00033 #include <net/if.h>
00034 #ifdef SOLARIS
00035 #include <sys/sockio.h>
00036 #endif
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 270660 $")
00038
00039 #include "asterisk/channel.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/paths.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/http.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/strings.h"
00049 #include "asterisk/stringfields.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/acl.h"
00053 #include "asterisk/astobj2.h"
00054 #include "asterisk/ast_version.h"
00055
00056 #ifdef LOW_MEMORY
00057 #define MAX_PROFILE_BUCKETS 1
00058 #define MAX_ROUTE_BUCKETS 1
00059 #define MAX_USER_BUCKETS 1
00060 #else
00061 #define MAX_PROFILE_BUCKETS 17
00062 #define MAX_ROUTE_BUCKETS 563
00063 #define MAX_USER_BUCKETS 563
00064 #endif
00065
00066 #define VAR_BUF_SIZE 4096
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100 static struct in_addr __ourip = { .s_addr = 0x00000000, };
00101
00102
00103
00104 enum pp_variables {
00105 PP_MACADDRESS,
00106 PP_USERNAME,
00107 PP_FULLNAME,
00108 PP_SECRET,
00109 PP_LABEL,
00110 PP_CALLERID,
00111 PP_TIMEZONE,
00112 PP_LINENUMBER,
00113 PP_LINEKEYS,
00114 PP_VAR_LIST_LENGTH,
00115 };
00116
00117
00118
00119 static const struct pp_variable_lookup {
00120 enum pp_variables id;
00121 const char * const user_var;
00122 const char * const template_var;
00123 } pp_variable_list[] = {
00124 { PP_MACADDRESS, "macaddress", "MAC" },
00125 { PP_USERNAME, "username", "USERNAME" },
00126 { PP_FULLNAME, "fullname", "DISPLAY_NAME" },
00127 { PP_SECRET, "secret", "SECRET" },
00128 { PP_LABEL, "label", "LABEL" },
00129 { PP_CALLERID, "cid_number", "CALLERID" },
00130 { PP_TIMEZONE, "timezone", "TIMEZONE" },
00131 { PP_LINENUMBER, "linenumber", "LINE" },
00132 { PP_LINEKEYS, "linekeys", "LINEKEYS" },
00133 };
00134
00135
00136 struct phoneprov_file {
00137 AST_DECLARE_STRING_FIELDS(
00138 AST_STRING_FIELD(format);
00139 AST_STRING_FIELD(template);
00140 AST_STRING_FIELD(mime_type);
00141 );
00142 AST_LIST_ENTRY(phoneprov_file) entry;
00143 };
00144
00145
00146 struct phone_profile {
00147 AST_DECLARE_STRING_FIELDS(
00148 AST_STRING_FIELD(name);
00149 AST_STRING_FIELD(default_mime_type);
00150 AST_STRING_FIELD(staticdir);
00151 );
00152 struct varshead *headp;
00153 AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files;
00154 AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files;
00155 };
00156
00157 struct extension {
00158 AST_DECLARE_STRING_FIELDS(
00159 AST_STRING_FIELD(name);
00160 );
00161 int index;
00162 struct varshead *headp;
00163 AST_LIST_ENTRY(extension) entry;
00164 };
00165
00166
00167 struct user {
00168 AST_DECLARE_STRING_FIELDS(
00169 AST_STRING_FIELD(macaddress);
00170 );
00171 struct phone_profile *profile;
00172 AST_LIST_HEAD_NOLOCK(, extension) extensions;
00173 };
00174
00175
00176 struct http_route {
00177 AST_DECLARE_STRING_FIELDS(
00178 AST_STRING_FIELD(uri);
00179 );
00180 struct phoneprov_file *file;
00181 struct user *user;
00182
00183 };
00184
00185 static struct ao2_container *profiles;
00186 static struct ao2_container *http_routes;
00187 static struct ao2_container *users;
00188
00189 static char global_server[80] = "";
00190 static char global_serverport[6] = "";
00191 static char global_default_profile[80] = "";
00192
00193
00194 static struct varshead global_variables;
00195 static ast_mutex_t globals_lock;
00196
00197
00198 static int lookup_iface(const char *iface, struct in_addr *address)
00199 {
00200 int mysock, res = 0;
00201 struct ifreq ifr;
00202 struct sockaddr_in *sin;
00203
00204 memset(&ifr, 0, sizeof(ifr));
00205 ast_copy_string(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
00206
00207 mysock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
00208 if (mysock < 0) {
00209 ast_log(LOG_ERROR, "Failed to create socket: %s\n", strerror(errno));
00210 return -1;
00211 }
00212
00213 res = ioctl(mysock, SIOCGIFADDR, &ifr);
00214
00215 close(mysock);
00216
00217 if (res < 0) {
00218 ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
00219 memcpy(address, &__ourip, sizeof(__ourip));
00220 return -1;
00221 } else {
00222 sin = (struct sockaddr_in *)&ifr.ifr_addr;
00223 memcpy(address, &sin->sin_addr, sizeof(*address));
00224 return 0;
00225 }
00226 }
00227
00228 static struct phone_profile *unref_profile(struct phone_profile *prof)
00229 {
00230 ao2_ref(prof, -1);
00231
00232 return NULL;
00233 }
00234
00235
00236 static struct phone_profile *find_profile(const char *name)
00237 {
00238 struct phone_profile tmp = {
00239 .name = name,
00240 };
00241
00242 return ao2_find(profiles, &tmp, OBJ_POINTER);
00243 }
00244
00245 static int profile_hash_fn(const void *obj, const int flags)
00246 {
00247 const struct phone_profile *profile = obj;
00248
00249 return ast_str_case_hash(profile->name);
00250 }
00251
00252 static int profile_cmp_fn(void *obj, void *arg, int flags)
00253 {
00254 const struct phone_profile *profile1 = obj, *profile2 = arg;
00255
00256 return !strcasecmp(profile1->name, profile2->name) ? CMP_MATCH | CMP_STOP : 0;
00257 }
00258
00259 static void delete_file(struct phoneprov_file *file)
00260 {
00261 ast_string_field_free_memory(file);
00262 free(file);
00263 }
00264
00265 static void profile_destructor(void *obj)
00266 {
00267 struct phone_profile *profile = obj;
00268 struct phoneprov_file *file;
00269 struct ast_var_t *var;
00270
00271 while ((file = AST_LIST_REMOVE_HEAD(&profile->static_files, entry)))
00272 delete_file(file);
00273
00274 while ((file = AST_LIST_REMOVE_HEAD(&profile->dynamic_files, entry)))
00275 delete_file(file);
00276
00277 while ((var = AST_LIST_REMOVE_HEAD(profile->headp, entries)))
00278 ast_var_delete(var);
00279
00280 ast_free(profile->headp);
00281 ast_string_field_free_memory(profile);
00282 }
00283
00284 static struct http_route *unref_route(struct http_route *route)
00285 {
00286 ao2_ref(route, -1);
00287
00288 return NULL;
00289 }
00290
00291 static int routes_hash_fn(const void *obj, const int flags)
00292 {
00293 const struct http_route *route = obj;
00294
00295 return ast_str_case_hash(route->uri);
00296 }
00297
00298 static int routes_cmp_fn(void *obj, void *arg, int flags)
00299 {
00300 const struct http_route *route1 = obj, *route2 = arg;
00301
00302 return !strcasecmp(route1->uri, route2->uri) ? CMP_MATCH | CMP_STOP : 0;
00303 }
00304
00305 static void route_destructor(void *obj)
00306 {
00307 struct http_route *route = obj;
00308
00309 ast_string_field_free_memory(route);
00310 }
00311
00312
00313 static int load_file(const char *filename, char **ret)
00314 {
00315 int len = 0;
00316 FILE *f;
00317
00318 if (!(f = fopen(filename, "r"))) {
00319 *ret = NULL;
00320 return -1;
00321 }
00322
00323 fseek(f, 0, SEEK_END);
00324 len = ftell(f);
00325 fseek(f, 0, SEEK_SET);
00326 if (!(*ret = ast_malloc(len + 1)))
00327 return -2;
00328
00329 if (len != fread(*ret, sizeof(char), len, f)) {
00330 free(*ret);
00331 *ret = NULL;
00332 return -3;
00333 }
00334
00335 fclose(f);
00336
00337 (*ret)[len] = '\0';
00338
00339 return len;
00340 }
00341
00342
00343
00344
00345
00346 static void set_timezone_variables(struct varshead *headp, const char *zone)
00347 {
00348 time_t utc_time;
00349 int dstenable;
00350 time_t dststart;
00351 time_t dstend;
00352 struct ast_tm tm_info;
00353 int tzoffset;
00354 char buffer[21];
00355 struct ast_var_t *var;
00356 struct timeval when;
00357
00358 time(&utc_time);
00359 ast_get_dst_info(&utc_time, &dstenable, &dststart, &dstend, &tzoffset, zone);
00360 snprintf(buffer, sizeof(buffer), "%d", tzoffset);
00361 var = ast_var_assign("TZOFFSET", buffer);
00362 if (var)
00363 AST_LIST_INSERT_TAIL(headp, var, entries);
00364
00365 if (!dstenable)
00366 return;
00367
00368 if ((var = ast_var_assign("DST_ENABLE", "1")))
00369 AST_LIST_INSERT_TAIL(headp, var, entries);
00370
00371 when.tv_sec = dststart;
00372 ast_localtime(&when, &tm_info, zone);
00373
00374 snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon+1);
00375 if ((var = ast_var_assign("DST_START_MONTH", buffer)))
00376 AST_LIST_INSERT_TAIL(headp, var, entries);
00377
00378 snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
00379 if ((var = ast_var_assign("DST_START_MDAY", buffer)))
00380 AST_LIST_INSERT_TAIL(headp, var, entries);
00381
00382 snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
00383 if ((var = ast_var_assign("DST_START_HOUR", buffer)))
00384 AST_LIST_INSERT_TAIL(headp, var, entries);
00385
00386 when.tv_sec = dstend;
00387 ast_localtime(&when, &tm_info, zone);
00388
00389 snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mon + 1);
00390 if ((var = ast_var_assign("DST_END_MONTH", buffer)))
00391 AST_LIST_INSERT_TAIL(headp, var, entries);
00392
00393 snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_mday);
00394 if ((var = ast_var_assign("DST_END_MDAY", buffer)))
00395 AST_LIST_INSERT_TAIL(headp, var, entries);
00396
00397 snprintf(buffer, sizeof(buffer), "%d", tm_info.tm_hour);
00398 if ((var = ast_var_assign("DST_END_HOUR", buffer)))
00399 AST_LIST_INSERT_TAIL(headp, var, entries);
00400 }
00401
00402
00403 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)
00404 {
00405 struct http_route *route;
00406 struct http_route search_route = {
00407 .uri = uri,
00408 };
00409 struct ast_str *result;
00410 char path[PATH_MAX];
00411 char *file = NULL;
00412 int len;
00413 int fd;
00414 struct ast_str *http_header;
00415
00416 if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
00417 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00418 return -1;
00419 }
00420
00421 if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
00422 goto out404;
00423 }
00424
00425 snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
00426
00427 if (!route->user) {
00428
00429 fd = open(path, O_RDONLY);
00430 if (fd < 0) {
00431 goto out500;
00432 }
00433
00434 len = lseek(fd, 0, SEEK_END);
00435 lseek(fd, 0, SEEK_SET);
00436 if (len < 0) {
00437 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00438 close(fd);
00439 goto out500;
00440 }
00441
00442 http_header = ast_str_create(80);
00443 ast_str_set(&http_header, 0, "Content-type: %s",
00444 route->file->mime_type);
00445
00446 ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
00447
00448 close(fd);
00449 route = unref_route(route);
00450 return 0;
00451 } else {
00452 struct ast_str *tmp;
00453
00454 len = load_file(path, &file);
00455 if (len < 0) {
00456 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00457 if (file) {
00458 ast_free(file);
00459 }
00460
00461 goto out500;
00462 }
00463
00464 if (!file) {
00465 goto out500;
00466 }
00467
00468 if (!(tmp = ast_str_create(len))) {
00469 if (file) {
00470 ast_free(file);
00471 }
00472
00473 goto out500;
00474 }
00475
00476
00477
00478 if (ast_strlen_zero(global_server)) {
00479 union {
00480 struct sockaddr sa;
00481 struct sockaddr_in sa_in;
00482 } name;
00483 socklen_t namelen = sizeof(name.sa);
00484 int res;
00485
00486 if ((res = getsockname(ser->fd, &name.sa, &namelen))) {
00487 ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
00488 } else {
00489 struct ast_var_t *var;
00490 struct extension *exten_iter;
00491
00492 if ((var = ast_var_assign("SERVER", ast_inet_ntoa(name.sa_in.sin_addr)))) {
00493 AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
00494 AST_LIST_INSERT_TAIL(exten_iter->headp, var, entries);
00495 }
00496 }
00497 }
00498 }
00499
00500 ast_str_substitute_variables_varshead(&tmp, 0, AST_LIST_FIRST(&route->user->extensions)->headp, file);
00501
00502 if (file) {
00503 ast_free(file);
00504 }
00505
00506 http_header = ast_str_create(80);
00507 ast_str_set(&http_header, 0, "Content-type: %s",
00508 route->file->mime_type);
00509
00510 if (!(result = ast_str_create(512))) {
00511 ast_log(LOG_ERROR, "Could not create result string!\n");
00512 if (tmp) {
00513 ast_free(tmp);
00514 }
00515 ast_free(http_header);
00516 goto out500;
00517 }
00518 ast_str_append(&result, 0, "%s", ast_str_buffer(tmp));
00519
00520 ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
00521 if (tmp) {
00522 ast_free(tmp);
00523 }
00524
00525 route = unref_route(route);
00526
00527 return 0;
00528 }
00529
00530 out404:
00531 ast_http_error(ser, 404, "Not Found", "Nothing to see here. Move along.");
00532 return -1;
00533
00534 out500:
00535 route = unref_route(route);
00536 ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
00537 return -1;
00538 }
00539
00540
00541
00542
00543
00544
00545 static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
00546 {
00547 struct http_route *route;
00548
00549 if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
00550 return;
00551 }
00552
00553 if (ast_string_field_init(route, 32)) {
00554 ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
00555 route = unref_route(route);
00556 return;
00557 }
00558
00559 ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
00560 route->user = user;
00561 route->file = pp_file;
00562
00563 ao2_link(http_routes, route);
00564
00565 route = unref_route(route);
00566 }
00567
00568
00569
00570
00571
00572 static void build_profile(const char *name, struct ast_variable *v)
00573 {
00574 struct phone_profile *profile;
00575 struct ast_var_t *var;
00576
00577 if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
00578 return;
00579 }
00580
00581 if (ast_string_field_init(profile, 32)) {
00582 profile = unref_profile(profile);
00583 return;
00584 }
00585
00586 if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
00587 profile = unref_profile(profile);
00588 return;
00589 }
00590
00591 AST_LIST_HEAD_INIT_NOLOCK(&profile->static_files);
00592 AST_LIST_HEAD_INIT_NOLOCK(&profile->dynamic_files);
00593
00594 ast_string_field_set(profile, name, name);
00595 for (; v; v = v->next) {
00596 if (!strcasecmp(v->name, "mime_type")) {
00597 ast_string_field_set(profile, default_mime_type, v->value);
00598 } else if (!strcasecmp(v->name, "setvar")) {
00599 struct ast_var_t *variable;
00600 char *value_copy = ast_strdupa(v->value);
00601
00602 AST_DECLARE_APP_ARGS(args,
00603 AST_APP_ARG(varname);
00604 AST_APP_ARG(varval);
00605 );
00606
00607 AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
00608 do {
00609 if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00610 break;
00611 args.varname = ast_strip(args.varname);
00612 args.varval = ast_strip(args.varval);
00613 if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00614 break;
00615 if ((variable = ast_var_assign(args.varname, args.varval)))
00616 AST_LIST_INSERT_TAIL(profile->headp, variable, entries);
00617 } while (0);
00618 } else if (!strcasecmp(v->name, "staticdir")) {
00619 ast_string_field_set(profile, staticdir, v->value);
00620 } else {
00621 struct phoneprov_file *pp_file;
00622 char *file_extension;
00623 char *value_copy = ast_strdupa(v->value);
00624
00625 AST_DECLARE_APP_ARGS(args,
00626 AST_APP_ARG(filename);
00627 AST_APP_ARG(mimetype);
00628 );
00629
00630 if (!(pp_file = ast_calloc_with_stringfields(1, struct phoneprov_file, 32))) {
00631 profile = unref_profile(profile);
00632 return;
00633 }
00634
00635 if ((file_extension = strrchr(pp_file->format, '.')))
00636 file_extension++;
00637
00638 AST_STANDARD_APP_ARGS(args, value_copy);
00639
00640
00641
00642
00643
00644
00645
00646 ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype,
00647 (S_OR(S_OR(ast_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
00648
00649 if (!strcasecmp(v->name, "static_file")) {
00650 ast_string_field_set(pp_file, format, args.filename);
00651 ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
00652 AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
00653
00654 build_route(pp_file, NULL, NULL);
00655 } else {
00656 ast_string_field_set(pp_file, format, v->name);
00657 ast_string_field_set(pp_file, template, args.filename);
00658 AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
00659 }
00660 }
00661 }
00662
00663
00664
00665
00666 ast_mutex_lock(&globals_lock);
00667 AST_LIST_TRAVERSE(&global_variables, var, entries) {
00668 struct ast_var_t *new_var;
00669 if ((new_var = ast_var_assign(var->name, var->value))) {
00670 AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
00671 }
00672 }
00673 ast_mutex_unlock(&globals_lock);
00674
00675 ao2_link(profiles, profile);
00676
00677 profile = unref_profile(profile);
00678 }
00679
00680 static struct extension *delete_extension(struct extension *exten)
00681 {
00682 struct ast_var_t *var;
00683 while ((var = AST_LIST_REMOVE_HEAD(exten->headp, entries))) {
00684 ast_var_delete(var);
00685 }
00686 ast_free(exten->headp);
00687 ast_string_field_free_memory(exten);
00688
00689 ast_free(exten);
00690
00691 return NULL;
00692 }
00693
00694 static struct extension *build_extension(struct ast_config *cfg, const char *name)
00695 {
00696 struct extension *exten;
00697 struct ast_var_t *var;
00698 const char *tmp;
00699 int i;
00700
00701 if (!(exten = ast_calloc_with_stringfields(1, struct extension, 32))) {
00702 return NULL;
00703 }
00704
00705 ast_string_field_set(exten, name, name);
00706
00707 if (!(exten->headp = ast_calloc(1, sizeof(*exten->headp)))) {
00708 ast_free(exten);
00709 exten = NULL;
00710 return NULL;
00711 }
00712
00713 for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
00714 tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
00715
00716
00717 if (i == PP_USERNAME && !tmp) {
00718 if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, exten->name))) {
00719 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00720 }
00721 continue;
00722 } else if (i == PP_TIMEZONE) {
00723
00724 set_timezone_variables(exten->headp, tmp);
00725 } else if (i == PP_LINENUMBER) {
00726 if (!tmp) {
00727 tmp = "1";
00728 }
00729 exten->index = atoi(tmp);
00730 } else if (i == PP_LINEKEYS) {
00731 if (!tmp) {
00732 tmp = "1";
00733 }
00734 }
00735
00736 if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp))) {
00737 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00738 }
00739 }
00740
00741 if (!ast_strlen_zero(global_server)) {
00742 if ((var = ast_var_assign("SERVER", global_server)))
00743 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00744 }
00745
00746 if (!ast_strlen_zero(global_serverport)) {
00747 if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
00748 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00749 }
00750
00751 return exten;
00752 }
00753
00754 static struct user *unref_user(struct user *user)
00755 {
00756 ao2_ref(user, -1);
00757
00758 return NULL;
00759 }
00760
00761
00762 static struct user *find_user(const char *macaddress)
00763 {
00764 struct user tmp = {
00765 .macaddress = macaddress,
00766 };
00767
00768 return ao2_find(users, &tmp, OBJ_POINTER);
00769 }
00770
00771 static int users_hash_fn(const void *obj, const int flags)
00772 {
00773 const struct user *user = obj;
00774
00775 return ast_str_case_hash(user->macaddress);
00776 }
00777
00778 static int users_cmp_fn(void *obj, void *arg, int flags)
00779 {
00780 const struct user *user1 = obj, *user2 = arg;
00781
00782 return !strcasecmp(user1->macaddress, user2->macaddress) ? CMP_MATCH | CMP_STOP : 0;
00783 }
00784
00785
00786 static void user_destructor(void *obj)
00787 {
00788 struct user *user = obj;
00789 struct extension *exten;
00790
00791 while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
00792 exten = delete_extension(exten);
00793 }
00794
00795 if (user->profile) {
00796 user->profile = unref_profile(user->profile);
00797 }
00798
00799 ast_string_field_free_memory(user);
00800 }
00801
00802
00803 static void delete_users(void)
00804 {
00805 struct ao2_iterator i;
00806 struct user *user;
00807
00808 i = ao2_iterator_init(users, 0);
00809 while ((user = ao2_iterator_next(&i))) {
00810 ao2_unlink(users, user);
00811 user = unref_user(user);
00812 }
00813 ao2_iterator_destroy(&i);
00814 }
00815
00816
00817 static struct user *build_user(const char *mac, struct phone_profile *profile)
00818 {
00819 struct user *user;
00820
00821 if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
00822 profile = unref_profile(profile);
00823 return NULL;
00824 }
00825
00826 if (ast_string_field_init(user, 32)) {
00827 profile = unref_profile(profile);
00828 user = unref_user(user);
00829 return NULL;
00830 }
00831
00832 ast_string_field_set(user, macaddress, mac);
00833 user->profile = profile;
00834
00835 return user;
00836 }
00837
00838
00839 static int add_user_extension(struct user *user, struct extension *exten)
00840 {
00841 struct ast_var_t *var;
00842 struct ast_str *str = ast_str_create(16);
00843
00844 if (!str) {
00845 return -1;
00846 }
00847
00848
00849
00850 AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
00851 struct ast_var_t *var2;
00852
00853 ast_str_substitute_variables_varshead(&str, 0, exten->headp, var->value);
00854 if ((var2 = ast_var_assign(var->name, ast_str_buffer(str)))) {
00855 AST_LIST_INSERT_TAIL(exten->headp, var2, entries);
00856 }
00857 }
00858
00859 ast_free(str);
00860
00861 if (AST_LIST_EMPTY(&user->extensions)) {
00862 AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
00863 } else {
00864 struct extension *exten_iter;
00865
00866 AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
00867 if (exten->index < exten_iter->index) {
00868 AST_LIST_INSERT_BEFORE_CURRENT(exten, entry);
00869 } else if (exten->index == exten_iter->index) {
00870 ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
00871 return -1;
00872 } else if (!AST_LIST_NEXT(exten_iter, entry)) {
00873 AST_LIST_INSERT_TAIL(&user->extensions, exten, entry);
00874 }
00875 }
00876 AST_LIST_TRAVERSE_SAFE_END;
00877 }
00878
00879 return 0;
00880 }
00881
00882
00883 static int build_user_routes(struct user *user)
00884 {
00885 struct phoneprov_file *pp_file;
00886 struct ast_str *str;
00887
00888 if (!(str = ast_str_create(16))) {
00889 return -1;
00890 }
00891
00892 AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
00893 ast_str_substitute_variables_varshead(&str, 0, AST_LIST_FIRST(&user->extensions)->headp, pp_file->format);
00894 build_route(pp_file, user, ast_str_buffer(str));
00895 }
00896
00897 ast_free(str);
00898 return 0;
00899 }
00900
00901
00902 static int set_config(void)
00903 {
00904 struct ast_config *cfg, *phoneprov_cfg;
00905 char *cat;
00906 struct ast_variable *v;
00907 struct ast_flags config_flags = { 0 };
00908 struct ast_var_t *var;
00909
00910
00911
00912 if ((cfg = ast_config_load("sip.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
00913 ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
00914 ast_config_destroy(cfg);
00915 }
00916
00917 if (!(cfg = ast_config_load("users.conf", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
00918 ast_log(LOG_WARNING, "Unable to load users.conf\n");
00919 return 0;
00920 }
00921
00922
00923 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00924 if (!strcasecmp(v->name, "vmexten")) {
00925 if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value))) {
00926 ast_mutex_lock(&globals_lock);
00927 AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00928 ast_mutex_unlock(&globals_lock);
00929 }
00930 }
00931 if (!strcasecmp(v->name, "localextenlength")) {
00932 if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
00933 ast_mutex_lock(&globals_lock);
00934 AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00935 ast_mutex_unlock(&globals_lock);
00936 }
00937 }
00938
00939 if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags)) || phoneprov_cfg == CONFIG_STATUS_FILEINVALID) {
00940 ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
00941 ast_config_destroy(cfg);
00942 return -1;
00943 }
00944
00945 cat = NULL;
00946 while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
00947 if (!strcasecmp(cat, "general")) {
00948 for (v = ast_variable_browse(phoneprov_cfg, cat); v; v = v->next) {
00949 if (!strcasecmp(v->name, "serveraddr"))
00950 ast_copy_string(global_server, v->value, sizeof(global_server));
00951 else if (!strcasecmp(v->name, "serveriface")) {
00952 struct in_addr addr;
00953 lookup_iface(v->value, &addr);
00954 ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
00955 } else if (!strcasecmp(v->name, "serverport"))
00956 ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
00957 else if (!strcasecmp(v->name, "default_profile"))
00958 ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
00959 }
00960 } else
00961 build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
00962 }
00963
00964 ast_config_destroy(phoneprov_cfg);
00965
00966 cat = NULL;
00967 while ((cat = ast_category_browse(cfg, cat))) {
00968 const char *tmp, *mac;
00969 struct user *user;
00970 struct phone_profile *profile;
00971 struct extension *exten;
00972
00973 if (!strcasecmp(cat, "general")) {
00974 continue;
00975 }
00976
00977 if (!strcasecmp(cat, "authentication"))
00978 continue;
00979
00980 if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))
00981 continue;
00982
00983 if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
00984 ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
00985 continue;
00986 }
00987
00988 tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
00989 if (ast_strlen_zero(tmp)) {
00990 ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
00991 continue;
00992 }
00993
00994 if (!(user = find_user(mac))) {
00995 if (!(profile = find_profile(tmp))) {
00996 ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
00997 continue;
00998 }
00999
01000 if (!(user = build_user(mac, profile))) {
01001 ast_log(LOG_WARNING, "Could not create user for '%s' - skipping\n", user->macaddress);
01002 continue;
01003 }
01004
01005 if (!(exten = build_extension(cfg, cat))) {
01006 ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01007 user = unref_user(user);
01008 continue;
01009 }
01010
01011 if (add_user_extension(user, exten)) {
01012 ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01013 user = unref_user(user);
01014 exten = delete_extension(exten);
01015 continue;
01016 }
01017
01018 if (build_user_routes(user)) {
01019 ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->macaddress);
01020 user = unref_user(user);
01021 continue;
01022 }
01023
01024 ao2_link(users, user);
01025 user = unref_user(user);
01026 } else {
01027 if (!(exten = build_extension(cfg, cat))) {
01028 ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01029 user = unref_user(user);
01030 continue;
01031 }
01032
01033 if (add_user_extension(user, exten)) {
01034 ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01035 user = unref_user(user);
01036 exten = delete_extension(exten);
01037 continue;
01038 }
01039
01040 user = unref_user(user);
01041 }
01042 }
01043
01044 ast_config_destroy(cfg);
01045
01046 return 0;
01047 }
01048
01049
01050 static void delete_routes(void)
01051 {
01052 struct ao2_iterator i;
01053 struct http_route *route;
01054
01055 i = ao2_iterator_init(http_routes, 0);
01056 while ((route = ao2_iterator_next(&i))) {
01057 ao2_unlink(http_routes, route);
01058 route = unref_route(route);
01059 }
01060 ao2_iterator_destroy(&i);
01061 }
01062
01063
01064 static void delete_profiles(void)
01065 {
01066 struct ao2_iterator i;
01067 struct phone_profile *profile;
01068
01069 i = ao2_iterator_init(profiles, 0);
01070 while ((profile = ao2_iterator_next(&i))) {
01071 ao2_unlink(profiles, profile);
01072 profile = unref_profile(profile);
01073 }
01074 ao2_iterator_destroy(&i);
01075 }
01076
01077
01078 static int pp_each_user_helper(struct ast_channel *chan, char *data, char *buf, struct ast_str **bufstr, int len)
01079 {
01080 char *tmp;
01081 struct ao2_iterator i;
01082 struct user *user;
01083 struct ast_str *str;
01084 AST_DECLARE_APP_ARGS(args,
01085 AST_APP_ARG(string);
01086 AST_APP_ARG(exclude_mac);
01087 );
01088 AST_STANDARD_APP_ARGS(args, data);
01089
01090 if (!(str = ast_str_create(16))) {
01091 return -1;
01092 }
01093
01094
01095 while ((tmp = strstr(args.string, "%{")))
01096 *tmp = '$';
01097
01098 i = ao2_iterator_init(users, 0);
01099 while ((user = ao2_iterator_next(&i))) {
01100 if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
01101 continue;
01102 }
01103 ast_str_substitute_variables_varshead(&str, len, AST_LIST_FIRST(&user->extensions)->headp, args.string);
01104 if (buf) {
01105 size_t slen = len;
01106 ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
01107 } else {
01108 ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
01109 }
01110 user = unref_user(user);
01111 }
01112 ao2_iterator_destroy(&i);
01113
01114 ast_free(str);
01115 return 0;
01116 }
01117
01118 static int pp_each_user_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01119 {
01120 return pp_each_user_helper(chan, data, buf, NULL, len);
01121 }
01122
01123 static int pp_each_user_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01124 {
01125 return pp_each_user_helper(chan, data, NULL, buf, len);
01126 }
01127
01128 static struct ast_custom_function pp_each_user_function = {
01129 .name = "PP_EACH_USER",
01130 .read = pp_each_user_read,
01131 .read2 = pp_each_user_read2,
01132 };
01133
01134
01135 static int pp_each_extension_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, int len)
01136 {
01137 struct user *user;
01138 struct extension *exten;
01139 char path[PATH_MAX];
01140 char *file;
01141 int filelen;
01142 struct ast_str *str;
01143 AST_DECLARE_APP_ARGS(args,
01144 AST_APP_ARG(mac);
01145 AST_APP_ARG(template);
01146 );
01147
01148 AST_STANDARD_APP_ARGS(args, data);
01149
01150 if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
01151 ast_log(LOG_WARNING, "PP_EACH_EXTENSION requries both a macaddress and template filename.\n");
01152 return 0;
01153 }
01154
01155 if (!(user = find_user(args.mac))) {
01156 ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
01157 return 0;
01158 }
01159
01160 snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
01161 filelen = load_file(path, &file);
01162 if (filelen < 0) {
01163 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
01164 if (file) {
01165 ast_free(file);
01166 }
01167 return 0;
01168 }
01169
01170 if (!file) {
01171 return 0;
01172 }
01173
01174 if (!(str = ast_str_create(filelen))) {
01175 return 0;
01176 }
01177
01178 AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
01179 ast_str_substitute_variables_varshead(&str, 0, exten->headp, file);
01180 if (buf) {
01181 size_t slen = len;
01182 ast_build_string(&buf, &slen, "%s", ast_str_buffer(str));
01183 } else {
01184 ast_str_append(bufstr, len, "%s", ast_str_buffer(str));
01185 }
01186 }
01187
01188 ast_free(file);
01189 ast_free(str);
01190
01191 user = unref_user(user);
01192
01193 return 0;
01194 }
01195
01196 static int pp_each_extension_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01197 {
01198 return pp_each_extension_helper(chan, cmd, data, buf, NULL, len);
01199 }
01200
01201 static int pp_each_extension_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01202 {
01203 return pp_each_extension_helper(chan, cmd, data, NULL, buf, len);
01204 }
01205
01206 static struct ast_custom_function pp_each_extension_function = {
01207 .name = "PP_EACH_EXTENSION",
01208 .read = pp_each_extension_read,
01209 .read2 = pp_each_extension_read2,
01210 };
01211
01212
01213 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01214 {
01215 #define FORMAT "%-40.40s %-30.30s\n"
01216 struct ao2_iterator i;
01217 struct http_route *route;
01218
01219 switch(cmd) {
01220 case CLI_INIT:
01221 e->command = "phoneprov show routes";
01222 e->usage =
01223 "Usage: phoneprov show routes\n"
01224 " Lists all registered phoneprov http routes.\n";
01225 return NULL;
01226 case CLI_GENERATE:
01227 return NULL;
01228 }
01229
01230
01231
01232 ast_cli(a->fd, "Static routes\n\n");
01233 ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
01234 i = ao2_iterator_init(http_routes, 0);
01235 while ((route = ao2_iterator_next(&i))) {
01236 if (!route->user)
01237 ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01238 route = unref_route(route);
01239 }
01240 ao2_iterator_destroy(&i);
01241
01242 ast_cli(a->fd, "\nDynamic routes\n\n");
01243 ast_cli(a->fd, FORMAT, "Relative URI", "Template");
01244
01245 i = ao2_iterator_init(http_routes, 0);
01246 while ((route = ao2_iterator_next(&i))) {
01247 if (route->user)
01248 ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01249 route = unref_route(route);
01250 }
01251 ao2_iterator_destroy(&i);
01252
01253 return CLI_SUCCESS;
01254 }
01255
01256 static struct ast_cli_entry pp_cli[] = {
01257 AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
01258 };
01259
01260 static struct ast_http_uri phoneprovuri = {
01261 .callback = phoneprov_callback,
01262 .description = "Asterisk HTTP Phone Provisioning Tool",
01263 .uri = "phoneprov",
01264 .has_subtree = 1,
01265 .data = NULL,
01266 .key = __FILE__,
01267 };
01268
01269 static int load_module(void)
01270 {
01271 profiles = ao2_container_alloc(MAX_PROFILE_BUCKETS, profile_hash_fn, profile_cmp_fn);
01272
01273 http_routes = ao2_container_alloc(MAX_ROUTE_BUCKETS, routes_hash_fn, routes_cmp_fn);
01274
01275 users = ao2_container_alloc(MAX_USER_BUCKETS, users_hash_fn, users_cmp_fn);
01276
01277 AST_LIST_HEAD_INIT_NOLOCK(&global_variables);
01278 ast_mutex_init(&globals_lock);
01279
01280 ast_custom_function_register(&pp_each_user_function);
01281 ast_custom_function_register(&pp_each_extension_function);
01282 ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
01283
01284 set_config();
01285 ast_http_uri_link(&phoneprovuri);
01286
01287 return 0;
01288 }
01289
01290 static int unload_module(void)
01291 {
01292 struct ast_var_t *var;
01293
01294 ast_http_uri_unlink(&phoneprovuri);
01295 ast_custom_function_unregister(&pp_each_user_function);
01296 ast_custom_function_unregister(&pp_each_extension_function);
01297 ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
01298
01299 delete_routes();
01300 delete_users();
01301 delete_profiles();
01302 ao2_ref(profiles, -1);
01303 ao2_ref(http_routes, -1);
01304 ao2_ref(users, -1);
01305
01306 ast_mutex_lock(&globals_lock);
01307 while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01308 ast_var_delete(var);
01309 }
01310 ast_mutex_unlock(&globals_lock);
01311
01312 ast_mutex_destroy(&globals_lock);
01313
01314 return 0;
01315 }
01316
01317 static int reload(void)
01318 {
01319 struct ast_var_t *var;
01320
01321 delete_routes();
01322 delete_users();
01323 delete_profiles();
01324
01325 ast_mutex_lock(&globals_lock);
01326 while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01327 ast_var_delete(var);
01328 }
01329 ast_mutex_unlock(&globals_lock);
01330
01331 set_config();
01332
01333 return 0;
01334 }
01335
01336 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP Phone Provisioning",
01337 .load = load_module,
01338 .unload = unload_module,
01339 .reload = reload,
01340 );