Merged revisions 117135 via svnmerge from
[asterisk/asterisk.git] / res / res_smdi.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Copyright (C) 2005-2008, Digium, Inc.
5  *
6  * Matthew A. Nicholson <mnicholson@digium.com>
7  * Russell Bryant <russell@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*!
21  * \file
22  * \brief SMDI support for Asterisk.
23  * \author Matthew A. Nicholson <mnicholson@digium.com>
24  * \author Russell Bryant <russell@digium.com>
25  *
26  * Here is a useful mailing list post that describes SMDI protocol details:
27  * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
28  *
29  * \todo This module currently has its own mailbox monitoring thread.  This should
30  * be converted to MWI subscriptions and just let the optional global voicemail
31  * polling thread handle it.
32  */
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include <termios.h>
39 #include <sys/time.h>
40 #include <time.h>
41 #include <ctype.h>
42
43 #include "asterisk/module.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/smdi.h"
47 #include "asterisk/config.h"
48 #include "asterisk/astobj.h"
49 #include "asterisk/io.h"
50 #include "asterisk/stringfields.h"
51 #include "asterisk/linkedlists.h"
52 #include "asterisk/app.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/channel.h"
55
56 /* Message expiry time in milliseconds */
57 #define SMDI_MSG_EXPIRY_TIME    30000 /* 30 seconds */
58
59 static const char config_file[] = "smdi.conf";
60
61 /*! \brief SMDI message desk message queue. */
62 struct ast_smdi_md_queue {
63         ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
64 };
65
66 /*! \brief SMDI message waiting indicator message queue. */
67 struct ast_smdi_mwi_queue {
68         ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
69 };
70
71 struct ast_smdi_interface {
72         ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
73         struct ast_smdi_md_queue md_q;
74         ast_mutex_t md_q_lock;
75         ast_cond_t md_q_cond;
76         struct ast_smdi_mwi_queue mwi_q;
77         ast_mutex_t mwi_q_lock;
78         ast_cond_t mwi_q_cond;
79         FILE *file;
80         int fd;
81         pthread_t thread;
82         struct termios mode;
83         int msdstrip;
84         long msg_expiry;
85 };
86
87 /*! \brief SMDI interface container. */
88 struct ast_smdi_interface_container {
89         ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
90 } smdi_ifaces;
91
92 /*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
93 struct mailbox_mapping {
94         /*! This is the current state of the mailbox.  It is simply on or
95          *  off to indicate if there are messages waiting or not. */
96         unsigned int cur_state:1;
97         /*! A Pointer to the appropriate SMDI interface */
98         struct ast_smdi_interface *iface;
99         AST_DECLARE_STRING_FIELDS(
100                 /*! The Name of the mailbox for the SMDI link. */
101                 AST_STRING_FIELD(smdi);
102                 /*! The name of the mailbox on the Asterisk side */
103                 AST_STRING_FIELD(mailbox);
104                 /*! The name of the voicemail context in use */
105                 AST_STRING_FIELD(context);
106         );
107         AST_LIST_ENTRY(mailbox_mapping) entry;
108 };
109
110 /*! 10 seconds */
111 #define DEFAULT_POLLING_INTERVAL 10
112
113 /*! \brief Data that gets used by the SMDI MWI monitoring thread */
114 static struct {
115         /*! The thread ID */
116         pthread_t thread;
117         ast_mutex_t lock;
118         ast_cond_t cond;
119         /*! A list of mailboxes that need to be monitored */
120         AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
121         /*! Polling Interval for checking mailbox status */
122         unsigned int polling_interval;
123         /*! Set to 1 to tell the polling thread to stop */
124         unsigned int stop:1;
125         /*! The time that the last poll began */
126         struct timeval last_poll;
127 } mwi_monitor = {
128         .thread = AST_PTHREADT_NULL,
129 };
130
131 static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
132 {
133         if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
134                 pthread_cancel(iface->thread);
135                 pthread_join(iface->thread, NULL);
136         }
137         
138         iface->thread = AST_PTHREADT_STOP;
139         
140         if (iface->file) 
141                 fclose(iface->file);
142         
143         ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
144         ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
145         ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
146         ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
147
148         ast_mutex_destroy(&iface->md_q_lock);
149         ast_cond_destroy(&iface->md_q_cond);
150
151         ast_mutex_destroy(&iface->mwi_q_lock);
152         ast_cond_destroy(&iface->mwi_q_cond);
153
154         free(iface);
155
156         ast_module_unref(ast_module_info->self);
157 }
158
159 void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
160 {
161         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
162 }
163
164 /*! 
165  * \internal
166  * \brief Push an SMDI message to the back of an interface's message queue.
167  * \param iface a pointer to the interface to use.
168  * \param md_msg a pointer to the message to use.
169  */
170 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
171 {
172         ast_mutex_lock(&iface->md_q_lock);
173         ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
174         ast_cond_broadcast(&iface->md_q_cond);
175         ast_mutex_unlock(&iface->md_q_lock);
176 }
177
178 /*!
179  * \internal
180  * \brief Push an SMDI message to the back of an interface's message queue.
181  * \param iface a pointer to the interface to use.
182  * \param mwi_msg a pointer to the message to use.
183  */
184 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
185 {
186         ast_mutex_lock(&iface->mwi_q_lock);
187         ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
188         ast_cond_broadcast(&iface->mwi_q_cond);
189         ast_mutex_unlock(&iface->mwi_q_lock);
190 }
191
192 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
193 {
194         FILE *file;
195         int i;
196         
197         if (!(file = fopen(iface->name, "w"))) {
198                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
199                 return 1;
200         }       
201         
202         ASTOBJ_WRLOCK(iface);
203         
204         fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
205         
206         for (i = 0; i < iface->msdstrip; i++)
207                 fprintf(file, "0");
208
209         fprintf(file, "%s!\x04", mailbox);
210
211         fclose(file);
212
213         ASTOBJ_UNLOCK(iface);
214         ast_debug(1, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
215
216         return 0;
217 }
218
219 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
220 {
221         return smdi_toggle_mwi(iface, mailbox, 1);
222 }
223
224 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
225 {
226         return smdi_toggle_mwi(iface, mailbox, 0);
227 }
228
229 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
230 {
231         ast_mutex_lock(&iface->md_q_lock);
232         ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
233         ast_cond_broadcast(&iface->md_q_cond);
234         ast_mutex_unlock(&iface->md_q_lock);
235 }
236
237 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
238 {
239         ast_mutex_lock(&iface->mwi_q_lock);
240         ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
241         ast_cond_broadcast(&iface->mwi_q_cond);
242         ast_mutex_unlock(&iface->mwi_q_lock);
243 }
244
245 enum smdi_message_type {
246         SMDI_MWI,
247         SMDI_MD,
248 };
249
250 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
251 {
252         switch (type) {
253         case SMDI_MWI:
254                 return ast_mutex_lock(&iface->mwi_q_lock);
255         case SMDI_MD:   
256                 return ast_mutex_lock(&iface->md_q_lock);
257         }
258         
259         return -1;
260 }
261
262 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
263 {
264         switch (type) {
265         case SMDI_MWI:
266                 return ast_mutex_unlock(&iface->mwi_q_lock);
267         case SMDI_MD:
268                 return ast_mutex_unlock(&iface->md_q_lock);
269         }
270
271         return -1;
272 }
273
274 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
275 {
276         switch (type) {
277         case SMDI_MWI:
278                 return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
279         case SMDI_MD:
280                 return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
281         }
282         return NULL;
283 }
284
285 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
286 {
287         struct ast_smdi_md_message *md_msg = msg;
288         struct ast_smdi_mwi_message *mwi_msg = msg;
289
290         switch (type) {
291         case SMDI_MWI:
292                 return mwi_msg->timestamp;
293         case SMDI_MD:
294                 return md_msg->timestamp;
295         }
296
297         return ast_tv(0, 0);
298 }
299
300 static inline void unref_msg(void *msg, enum smdi_message_type type)
301 {
302         struct ast_smdi_md_message *md_msg = msg;
303         struct ast_smdi_mwi_message *mwi_msg = msg;
304
305         switch (type) {
306         case SMDI_MWI:
307                 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
308         case SMDI_MD:
309                 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
310         }
311 }
312
313 static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
314 {
315         struct timeval now = ast_tvnow();
316         long elapsed = 0;
317         void *msg;
318         
319         lock_msg_q(iface, type);
320         msg = unlink_from_msg_q(iface, type);
321         unlock_msg_q(iface, type);
322
323         /* purge old messages */
324         while (msg) {
325                 elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
326
327                 if (elapsed > iface->msg_expiry) {
328                         /* found an expired message */
329                         unref_msg(msg, type);
330                         ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue.  "
331                                 "Message was %ld milliseconds too old.\n",
332                                 iface->name, (type == SMDI_MD) ? "MD" : "MWI", 
333                                 elapsed - iface->msg_expiry);
334
335                         lock_msg_q(iface, type);
336                         msg = unlink_from_msg_q(iface, type);
337                         unlock_msg_q(iface, type);
338                 } else {
339                         /* good message, put it back and return */
340                         switch (type) {
341                         case SMDI_MD:
342                                 ast_smdi_md_message_push(iface, msg);
343                                 break;
344                         case SMDI_MWI:
345                                 ast_smdi_mwi_message_push(iface, msg);
346                                 break;
347                         }
348                         unref_msg(msg, type);
349                         break;
350                 }
351         }
352 }
353
354 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
355 {
356         void *msg;
357
358         purge_old_messages(iface, type);
359
360         lock_msg_q(iface, type);
361         msg = unlink_from_msg_q(iface, type);
362         unlock_msg_q(iface, type);
363
364         return msg;
365 }
366
367 enum {
368         OPT_SEARCH_TERMINAL = (1 << 0),
369         OPT_SEARCH_NUMBER   = (1 << 1),
370 };
371
372 static void *smdi_msg_find(struct ast_smdi_interface *iface,
373         enum smdi_message_type type, const char *search_key, struct ast_flags options)
374 {
375         void *msg = NULL;
376
377         purge_old_messages(iface, type);
378
379         switch (type) {
380         case SMDI_MD:
381                 if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
382                         struct ast_smdi_md_message *md_msg = NULL;
383
384                         /* Searching by the message desk terminal */
385
386                         ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
387                                 if (!strcasecmp(iterator->mesg_desk_term, search_key))
388                                         md_msg = ASTOBJ_REF(iterator);
389                         } while (0); );
390
391                         msg = md_msg;
392                 } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
393                         struct ast_smdi_md_message *md_msg = NULL;
394
395                         /* Searching by the message desk number */
396
397                         ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
398                                 if (!strcasecmp(iterator->mesg_desk_num, search_key))
399                                         md_msg = ASTOBJ_REF(iterator);
400                         } while (0); );
401
402                         msg = md_msg;
403                 } else {
404                         /* Searching by the forwarding station */
405                         msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key);
406                 }
407                 break;
408         case SMDI_MWI:
409                 msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
410                 break;
411         }
412
413         return msg;
414 }
415
416 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, 
417         enum smdi_message_type type, const char *search_key, struct ast_flags options)
418 {
419         struct timeval start;
420         long diff = 0;
421         void *msg;
422         ast_cond_t *cond = NULL;
423         ast_mutex_t *lock = NULL;
424
425         switch (type) {
426         case SMDI_MWI:
427                 cond = &iface->mwi_q_cond;
428                 lock = &iface->mwi_q_lock;
429                 break;
430         case SMDI_MD:
431                 cond = &iface->md_q_cond;
432                 lock = &iface->md_q_lock;
433                 break;
434         }
435
436         start = ast_tvnow();
437
438         while (diff < timeout) {
439                 struct timespec ts = { 0, };
440                 struct timeval tv;
441
442                 lock_msg_q(iface, type);
443
444                 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
445                         unlock_msg_q(iface, type);
446                         return msg;
447                 }
448
449                 tv = ast_tvadd(start, ast_tv(0, timeout));
450                 ts.tv_sec = tv.tv_sec;
451                 ts.tv_nsec = tv.tv_usec * 1000;
452
453                 /* If there were no messages in the queue, then go to sleep until one
454                  * arrives. */
455
456                 ast_cond_timedwait(cond, lock, &ts);
457
458                 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
459                         unlock_msg_q(iface, type);
460                         return msg;
461                 }
462
463                 unlock_msg_q(iface, type);
464
465                 /* check timeout */
466                 diff = ast_tvdiff_ms(ast_tvnow(), start);
467         }
468
469         return NULL;
470 }
471
472 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
473 {
474         return smdi_msg_pop(iface, SMDI_MD);
475 }
476
477 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
478 {
479         struct ast_flags options = { 0 };
480         return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
481 }
482
483 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
484 {
485         return smdi_msg_pop(iface, SMDI_MWI);
486 }
487
488 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
489 {
490         struct ast_flags options = { 0 };
491         return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
492 }
493
494 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
495         const char *station)
496 {
497         struct ast_flags options = { 0 };
498         return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
499 }
500
501 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
502 {
503         return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
504 }
505
506 /*! 
507  * \internal
508  * \brief Read an SMDI message.
509  *
510  * \param iface_p the SMDI interface to read from.
511  *
512  * This function loops and reads from and SMDI interface.  It must be stopped
513  * using pthread_cancel().
514  */
515 static void *smdi_read(void *iface_p)
516 {
517         struct ast_smdi_interface *iface = iface_p;
518         struct ast_smdi_md_message *md_msg;
519         struct ast_smdi_mwi_message *mwi_msg;
520         char c = '\0';
521         char *cp = NULL;
522         int i;
523         int start = 0;
524                 
525         /* read an smdi message */
526         while ((c = fgetc(iface->file))) {
527
528                 /* check if this is the start of a message */
529                 if (!start) {
530                         if (c == 'M') {
531                                 ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
532                                 start = 1;
533                         }
534                         continue;
535                 }
536                 
537                 if (c == 'D') { /* MD message */
538                         start = 0;
539
540                         ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
541
542                         if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
543                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
544                                 return NULL;
545                         }
546                         
547                         ASTOBJ_INIT(md_msg);
548
549                         /* read the message desk number */
550                         for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
551                                 md_msg->mesg_desk_num[i] = fgetc(iface->file);
552                                 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
553                         }
554
555                         md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
556                         
557                         ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
558
559                         /* read the message desk terminal number */
560                         for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
561                                 md_msg->mesg_desk_term[i] = fgetc(iface->file);
562                                 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
563                         }
564
565                         md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
566
567                         ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
568
569                         /* read the message type */
570                         md_msg->type = fgetc(iface->file);
571                  
572                         ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
573
574                         /* read the forwarding station number (may be blank) */
575                         cp = &md_msg->fwd_st[0];
576                         for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
577                                 if ((c = fgetc(iface->file)) == ' ') {
578                                         *cp = '\0';
579                                         ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
580                                         break;
581                                 }
582
583                                 /* store c in md_msg->fwd_st */
584                                 if (i >= iface->msdstrip) {
585                                         ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
586                                         *cp++ = c;
587                                 } else {
588                                         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);
589                                 }
590                         }
591
592                         /* make sure the value is null terminated, even if this truncates it */
593                         md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
594                         cp = NULL;
595
596                         ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
597
598                         /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
599                          * up a message on this field */
600                         ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
601
602                         /* read the calling station number (may be blank) */
603                         cp = &md_msg->calling_st[0];
604                         for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
605                                 if (!isdigit((c = fgetc(iface->file)))) {
606                                         *cp = '\0';
607                                         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);
608                                         if (c == ' ') {
609                                                 /* Don't break on a space.  We may read the space before the calling station
610                                                  * here if the forwarding station buffer filled up. */
611                                                 i--; /* We're still on the same character */
612                                                 continue;
613                                         }
614                                         break;
615                                 }
616
617                                 /* store c in md_msg->calling_st */
618                                 if (i >= iface->msdstrip) {
619                                         ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
620                                         *cp++ = c;
621                                 } else {
622                                         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);
623                                 }
624                         }
625
626                         /* make sure the value is null terminated, even if this truncates it */
627                         md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
628                         cp = NULL;
629
630                         ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
631
632                         /* add the message to the message queue */
633                         md_msg->timestamp = ast_tvnow();
634                         ast_smdi_md_message_push(iface, md_msg);
635                         ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
636                         
637                         ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
638
639                 } else if (c == 'W') { /* MWI message */
640                         start = 0;
641
642                         ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
643
644                         if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
645                                 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
646                                 return NULL;
647                         }
648
649                         ASTOBJ_INIT(mwi_msg);
650
651                         /* discard the 'I' (from 'MWI') */
652                         fgetc(iface->file);
653                         
654                         /* read the forwarding station number (may be blank) */
655                         cp = &mwi_msg->fwd_st[0];
656                         for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
657                                 if ((c = fgetc(iface->file)) == ' ') {
658                                         *cp = '\0';
659                                         break;
660                                 }
661
662                                 /* store c in md_msg->fwd_st */
663                                 if (i >= iface->msdstrip)
664                                         *cp++ = c;
665                         }
666
667                         /* make sure the station number is null terminated, even if this will truncate it */
668                         mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
669                         cp = NULL;
670                         
671                         /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
672                          * up a message on this field */
673                         ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
674
675                         /* read the mwi failure cause */
676                         for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
677                                 mwi_msg->cause[i] = fgetc(iface->file);
678
679                         mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
680
681                         /* add the message to the message queue */
682                         mwi_msg->timestamp = ast_tvnow();
683                         ast_smdi_mwi_message_push(iface, mwi_msg);
684                         ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
685                         
686                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
687                 } else {
688                         ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
689                         start = 0;
690                 }
691         }
692
693         ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
694         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
695         return NULL;
696 }
697
698 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
699 {
700         ast_free(msg);
701 }
702
703 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
704 {
705         ast_free(msg);
706 }
707
708 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
709 {
710         ast_string_field_free_memory(mm);
711         ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
712         free(mm);
713 }
714
715 static void destroy_all_mailbox_mappings(void)
716 {
717         struct mailbox_mapping *mm;
718
719         ast_mutex_lock(&mwi_monitor.lock);
720         while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
721                 destroy_mailbox_mapping(mm);
722         ast_mutex_unlock(&mwi_monitor.lock);
723 }
724
725 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
726 {
727         struct mailbox_mapping *mm;
728         char *mailbox, *context;
729
730         if (!(mm = ast_calloc(1, sizeof(*mm))))
731                 return;
732         
733         if (ast_string_field_init(mm, 32)) {
734                 free(mm);
735                 return;
736         }
737
738         ast_string_field_set(mm, smdi, var->name);
739
740         context = ast_strdupa(var->value);
741         mailbox = strsep(&context, "@");
742         if (ast_strlen_zero(context))
743                 context = "default";
744
745         ast_string_field_set(mm, mailbox, mailbox);
746         ast_string_field_set(mm, context, context);
747
748         mm->iface = ASTOBJ_REF(iface);
749
750         ast_mutex_lock(&mwi_monitor.lock);
751         AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
752         ast_mutex_unlock(&mwi_monitor.lock);
753 }
754
755 /*!
756  * \note Called with the mwi_monitor.lock locked
757  */
758 static void poll_mailbox(struct mailbox_mapping *mm)
759 {
760         char buf[1024];
761         unsigned int state;
762
763         snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
764
765         state = !!ast_app_has_voicemail(mm->mailbox, NULL);
766
767         if (state != mm->cur_state) {
768                 if (state)
769                         ast_smdi_mwi_set(mm->iface, mm->smdi);
770                 else
771                         ast_smdi_mwi_unset(mm->iface, mm->smdi);
772
773                 mm->cur_state = state;
774         }
775 }
776
777 static void *mwi_monitor_handler(void *data)
778 {
779         while (!mwi_monitor.stop) {
780                 struct timespec ts = { 0, };
781                 struct timeval tv;
782                 struct mailbox_mapping *mm;
783
784                 ast_mutex_lock(&mwi_monitor.lock);
785
786                 mwi_monitor.last_poll = ast_tvnow();
787
788                 AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
789                         poll_mailbox(mm);
790
791                 /* Sleep up to the configured polling interval.  Allow unload_module()
792                  * to signal us to wake up and exit. */
793                 tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
794                 ts.tv_sec = tv.tv_sec;
795                 ts.tv_nsec = tv.tv_usec * 1000;
796                 ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
797
798                 ast_mutex_unlock(&mwi_monitor.lock);
799         }
800
801         return NULL;
802 }
803
804 static struct ast_smdi_interface *alloc_smdi_interface(void)
805 {
806         struct ast_smdi_interface *iface;
807
808         if (!(iface = ast_calloc(1, sizeof(*iface))))
809                 return NULL;
810
811         ASTOBJ_INIT(iface);
812         ASTOBJ_CONTAINER_INIT(&iface->md_q);
813         ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
814
815         ast_mutex_init(&iface->md_q_lock);
816         ast_cond_init(&iface->md_q_cond, NULL);
817
818         ast_mutex_init(&iface->mwi_q_lock);
819         ast_cond_init(&iface->mwi_q_cond, NULL);
820
821         return iface;
822 }
823
824 /*!
825  * \internal
826  * \brief Load and reload SMDI configuration.
827  * \param reload this should be 1 if we are reloading and 0 if not.
828  *
829  * This function loads/reloads the SMDI configuration and starts and stops
830  * interfaces accordingly.
831  *
832  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
833  */
834 static int smdi_load(int reload)
835 {
836         struct ast_config *conf;
837         struct ast_variable *v;
838         struct ast_smdi_interface *iface = NULL;
839         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
840         int res = 0;
841
842         /* Config options */
843         speed_t baud_rate = B9600;     /* 9600 baud rate */
844         tcflag_t paritybit = PARENB;   /* even parity checking */
845         tcflag_t charsize = CS7;       /* seven bit characters */
846         int stopbits = 0;              /* One stop bit */
847         
848         int msdstrip = 0;              /* strip zero digits */
849         long msg_expiry = SMDI_MSG_EXPIRY_TIME;
850
851         if (!(conf = ast_config_load(config_file, config_flags))) {
852                 if (reload)
853                         ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
854                 else
855                         ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
856                 return 1;
857         } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
858                 return 0;
859
860         /* Mark all interfaces that we are listening on.  We will unmark them
861          * as we find them in the config file, this way we know any interfaces
862          * still marked after we have finished parsing the config file should
863          * be stopped.
864          */
865         if (reload)
866                 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
867
868         for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
869                 if (!strcasecmp(v->name, "baudrate")) {
870                         if (!strcasecmp(v->value, "9600"))
871                                 baud_rate = B9600;
872                         else if (!strcasecmp(v->value, "4800"))
873                                 baud_rate = B4800;
874                         else if (!strcasecmp(v->value, "2400"))
875                                 baud_rate = B2400;
876                         else if (!strcasecmp(v->value, "1200"))
877                                 baud_rate = B1200;
878                         else {
879                                 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
880                                 baud_rate = B9600;
881                         }
882                 } else if (!strcasecmp(v->name, "msdstrip")) {
883                         if (!sscanf(v->value, "%d", &msdstrip)) {
884                                 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
885                                 msdstrip = 0;
886                         } else if (0 > msdstrip || msdstrip > 9) {
887                                 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
888                                 msdstrip = 0;
889                         }
890                 } else if (!strcasecmp(v->name, "msgexpirytime")) {
891                         if (!sscanf(v->value, "%ld", &msg_expiry)) {
892                                 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
893                                 msg_expiry = SMDI_MSG_EXPIRY_TIME;
894                         }
895                 } else if (!strcasecmp(v->name, "paritybit")) {
896                         if (!strcasecmp(v->value, "even"))
897                                 paritybit = PARENB;
898                         else if (!strcasecmp(v->value, "odd"))
899                                 paritybit = PARENB | PARODD;
900                         else if (!strcasecmp(v->value, "none"))
901                                 paritybit = ~PARENB;
902                         else {
903                                 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
904                                 paritybit = PARENB;
905                         }
906                 } else if (!strcasecmp(v->name, "charsize")) {
907                         if (!strcasecmp(v->value, "7"))
908                                 charsize = CS7;
909                         else if (!strcasecmp(v->value, "8"))
910                                 charsize = CS8;
911                         else {
912                                 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
913                                 charsize = CS7;
914                         }
915                 } else if (!strcasecmp(v->name, "twostopbits")) {
916                         stopbits = ast_true(v->name);
917                 } else if (!strcasecmp(v->name, "smdiport")) {
918                         if (reload) {
919                                 /* we are reloading, check if we are already
920                                  * monitoring this interface, if we are we do
921                                  * not want to start it again.  This also has
922                                  * the side effect of not updating different
923                                  * setting for the serial port, but it should
924                                  * be trivial to rewrite this section so that
925                                  * options on the port are changed without
926                                  * restarting the interface.  Or the interface
927                                  * could be restarted with out emptying the
928                                  * queue. */
929                                 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
930                                         ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
931                                         ASTOBJ_UNMARK(iface);
932                                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
933                                         continue;
934                                 }
935                         }
936                         
937                         if (!(iface = alloc_smdi_interface()))
938                                 continue;
939
940                         ast_copy_string(iface->name, v->value, sizeof(iface->name));
941
942                         iface->thread = AST_PTHREADT_NULL;
943
944                         if (!(iface->file = fopen(iface->name, "r"))) {
945                                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
946                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
947                                 continue;
948                         }
949
950                         iface->fd = fileno(iface->file);
951
952                         /* Set the proper attributes for our serial port. */
953
954                         /* get the current attributes from the port */
955                         if (tcgetattr(iface->fd, &iface->mode)) {
956                                 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
957                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
958                                 continue;
959                         }
960
961                         /* set the desired speed */
962                         if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
963                                 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
964                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
965                                 continue;
966                         }
967                         
968                         /* set the stop bits */
969                         if (stopbits)
970                                 iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
971                         else
972                                 iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
973                         
974                         /* set the parity */
975                         iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
976                         
977                         /* set the character size */
978                         iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
979                         
980                         /* commit the desired attributes */
981                         if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
982                                 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
983                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
984                                 continue;
985                         }
986
987                         /* set the msdstrip */
988                         iface->msdstrip = msdstrip;
989
990                         /* set the message expiry time */
991                         iface->msg_expiry = msg_expiry;
992
993                         /* start the listener thread */
994                         ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
995                         if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
996                                 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
997                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
998                                 continue;
999                         }
1000
1001                         ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
1002                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1003                         ast_module_ref(ast_module_info->self);
1004                 } else {
1005                         ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
1006                 }
1007         }
1008
1009         destroy_all_mailbox_mappings();
1010         mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1011         
1012         iface = NULL;
1013
1014         for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
1015                 if (!strcasecmp(v->name, "smdiport")) {
1016                         if (iface)
1017                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1018
1019                         if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
1020                                 ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name);
1021                                 continue;
1022                         }
1023                 } else if (!strcasecmp(v->name, "pollinginterval")) {
1024                         if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) {
1025                                 ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
1026                                 mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1027                         }
1028                 } else {
1029                         if (!iface) {
1030                                 ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
1031                                 continue;
1032                         }
1033                         append_mailbox_mapping(v, iface);
1034                 }
1035         }
1036
1037         if (iface)
1038                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1039
1040         ast_config_destroy(conf);
1041         
1042         if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
1043                 && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
1044                 ast_log(LOG_ERROR, "Failed to start MWI monitoring thread.  This module will not operate.\n");
1045                 return AST_MODULE_LOAD_FAILURE;
1046         }
1047
1048         /* Prune any interfaces we should no longer monitor. */
1049         if (reload)
1050                 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
1051         
1052         ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
1053         /* TODO: this is bad, we need an ASTOBJ method for this! */
1054         if (!smdi_ifaces.head)
1055                 res = 1;
1056         ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
1057         
1058         return res;
1059 }
1060
1061 struct smdi_msg_datastore {
1062         unsigned int id;
1063         struct ast_smdi_interface *iface;
1064         struct ast_smdi_md_message *md_msg;
1065 };
1066
1067 static void smdi_msg_datastore_destroy(void *data)
1068 {
1069         struct smdi_msg_datastore *smd = data;
1070
1071         if (smd->iface)
1072                 ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
1073
1074         if (smd->md_msg)
1075                 ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
1076
1077         free(smd);
1078 }
1079
1080 static const struct ast_datastore_info smdi_msg_datastore_info = {
1081         .type = "SMDIMSG",
1082         .destroy = smdi_msg_datastore_destroy,
1083 };
1084
1085 static int smdi_msg_id;
1086
1087 /*! In milliseconds */
1088 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
1089
1090 AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS
1091         AST_APP_OPTION('t', OPT_SEARCH_TERMINAL),
1092         AST_APP_OPTION('n', OPT_SEARCH_NUMBER),
1093 END_OPTIONS );
1094
1095 static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1096 {
1097         struct ast_module_user *u;
1098         AST_DECLARE_APP_ARGS(args,
1099                 AST_APP_ARG(port);
1100                 AST_APP_ARG(search_key);
1101                 AST_APP_ARG(timeout);
1102                 AST_APP_ARG(options);
1103         );
1104         struct ast_flags options = { 0 };
1105         unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1106         int res = -1;
1107         char *parse = NULL;
1108         struct smdi_msg_datastore *smd = NULL;
1109         struct ast_datastore *datastore = NULL;
1110         struct ast_smdi_interface *iface = NULL;
1111         struct ast_smdi_md_message *md_msg = NULL;
1112
1113         u = ast_module_user_add(chan);
1114
1115         if (ast_strlen_zero(data)) {
1116                 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
1117                 goto return_error;
1118         }
1119
1120         if (!chan) {
1121                 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
1122                 goto return_error;
1123         }
1124
1125         ast_autoservice_start(chan);
1126
1127         parse = ast_strdupa(data);
1128         AST_STANDARD_APP_ARGS(args, parse);
1129
1130         if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
1131                 ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
1132                 goto return_error;
1133         }
1134
1135         if (!(iface = ast_smdi_interface_find(args.port))) {
1136                 ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
1137                 goto return_error;
1138         }
1139
1140         if (!ast_strlen_zero(args.options)) {
1141                 ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
1142         }
1143
1144         if (!ast_strlen_zero(args.timeout)) {
1145                 if (sscanf(args.timeout, "%u", &timeout) != 1) {
1146                         ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
1147                         timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1148                 }
1149         }
1150
1151         if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) {
1152                 ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after "
1153                         "waiting %u ms.\n", args.search_key, timeout);
1154                 goto return_error;
1155         }
1156
1157         if (!(smd = ast_calloc(1, sizeof(*smd))))
1158                 goto return_error;
1159
1160         smd->iface = ASTOBJ_REF(iface);
1161         smd->md_msg = ASTOBJ_REF(md_msg);
1162         smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
1163         snprintf(buf, len, "%u", smd->id);
1164
1165         if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf)))
1166                 goto return_error;
1167
1168         datastore->data = smd;
1169
1170         ast_channel_lock(chan);
1171         ast_channel_datastore_add(chan, datastore);
1172         ast_channel_unlock(chan);
1173
1174         res = 0;
1175
1176 return_error:
1177         if (iface)
1178                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1179
1180         if (md_msg)
1181                 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
1182
1183         if (smd && !datastore)
1184                 smdi_msg_datastore_destroy(smd);
1185
1186         if (parse)
1187                 ast_autoservice_stop(chan);
1188
1189         ast_module_user_remove(u);
1190
1191         return res;
1192 }
1193
1194 static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1195 {
1196         struct ast_module_user *u;
1197         int res = -1;
1198         AST_DECLARE_APP_ARGS(args,
1199                 AST_APP_ARG(id);
1200                 AST_APP_ARG(component);
1201         );
1202         char *parse;
1203         struct ast_datastore *datastore = NULL;
1204         struct smdi_msg_datastore *smd = NULL;
1205
1206         u = ast_module_user_add(chan);
1207
1208         if (!chan) {
1209                 ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
1210                 goto return_error;
1211         }
1212
1213         if (ast_strlen_zero(data)) {
1214                 ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
1215                 goto return_error;
1216         }
1217
1218         parse = ast_strdupa(data);
1219         AST_STANDARD_APP_ARGS(args, parse);
1220
1221         if (ast_strlen_zero(args.id)) {
1222                 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1223                 goto return_error;
1224         }
1225
1226         if (ast_strlen_zero(args.component)) {
1227                 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1228                 goto return_error;
1229         }
1230
1231         ast_channel_lock(chan);
1232         datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
1233         ast_channel_unlock(chan);
1234         
1235         if (!datastore) {
1236                 ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
1237                 goto return_error;
1238         }
1239
1240         smd = datastore->data;
1241
1242         if (!strcasecmp(args.component, "number")) {
1243                 ast_copy_string(buf, smd->md_msg->mesg_desk_num, len);
1244         } else if (!strcasecmp(args.component, "terminal")) {
1245                 ast_copy_string(buf, smd->md_msg->mesg_desk_term, len);
1246         } else if (!strcasecmp(args.component, "station")) {
1247                 ast_copy_string(buf, smd->md_msg->fwd_st, len);
1248         } else if (!strcasecmp(args.component, "callerid")) {
1249                 ast_copy_string(buf, smd->md_msg->calling_st, len);
1250         } else if (!strcasecmp(args.component, "type")) {
1251                 snprintf(buf, len, "%c", smd->md_msg->type);
1252         } else {
1253                 ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
1254                         args.component);
1255                 goto return_error;
1256         }
1257
1258         res = 0;
1259
1260 return_error:
1261         ast_module_user_remove(u);
1262
1263         return 0;
1264 }
1265
1266 static struct ast_custom_function smdi_msg_retrieve_function = {
1267         .name = "SMDI_MSG_RETRIEVE",
1268         .synopsis = "Retrieve an SMDI message.",
1269         .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])",
1270         .desc = 
1271         "   This function is used to retrieve an incoming SMDI message.  It returns\n"
1272         "an ID which can be used with the SMDI_MSG() function to access details of\n"
1273         "the message.  Note that this is a destructive function in the sense that\n"
1274         "once an SMDI message is retrieved using this function, it is no longer in\n"
1275         "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
1276         "channels.  The timeout for this function is optional, and the default is\n"
1277         "3 seconds.  When providing a timeout, it should be in milliseconds.\n"
1278         "   The default search is done on the forwarding station ID.  However, if\n"
1279         "you set one of the search key options in the options field, you can change\n"
1280         "this behavior.\n"
1281         "   Options:\n"
1282         "     t - Instead of searching on the forwarding station, search on the message\n"
1283         "         desk terminal.\n"
1284         "     n - Instead of searching on the forwarding station, search on the message\n"
1285         "         desk number.\n"
1286         "",
1287         .read = smdi_msg_retrieve_read,
1288 };
1289
1290 static struct ast_custom_function smdi_msg_function = {
1291         .name = "SMDI_MSG",
1292         .synopsis = "Retrieve details about an SMDI message.",
1293         .syntax = "SMDI_MSG(<message_id>,<component>)",
1294         .desc = 
1295         "   This function is used to access details of an SMDI message that was\n"
1296         "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
1297         "function.\n"
1298         "   Valid message components are:\n"
1299         "      number   - The message desk number\n"
1300         "      terminal - The message desk terminal\n"
1301         "      station  - The forwarding station\n"
1302         "      callerid - The callerID of the calling party that was forwarded\n"
1303         "      type     - The call type.  The value here is the exact character\n"
1304         "                 that came in on the SMDI link.  Typically, example values\n"
1305         "                 are: D - Direct Calls, A - Forward All Calls,\n"
1306         "                      B - Forward Busy Calls, N - Forward No Answer Calls\n"
1307         "",
1308         .read = smdi_msg_read,
1309 };
1310
1311 static int load_module(void)
1312 {
1313         int res;
1314         
1315         /* initialize our containers */
1316         memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
1317         ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
1318         
1319         ast_mutex_init(&mwi_monitor.lock);
1320         ast_cond_init(&mwi_monitor.cond, NULL);
1321
1322         ast_custom_function_register(&smdi_msg_retrieve_function);
1323         ast_custom_function_register(&smdi_msg_function);
1324
1325         /* load the config and start the listener threads*/
1326         res = smdi_load(0);
1327         if (res < 0) {
1328                 return res;
1329         } else if (res == 1) {
1330                 ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
1331                 return AST_MODULE_LOAD_DECLINE;
1332         }
1333         
1334         return AST_MODULE_LOAD_SUCCESS;
1335 }
1336
1337 static int unload_module(void)
1338 {
1339         /* this destructor stops any running smdi_read threads */
1340         ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
1341         ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
1342
1343         destroy_all_mailbox_mappings();
1344
1345         ast_mutex_lock(&mwi_monitor.lock);
1346         mwi_monitor.stop = 1;
1347         ast_cond_signal(&mwi_monitor.cond);
1348         ast_mutex_unlock(&mwi_monitor.lock);
1349
1350         if (mwi_monitor.thread != AST_PTHREADT_NULL) {
1351                 pthread_join(mwi_monitor.thread, NULL);
1352         }
1353
1354         ast_custom_function_unregister(&smdi_msg_retrieve_function);
1355         ast_custom_function_unregister(&smdi_msg_function);
1356
1357         return 0;
1358 }
1359
1360 static int reload(void)
1361 {
1362         int res;
1363
1364         res = smdi_load(1);
1365
1366         if (res < 0) {
1367                 return res;
1368         } else if (res == 1) {
1369                 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
1370                 return 0;
1371         } else
1372                 return 0;
1373 }
1374
1375 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
1376                 .load = load_module,
1377                 .unload = unload_module,
1378                 .reload = reload,
1379                );