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