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: 168157 $")
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 struct sockaddr name;
00498 socklen_t namelen = sizeof(name);
00499 int res;
00500
00501 if ((res = getsockname(ser->fd, &name, &namelen))) {
00502 ast_log(LOG_WARNING, "Could not get server IP, breakage likely.\n");
00503 } else {
00504 struct ast_var_t *var;
00505 struct extension *exten_iter;
00506
00507 if ((var = ast_var_assign("SERVER", ast_inet_ntoa(((struct sockaddr_in *)&name)->sin_addr)))) {
00508 AST_LIST_TRAVERSE(&route->user->extensions, exten_iter, entry) {
00509 AST_LIST_INSERT_TAIL(exten_iter->headp, var, entries);
00510 }
00511 }
00512 }
00513 }
00514
00515 pbx_substitute_variables_varshead(AST_LIST_FIRST(&route->user->extensions)->headp, file, tmp, bufsize);
00516
00517 if (file) {
00518 ast_free(file);
00519 }
00520
00521 ast_str_append(&result, 0,
00522 "Content-Type: %s\r\n"
00523 "Content-length: %d\r\n"
00524 "\r\n"
00525 "%s", route->file->mime_type, (int) strlen(tmp), tmp);
00526
00527 if (tmp) {
00528 ast_free(tmp);
00529 }
00530
00531 route = unref_route(route);
00532
00533 return result;
00534 }
00535
00536 out404:
00537 *status = 404;
00538 *title = strdup("Not Found");
00539 *contentlength = 0;
00540 return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
00541
00542 out500:
00543 route = unref_route(route);
00544 *status = 500;
00545 *title = strdup("Internal Server Error");
00546 *contentlength = 0;
00547 return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
00548 }
00549
00550
00551
00552
00553
00554
00555 static void build_route(struct phoneprov_file *pp_file, struct user *user, char *uri)
00556 {
00557 struct http_route *route;
00558
00559 if (!(route = ao2_alloc(sizeof(*route), route_destructor))) {
00560 return;
00561 }
00562
00563 if (ast_string_field_init(route, 32)) {
00564 ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", pp_file->format);
00565 route = unref_route(route);
00566 return;
00567 }
00568
00569 ast_string_field_set(route, uri, S_OR(uri, pp_file->format));
00570 route->user = user;
00571 route->file = pp_file;
00572
00573 ao2_link(http_routes, route);
00574
00575 route = unref_route(route);
00576 }
00577
00578
00579
00580
00581
00582 static void build_profile(const char *name, struct ast_variable *v)
00583 {
00584 struct phone_profile *profile;
00585 struct ast_var_t *var;
00586
00587 if (!(profile = ao2_alloc(sizeof(*profile), profile_destructor))) {
00588 return;
00589 }
00590
00591 if (ast_string_field_init(profile, 32)) {
00592 profile = unref_profile(profile);
00593 return;
00594 }
00595
00596 if (!(profile->headp = ast_calloc(1, sizeof(*profile->headp)))) {
00597 profile = unref_profile(profile);
00598 return;
00599 }
00600
00601 AST_LIST_HEAD_INIT_NOLOCK(&profile->static_files);
00602 AST_LIST_HEAD_INIT_NOLOCK(&profile->dynamic_files);
00603
00604 ast_string_field_set(profile, name, name);
00605 for (; v; v = v->next) {
00606 if (!strcasecmp(v->name, "mime_type")) {
00607 ast_string_field_set(profile, default_mime_type, v->value);
00608 } else if (!strcasecmp(v->name, "setvar")) {
00609 struct ast_var_t *variable;
00610 char *value_copy = ast_strdupa(v->value);
00611
00612 AST_DECLARE_APP_ARGS(args,
00613 AST_APP_ARG(varname);
00614 AST_APP_ARG(varval);
00615 );
00616
00617 AST_NONSTANDARD_APP_ARGS(args, value_copy, '=');
00618 do {
00619 if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00620 break;
00621 args.varname = ast_strip(args.varname);
00622 args.varval = ast_strip(args.varval);
00623 if (ast_strlen_zero(args.varname) || ast_strlen_zero(args.varval))
00624 break;
00625 if ((variable = ast_var_assign(args.varname, args.varval)))
00626 AST_LIST_INSERT_TAIL(profile->headp, variable, entries);
00627 } while (0);
00628 } else if (!strcasecmp(v->name, "staticdir")) {
00629 ast_string_field_set(profile, staticdir, v->value);
00630 } else {
00631 struct phoneprov_file *pp_file;
00632 char *file_extension;
00633 char *value_copy = ast_strdupa(v->value);
00634
00635 AST_DECLARE_APP_ARGS(args,
00636 AST_APP_ARG(filename);
00637 AST_APP_ARG(mimetype);
00638 );
00639
00640 if (!(pp_file = ast_calloc(1, sizeof(*pp_file)))) {
00641 profile = unref_profile(profile);
00642 return;
00643 }
00644 if (ast_string_field_init(pp_file, 32)) {
00645 ast_free(pp_file);
00646 profile = unref_profile(profile);
00647 return;
00648 }
00649
00650 if ((file_extension = strrchr(pp_file->format, '.')))
00651 file_extension++;
00652
00653 AST_STANDARD_APP_ARGS(args, value_copy);
00654
00655
00656
00657
00658
00659
00660
00661 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"))));
00662
00663 if (!strcasecmp(v->name, "static_file")) {
00664 ast_string_field_set(pp_file, format, args.filename);
00665 ast_string_field_build(pp_file, template, "%s%s", profile->staticdir, args.filename);
00666 AST_LIST_INSERT_TAIL(&profile->static_files, pp_file, entry);
00667
00668 build_route(pp_file, NULL, NULL);
00669 } else {
00670 ast_string_field_set(pp_file, format, v->name);
00671 ast_string_field_set(pp_file, template, args.filename);
00672 AST_LIST_INSERT_TAIL(&profile->dynamic_files, pp_file, entry);
00673 }
00674 }
00675 }
00676
00677
00678
00679
00680 ast_mutex_lock(&globals_lock);
00681 AST_LIST_TRAVERSE(&global_variables, var, entries) {
00682 struct ast_var_t *new_var;
00683 if ((new_var = ast_var_assign(var->name, var->value))) {
00684 AST_LIST_INSERT_TAIL(profile->headp, new_var, entries);
00685 }
00686 }
00687 ast_mutex_unlock(&globals_lock);
00688
00689 ao2_link(profiles, profile);
00690
00691 profile = unref_profile(profile);
00692 }
00693
00694 static struct extension *delete_extension(struct extension *exten)
00695 {
00696 struct ast_var_t *var;
00697 while ((var = AST_LIST_REMOVE_HEAD(exten->headp, entries))) {
00698 ast_var_delete(var);
00699 }
00700 ast_free(exten->headp);
00701 ast_string_field_free_memory(exten);
00702
00703 ast_free(exten);
00704
00705 return NULL;
00706 }
00707
00708 static struct extension *build_extension(struct ast_config *cfg, const char *name)
00709 {
00710 struct extension *exten;
00711 struct ast_var_t *var;
00712 const char *tmp;
00713 int i;
00714
00715 if (!(exten = ast_calloc(1, sizeof(*exten)))) {
00716 return NULL;
00717 }
00718
00719 if (ast_string_field_init(exten, 32)) {
00720 ast_free(exten);
00721 exten = NULL;
00722 return NULL;
00723 }
00724
00725 ast_string_field_set(exten, name, name);
00726
00727 if (!(exten->headp = ast_calloc(1, sizeof(*exten->headp)))) {
00728 ast_free(exten);
00729 exten = NULL;
00730 return NULL;
00731 }
00732
00733 for (i = 0; i < PP_VAR_LIST_LENGTH; i++) {
00734 tmp = ast_variable_retrieve(cfg, name, pp_variable_list[i].user_var);
00735
00736
00737 if (i == PP_USERNAME && !tmp) {
00738 if ((var = ast_var_assign(pp_variable_list[PP_USERNAME].template_var, exten->name))) {
00739 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00740 }
00741 continue;
00742 } else if (i == PP_TIMEZONE) {
00743
00744 set_timezone_variables(exten->headp, tmp);
00745 } else if (i == PP_LINENUMBER) {
00746 if (!tmp) {
00747 tmp = "1";
00748 }
00749 exten->index = atoi(tmp);
00750 }
00751
00752 if (tmp && (var = ast_var_assign(pp_variable_list[i].template_var, tmp))) {
00753 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00754 }
00755 }
00756
00757 if (!ast_strlen_zero(global_server)) {
00758 if ((var = ast_var_assign("SERVER", global_server)))
00759 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00760 }
00761
00762 if (!ast_strlen_zero(global_serverport)) {
00763 if ((var = ast_var_assign("SERVER_PORT", global_serverport)))
00764 AST_LIST_INSERT_TAIL(exten->headp, var, entries);
00765 }
00766
00767 return exten;
00768 }
00769
00770 static struct user *unref_user(struct user *user)
00771 {
00772 ao2_ref(user, -1);
00773
00774 return NULL;
00775 }
00776
00777
00778 static struct user *find_user(const char *macaddress)
00779 {
00780 struct user tmp = {
00781 .macaddress = macaddress,
00782 };
00783
00784 return ao2_find(users, &tmp, OBJ_POINTER);
00785 }
00786
00787 static int users_hash_fn(const void *obj, const int flags)
00788 {
00789 const struct user *user = obj;
00790
00791 return ast_str_hash(user->macaddress);
00792 }
00793
00794 static int users_cmp_fn(void *obj, void *arg, int flags)
00795 {
00796 const struct user *user1 = obj, *user2 = arg;
00797
00798 return !strcasecmp(user1->macaddress, user2->macaddress) ? CMP_MATCH | CMP_STOP : 0;
00799 }
00800
00801
00802 static void user_destructor(void *obj)
00803 {
00804 struct user *user = obj;
00805 struct extension *exten;
00806
00807 while ((exten = AST_LIST_REMOVE_HEAD(&user->extensions, entry))) {
00808 exten = delete_extension(exten);
00809 }
00810
00811 if (user->profile) {
00812 user->profile = unref_profile(user->profile);
00813 }
00814
00815 ast_string_field_free_memory(user);
00816 }
00817
00818
00819 static void delete_users(void)
00820 {
00821 struct ao2_iterator i;
00822 struct user *user;
00823
00824 i = ao2_iterator_init(users, 0);
00825 while ((user = ao2_iterator_next(&i))) {
00826 ao2_unlink(users, user);
00827 user = unref_user(user);
00828 }
00829 }
00830
00831
00832 static struct user *build_user(const char *mac, struct phone_profile *profile)
00833 {
00834 struct user *user;
00835
00836
00837 if (!(user = ao2_alloc(sizeof(*user), user_destructor))) {
00838 profile = unref_profile(profile);
00839 return NULL;
00840 }
00841
00842 if (ast_string_field_init(user, 32)) {
00843 profile = unref_profile(profile);
00844 user = unref_user(user);
00845 return NULL;
00846 }
00847
00848 ast_string_field_set(user, macaddress, mac);
00849 user->profile = profile;
00850
00851 return user;
00852 }
00853
00854
00855 static int add_user_extension(struct user *user, struct extension *exten)
00856 {
00857 struct ast_var_t *var;
00858
00859
00860
00861 AST_LIST_TRAVERSE(user->profile->headp, var, entries) {
00862 char expand_buf[VAR_BUF_SIZE] = {0,};
00863 struct ast_var_t *var2;
00864
00865 pbx_substitute_variables_varshead(exten->headp, var->value, expand_buf, sizeof(expand_buf));
00866 if ((var2 = ast_var_assign(var->name, expand_buf)))
00867 AST_LIST_INSERT_TAIL(exten->headp, var2, entries);
00868 }
00869
00870 if (AST_LIST_EMPTY(&user->extensions)) {
00871 AST_LIST_INSERT_HEAD(&user->extensions, exten, entry);
00872 } else {
00873 struct extension *exten_iter;
00874
00875 AST_LIST_TRAVERSE_SAFE_BEGIN(&user->extensions, exten_iter, entry) {
00876 if (exten->index < exten_iter->index) {
00877 AST_LIST_INSERT_BEFORE_CURRENT(exten, entry);
00878 } else if (exten->index == exten_iter->index) {
00879 ast_log(LOG_WARNING, "Duplicate linenumber=%d for %s\n", exten->index, user->macaddress);
00880 return -1;
00881 } else if (!AST_LIST_NEXT(exten_iter, entry)) {
00882 AST_LIST_INSERT_TAIL(&user->extensions, exten, entry);
00883 }
00884 }
00885 AST_LIST_TRAVERSE_SAFE_END;
00886 }
00887
00888 return 0;
00889 }
00890
00891
00892 static int build_user_routes(struct user *user)
00893 {
00894 struct phoneprov_file *pp_file;
00895
00896 AST_LIST_TRAVERSE(&user->profile->dynamic_files, pp_file, entry) {
00897 char expand_buf[VAR_BUF_SIZE] = { 0, };
00898
00899 pbx_substitute_variables_varshead(AST_LIST_FIRST(&user->extensions)->headp, pp_file->format, expand_buf, sizeof(expand_buf));
00900 build_route(pp_file, user, expand_buf);
00901 }
00902
00903 return 0;
00904 }
00905
00906
00907 static int set_config(void)
00908 {
00909 struct ast_config *cfg, *phoneprov_cfg;
00910 char *cat;
00911 struct ast_variable *v;
00912 struct ast_flags config_flags = { 0 };
00913 struct ast_var_t *var;
00914
00915
00916
00917 if ((cfg = ast_config_load("sip.conf", config_flags))) {
00918 ast_copy_string(global_serverport, S_OR(ast_variable_retrieve(cfg, "general", "bindport"), "5060"), sizeof(global_serverport));
00919 ast_config_destroy(cfg);
00920 }
00921
00922 if (!(cfg = ast_config_load("users.conf", config_flags))) {
00923 ast_log(LOG_WARNING, "Unable to load users.conf\n");
00924 return 0;
00925 }
00926
00927
00928 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00929 if (!strcasecmp(v->name, "vmexten")) {
00930 if ((var = ast_var_assign("VOICEMAIL_EXTEN", v->value))) {
00931 ast_mutex_lock(&globals_lock);
00932 AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00933 ast_mutex_unlock(&globals_lock);
00934 }
00935 }
00936 if (!strcasecmp(v->name, "localextenlength")) {
00937 if ((var = ast_var_assign("EXTENSION_LENGTH", v->value)))
00938 ast_mutex_lock(&globals_lock);
00939 AST_LIST_INSERT_TAIL(&global_variables, var, entries);
00940 ast_mutex_unlock(&globals_lock);
00941 }
00942 }
00943
00944 if (!(phoneprov_cfg = ast_config_load("phoneprov.conf", config_flags))) {
00945 ast_log(LOG_ERROR, "Unable to load config phoneprov.conf\n");
00946 ast_config_destroy(cfg);
00947 return -1;
00948 }
00949
00950 cat = NULL;
00951 while ((cat = ast_category_browse(phoneprov_cfg, cat))) {
00952 if (!strcasecmp(cat, "general")) {
00953 for (v = ast_variable_browse(phoneprov_cfg, cat); v; v = v->next) {
00954 if (!strcasecmp(v->name, "serveraddr"))
00955 ast_copy_string(global_server, v->value, sizeof(global_server));
00956 else if (!strcasecmp(v->name, "serveriface")) {
00957 struct in_addr addr;
00958 lookup_iface(v->value, &addr);
00959 ast_copy_string(global_server, ast_inet_ntoa(addr), sizeof(global_server));
00960 } else if (!strcasecmp(v->name, "serverport"))
00961 ast_copy_string(global_serverport, v->value, sizeof(global_serverport));
00962 else if (!strcasecmp(v->name, "default_profile"))
00963 ast_copy_string(global_default_profile, v->value, sizeof(global_default_profile));
00964 }
00965 } else
00966 build_profile(cat, ast_variable_browse(phoneprov_cfg, cat));
00967 }
00968
00969 ast_config_destroy(phoneprov_cfg);
00970
00971 cat = NULL;
00972 while ((cat = ast_category_browse(cfg, cat))) {
00973 const char *tmp, *mac;
00974 struct user *user;
00975 struct phone_profile *profile;
00976 struct extension *exten;
00977
00978 if (!strcasecmp(cat, "general")) {
00979 continue;
00980 }
00981
00982 if (!strcasecmp(cat, "authentication"))
00983 continue;
00984
00985 if (!((tmp = ast_variable_retrieve(cfg, cat, "autoprov")) && ast_true(tmp)))
00986 continue;
00987
00988 if (!(mac = ast_variable_retrieve(cfg, cat, "macaddress"))) {
00989 ast_log(LOG_WARNING, "autoprov set for %s, but no mac address - skipping.\n", cat);
00990 continue;
00991 }
00992
00993 tmp = S_OR(ast_variable_retrieve(cfg, cat, "profile"), global_default_profile);
00994 if (ast_strlen_zero(tmp)) {
00995 ast_log(LOG_WARNING, "No profile for user [%s] with mac '%s' - skipping\n", cat, mac);
00996 continue;
00997 }
00998
00999 if (!(user = find_user(mac))) {
01000 if (!(profile = find_profile(tmp))) {
01001 ast_log(LOG_WARNING, "Could not look up profile '%s' - skipping.\n", tmp);
01002 continue;
01003 }
01004
01005 if (!(user = build_user(mac, profile))) {
01006 ast_log(LOG_WARNING, "Could not create user for '%s' - skipping\n", user->macaddress);
01007 continue;
01008 }
01009
01010 if (!(exten = build_extension(cfg, cat))) {
01011 ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01012 user = unref_user(user);
01013 continue;
01014 }
01015
01016 if (add_user_extension(user, exten)) {
01017 ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01018 user = unref_user(user);
01019 exten = delete_extension(exten);
01020 continue;
01021 }
01022
01023 if (build_user_routes(user)) {
01024 ast_log(LOG_WARNING, "Could not create http routes for %s - skipping\n", user->macaddress);
01025 user = unref_user(user);
01026 continue;
01027 }
01028
01029 ao2_link(users, user);
01030 user = unref_user(user);
01031 } else {
01032 if (!(exten = build_extension(cfg, cat))) {
01033 ast_log(LOG_WARNING, "Could not create extension for %s - skipping\n", user->macaddress);
01034 user = unref_user(user);
01035 continue;
01036 }
01037
01038 if (add_user_extension(user, exten)) {
01039 ast_log(LOG_WARNING, "Could not add extension '%s' to user '%s'\n", exten->name, user->macaddress);
01040 user = unref_user(user);
01041 exten = delete_extension(exten);
01042 continue;
01043 }
01044
01045 user = unref_user(user);
01046 }
01047 }
01048
01049 ast_config_destroy(cfg);
01050
01051 return 0;
01052 }
01053
01054
01055 static void delete_routes(void)
01056 {
01057 struct ao2_iterator i;
01058 struct http_route *route;
01059
01060 i = ao2_iterator_init(http_routes, 0);
01061 while ((route = ao2_iterator_next(&i))) {
01062 ao2_unlink(http_routes, route);
01063 route = unref_route(route);
01064 }
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 }
01079
01080
01081 static int pp_each_user_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01082 {
01083 char *tmp, expand_buf[VAR_BUF_SIZE] = {0,};
01084 struct ao2_iterator i;
01085 struct user *user;
01086 AST_DECLARE_APP_ARGS(args,
01087 AST_APP_ARG(string);
01088 AST_APP_ARG(exclude_mac);
01089 );
01090 AST_STANDARD_APP_ARGS(args, data);
01091
01092
01093 while ((tmp = strstr(args.string, "%{")))
01094 *tmp = '$';
01095
01096 i = ao2_iterator_init(users, 0);
01097 while ((user = ao2_iterator_next(&i))) {
01098 if (!ast_strlen_zero(args.exclude_mac) && !strcasecmp(user->macaddress, args.exclude_mac)) {
01099 continue;
01100 }
01101 pbx_substitute_variables_varshead(AST_LIST_FIRST(&user->extensions)->headp, args.string, expand_buf, sizeof(expand_buf));
01102 ast_build_string(&buf, &len, "%s", expand_buf);
01103 user = unref_user(user);
01104 }
01105
01106 return 0;
01107 }
01108
01109 static struct ast_custom_function pp_each_user_function = {
01110 .name = "PP_EACH_USER",
01111 .synopsis = "Generate a string for each phoneprov user",
01112 .syntax = "PP_EACH_USER(<string>|<exclude_mac>)",
01113 .desc =
01114 "Pass in a string, with phoneprov variables you want substituted in the format of\n"
01115 "%{VARNAME}, and you will get the string rendered for each user in phoneprov\n"
01116 "excluding ones with MAC address <exclude_mac>. Probably not useful outside of\n"
01117 "res_phoneprov.\n"
01118 "\nExample: ${PP_EACH_USER(<item><fn>%{DISPLAY_NAME}</fn></item>|${MAC})",
01119 .read = pp_each_user_exec,
01120 };
01121
01122
01123 static int pp_each_extension_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01124 {
01125 struct user *user;
01126 struct extension *exten;
01127 char path[PATH_MAX];
01128 char *file;
01129 int filelen;
01130 AST_DECLARE_APP_ARGS(args,
01131 AST_APP_ARG(mac);
01132 AST_APP_ARG(template);
01133 );
01134
01135 AST_STANDARD_APP_ARGS(args, data);
01136
01137 if (ast_strlen_zero(args.mac) || ast_strlen_zero(args.template)) {
01138 ast_log(LOG_WARNING, "PP_EACH_EXTENSION requries both a macaddress and template filename.\n");
01139 return 0;
01140 }
01141
01142 if (!(user = find_user(args.mac))) {
01143 ast_log(LOG_WARNING, "Could not find user with mac = '%s'\n", args.mac);
01144 return 0;
01145 }
01146
01147 snprintf(path, sizeof(path), "%s/phoneprov/%s", ast_config_AST_DATA_DIR, args.template);
01148 filelen = load_file(path, &file);
01149 if (filelen < 0) {
01150 ast_log(LOG_WARNING, "Could not load file: %s (%d)\n", path, filelen);
01151 if (file) {
01152 ast_free(file);
01153 }
01154 return 0;
01155 }
01156
01157 if (!file) {
01158 return 0;
01159 }
01160
01161 AST_LIST_TRAVERSE(&user->extensions, exten, entry) {
01162 char expand_buf[VAR_BUF_SIZE] = {0,};
01163 pbx_substitute_variables_varshead(exten->headp, file, expand_buf, sizeof(expand_buf));
01164 ast_build_string(&buf, &len, "%s", expand_buf);
01165 }
01166
01167 ast_free(file);
01168
01169 user = unref_user(user);
01170
01171 return 0;
01172 }
01173
01174 static struct ast_custom_function pp_each_extension_function = {
01175 .name = "PP_EACH_EXTENSION",
01176 .synopsis = "Execute specified template for each extension",
01177 .syntax = "PP_EACH_EXTENSION(<mac>|<template>)",
01178 .desc =
01179 "Output the specified template for each extension associated with the specified\n"
01180 "MAC address.",
01181 .read = pp_each_extension_exec,
01182 };
01183
01184
01185 static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01186 {
01187 #define FORMAT "%-40.40s %-30.30s\n"
01188 struct ao2_iterator i;
01189 struct http_route *route;
01190
01191 switch(cmd) {
01192 case CLI_INIT:
01193 e->command = "phoneprov show routes";
01194 e->usage =
01195 "Usage: phoneprov show routes\n"
01196 " Lists all registered phoneprov http routes.\n";
01197 return NULL;
01198 case CLI_GENERATE:
01199 return NULL;
01200 }
01201
01202
01203
01204 ast_cli(a->fd, "Static routes\n\n");
01205 ast_cli(a->fd, FORMAT, "Relative URI", "Physical location");
01206 i = ao2_iterator_init(http_routes, 0);
01207 while ((route = ao2_iterator_next(&i))) {
01208 if (!route->user)
01209 ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01210 route = unref_route(route);
01211 }
01212
01213 ast_cli(a->fd, "\nDynamic routes\n\n");
01214 ast_cli(a->fd, FORMAT, "Relative URI", "Template");
01215
01216 i = ao2_iterator_init(http_routes, 0);
01217 while ((route = ao2_iterator_next(&i))) {
01218 if (route->user)
01219 ast_cli(a->fd, FORMAT, route->uri, route->file->template);
01220 route = unref_route(route);
01221 }
01222
01223 return CLI_SUCCESS;
01224 }
01225
01226 static struct ast_cli_entry pp_cli[] = {
01227 AST_CLI_DEFINE(handle_show_routes, "Show registered phoneprov http routes"),
01228 };
01229
01230 static struct ast_http_uri phoneprovuri = {
01231 .callback = phoneprov_callback,
01232 .description = "Asterisk HTTP Phone Provisioning Tool",
01233 .uri = "phoneprov",
01234 .has_subtree = 1,
01235 .supports_get = 1,
01236 .data = NULL,
01237 .key = __FILE__,
01238 };
01239
01240 static int load_module(void)
01241 {
01242 profiles = ao2_container_alloc(MAX_PROFILE_BUCKETS, profile_hash_fn, profile_cmp_fn);
01243
01244 http_routes = ao2_container_alloc(MAX_ROUTE_BUCKETS, routes_hash_fn, routes_cmp_fn);
01245
01246 users = ao2_container_alloc(MAX_USER_BUCKETS, users_hash_fn, users_cmp_fn);
01247
01248 AST_LIST_HEAD_INIT_NOLOCK(&global_variables);
01249 ast_mutex_init(&globals_lock);
01250
01251 ast_custom_function_register(&pp_each_user_function);
01252 ast_custom_function_register(&pp_each_extension_function);
01253 ast_cli_register_multiple(pp_cli, ARRAY_LEN(pp_cli));
01254
01255 set_config();
01256 ast_http_uri_link(&phoneprovuri);
01257
01258 return 0;
01259 }
01260
01261 static int unload_module(void)
01262 {
01263 struct ast_var_t *var;
01264
01265 ast_http_uri_unlink(&phoneprovuri);
01266 ast_custom_function_unregister(&pp_each_user_function);
01267 ast_custom_function_unregister(&pp_each_extension_function);
01268 ast_cli_unregister_multiple(pp_cli, ARRAY_LEN(pp_cli));
01269
01270 delete_routes();
01271 delete_users();
01272 delete_profiles();
01273 ao2_ref(profiles, -1);
01274 ao2_ref(http_routes, -1);
01275 ao2_ref(users, -1);
01276
01277 ast_mutex_lock(&globals_lock);
01278 while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01279 ast_var_delete(var);
01280 }
01281 ast_mutex_unlock(&globals_lock);
01282
01283 ast_mutex_destroy(&globals_lock);
01284
01285 return 0;
01286 }
01287
01288 static int reload(void)
01289 {
01290 struct ast_var_t *var;
01291
01292 delete_routes();
01293 delete_users();
01294 delete_profiles();
01295
01296 ast_mutex_lock(&globals_lock);
01297 while ((var = AST_LIST_REMOVE_HEAD(&global_variables, entries))) {
01298 ast_var_delete(var);
01299 }
01300 ast_mutex_unlock(&globals_lock);
01301
01302 set_config();
01303
01304 return 0;
01305 }
01306
01307 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "HTTP Phone Provisioning",
01308 .load = load_module,
01309 .unload = unload_module,
01310 .reload = reload,
01311 );