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