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