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
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 268732 $")
00037
00038 #include <termios.h>
00039 #include <sys/time.h>
00040 #include <time.h>
00041 #include <ctype.h>
00042
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/utils.h"
00046 #define AST_API_MODULE
00047 #include "asterisk/smdi.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/astobj.h"
00050 #include "asterisk/io.h"
00051 #include "asterisk/stringfields.h"
00052 #include "asterisk/linkedlists.h"
00053 #include "asterisk/app.h"
00054 #include "asterisk/pbx.h"
00055 #include "asterisk/channel.h"
00056
00057
00058 #define SMDI_MSG_EXPIRY_TIME 30000
00059
00060 static const char config_file[] = "smdi.conf";
00061 static int smdi_loaded;
00062
00063
00064 struct ast_smdi_md_queue {
00065 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
00066 };
00067
00068
00069 struct ast_smdi_mwi_queue {
00070 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
00071 };
00072
00073 struct ast_smdi_interface {
00074 ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
00075 struct ast_smdi_md_queue md_q;
00076 ast_mutex_t md_q_lock;
00077 ast_cond_t md_q_cond;
00078 struct ast_smdi_mwi_queue mwi_q;
00079 ast_mutex_t mwi_q_lock;
00080 ast_cond_t mwi_q_cond;
00081 FILE *file;
00082 int fd;
00083 pthread_t thread;
00084 struct termios mode;
00085 int msdstrip;
00086 long msg_expiry;
00087 };
00088
00089
00090 struct ast_smdi_interface_container {
00091 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
00092 } smdi_ifaces;
00093
00094
00095 struct mailbox_mapping {
00096
00097
00098 unsigned int cur_state:1;
00099
00100 struct ast_smdi_interface *iface;
00101 AST_DECLARE_STRING_FIELDS(
00102
00103 AST_STRING_FIELD(smdi);
00104
00105 AST_STRING_FIELD(mailbox);
00106
00107 AST_STRING_FIELD(context);
00108 );
00109 AST_LIST_ENTRY(mailbox_mapping) entry;
00110 };
00111
00112
00113 #define DEFAULT_POLLING_INTERVAL 10
00114
00115
00116 static struct {
00117
00118 pthread_t thread;
00119 ast_mutex_t lock;
00120 ast_cond_t cond;
00121
00122 AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
00123
00124 unsigned int polling_interval;
00125
00126 unsigned int stop:1;
00127
00128 struct timeval last_poll;
00129 } mwi_monitor = {
00130 .thread = AST_PTHREADT_NULL,
00131 };
00132
00133 static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
00134 {
00135 if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
00136 pthread_cancel(iface->thread);
00137 pthread_join(iface->thread, NULL);
00138 }
00139
00140 iface->thread = AST_PTHREADT_STOP;
00141
00142 if (iface->file)
00143 fclose(iface->file);
00144
00145 ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
00146 ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
00147 ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
00148 ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
00149
00150 ast_mutex_destroy(&iface->md_q_lock);
00151 ast_cond_destroy(&iface->md_q_cond);
00152
00153 ast_mutex_destroy(&iface->mwi_q_lock);
00154 ast_cond_destroy(&iface->mwi_q_cond);
00155
00156 free(iface);
00157
00158 ast_module_unref(ast_module_info->self);
00159 }
00160
00161 void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
00162 {
00163 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00164 }
00165
00166
00167
00168
00169
00170
00171
00172 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00173 {
00174 ast_mutex_lock(&iface->md_q_lock);
00175 ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
00176 ast_cond_broadcast(&iface->md_q_cond);
00177 ast_mutex_unlock(&iface->md_q_lock);
00178 }
00179
00180
00181
00182
00183
00184
00185
00186 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00187 {
00188 ast_mutex_lock(&iface->mwi_q_lock);
00189 ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
00190 ast_cond_broadcast(&iface->mwi_q_cond);
00191 ast_mutex_unlock(&iface->mwi_q_lock);
00192 }
00193
00194 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
00195 {
00196 FILE *file;
00197 int i;
00198
00199 if (!(file = fopen(iface->name, "w"))) {
00200 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
00201 return 1;
00202 }
00203
00204 ASTOBJ_WRLOCK(iface);
00205
00206 fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
00207
00208 for (i = 0; i < iface->msdstrip; i++)
00209 fprintf(file, "0");
00210
00211 fprintf(file, "%s!\x04", mailbox);
00212
00213 fclose(file);
00214
00215 ASTOBJ_UNLOCK(iface);
00216 ast_debug(1, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
00217
00218 return 0;
00219 }
00220
00221 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
00222 {
00223 return smdi_toggle_mwi(iface, mailbox, 1);
00224 }
00225
00226 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
00227 {
00228 return smdi_toggle_mwi(iface, mailbox, 0);
00229 }
00230
00231 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00232 {
00233 ast_mutex_lock(&iface->md_q_lock);
00234 ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
00235 ast_cond_broadcast(&iface->md_q_cond);
00236 ast_mutex_unlock(&iface->md_q_lock);
00237 }
00238
00239 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00240 {
00241 ast_mutex_lock(&iface->mwi_q_lock);
00242 ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
00243 ast_cond_broadcast(&iface->mwi_q_cond);
00244 ast_mutex_unlock(&iface->mwi_q_lock);
00245 }
00246
00247 enum smdi_message_type {
00248 SMDI_MWI,
00249 SMDI_MD,
00250 };
00251
00252 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00253 {
00254 switch (type) {
00255 case SMDI_MWI:
00256 return ast_mutex_lock(&iface->mwi_q_lock);
00257 case SMDI_MD:
00258 return ast_mutex_lock(&iface->md_q_lock);
00259 }
00260
00261 return -1;
00262 }
00263
00264 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00265 {
00266 switch (type) {
00267 case SMDI_MWI:
00268 return ast_mutex_unlock(&iface->mwi_q_lock);
00269 case SMDI_MD:
00270 return ast_mutex_unlock(&iface->md_q_lock);
00271 }
00272
00273 return -1;
00274 }
00275
00276 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00277 {
00278 switch (type) {
00279 case SMDI_MWI:
00280 return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
00281 case SMDI_MD:
00282 return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
00283 }
00284 return NULL;
00285 }
00286
00287 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
00288 {
00289 struct ast_smdi_md_message *md_msg = msg;
00290 struct ast_smdi_mwi_message *mwi_msg = msg;
00291
00292 switch (type) {
00293 case SMDI_MWI:
00294 return mwi_msg->timestamp;
00295 case SMDI_MD:
00296 return md_msg->timestamp;
00297 }
00298
00299 return ast_tv(0, 0);
00300 }
00301
00302 static inline void unref_msg(void *msg, enum smdi_message_type type)
00303 {
00304 struct ast_smdi_md_message *md_msg = msg;
00305 struct ast_smdi_mwi_message *mwi_msg = msg;
00306
00307 switch (type) {
00308 case SMDI_MWI:
00309 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00310 break;
00311 case SMDI_MD:
00312 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00313 break;
00314 }
00315 }
00316
00317 static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
00318 {
00319 struct timeval now = ast_tvnow();
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 while (msg) {
00329 elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
00330
00331 if (elapsed > iface->msg_expiry) {
00332
00333 unref_msg(msg, type);
00334 ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue. "
00335 "Message was %ld milliseconds too old.\n",
00336 iface->name, (type == SMDI_MD) ? "MD" : "MWI",
00337 elapsed - iface->msg_expiry);
00338
00339 lock_msg_q(iface, type);
00340 msg = unlink_from_msg_q(iface, type);
00341 unlock_msg_q(iface, type);
00342 } else {
00343
00344 switch (type) {
00345 case SMDI_MD:
00346 ast_smdi_md_message_push(iface, msg);
00347 break;
00348 case SMDI_MWI:
00349 ast_smdi_mwi_message_push(iface, msg);
00350 break;
00351 }
00352 unref_msg(msg, type);
00353 break;
00354 }
00355 }
00356 }
00357
00358 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
00359 {
00360 void *msg;
00361
00362 purge_old_messages(iface, type);
00363
00364 lock_msg_q(iface, type);
00365 msg = unlink_from_msg_q(iface, type);
00366 unlock_msg_q(iface, type);
00367
00368 return msg;
00369 }
00370
00371 enum {
00372 OPT_SEARCH_TERMINAL = (1 << 0),
00373 OPT_SEARCH_NUMBER = (1 << 1),
00374 };
00375
00376 static void *smdi_msg_find(struct ast_smdi_interface *iface,
00377 enum smdi_message_type type, const char *search_key, struct ast_flags options)
00378 {
00379 void *msg = NULL;
00380
00381 purge_old_messages(iface, type);
00382
00383 switch (type) {
00384 case SMDI_MD:
00385 if (ast_strlen_zero(search_key)) {
00386 struct ast_smdi_md_message *md_msg = NULL;
00387
00388
00389
00390
00391 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00392 md_msg = ASTOBJ_REF(iterator);
00393 } while (0); );
00394
00395 msg = md_msg;
00396 } else if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
00397 struct ast_smdi_md_message *md_msg = NULL;
00398
00399
00400
00401 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00402 if (!strcasecmp(iterator->mesg_desk_term, search_key))
00403 md_msg = ASTOBJ_REF(iterator);
00404 } while (0); );
00405
00406 msg = md_msg;
00407 } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
00408 struct ast_smdi_md_message *md_msg = NULL;
00409
00410
00411
00412 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00413 if (!strcasecmp(iterator->mesg_desk_num, search_key))
00414 md_msg = ASTOBJ_REF(iterator);
00415 } while (0); );
00416
00417 msg = md_msg;
00418 } else {
00419
00420 msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key);
00421 }
00422 break;
00423 case SMDI_MWI:
00424 if (ast_strlen_zero(search_key)) {
00425 struct ast_smdi_mwi_message *mwi_msg = NULL;
00426
00427
00428
00429
00430 ASTOBJ_CONTAINER_TRAVERSE(&iface->mwi_q, !mwi_msg, do {
00431 mwi_msg = ASTOBJ_REF(iterator);
00432 } while (0); );
00433
00434 msg = mwi_msg;
00435 } else {
00436 msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
00437 }
00438 break;
00439 }
00440
00441 return msg;
00442 }
00443
00444 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout,
00445 enum smdi_message_type type, const char *search_key, struct ast_flags options)
00446 {
00447 struct timeval start;
00448 long diff = 0;
00449 void *msg;
00450 ast_cond_t *cond = NULL;
00451 ast_mutex_t *lock = NULL;
00452
00453 switch (type) {
00454 case SMDI_MWI:
00455 cond = &iface->mwi_q_cond;
00456 lock = &iface->mwi_q_lock;
00457 break;
00458 case SMDI_MD:
00459 cond = &iface->md_q_cond;
00460 lock = &iface->md_q_lock;
00461 break;
00462 }
00463
00464 start = ast_tvnow();
00465
00466 while (diff < timeout) {
00467 struct timespec ts = { 0, };
00468 struct timeval wait;
00469
00470 lock_msg_q(iface, type);
00471
00472 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00473 unlock_msg_q(iface, type);
00474 return msg;
00475 }
00476
00477 wait = ast_tvadd(start, ast_tv(0, timeout));
00478 ts.tv_sec = wait.tv_sec;
00479 ts.tv_nsec = wait.tv_usec * 1000;
00480
00481
00482
00483
00484 ast_cond_timedwait(cond, lock, &ts);
00485
00486 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00487 unlock_msg_q(iface, type);
00488 return msg;
00489 }
00490
00491 unlock_msg_q(iface, type);
00492
00493
00494 diff = ast_tvdiff_ms(ast_tvnow(), start);
00495 }
00496
00497 return NULL;
00498 }
00499
00500 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
00501 {
00502 return smdi_msg_pop(iface, SMDI_MD);
00503 }
00504
00505 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
00506 {
00507 struct ast_flags options = { 0 };
00508 return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
00509 }
00510
00511 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
00512 {
00513 return smdi_msg_pop(iface, SMDI_MWI);
00514 }
00515
00516 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
00517 {
00518 struct ast_flags options = { 0 };
00519 return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
00520 }
00521
00522 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
00523 const char *station)
00524 {
00525 struct ast_flags options = { 0 };
00526 return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
00527 }
00528
00529 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
00530 {
00531 return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 static void *smdi_read(void *iface_p)
00544 {
00545 struct ast_smdi_interface *iface = iface_p;
00546 struct ast_smdi_md_message *md_msg;
00547 struct ast_smdi_mwi_message *mwi_msg;
00548 char c = '\0';
00549 char *cp = NULL;
00550 int i;
00551 int start = 0;
00552
00553
00554 while ((c = fgetc(iface->file))) {
00555
00556
00557 if (!start) {
00558 if (c == 'M') {
00559 ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
00560 start = 1;
00561 }
00562 continue;
00563 }
00564
00565 if (c == 'D') {
00566 start = 0;
00567
00568 ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
00569
00570 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
00571 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00572 return NULL;
00573 }
00574
00575 ASTOBJ_INIT(md_msg);
00576
00577
00578 for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
00579 md_msg->mesg_desk_num[i] = fgetc(iface->file);
00580 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
00581 }
00582
00583 md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
00584
00585 ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
00586
00587
00588 for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
00589 md_msg->mesg_desk_term[i] = fgetc(iface->file);
00590 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
00591 }
00592
00593 md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
00594
00595 ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
00596
00597
00598 md_msg->type = fgetc(iface->file);
00599
00600 ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
00601
00602
00603 cp = &md_msg->fwd_st[0];
00604 for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
00605 if ((c = fgetc(iface->file)) == ' ') {
00606 *cp = '\0';
00607 ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
00608 break;
00609 }
00610
00611
00612 if (i >= iface->msdstrip) {
00613 ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
00614 *cp++ = c;
00615 } else {
00616 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);
00617 }
00618 }
00619
00620
00621 md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
00622 cp = NULL;
00623
00624 ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
00625
00626
00627
00628 ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
00629
00630
00631 cp = &md_msg->calling_st[0];
00632 for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
00633 if (!isdigit((c = fgetc(iface->file)))) {
00634 *cp = '\0';
00635 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);
00636 if (c == ' ') {
00637
00638
00639 i--;
00640 continue;
00641 }
00642 break;
00643 }
00644
00645
00646 if (i >= iface->msdstrip) {
00647 ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
00648 *cp++ = c;
00649 } else {
00650 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);
00651 }
00652 }
00653
00654
00655 md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
00656 cp = NULL;
00657
00658 ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
00659
00660
00661 md_msg->timestamp = ast_tvnow();
00662 ast_smdi_md_message_push(iface, md_msg);
00663 ast_log(LOG_DEBUG, "Received SMDI MD message on %s\n", iface->name);
00664
00665 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00666
00667 } else if (c == 'W') {
00668 start = 0;
00669
00670 ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
00671
00672 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
00673 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00674 return NULL;
00675 }
00676
00677 ASTOBJ_INIT(mwi_msg);
00678
00679
00680 fgetc(iface->file);
00681
00682
00683 cp = &mwi_msg->fwd_st[0];
00684 for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
00685 if ((c = fgetc(iface->file)) == ' ') {
00686 *cp = '\0';
00687 break;
00688 }
00689
00690
00691 if (i >= iface->msdstrip)
00692 *cp++ = c;
00693 }
00694
00695
00696 mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
00697 cp = NULL;
00698
00699
00700
00701 ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
00702
00703
00704 for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
00705 mwi_msg->cause[i] = fgetc(iface->file);
00706
00707 mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
00708
00709
00710 mwi_msg->timestamp = ast_tvnow();
00711 ast_smdi_mwi_message_push(iface, mwi_msg);
00712 ast_log(LOG_DEBUG, "Received SMDI MWI message on %s\n", iface->name);
00713
00714 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00715 } else {
00716 ast_log(LOG_ERROR, "Unknown SMDI message type received on %s (M%c).\n", iface->name, c);
00717 start = 0;
00718 }
00719 }
00720
00721 ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
00722 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00723 return NULL;
00724 }
00725
00726 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
00727 {
00728 ast_free(msg);
00729 }
00730
00731 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
00732 {
00733 ast_free(msg);
00734 }
00735
00736 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
00737 {
00738 ast_string_field_free_memory(mm);
00739 ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
00740 free(mm);
00741 }
00742
00743 static void destroy_all_mailbox_mappings(void)
00744 {
00745 struct mailbox_mapping *mm;
00746
00747 ast_mutex_lock(&mwi_monitor.lock);
00748 while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
00749 destroy_mailbox_mapping(mm);
00750 ast_mutex_unlock(&mwi_monitor.lock);
00751 }
00752
00753 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
00754 {
00755 struct mailbox_mapping *mm;
00756 char *mailbox, *context;
00757
00758 if (!(mm = ast_calloc(1, sizeof(*mm))))
00759 return;
00760
00761 if (ast_string_field_init(mm, 32)) {
00762 free(mm);
00763 return;
00764 }
00765
00766 ast_string_field_set(mm, smdi, var->name);
00767
00768 context = ast_strdupa(var->value);
00769 mailbox = strsep(&context, "@");
00770 if (ast_strlen_zero(context))
00771 context = "default";
00772
00773 ast_string_field_set(mm, mailbox, mailbox);
00774 ast_string_field_set(mm, context, context);
00775
00776 mm->iface = ASTOBJ_REF(iface);
00777
00778 ast_mutex_lock(&mwi_monitor.lock);
00779 AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
00780 ast_mutex_unlock(&mwi_monitor.lock);
00781 }
00782
00783
00784
00785
00786 static void poll_mailbox(struct mailbox_mapping *mm)
00787 {
00788 char buf[1024];
00789 unsigned int state;
00790
00791 snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
00792
00793 state = !!ast_app_has_voicemail(mm->mailbox, NULL);
00794
00795 if (state != mm->cur_state) {
00796 if (state)
00797 ast_smdi_mwi_set(mm->iface, mm->smdi);
00798 else
00799 ast_smdi_mwi_unset(mm->iface, mm->smdi);
00800
00801 mm->cur_state = state;
00802 }
00803 }
00804
00805 static void *mwi_monitor_handler(void *data)
00806 {
00807 while (!mwi_monitor.stop) {
00808 struct timespec ts = { 0, };
00809 struct timeval polltime;
00810 struct mailbox_mapping *mm;
00811
00812 ast_mutex_lock(&mwi_monitor.lock);
00813
00814 mwi_monitor.last_poll = ast_tvnow();
00815
00816 AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
00817 poll_mailbox(mm);
00818
00819
00820
00821 polltime = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
00822 ts.tv_sec = polltime.tv_sec;
00823 ts.tv_nsec = polltime.tv_usec * 1000;
00824 ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
00825
00826 ast_mutex_unlock(&mwi_monitor.lock);
00827 }
00828
00829 return NULL;
00830 }
00831
00832 static struct ast_smdi_interface *alloc_smdi_interface(void)
00833 {
00834 struct ast_smdi_interface *iface;
00835
00836 if (!(iface = ast_calloc(1, sizeof(*iface))))
00837 return NULL;
00838
00839 ASTOBJ_INIT(iface);
00840 ASTOBJ_CONTAINER_INIT(&iface->md_q);
00841 ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
00842
00843 ast_mutex_init(&iface->md_q_lock);
00844 ast_cond_init(&iface->md_q_cond, NULL);
00845
00846 ast_mutex_init(&iface->mwi_q_lock);
00847 ast_cond_init(&iface->mwi_q_cond, NULL);
00848
00849 return iface;
00850 }
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862 static int smdi_load(int reload)
00863 {
00864 struct ast_config *conf;
00865 struct ast_variable *v;
00866 struct ast_smdi_interface *iface = NULL;
00867 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00868 int res = 0;
00869
00870
00871 speed_t baud_rate = B9600;
00872 tcflag_t paritybit = PARENB;
00873 tcflag_t charsize = CS7;
00874 int stopbits = 0;
00875
00876 int msdstrip = 0;
00877 long msg_expiry = SMDI_MSG_EXPIRY_TIME;
00878
00879 if (!(conf = ast_config_load(config_file, config_flags)) || conf == CONFIG_STATUS_FILEINVALID) {
00880 if (reload)
00881 ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
00882 else
00883 ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
00884 return 1;
00885 } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
00886 return 0;
00887
00888
00889
00890
00891
00892
00893 if (reload)
00894 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
00895
00896 for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
00897 if (!strcasecmp(v->name, "baudrate")) {
00898 if (!strcasecmp(v->value, "9600"))
00899 baud_rate = B9600;
00900 else if (!strcasecmp(v->value, "4800"))
00901 baud_rate = B4800;
00902 else if (!strcasecmp(v->value, "2400"))
00903 baud_rate = B2400;
00904 else if (!strcasecmp(v->value, "1200"))
00905 baud_rate = B1200;
00906 else {
00907 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
00908 baud_rate = B9600;
00909 }
00910 } else if (!strcasecmp(v->name, "msdstrip")) {
00911 if (!sscanf(v->value, "%30d", &msdstrip)) {
00912 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00913 msdstrip = 0;
00914 } else if (0 > msdstrip || msdstrip > 9) {
00915 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00916 msdstrip = 0;
00917 }
00918 } else if (!strcasecmp(v->name, "msgexpirytime")) {
00919 if (!sscanf(v->value, "%30ld", &msg_expiry)) {
00920 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
00921 msg_expiry = SMDI_MSG_EXPIRY_TIME;
00922 }
00923 } else if (!strcasecmp(v->name, "paritybit")) {
00924 if (!strcasecmp(v->value, "even"))
00925 paritybit = PARENB;
00926 else if (!strcasecmp(v->value, "odd"))
00927 paritybit = PARENB | PARODD;
00928 else if (!strcasecmp(v->value, "none"))
00929 paritybit = ~PARENB;
00930 else {
00931 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
00932 paritybit = PARENB;
00933 }
00934 } else if (!strcasecmp(v->name, "charsize")) {
00935 if (!strcasecmp(v->value, "7"))
00936 charsize = CS7;
00937 else if (!strcasecmp(v->value, "8"))
00938 charsize = CS8;
00939 else {
00940 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
00941 charsize = CS7;
00942 }
00943 } else if (!strcasecmp(v->name, "twostopbits")) {
00944 stopbits = ast_true(v->name);
00945 } else if (!strcasecmp(v->name, "smdiport")) {
00946 if (reload) {
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00958 ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
00959 ASTOBJ_UNMARK(iface);
00960 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00961 continue;
00962 }
00963 }
00964
00965 if (!(iface = alloc_smdi_interface()))
00966 continue;
00967
00968 ast_copy_string(iface->name, v->value, sizeof(iface->name));
00969
00970 iface->thread = AST_PTHREADT_NULL;
00971
00972 if (!(iface->file = fopen(iface->name, "r"))) {
00973 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
00974 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00975 continue;
00976 }
00977
00978 iface->fd = fileno(iface->file);
00979
00980
00981
00982
00983 if (tcgetattr(iface->fd, &iface->mode)) {
00984 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
00985 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00986 continue;
00987 }
00988
00989
00990 if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
00991 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
00992 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00993 continue;
00994 }
00995
00996
00997 if (stopbits)
00998 iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;
00999 else
01000 iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;
01001
01002
01003 iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
01004
01005
01006 iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
01007
01008
01009 if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
01010 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
01011 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01012 continue;
01013 }
01014
01015
01016 iface->msdstrip = msdstrip;
01017
01018
01019 iface->msg_expiry = msg_expiry;
01020
01021
01022 ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
01023 if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
01024 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
01025 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01026 continue;
01027 }
01028
01029 ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
01030 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01031 ast_module_ref(ast_module_info->self);
01032 } else {
01033 ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
01034 }
01035 }
01036
01037 destroy_all_mailbox_mappings();
01038 mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
01039
01040 iface = NULL;
01041
01042 for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
01043 if (!strcasecmp(v->name, "smdiport")) {
01044 if (iface)
01045 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01046
01047 if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
01048 ast_log(LOG_NOTICE, "SMDI interface %s not found\n", v->value);
01049 continue;
01050 }
01051 } else if (!strcasecmp(v->name, "pollinginterval")) {
01052 if (sscanf(v->value, "%30u", &mwi_monitor.polling_interval) != 1) {
01053 ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
01054 mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
01055 }
01056 } else {
01057 if (!iface) {
01058 ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
01059 continue;
01060 }
01061 append_mailbox_mapping(v, iface);
01062 }
01063 }
01064
01065 if (iface)
01066 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01067
01068 ast_config_destroy(conf);
01069
01070 if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
01071 && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
01072 ast_log(LOG_ERROR, "Failed to start MWI monitoring thread. This module will not operate.\n");
01073 return AST_MODULE_LOAD_FAILURE;
01074 }
01075
01076
01077 if (reload)
01078 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
01079
01080 ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
01081
01082 if (!smdi_ifaces.head)
01083 res = 1;
01084 ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
01085
01086 return res;
01087 }
01088
01089 struct smdi_msg_datastore {
01090 unsigned int id;
01091 struct ast_smdi_interface *iface;
01092 struct ast_smdi_md_message *md_msg;
01093 };
01094
01095 static void smdi_msg_datastore_destroy(void *data)
01096 {
01097 struct smdi_msg_datastore *smd = data;
01098
01099 if (smd->iface)
01100 ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
01101
01102 if (smd->md_msg)
01103 ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
01104
01105 free(smd);
01106 }
01107
01108 static const struct ast_datastore_info smdi_msg_datastore_info = {
01109 .type = "SMDIMSG",
01110 .destroy = smdi_msg_datastore_destroy,
01111 };
01112
01113 static int smdi_msg_id;
01114
01115
01116 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
01117
01118 AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS
01119 AST_APP_OPTION('t', OPT_SEARCH_TERMINAL),
01120 AST_APP_OPTION('n', OPT_SEARCH_NUMBER),
01121 END_OPTIONS );
01122
01123 static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01124 {
01125 struct ast_module_user *u;
01126 AST_DECLARE_APP_ARGS(args,
01127 AST_APP_ARG(port);
01128 AST_APP_ARG(search_key);
01129 AST_APP_ARG(timeout);
01130 AST_APP_ARG(options);
01131 );
01132 struct ast_flags options = { 0 };
01133 unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
01134 int res = -1;
01135 char *parse = NULL;
01136 struct smdi_msg_datastore *smd = NULL;
01137 struct ast_datastore *datastore = NULL;
01138 struct ast_smdi_interface *iface = NULL;
01139 struct ast_smdi_md_message *md_msg = NULL;
01140
01141 u = ast_module_user_add(chan);
01142
01143 if (ast_strlen_zero(data)) {
01144 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
01145 goto return_error;
01146 }
01147
01148 if (!chan) {
01149 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
01150 goto return_error;
01151 }
01152
01153 ast_autoservice_start(chan);
01154
01155 parse = ast_strdupa(data);
01156 AST_STANDARD_APP_ARGS(args, parse);
01157
01158 if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
01159 ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
01160 goto return_error;
01161 }
01162
01163 if (!(iface = ast_smdi_interface_find(args.port))) {
01164 ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
01165 goto return_error;
01166 }
01167
01168 if (!ast_strlen_zero(args.options)) {
01169 ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
01170 }
01171
01172 if (!ast_strlen_zero(args.timeout)) {
01173 if (sscanf(args.timeout, "%30u", &timeout) != 1) {
01174 ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
01175 timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
01176 }
01177 }
01178
01179 if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) {
01180 ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after "
01181 "waiting %u ms.\n", args.search_key, timeout);
01182 goto return_error;
01183 }
01184
01185 if (!(smd = ast_calloc(1, sizeof(*smd))))
01186 goto return_error;
01187
01188 smd->iface = ASTOBJ_REF(iface);
01189 smd->md_msg = ASTOBJ_REF(md_msg);
01190 smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
01191 snprintf(buf, len, "%u", smd->id);
01192
01193 if (!(datastore = ast_datastore_alloc(&smdi_msg_datastore_info, buf)))
01194 goto return_error;
01195
01196 datastore->data = smd;
01197
01198 ast_channel_lock(chan);
01199 ast_channel_datastore_add(chan, datastore);
01200 ast_channel_unlock(chan);
01201
01202 res = 0;
01203
01204 return_error:
01205 if (iface)
01206 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01207
01208 if (md_msg)
01209 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
01210
01211 if (smd && !datastore)
01212 smdi_msg_datastore_destroy(smd);
01213
01214 if (parse)
01215 ast_autoservice_stop(chan);
01216
01217 ast_module_user_remove(u);
01218
01219 return res;
01220 }
01221
01222 static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01223 {
01224 struct ast_module_user *u;
01225 int res = -1;
01226 AST_DECLARE_APP_ARGS(args,
01227 AST_APP_ARG(id);
01228 AST_APP_ARG(component);
01229 );
01230 char *parse;
01231 struct ast_datastore *datastore = NULL;
01232 struct smdi_msg_datastore *smd = NULL;
01233
01234 u = ast_module_user_add(chan);
01235
01236 if (!chan) {
01237 ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
01238 goto return_error;
01239 }
01240
01241 if (ast_strlen_zero(data)) {
01242 ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
01243 goto return_error;
01244 }
01245
01246 parse = ast_strdupa(data);
01247 AST_STANDARD_APP_ARGS(args, parse);
01248
01249 if (ast_strlen_zero(args.id)) {
01250 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
01251 goto return_error;
01252 }
01253
01254 if (ast_strlen_zero(args.component)) {
01255 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
01256 goto return_error;
01257 }
01258
01259 ast_channel_lock(chan);
01260 datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
01261 ast_channel_unlock(chan);
01262
01263 if (!datastore) {
01264 ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
01265 goto return_error;
01266 }
01267
01268 smd = datastore->data;
01269
01270 if (!strcasecmp(args.component, "number")) {
01271 ast_copy_string(buf, smd->md_msg->mesg_desk_num, len);
01272 } else if (!strcasecmp(args.component, "terminal")) {
01273 ast_copy_string(buf, smd->md_msg->mesg_desk_term, len);
01274 } else if (!strcasecmp(args.component, "station")) {
01275 ast_copy_string(buf, smd->md_msg->fwd_st, len);
01276 } else if (!strcasecmp(args.component, "callerid")) {
01277 ast_copy_string(buf, smd->md_msg->calling_st, len);
01278 } else if (!strcasecmp(args.component, "type")) {
01279 snprintf(buf, len, "%c", smd->md_msg->type);
01280 } else {
01281 ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
01282 args.component);
01283 goto return_error;
01284 }
01285
01286 res = 0;
01287
01288 return_error:
01289 ast_module_user_remove(u);
01290
01291 return res;
01292 }
01293
01294 static struct ast_custom_function smdi_msg_retrieve_function = {
01295 .name = "SMDI_MSG_RETRIEVE",
01296 .synopsis = "Retrieve an SMDI message.",
01297 .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])",
01298 .desc =
01299 " This function is used to retrieve an incoming SMDI message. It returns\n"
01300 "an ID which can be used with the SMDI_MSG() function to access details of\n"
01301 "the message. Note that this is a destructive function in the sense that\n"
01302 "once an SMDI message is retrieved using this function, it is no longer in\n"
01303 "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
01304 "channels. The timeout for this function is optional, and the default is\n"
01305 "3 seconds. When providing a timeout, it should be in milliseconds.\n"
01306 " The default search is done on the forwarding station ID. However, if\n"
01307 "you set one of the search key options in the options field, you can change\n"
01308 "this behavior.\n"
01309 " Options:\n"
01310 " t - Instead of searching on the forwarding station, search on the message\n"
01311 " desk terminal.\n"
01312 " n - Instead of searching on the forwarding station, search on the message\n"
01313 " desk number.\n"
01314 "",
01315 .read = smdi_msg_retrieve_read,
01316 };
01317
01318 static struct ast_custom_function smdi_msg_function = {
01319 .name = "SMDI_MSG",
01320 .synopsis = "Retrieve details about an SMDI message.",
01321 .syntax = "SMDI_MSG(<message_id>,<component>)",
01322 .desc =
01323 " This function is used to access details of an SMDI message that was\n"
01324 "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
01325 "function.\n"
01326 " Valid message components are:\n"
01327 " number - The message desk number\n"
01328 " terminal - The message desk terminal\n"
01329 " station - The forwarding station\n"
01330 " callerid - The callerID of the calling party that was forwarded\n"
01331 " type - The call type. The value here is the exact character\n"
01332 " that came in on the SMDI link. Typically, example values\n"
01333 " are: D - Direct Calls, A - Forward All Calls,\n"
01334 " B - Forward Busy Calls, N - Forward No Answer Calls\n"
01335 "",
01336 .read = smdi_msg_read,
01337 };
01338
01339 static int _unload_module(int fromload);
01340
01341 static int load_module(void)
01342 {
01343 int res;
01344 smdi_loaded = 1;
01345
01346
01347 memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
01348 ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
01349
01350 ast_mutex_init(&mwi_monitor.lock);
01351 ast_cond_init(&mwi_monitor.cond, NULL);
01352
01353
01354 res = smdi_load(0);
01355 if (res < 0) {
01356 _unload_module(1);
01357 return res;
01358 } else if (res == 1) {
01359 _unload_module(1);
01360 ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
01361 return AST_MODULE_LOAD_DECLINE;
01362 }
01363
01364 ast_custom_function_register(&smdi_msg_retrieve_function);
01365 ast_custom_function_register(&smdi_msg_function);
01366
01367 return AST_MODULE_LOAD_SUCCESS;
01368 }
01369
01370 static int _unload_module(int fromload)
01371 {
01372 if (!smdi_loaded) {
01373 return 0;
01374 }
01375
01376
01377 ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
01378 ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
01379
01380 destroy_all_mailbox_mappings();
01381
01382 ast_mutex_lock(&mwi_monitor.lock);
01383 mwi_monitor.stop = 1;
01384 ast_cond_signal(&mwi_monitor.cond);
01385 ast_mutex_unlock(&mwi_monitor.lock);
01386
01387 if (mwi_monitor.thread != AST_PTHREADT_NULL) {
01388 pthread_join(mwi_monitor.thread, NULL);
01389 }
01390
01391 if (!fromload) {
01392 ast_custom_function_unregister(&smdi_msg_retrieve_function);
01393 ast_custom_function_unregister(&smdi_msg_function);
01394 }
01395
01396 smdi_loaded = 0;
01397 return 0;
01398 }
01399
01400 static int unload_module(void)
01401 {
01402 return _unload_module(0);
01403 }
01404
01405 static int reload(void)
01406 {
01407 int res;
01408
01409 res = smdi_load(1);
01410
01411 if (res < 0) {
01412 return res;
01413 } else if (res == 1) {
01414 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
01415 return 0;
01416 } else
01417 return 0;
01418 }
01419
01420 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
01421 .load = load_module,
01422 .unload = unload_module,
01423 .reload = reload,
01424 );