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 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 146799 $")
00033
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <errno.h>
00037 #include <termios.h>
00038 #include <sys/time.h>
00039 #include <time.h>
00040 #include <ctype.h>
00041
00042 #include "asterisk/module.h"
00043 #include "asterisk/lock.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/smdi.h"
00046 #include "asterisk/config.h"
00047 #include "asterisk/astobj.h"
00048 #include "asterisk/io.h"
00049 #include "asterisk/logger.h"
00050 #include "asterisk/utils.h"
00051 #include "asterisk/options.h"
00052 #include "asterisk/stringfields.h"
00053 #include "asterisk/linkedlists.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/pbx.h"
00056
00057
00058 #define SMDI_MSG_EXPIRY_TIME 30000
00059
00060 static const char config_file[] = "smdi.conf";
00061
00062
00063 struct ast_smdi_md_queue {
00064 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
00065 };
00066
00067
00068 struct ast_smdi_mwi_queue {
00069 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
00070 };
00071
00072 struct ast_smdi_interface {
00073 ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
00074 struct ast_smdi_md_queue md_q;
00075 ast_mutex_t md_q_lock;
00076 ast_cond_t md_q_cond;
00077 struct ast_smdi_mwi_queue mwi_q;
00078 ast_mutex_t mwi_q_lock;
00079 ast_cond_t mwi_q_cond;
00080 FILE *file;
00081 int fd;
00082 pthread_t thread;
00083 struct termios mode;
00084 int msdstrip;
00085 long msg_expiry;
00086 };
00087
00088
00089 struct ast_smdi_interface_container {
00090 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
00091 } smdi_ifaces;
00092
00093
00094 struct mailbox_mapping {
00095
00096
00097 unsigned int cur_state:1;
00098
00099 struct ast_smdi_interface *iface;
00100 AST_DECLARE_STRING_FIELDS(
00101
00102 AST_STRING_FIELD(smdi);
00103
00104 AST_STRING_FIELD(mailbox);
00105
00106 AST_STRING_FIELD(context);
00107 );
00108 AST_LIST_ENTRY(mailbox_mapping) entry;
00109 };
00110
00111
00112 #define DEFAULT_POLLING_INTERVAL 10
00113
00114
00115 static struct {
00116
00117 pthread_t thread;
00118 ast_mutex_t lock;
00119 ast_cond_t cond;
00120
00121 AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
00122
00123 unsigned int polling_interval;
00124
00125 unsigned int stop:1;
00126
00127 struct timeval last_poll;
00128 } mwi_monitor = {
00129 .thread = AST_PTHREADT_NULL,
00130 };
00131
00132 static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
00133 {
00134 if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
00135 pthread_cancel(iface->thread);
00136 pthread_join(iface->thread, NULL);
00137 }
00138
00139 iface->thread = AST_PTHREADT_STOP;
00140
00141 if (iface->file)
00142 fclose(iface->file);
00143
00144 ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
00145 ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
00146 ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
00147 ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
00148
00149 ast_mutex_destroy(&iface->md_q_lock);
00150 ast_cond_destroy(&iface->md_q_cond);
00151
00152 ast_mutex_destroy(&iface->mwi_q_lock);
00153 ast_cond_destroy(&iface->mwi_q_cond);
00154
00155 free(iface);
00156
00157 ast_module_unref(ast_module_info->self);
00158 }
00159
00160 void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
00161 {
00162 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00163 }
00164
00165
00166
00167
00168
00169
00170
00171 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00172 {
00173 ast_mutex_lock(&iface->md_q_lock);
00174 ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
00175 ast_cond_broadcast(&iface->md_q_cond);
00176 ast_mutex_unlock(&iface->md_q_lock);
00177 }
00178
00179
00180
00181
00182
00183
00184
00185 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00186 {
00187 ast_mutex_lock(&iface->mwi_q_lock);
00188 ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
00189 ast_cond_broadcast(&iface->mwi_q_cond);
00190 ast_mutex_unlock(&iface->mwi_q_lock);
00191 }
00192
00193 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
00194 {
00195 FILE *file;
00196 int i;
00197
00198 if (!(file = fopen(iface->name, "w"))) {
00199 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
00200 return 1;
00201 }
00202
00203 ASTOBJ_WRLOCK(iface);
00204
00205 fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
00206
00207 for (i = 0; i < iface->msdstrip; i++)
00208 fprintf(file, "0");
00209
00210 fprintf(file, "%s!\x04", mailbox);
00211
00212 fclose(file);
00213
00214 ASTOBJ_UNLOCK(iface);
00215
00216 ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset",
00217 mailbox, iface->name);
00218
00219 return 0;
00220 }
00221
00222 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
00223 {
00224 return smdi_toggle_mwi(iface, mailbox, 1);
00225 }
00226
00227 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
00228 {
00229 return smdi_toggle_mwi(iface, mailbox, 0);
00230 }
00231
00232 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00233 {
00234 ast_mutex_lock(&iface->md_q_lock);
00235 ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
00236 ast_cond_broadcast(&iface->md_q_cond);
00237 ast_mutex_unlock(&iface->md_q_lock);
00238 }
00239
00240 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00241 {
00242 ast_mutex_lock(&iface->mwi_q_lock);
00243 ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
00244 ast_cond_broadcast(&iface->mwi_q_cond);
00245 ast_mutex_unlock(&iface->mwi_q_lock);
00246 }
00247
00248 enum smdi_message_type {
00249 SMDI_MWI,
00250 SMDI_MD,
00251 };
00252
00253 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00254 {
00255 switch (type) {
00256 case SMDI_MWI:
00257 return ast_mutex_lock(&iface->mwi_q_lock);
00258 case SMDI_MD:
00259 return ast_mutex_lock(&iface->md_q_lock);
00260 }
00261
00262 return -1;
00263 }
00264
00265 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00266 {
00267 switch (type) {
00268 case SMDI_MWI:
00269 return ast_mutex_unlock(&iface->mwi_q_lock);
00270 case SMDI_MD:
00271 return ast_mutex_unlock(&iface->md_q_lock);
00272 }
00273
00274 return -1;
00275 }
00276
00277 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00278 {
00279 switch (type) {
00280 case SMDI_MWI:
00281 return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
00282 case SMDI_MD:
00283 return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
00284 }
00285
00286 return NULL;
00287 }
00288
00289 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
00290 {
00291 struct ast_smdi_md_message *md_msg = msg;
00292 struct ast_smdi_mwi_message *mwi_msg = msg;
00293
00294 switch (type) {
00295 case SMDI_MWI:
00296 return mwi_msg->timestamp;
00297 case SMDI_MD:
00298 return md_msg->timestamp;
00299 }
00300
00301 return ast_tv(0, 0);
00302 }
00303
00304 static inline void unref_msg(void *msg, enum smdi_message_type type)
00305 {
00306 struct ast_smdi_md_message *md_msg = msg;
00307 struct ast_smdi_mwi_message *mwi_msg = msg;
00308
00309 switch (type) {
00310 case SMDI_MWI:
00311 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00312 case SMDI_MD:
00313 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00314 }
00315 }
00316
00317 static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
00318 {
00319 struct timeval now;
00320 long elapsed = 0;
00321 void *msg;
00322
00323 lock_msg_q(iface, type);
00324 msg = unlink_from_msg_q(iface, type);
00325 unlock_msg_q(iface, type);
00326
00327
00328 now = ast_tvnow();
00329 while (msg) {
00330 elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
00331
00332 if (elapsed > iface->msg_expiry) {
00333
00334 unref_msg(msg, type);
00335 ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue. "
00336 "Message was %ld milliseconds too old.\n",
00337 iface->name, (type == SMDI_MD) ? "MD" : "MWI",
00338 elapsed - iface->msg_expiry);
00339
00340 lock_msg_q(iface, type);
00341 msg = unlink_from_msg_q(iface, type);
00342 unlock_msg_q(iface, type);
00343 } else {
00344
00345 switch (type) {
00346 case SMDI_MD:
00347 ast_smdi_md_message_push(iface, msg);
00348 break;
00349 case SMDI_MWI:
00350 ast_smdi_mwi_message_push(iface, msg);
00351 break;
00352 }
00353 unref_msg(msg, type);
00354 break;
00355 }
00356 }
00357 }
00358
00359 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
00360 {
00361 void *msg;
00362
00363 purge_old_messages(iface, type);
00364
00365 lock_msg_q(iface, type);
00366 msg = unlink_from_msg_q(iface, type);
00367 unlock_msg_q(iface, type);
00368
00369 return msg;
00370 }
00371
00372 enum {
00373 OPT_SEARCH_TERMINAL = (1 << 0),
00374 OPT_SEARCH_NUMBER = (1 << 1),
00375 };
00376
00377 static void *smdi_msg_find(struct ast_smdi_interface *iface,
00378 enum smdi_message_type type, const char *search_key, struct ast_flags options)
00379 {
00380 void *msg = NULL;
00381
00382 purge_old_messages(iface, type);
00383
00384 switch (type) {
00385 case SMDI_MD:
00386 if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
00387 struct ast_smdi_md_message *md_msg = NULL;
00388
00389
00390
00391 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00392 if (!strcasecmp(iterator->mesg_desk_term, search_key))
00393 md_msg = ASTOBJ_REF(iterator);
00394 } while (0); );
00395
00396 msg = md_msg;
00397 } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
00398 struct ast_smdi_md_message *md_msg = NULL;
00399
00400
00401
00402 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00403 if (!strcasecmp(iterator->mesg_desk_num, search_key))
00404 md_msg = ASTOBJ_REF(iterator);
00405 } while (0); );
00406
00407 msg = md_msg;
00408 } else {
00409
00410 msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key);
00411 }
00412 break;
00413 case SMDI_MWI:
00414 msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
00415 break;
00416 }
00417
00418 return msg;
00419 }
00420
00421 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout,
00422 enum smdi_message_type type, const char *search_key, struct ast_flags options)
00423 {
00424 struct timeval start;
00425 long diff = 0;
00426 void *msg;
00427 ast_cond_t *cond = NULL;
00428 ast_mutex_t *lock = NULL;
00429
00430 switch (type) {
00431 case SMDI_MWI:
00432 cond = &iface->mwi_q_cond;
00433 lock = &iface->mwi_q_lock;
00434 break;
00435 case SMDI_MD:
00436 cond = &iface->md_q_cond;
00437 lock = &iface->md_q_lock;
00438 break;
00439 }
00440
00441 start = ast_tvnow();
00442 while (diff < timeout) {
00443 struct timespec ts = { 0, };
00444 struct timeval tv;
00445
00446 lock_msg_q(iface, type);
00447
00448 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00449 unlock_msg_q(iface, type);
00450 return msg;
00451 }
00452
00453 tv = ast_tvadd(start, ast_tv(0, timeout));
00454 ts.tv_sec = tv.tv_sec;
00455 ts.tv_nsec = tv.tv_usec * 1000;
00456
00457
00458
00459
00460 ast_cond_timedwait(cond, lock, &ts);
00461
00462 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00463 unlock_msg_q(iface, type);
00464 return msg;
00465 }
00466
00467 unlock_msg_q(iface, type);
00468
00469
00470 diff = ast_tvdiff_ms(ast_tvnow(), start);
00471 }
00472
00473 return NULL;
00474 }
00475
00476 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
00477 {
00478 return smdi_msg_pop(iface, SMDI_MD);
00479 }
00480
00481 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
00482 {
00483 struct ast_flags options = { 0 };
00484 return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
00485 }
00486
00487 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
00488 {
00489 return smdi_msg_pop(iface, SMDI_MWI);
00490 }
00491
00492 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
00493 {
00494 struct ast_flags options = { 0 };
00495 return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
00496 }
00497
00498 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
00499 const char *station)
00500 {
00501 struct ast_flags options = { 0 };
00502 return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
00503 }
00504
00505 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
00506 {
00507 return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
00508 }
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519 static void *smdi_read(void *iface_p)
00520 {
00521 struct ast_smdi_interface *iface = iface_p;
00522 struct ast_smdi_md_message *md_msg;
00523 struct ast_smdi_mwi_message *mwi_msg;
00524 char c = '\0';
00525 char *cp = NULL;
00526 int i;
00527 int start = 0;
00528
00529
00530 while ((c = fgetc(iface->file))) {
00531
00532
00533 if (!start) {
00534 if (c == 'M') {
00535 ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
00536 start = 1;
00537 }
00538 continue;
00539 }
00540
00541 if (c == 'D') {
00542 start = 0;
00543
00544 ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
00545
00546 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
00547 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00548 return NULL;
00549 }
00550
00551 ASTOBJ_INIT(md_msg);
00552
00553
00554 for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
00555 md_msg->mesg_desk_num[i] = fgetc(iface->file);
00556 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
00557 }
00558
00559 md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
00560
00561 ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
00562
00563
00564 for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
00565 md_msg->mesg_desk_term[i] = fgetc(iface->file);
00566 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
00567 }
00568
00569 md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
00570
00571 ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
00572
00573
00574 md_msg->type = fgetc(iface->file);
00575
00576 ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
00577
00578
00579 cp = &md_msg->fwd_st[0];
00580 for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
00581 if ((c = fgetc(iface->file)) == ' ') {
00582 *cp = '\0';
00583 ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
00584 break;
00585 }
00586
00587
00588 if (i >= iface->msdstrip) {
00589 ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
00590 *cp++ = c;
00591 } else {
00592 ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the fwd station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
00593 }
00594 }
00595
00596
00597 md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
00598 cp = NULL;
00599
00600 ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
00601
00602
00603
00604 ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
00605
00606
00607 cp = &md_msg->calling_st[0];
00608 for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
00609 if (!isdigit((c = fgetc(iface->file)))) {
00610 *cp = '\0';
00611 ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c);
00612 if (c == ' ') {
00613
00614
00615 i--;
00616 continue;
00617 }
00618 break;
00619 }
00620
00621
00622 if (i >= iface->msdstrip) {
00623 ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
00624 *cp++ = c;
00625 } else {
00626 ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
00627 }
00628 }
00629
00630
00631 md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
00632 cp = NULL;
00633
00634 ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
00635
00636
00637 md_msg->timestamp = ast_tvnow();
00638 ast_smdi_md_message_push(iface, md_msg);
00639 ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
00640
00641 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00642
00643 } else if (c == 'W') {
00644 start = 0;
00645
00646 ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
00647
00648 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
00649 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00650 return NULL;
00651 }
00652
00653 ASTOBJ_INIT(mwi_msg);
00654
00655
00656 fgetc(iface->file);
00657
00658
00659 cp = &mwi_msg->fwd_st[0];
00660 for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
00661 if ((c = fgetc(iface->file)) == ' ') {
00662 *cp = '\0';
00663 break;
00664 }
00665
00666
00667 if (i >= iface->msdstrip)
00668 *cp++ = c;
00669 }
00670
00671
00672 mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
00673 cp = NULL;
00674
00675
00676
00677 ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
00678
00679
00680 for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
00681 mwi_msg->cause[i] = fgetc(iface->file);
00682
00683 mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
00684
00685
00686 mwi_msg->timestamp = ast_tvnow();
00687 ast_smdi_mwi_message_push(iface, mwi_msg);
00688 ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
00689
00690 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00691 } else {
00692 ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
00693 start = 0;
00694 }
00695 }
00696
00697 ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
00698 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00699 return NULL;
00700 }
00701
00702 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
00703 {
00704 free(msg);
00705 }
00706
00707 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
00708 {
00709 free(msg);
00710 }
00711
00712 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
00713 {
00714 ast_string_field_free_memory(mm);
00715 ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
00716 free(mm);
00717 }
00718
00719 static void destroy_all_mailbox_mappings(void)
00720 {
00721 struct mailbox_mapping *mm;
00722
00723 ast_mutex_lock(&mwi_monitor.lock);
00724 while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
00725 destroy_mailbox_mapping(mm);
00726 ast_mutex_unlock(&mwi_monitor.lock);
00727 }
00728
00729 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
00730 {
00731 struct mailbox_mapping *mm;
00732 char *mailbox, *context;
00733
00734 if (!(mm = ast_calloc(1, sizeof(*mm))))
00735 return;
00736
00737 if (ast_string_field_init(mm, 32)) {
00738 free(mm);
00739 return;
00740 }
00741
00742 ast_string_field_set(mm, smdi, var->name);
00743
00744 context = ast_strdupa(var->value);
00745 mailbox = strsep(&context, "@");
00746 if (ast_strlen_zero(context))
00747 context = "default";
00748
00749 ast_string_field_set(mm, mailbox, mailbox);
00750 ast_string_field_set(mm, context, context);
00751
00752 mm->iface = ASTOBJ_REF(iface);
00753
00754 ast_mutex_lock(&mwi_monitor.lock);
00755 AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
00756 ast_mutex_unlock(&mwi_monitor.lock);
00757 }
00758
00759
00760
00761
00762 static void poll_mailbox(struct mailbox_mapping *mm)
00763 {
00764 char buf[1024];
00765 unsigned int state;
00766
00767 snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
00768
00769 state = !!ast_app_has_voicemail(mm->mailbox, NULL);
00770
00771 if (state != mm->cur_state) {
00772 if (state)
00773 ast_smdi_mwi_set(mm->iface, mm->smdi);
00774 else
00775 ast_smdi_mwi_unset(mm->iface, mm->smdi);
00776
00777 mm->cur_state = state;
00778 }
00779 }
00780
00781 static void *mwi_monitor_handler(void *data)
00782 {
00783 while (!mwi_monitor.stop) {
00784 struct timespec ts = { 0, };
00785 struct timeval tv;
00786 struct mailbox_mapping *mm;
00787
00788 ast_mutex_lock(&mwi_monitor.lock);
00789
00790 mwi_monitor.last_poll = ast_tvnow();
00791
00792 AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
00793 poll_mailbox(mm);
00794
00795
00796
00797 tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
00798 ts.tv_sec = tv.tv_sec;
00799 ts.tv_nsec = tv.tv_usec * 1000;
00800 ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
00801
00802 ast_mutex_unlock(&mwi_monitor.lock);
00803 }
00804
00805 return NULL;
00806 }
00807
00808 static struct ast_smdi_interface *alloc_smdi_interface(void)
00809 {
00810 struct ast_smdi_interface *iface;
00811
00812 if (!(iface = ast_calloc(1, sizeof(*iface))))
00813 return NULL;
00814
00815 ASTOBJ_INIT(iface);
00816 ASTOBJ_CONTAINER_INIT(&iface->md_q);
00817 ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
00818
00819 ast_mutex_init(&iface->md_q_lock);
00820 ast_cond_init(&iface->md_q_cond, NULL);
00821
00822 ast_mutex_init(&iface->mwi_q_lock);
00823 ast_cond_init(&iface->mwi_q_cond, NULL);
00824
00825 return iface;
00826 }
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838 static int smdi_load(int reload)
00839 {
00840 struct ast_config *conf;
00841 struct ast_variable *v;
00842 struct ast_smdi_interface *iface = NULL;
00843 int res = 0;
00844
00845
00846 speed_t baud_rate = B9600;
00847 tcflag_t paritybit = PARENB;
00848 tcflag_t charsize = CS7;
00849 int stopbits = 0;
00850
00851 int msdstrip = 0;
00852 long msg_expiry = SMDI_MSG_EXPIRY_TIME;
00853
00854 conf = ast_config_load(config_file);
00855
00856 if (!conf) {
00857 if (reload)
00858 ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
00859 else
00860 ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
00861 return 1;
00862 }
00863
00864
00865
00866
00867
00868
00869 if (reload)
00870 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
00871
00872 for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
00873 if (!strcasecmp(v->name, "baudrate")) {
00874 if (!strcasecmp(v->value, "9600"))
00875 baud_rate = B9600;
00876 else if (!strcasecmp(v->value, "4800"))
00877 baud_rate = B4800;
00878 else if (!strcasecmp(v->value, "2400"))
00879 baud_rate = B2400;
00880 else if (!strcasecmp(v->value, "1200"))
00881 baud_rate = B1200;
00882 else {
00883 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
00884 baud_rate = B9600;
00885 }
00886 } else if (!strcasecmp(v->name, "msdstrip")) {
00887 if (!sscanf(v->value, "%d", &msdstrip)) {
00888 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00889 msdstrip = 0;
00890 } else if (0 > msdstrip || msdstrip > 9) {
00891 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00892 msdstrip = 0;
00893 }
00894 } else if (!strcasecmp(v->name, "msgexpirytime")) {
00895 if (!sscanf(v->value, "%ld", &msg_expiry)) {
00896 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
00897 msg_expiry = SMDI_MSG_EXPIRY_TIME;
00898 }
00899 } else if (!strcasecmp(v->name, "paritybit")) {
00900 if (!strcasecmp(v->value, "even"))
00901 paritybit = PARENB;
00902 else if (!strcasecmp(v->value, "odd"))
00903 paritybit = PARENB | PARODD;
00904 else if (!strcasecmp(v->value, "none"))
00905 paritybit = ~PARENB;
00906 else {
00907 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
00908 paritybit = PARENB;
00909 }
00910 } else if (!strcasecmp(v->name, "charsize")) {
00911 if (!strcasecmp(v->value, "7"))
00912 charsize = CS7;
00913 else if (!strcasecmp(v->value, "8"))
00914 charsize = CS8;
00915 else {
00916 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
00917 charsize = CS7;
00918 }
00919 } else if (!strcasecmp(v->name, "twostopbits")) {
00920 stopbits = ast_true(v->name);
00921 } else if (!strcasecmp(v->name, "smdiport")) {
00922 if (reload) {
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00934 ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
00935 ASTOBJ_UNMARK(iface);
00936 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00937 continue;
00938