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