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