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: 222186 $")
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 static struct in_addr __ourip = { .s_addr = 0x00000000, };
00074
00075
00076
00077 enum pp_variables {
00078 PP_MACADDRESS,
00079 PP_USERNAME,
00080 PP_FULLNAME,
00081 PP_SECRET,
00082 PP_LABEL,
00083 PP_CALLERID,
00084 PP_TIMEZONE,
00085 PP_LINENUMBER,
00086 PP_VAR_LIST_LENGTH,
00087 };
00088
00089
00090
00091 static const struct pp_variable_lookup {
00092 enum pp_variables id;
00093 const char * const user_var;
00094 const char * const template_var;
00095 } pp_variable_list[] = {
00096 { PP_MACADDRESS, "macaddress", "MAC" },
00097 { PP_USERNAME, "username", "USERNAME" },
00098 { PP_FULLNAME, "fullname", "DISPLAY_NAME" },
00099 { PP_SECRET, "secret", "SECRET" },
00100 { PP_LABEL, "label", "LABEL" },
00101 { PP_CALLERID, "cid_number", "CALLERID" },
00102 { PP_TIMEZONE, "timezone", "TIMEZONE" },
00103 { PP_LINENUMBER, "linenumber", "LINE" },
00104 };
00105
00106
00107 struct phoneprov_file {
00108 AST_DECLARE_STRING_FIELDS(
00109 AST_STRING_FIELD(format);
00110 AST_STRING_FIELD(template);
00111 AST_STRING_FIELD(mime_type);
00112 );
00113 AST_LIST_ENTRY(phoneprov_file) entry;
00114 };
00115
00116
00117 struct phone_profile {
00118 AST_DECLARE_STRING_FIELDS(
00119 AST_STRING_FIELD(name);
00120 AST_STRING_FIELD(default_mime_type);
00121 AST_STRING_FIELD(staticdir);
00122 );
00123 struct varshead *headp;
00124 AST_LIST_HEAD_NOLOCK(, phoneprov_file) static_files;
00125 AST_LIST_HEAD_NOLOCK(, phoneprov_file) dynamic_files;
00126 };
00127
00128 struct extension {
00129 AST_DECLARE_STRING_FIELDS(
00130 AST_STRING_FIELD(name);
00131 );
00132 int index;
00133 struct varshead *headp;
00134 AST_LIST_ENTRY(extension) entry;
00135 };
00136
00137
00138 struct user {
00139 AST_DECLARE_STRING_FIELDS(
00140 AST_STRING_FIELD(macaddress);
00141 );
00142 struct phone_profile *profile;
00143 AST_LIST_HEAD_NOLOCK(, extension) extensions;
00144 };
00145
00146
00147 struct http_route {
00148 AST_DECLARE_STRING_FIELDS(
00149 AST_STRING_FIELD(uri);
00150 );
00151 struct phoneprov_file *file;
00152 struct user *user;
00153
00154 };
00155
00156 static struct ao2_container *profiles;
00157 static struct ao2_container *http_routes;
00158 static struct ao2_container *users;
00159
00160
00161 static struct {
00162 char *ext;
00163 char *mtype;
00164 } mimetypes[] = {
00165 { "png", "image/png" },
00166 { "xml", "text/xml" },
00167 { "jpg", "image/jpeg" },
00168 { "js", "application/x-javascript" },
00169 { "wav", "audio/x-wav" },
00170 { "mp3", "audio/mpeg" },
00171 };
00172
00173 static char global_server[80] = "";
00174 static char global_serverport[6] = "";
00175 static char global_default_profile[80] = "";
00176
00177
00178 static struct varshead global_variables;
00179 static ast_mutex_t globals_lock;
00180
00181
00182 static char *ftype2mtype(const char *ftype)
00183 {
00184 int x;
00185
00186 if (ast_strlen_zero(ftype))
00187 return NULL;
00188
00189 for (x = 0;x < ARRAY_LEN(mimetypes);x++) {
00190 if (!strcasecmp(ftype, mimetypes[x].ext))
00191 return mimetypes[x].mtype;
00192 }
00193
00194 return NULL;
00195 }
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_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_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 !strcmp(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 struct ast_str *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 *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
00404 {
00405 struct http_route *route;
00406 struct http_route search_route = {
00407 .uri = uri,
00408 };
00409 struct ast_str *result = ast_str_create(512);
00410 char path[PATH_MAX];
00411 char *file = NULL;
00412 int len;
00413 int fd;
00414 char buf[256];
00415 struct timeval now = ast_tvnow();
00416 struct ast_tm tm;
00417
00418 if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
00419 goto out404;
00420 }
00421
00422 snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, route->file->template);
00423
00424 if (!route->user) {
00425
00426 fd = open(path, O_RDONLY);
00427 if (fd < 0) {
00428 goto out500;
00429 }
00430
00431 len = lseek(fd, 0, SEEK_END);
00432 lseek(fd, 0, SEEK_SET);
00433 if (len < 0) {
00434 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00435 close(fd);
00436 goto out500;
00437 }
00438
00439 ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
00440 fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
00441 "Server: Asterisk/%s\r\n"
00442 "Date: %s\r\n"
00443 "Connection: close\r\n"
00444 "Cache-Control: no-cache, no-store\r\n"
00445 "Content-Length: %d\r\n"
00446 "Content-Type: %s\r\n\r\n",
00447 ast_get_version(), buf, len, route->file->mime_type);
00448
00449 while ((len = read(fd, buf, sizeof(buf))) > 0) {
00450 if (fwrite(buf, 1, len, ser->f) != len) {
00451 if (errno != EPIPE) {
00452 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00453 } else {
00454 ast_debug(3, "Requester closed the connection while downloading '%s'\n", path);
00455 }
00456 break;
00457 }
00458 }
00459
00460 close(fd);
00461 route = unref_route(route);
00462 return NULL;
00463 } else {
00464 int bufsize;
00465 char *tmp;
00466
00467 len = load_file(path, &file);
00468 if (len < 0) {
00469 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, len);
00470 if (file) {
00471 ast_free(file);
00472 }
00473
00474 goto out500;
00475 }
00476
00477 if (!file) {
00478 goto out500;
00479 }
00480
00481
00482 bufsize = len + VAR_BUF_SIZE;
00483
00484
00485
00486 if (!(tmp = ast_calloc(1, bufsize))) {
00487 if (file) {
00488 ast_free(file);
00489 }
00490
00491 goto out500;
00492 }
00493
00494
00495
00496 if (ast_strlen_zero(global_server)) {
00497 union {
00498 struct sockaddr sa;
00499 struct sockaddr_in sa_in;
00500 } name;
00501 socklen_t namelen = sizeof(name.sa);
00502 int res;
00503
00504 if ((res = getsockname(ser->fd, &name.sa, &namelen))) {
00505 ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
00506 } else {
00507 struct ast_var_t *var;
00508 struct extension *exten_iter;
00509
00510 if ((var = ast_var_assign("SERVER", ast_inet_ntoa(name.sa_in.sin_addr)))) {
00511 AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
00512 AST_LIST_INSERT_TAIL(exten_iter->headp, var, entries);
00513 }
00514 }
00515 }
00516 }
00517
00518 pbx_substitute_variables_varshead(AST_LIST_FIRST(&route->user->extensions)->headp, file, tmp, bufsize);
00519
00520 if (file) {
00521 ast_free(file);
00522 }
00523
00524 ast_str_append(&result, 0,
00525 "Content-Type: %s\r\n"
00526 "Content-length: %d\r\n"
00527 "\r\n"
00528 "%s", route->file->mime_type, (int) strlen(tmp), tmp);
00529
00530 if (tmp) {
00531 ast_free(tmp);
00532 }
00533
00534 route = unref_route(route);
00535
00536 return result;
00537 }
00538
00539 out404:
00540 *status = 404;
00541 *title = strdup("Not Found");
00542 *contentlength = 0;
00543 return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
00544
00545 out500:
00546 route = unref_route(route);
00547 *status = 500;
00548 *title = strdup("Internal Server Error");
00549 *contentlength = 0;
00550 return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
00551 }
00552
00553
00554
00555
00556
00557
00558 static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
00559 {
00560 struct http_route *route;
00561
00562 if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
00563 return;
00564 }
00565
00566 if (ast_string_field_init(route, 32)) {
00567 ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
00568 route = unref_route(route);
00569 return;
00570 }
00571
00572 ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
00573 route->user = user;
00574 route->file = pp_file;
00575
00576 ao2_link(http_routes, route);
00577
00578 route = unref_route(route);
00579 }
00580
00581
00582
00583
00584
00585 static void build_profile(const char *name, struct ast_variable *v)
00586 {
00587 struct phone_profile *profile;
00588 struct ast_var_t *var;
00589
00590 if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
00591 return;
00592 }
00593
00594 if (ast_string_field_init(profile, 32)) {
00595 profile = unref_profile(profile);
00596 return;
00597 }
00598
00599 if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
00600 profile = unref_profile(profile);
00601 return;
00602 }
00603
00604 AST_LIST_HEAD_INIT_NOLOCK(&profile->static_files);
00605 AST_LIST_HEAD_INIT_NOLOCK(&profile->dynamic_files);
00606
00607 ast_string_field_set(profile, name, name);
00608 for (; v; v = v->next) {
00609 if (!strcasecmp(v->name, "mime_type")) {
00610 ast_string_field_set(profile, default_mime_type, v->value);
00611 } else if (!strcasecmp(v->name, "setvar")) {
00612 struct ast_var_t *variable;
00613 char *value_copy = ast_strdupa(v->value);
00614
00615 AST_DECLARE_APP_ARGS(args,
00616 AST_APP_ARG(varname);
00617 AST_APP_ARG(varval);
00618 );
00619
00620 AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
00621 do {
00622 if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00623 break;
00624 args.varname = ast_strip(args.varname);
00625 args.varval = ast_strip(args.varval);
00626 if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00627 break;
00628 if ((variable = ast_var_assign(args.varname, args.varval)))
00629 AST_LIST_INSERT_TAIL(profile->headp, variable, entries);
00630 } while (0);
00631 } else if (!strcasecmp(v->name, "staticdir")) {
00632 ast_string_field_set(profile, staticdir, v->value);
00633 } else {
00634 struct phoneprov_file *pp_file;
00635 char *file_extension;
00636 char *value_copy = ast_strdupa(v->value);
00637
00638 AST_DECLARE_APP_ARGS(args,
00639 AST_APP_ARG(filename);
00640 AST_APP_ARG(mimetype);
00641 );
00642
00643 if (!(pp_file = ast_calloc(1, sizeof(*pp_file)))) {
00644 profile = unref_profile(profile);
00645 return;
00646 }
00647 if (ast_string_field_init(pp_file, 32)) {
00648 ast_free(pp_file);
00649 profile = unref_profile(profile);
00650 return;
00651 }
00652
00653 if ((file_extension = strrchr(pp_file->format, '.')))
00654 file_extension++;
00655
00656 AST_STANDARD_APP_ARGS(args, value_copy);
00657
00658
00659
00660
00661
00662
00663
00664 ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype, (S_OR(S_OR(ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
00665
00666 if (!strcasecmp(v->name, "static_file")) {
00667 ast_string_field_set(pp_file, format, args.filename);
00668 ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
00669 AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
00670
00671 build_route(pp_file, NULL, NULL);
00672 } else {
00673 ast_string_field_set(pp_file, format, v->name);
00674 ast_string_field_set(pp_file, template, args.filename);
00675 AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
00676 }
00677 }
00678 }
00679
00680
00681
00682
00683 ast_mutex_lock(&globals_lock);
00684 AST_LIST_TRAVERSE(&global_variables, var, entries) {
00685 struct ast_var_t *new_var;
00686 if ((new_var = ast_var_assign(var->name, var->value))) {
00687 AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
00688 }
00689 }
00690 ast_mutex_unlock(&globals_lock);
00691
00692 ao2_link(profiles, profile);
00693
00694 profile = unref_profile(profile);
00695 }
00696
00697 static struct extension *delete_extension(struct extension *exten)
00698 {
00699 struct ast_var_t *var;
00700 while ((var = AST_LIST_REMOVE_HEAD(exten->headp, entries))) {
00701 ast_var_delete(var);
00702 }
00703 ast_free(exten->headp);
00704 ast_string_field_free_memory(exten);
00705
00706 ast_free(exten);
00707
00708 return NULL;
00709 }
00710
00711 static struct extension *build_extension(struct ast_config *cfg, const char *name)
00712 {
00713 struct extension *exten;
00714 struct ast_var_t *var;
00715 const char *tmp;
00716 int i;
00717
00718 if (!(exten = ast_calloc(1, sizeof(*exten)))) {
00719 return NULL;
00720 }
00721
00722 if (ast_string_field_init(exten, 32)) {
00723 ast_free(exten);
00724 exten = NULL;
00725 return NULL;
00726 }
00727
00728 ast_string_field_set(exten, name, name);
00729
00730 if (!(exten->headp = ast_calloc(1, sizeof(*exten->headp)))) {
00731 ast_free(exten);
00732 exten = NULL;
00733 return NULL;
00734 }
00735
00736 for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
00737 tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
00738
00739
00740 if (i == PP_USERNAME && !tmp) {
00741 if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, exten->name))) {
00742 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00743 }
00744 continue;
00745 } else if (i == PP_TIMEZONE) {
00746
00747 set_timezone_variables(exten->headp, tmp);
00748 } else if (i == PP_LINENUMBER) {
00749 if (!tmp) {
00750 tmp = "1";
00751 }
00752 exten->index = atoi(tmp);
00753 }
00754
00755 if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp))) {
00756 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00757 }
00758 }
00759
00760 if (!ast_strlen_zero(global_server)) {
00761 if ((var = ast_var_assign("SERVER", global_server)))
00762 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00763 }
00764
00765 if (!ast_strlen_zero(global_serverport)) {
00766 if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
00767 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00768 }
00769
00770 return exten;
00771 }
00772
00773 static struct user *unref_user(struct user *user)
00774 {
00775 ao2_ref(user, -1);
00776
00777 return NULL;
00778 }
00779
00780
00781 static struct user *find_user(const char *macaddress)
00782 {
00783 struct user tmp = {
00784 .macaddress = macaddress,
00785 };
00786
00787 return ao2_find(users, &tmp, OBJ_POINTER);
00788 }
00789
00790 static int users_hash_fn(const void *obj, const int flags)
00791 {
00792 const struct user *user = obj;
00793
00794 return ast_str_hash(user->macaddress);
00795 }
00796
00797 static int users_cmp_fn(void *obj, void *arg, int flags)
00798 {
00799 const struct user *user1 = obj, *user2 = arg;
00800
00801 return !strcasecmp(user1->macaddress, user2->macaddress) ? CMP_MATCH | CMP_STOP : 0;
00802 }
00803
00804
00805 static void user_destructor(void *obj)
00806 {
00807 struct user *user = obj;
00808 struct extension *exten;
00809
00810 while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
00811 exten = delete_extension(exten);
00812 }
00813
00814 if (user->profile) {
00815 user->profile = unref_profile(user->profile);
00816 }
00817
00818 ast_string_field_free_memory(user);
00819 }
00820
00821
00822 static void delete_users(void)
00823 {
00824 struct ao2_iterator i;
00825 struct user *user;
00826
00827 i = ao2_iterator_init(users, 0);
00828 while ((user = ao2_iterator_next(&i))) {
00829 ao2_unlink(users, user);
00830 user = unref_user(user);
00831 }
00832 ao2_iterator_destroy(&i);
00833 }
00834
00835
00836 static struct user *build_user(const char *mac, struct phone_profile *profile)
00837 {
00838 struct user *user;
00839
00840
00841 if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
00842 profile = unref_profile(profile);
00843 return NULL;
00844 }
00845
00846 if (ast_string_field_init(user, 32)) {
00847 profile = unref_profile(profile);
00848 user = unref_user(user);
00849 return NULL;
00850 }
00851
00852 ast_string_field_set(user, macaddress, mac);
00853 user->profile = profile;
00854
00855 return user;
00856 }
00857
00858
00859 static int add_user_extension(struct user *user, struct extension *exten)
00860 {
00861 struct ast_var_t *var;
00862
00863
00864
00865 AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
00866 char expand_buf[VAR_BUF_SIZE] = {0,};
00867 struct ast_var_t *var2;
00868
00869 pbx_substitute_variables_varshead(exten->headp, var->value, expand_buf, sizeof(expand_buf));
00870 if ((var2 = ast_var_assign(var->name, expand_buf)))
00871 AST_LIST_INSERT_TAIL(exten->headp, var2, entries);
00872 }
00873
00874 if (AST_LIST_EMPTY(&user->extensions)) {
00875 AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
00876 } else {
00877 struct extension *exten_iter;
00878
00879 AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
00880 if (exten->index < exten_iter->index) {
00881 AST_LIST_INSERT_BEFORE_CURRENT(exten, entry);
00882 } else if (exten->index == exten_iter->index) {
00883 ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
00884 return -1;
00885 } else if (!AST_LIST_NEXT(exten_iter, entry)) {
00886 AST_LIST_INSERT_TAIL(&user->extensions, exten, entry);
00887 }
00888 }
00889 AST_LIST_TRAVERSE_SAFE_END;
00890 }
00891
00892 return 0;
00893 }
00894
00895
00896 static int build_user_routes(struct user *user)
00897 {
00898 struct phoneprov_file *pp_file;
00899
00900 AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
00901 char expand_buf[VAR_BUF_SIZE] = { 0, };
00902
00903 pbx_substitute_variables_varshead(AST_LIST_FIRST(&user->extensions)->headp, pp_file->format, expand_buf, sizeof(expand_buf));
00904 build_route(pp_file, user, expand_buf);
00905 }
00906
00907 return 0;
00908 }
00909
00910
00911 static int set_config(void)
00912 {
00913 struct ast_config *cfg, *phoneprov_cfg;
00914 char *cat;
00915 struct ast_variable *v;
00916 struct ast_flags config_flags = { 0 };
00917 struct ast_var_t *var;
00918
00919
00920
00921 if ((cfg = ast_config_load("sip.conf", config_flags))) {
00922 ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
00923 ast_config_destroy(cfg);
00924 }
00925
00926 if (!(cfg = ast_config_load("users.conf", config_flags))) {
00927 ast_log(LOG_WARNING, "Unable to load users.conf\n");
00928 return 0;
00929 }
00930
00931
00932 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00933 if (!strcasecmp(v->name, "vmexten")) {
00934 if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value))) {
00935 ast_mutex_lock(&globals_lock);
00936 AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00937 ast_mutex_unlock(&globals_lock);
00938 }
00939 }
00940 if (!strcasecmp(v->name, "localextenlength")) {
00941 if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
00942 ast_mutex_lock(&globals_lock);
00943 AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00944 ast_mutex_unlock(&globals_lock);
00945 }
00946 }
00947
00948 if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))) {
00949 ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
00950 ast_config_destroy(cfg);
00951 return -1;
00952 }
00953
00954 cat = NULL;
00955 while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
00956 if (!strcasecmp(cat, "general")) {
00957 for (v = ast_variable_browse(phoneprov_cfg, cat); v; v = v->next) {
00958 if (!strcasecmp(v->name, "serveraddr"))
00959 ast_copy_string(global_server, v->value, sizeof(global_server));
00960 else if (!strcasecmp(v->name, "serveriface")) {
00961 struct in_addr addr;
00962 lookup_iface(v->value, &addr);
00963 ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
00964 } else if (!strcasecmp(v->name, "serverport"))
00965 ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
00966 else if (!strcasecmp(v->name, "default_profile"))
00967 ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
00968 }
00969 } else
00970 build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
00971 }
00972
00973 ast_config_destroy(phoneprov_cfg);
00974
00975 cat = NULL;
00976 while ((cat = ast_category_browse(cfg, cat))) {
00977 const char *tmp, *mac;
00978 struct user *user;
00979 struct phone_profile *profile;
00980 struct extension *exten;
00981
00982 if (!strcasecmp(cat, "general")) {
00983 continue;
00984 }
00985
00986 if (!strcasecmp(cat, "authentication"))
00987 continue;
00988
00989 if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))
00990 continue;
00991
00992 if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
00993 ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
00994 continue;
00995 }
00996
00997 tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
00998 if (ast_strlen_zero(tmp)) {
00999 ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
01000 continue;
01001 }
01002
01003 if (!(user = find_user(mac))) {
01004 if (!(profile = find_profile(tmp))) {
01005 ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
01006 continue;
01007 }
01008
01009 if (!(user = build_user(mac, profile))) {
01010 ast_log(LOG_WARNING, "Could not create user for '%s' - skipping\n", user->macaddress);
01011 continue;
01012 }
01013
01014 if (!(exten = build_extension(cfg, cat))) {
01015 ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01016 user = unref_user(user);
01017 continue;
01018 }
01019
01020 if (add_user_extension(user, exten)) {
01021 ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01022 user = unref_user(user);
01023 exten = delete_extension(exten);
01024 continue;
01025 }
01026
01027 if (build_user_routes(user)) {
01028 ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->macaddress);
01029 user = unref_user(user);
01030 continue;
01031 }
01032
01033 ao2_link(users, user);
01034 user = unref_user(user);
01035 } else {
01036 if (!(exten = build_extension(cfg, cat))) {
01037 ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01038 user = unref_user(user);
01039 continue;
01040 }
01041
01042 if (add_user_extension(user, exten)) {
01043 ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01044 user = unref_user(user);
01045 exten = delete_extension(exten);
01046 continue;
01047 }
01048
01049 user = unref_user(user);
01050 }
01051 }
01052
01053 ast_config_destroy(cfg);
01054
01055 return 0;
01056 }
01057
01058
01059 static void delete_routes(void)
01060 {
01061 struct ao2_iterator i;
01062 struct http_route *route;
01063
01064 i = ao2_iterator_init(http_routes, 0);
01065 while ((route = ao2_iterator_next(&i))) {
01066 ao2_unlink(http_routes, route);
01067 route = unref_route(route);
01068 }
01069 ao2_iterator_destroy(&i);
01070 }
01071
01072
01073 static void delete_profiles(void)
01074 {
01075 struct ao2_iterator i;
01076 struct phone_profile *profile;
01077
01078 i = ao2_iterator_init(profiles, 0);
01079 while ((profile = ao2_iterator_next(&i))) {
01080 ao2_unlink(profiles, profile);
01081 profile = unref_profile(profile);
01082 }
01083 ao2_iterator_destroy(&i);
01084 }
01085
01086
01087 static int pp_each_user_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01088 {
01089 char *tmp, expand_buf[VAR_BUF_SIZE] = {0,};
01090 struct ao2_iterator i;
01091 struct user *user;
01092 AST_DECLARE_APP_ARGS(args,
01093 AST_APP_ARG(string);
01094 AST_APP_ARG(exclude_mac);
01095 );
01096 AST_STANDARD_APP_ARGS(args, data);
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 pbx_substitute_variables_varshead(AST_LIST_FIRST(&user->extensions)->headp, args.string, expand_buf, sizeof(expand_buf));
01108 ast_build_string(&buf, &len, "%s", expand_buf);
01109 user = unref_user(user);
01110 }
01111 ao2_iterator_destroy(&i);
01112
01113 return 0;
01114 }
01115
01116 static struct ast_custom_function pp_each_user_function = {
01117 .name = "PP_EACH_USER",
01118 .synopsis = "Generate a string for each phoneprov user",
01119 .syntax = "PP_EACH_USER(<string>|<exclude_mac>)",
01120 .desc =
01121 "Pass in a string, with phoneprov variables you want substituted in the format of\n"
01122 "%{VARNAME}, and you will get the string rendered for each user in phoneprov\n"
01123 "excluding ones with MAC address <exclude_mac>. Probably not useful outside of\n"
01124 "res_phoneprov.\n"
01125 "\nExample: ${PP_EACH_USER(<item><fn>%{DISPLAY_NAME}</fn></item>|${MAC})",
01126 .read = pp_each_user_exec,
01127 };
01128
01129
01130 static int pp_each_extension_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01131 {
01132 struct user *user;
01133 struct extension *exten;
01134 char path[PATH_MAX];
01135 char *file;
01136 int filelen;
01137 AST_DECLARE_APP_ARGS(args,
01138 AST_APP_ARG(mac);
01139 AST_APP_ARG(template);
01140 );
01141
01142 AST_STANDARD_APP_ARGS(args, data);
01143
01144 if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
01145 ast_log(LOG_WARNING, "PP_EACH_EXTENSION requries both a macaddress and template filename.\n");
01146 return 0;
01147 }
01148
01149 if (!(user = find_user(args.mac))) {
01150 ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
01151 return 0;
01152 }
01153
01154 snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
01155 filelen = load_file(path, &file);
01156 if (filelen < 0) {
01157 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
01158 if (file) {
01159 ast_free(file);
01160 }
01161 return 0;
01162 }
01163
01164 if (!file) {
01165 return 0;
01166 }
01167
01168 AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
01169 char expand_buf[VAR_BUF_SIZE] = {0,};
01170 pbx_substitute_variables_varshead(exten->headp, file, expand_buf, sizeof(expand_buf));
01171 ast_build_string(&buf, &len, "%s", expand_buf);
01172 }
01173
01174 ast_free(file);
01175
01176 user = unref_user(user);
01177
01178 return 0;
01179 }
01180
01181 static struct ast_custom_function pp_each_extension_function = {
01182 .name = "PP_EACH_EXTENSION",
01183 .synopsis = "Execute specified template for each extension",
01184 .syntax = "PP_EACH_EXTENSION(<mac>|<template>)",
01185 .desc =
01186 "Output the specified template for each extension associated with the specified\n"
01187 "MAC address.",
01188 .read = pp_each_extension_exec,
01189 };
01190
01191
01192 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01193 {
01194 #define FORMAT "%-40.40s %-30.30s\n"
01195 struct ao2_iterator i;
01196 struct http_route *route;
01197
01198 switch(cmd) {
01199 case CLI_INIT:
01200 e->command = "phoneprov show routes";
01201 e->usage =
01202 "Usage: phoneprov show routes\n"
01203 " Lists all registered phoneprov http routes.\n";
01204 return NULL;
01205 case CLI_GENERATE:
01206 return NULL;
01207 }
01208
01209
01210
01211 ast_cli(a->fd, "Static routes\n\n");
01212 ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
01213 i = ao2_iterator_init(http_routes, 0);
01214 while ((route = ao2_iterator_next(&i))) {
01215 if (!route->user)
01216 ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01217 route = unref_route(route);
01218 }
01219 ao2_iterator_destroy(&i);
01220
01221 ast_cli(a->fd, "\nDynamic routes\n\n");
01222 ast_cli(a->fd, FORMAT, "Relative URI", "Template");
01223
01224 i = ao2_iterator_init(http_routes, 0);
01225 while ((route = ao2_iterator_next(&i))) {
01226 if (route->user)
01227 ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01228 route = unref_route(route);
01229 }
01230 ao2_iterator_destroy(&i);
01231
01232 return CLI_SUCCESS;
01233 }
01234
01235 static struct ast_cli_entry pp_cli[] = {
01236 AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
01237 };
01238
01239 static struct ast_http_uri phoneprovuri = {
01240 .callback = phoneprov_callback,
01241 .description = "Asterisk HTTP Phone Provisioning Tool",
01242 .uri = "phoneprov",
01243 .has_subtree = 1,
01244 .supports_get = 1,
01245 .data = NULL,
01246 .key = __FILE__,
01247 };
01248
01249 static int load_module(void)
01250 {
01251 profiles = ao2_container_alloc(MAX_PROFILE_BUCKETS, profile_hash_fn, profile_cmp_fn);
01252
01253 http_routes = ao2_container_alloc(MAX_ROUTE_BUCKETS, routes_hash_fn, routes_cmp_fn);
01254
01255 users = ao2_container_alloc(MAX_USER_BUCKETS, users_hash_fn, users_cmp_fn);
01256
01257 AST_LIST_HEAD_INIT_NOLOCK(&global_variables);
01258 ast_mutex_init(&globals_lock);
01259
01260 ast_custom_function_register(&pp_each_user_function);
01261 ast_custom_function_register(&pp_each_extension_function);
01262 ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
01263
01264 set_config();
01265 ast_http_uri_link(&phoneprovuri);
01266
01267 return 0;
01268 }
01269
01270 static int unload_module(void)
01271 {
01272 struct ast_var_t *var;
01273
01274 ast_http_uri_unlink(&phoneprovuri);
01275 ast_custom_function_unregister(&pp_each_user_function);
01276 ast_custom_function_unregister(&pp_each_extension_function);
01277 ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
01278
01279 delete_routes();
01280 delete_users();
01281 delete_profiles();
01282 ao2_ref(profiles, -1);
01283 ao2_ref(http_routes, -1);
01284 ao2_ref(users, -1);
01285
01286 ast_mutex_lock(&globals_lock);
01287 while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01288 ast_var_delete(var);
01289 }
01290 ast_mutex_unlock(&globals_lock);
01291
01292 ast_mutex_destroy(&globals_lock);
01293
01294 return 0;
01295 }
01296
01297 static int reload(void)
01298 {
01299 struct ast_var_t *var;
01300
01301 delete_routes();
01302 delete_users();
01303 delete_profiles();
01304
01305 ast_mutex_lock(&globals_lock);
01306 while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01307 ast_var_delete(var);
01308 }
01309 ast_mutex_unlock(&globals_lock);
01310
01311 set_config();
01312
01313 return 0;
01314 }
01315
01316 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "HTTP Phone Provisioning",
01317 .load = load_module,
01318 .unload = unload_module,
01319 .reload = reload,
01320 );