Initialize the start time in smdi_msg_wait. Somehow this code got lost in trunk.
[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
423         start = ast_tvnow();
424
425         while (diff < timeout) {
426                 struct timespec ts = { 0, };
427                 struct timeval tv;
428
429                 lock_msg_q(iface, type);
430
431                 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
432                         unlock_msg_q(iface, type);
433                         return msg;
434                 }
435
436                 tv = ast_tvadd(start, ast_tv(0, timeout));
437                 ts.tv_sec = tv.tv_sec;
438                 ts.tv_nsec = tv.tv_usec * 1000;
439
440                 /* If there were no messages in the queue, then go to sleep until one
441                  * arrives. */
442
443                 ast_cond_timedwait(&iface->md_q_cond, &iface->md_q_lock, &ts);
444
445                 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
446                         unlock_msg_q(iface, type);
447                         return msg;
448                 }
449
450                 unlock_msg_q(iface, type);
451
452                 /* check timeout */
453                 diff = ast_tvdiff_ms(ast_tvnow(), start);
454         }
455
456         return NULL;
457 }
458
459 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
460 {
461         return smdi_msg_pop(iface, SMDI_MD);
462 }
463
464 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
465 {
466         struct ast_flags options = { 0 };
467         return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
468 }
469
470 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
471 {
472         return smdi_msg_pop(iface, SMDI_MWI);
473 }
474
475 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
476 {
477         struct ast_flags options = { 0 };
478         return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
479 }
480
481 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
482         const char *station)
483 {
484         struct ast_flags options = { 0 };
485         return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
486 }
487
488 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
489 {
490         return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
491 }
492
493 /*! 
494  * \internal
495  * \brief Read an SMDI message.
496  *
497  * \param iface_p the SMDI interface to read from.
498  *
499  * This function loops and reads from and SMDI interface.  It must be stopped
500  * using pthread_cancel().
501  */
502 static void *smdi_read(void *iface_p)
503 {
504         struct ast_smdi_interface *iface = iface_p;
505         struct ast_smdi_md_message *md_msg;
506         struct ast_smdi_mwi_message *mwi_msg;
507         char c = '\0';
508         char *cp = NULL;
509         int i;
510         int start = 0;
511                 
512         /* read an smdi message */
513         while ((c = fgetc(iface->file))) {
514
515                 /* check if this is the start of a message */
516                 if (!start) {
517                         if (c == 'M') {
518                                 ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
519                                 start = 1;
520                         }
521                         continue;
522                 }
523                 
524                 if (c == 'D') { /* MD message */
525                         start = 0;
526
527                         ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
528
529                         if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
530                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
531                                 return NULL;
532                         }
533                         
534                         ASTOBJ_INIT(md_msg);
535
536                         /* read the message desk number */
537                         for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
538                                 md_msg->mesg_desk_num[i] = fgetc(iface->file);
539                                 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
540                         }
541
542                         md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
543                         
544                         ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
545
546                         /* read the message desk terminal number */
547                         for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
548                                 md_msg->mesg_desk_term[i] = fgetc(iface->file);
549                                 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
550                         }
551
552                         md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
553
554                         ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
555
556                         /* read the message type */
557                         md_msg->type = fgetc(iface->file);
558                  
559                         ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
560
561                         /* read the forwarding station number (may be blank) */
562                         cp = &md_msg->fwd_st[0];
563                         for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
564                                 if ((c = fgetc(iface->file)) == ' ') {
565                                         *cp = '\0';
566                                         ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
567                                         break;
568                                 }
569
570                                 /* store c in md_msg->fwd_st */
571                                 if (i >= iface->msdstrip) {
572                                         ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
573                                         *cp++ = c;
574                                 } else {
575                                         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);
576                                 }
577                         }
578
579                         /* make sure the value is null terminated, even if this truncates it */
580                         md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
581                         cp = NULL;
582
583                         ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
584
585                         /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
586                          * up a message on this field */
587                         ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
588
589                         /* read the calling station number (may be blank) */
590                         cp = &md_msg->calling_st[0];
591                         for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
592                                 if (!isdigit((c = fgetc(iface->file)))) {
593                                         *cp = '\0';
594                                         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);
595                                         if (c == ' ') {
596                                                 /* Don't break on a space.  We may read the space before the calling station
597                                                  * here if the forwarding station buffer filled up. */
598                                                 i--; /* We're still on the same character */
599                                                 continue;
600                                         }
601                                         break;
602                                 }
603
604                                 /* store c in md_msg->calling_st */
605                                 if (i >= iface->msdstrip) {
606                                         ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
607                                         *cp++ = c;
608                                 } else {
609                                         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);
610                                 }
611                         }
612
613                         /* make sure the value is null terminated, even if this truncates it */
614                         md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
615                         cp = NULL;
616
617                         ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
618
619                         /* add the message to the message queue */
620                         md_msg->timestamp = ast_tvnow();
621                         ast_smdi_md_message_push(iface, md_msg);
622                         ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
623                         
624                         ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
625
626                 } else if (c == 'W') { /* MWI message */
627                         start = 0;
628
629                         ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
630
631                         if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
632                                 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
633                                 return NULL;
634                         }
635
636                         ASTOBJ_INIT(mwi_msg);
637
638                         /* discard the 'I' (from 'MWI') */
639                         fgetc(iface->file);
640                         
641                         /* read the forwarding station number (may be blank) */
642                         cp = &mwi_msg->fwd_st[0];
643                         for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
644                                 if ((c = fgetc(iface->file)) == ' ') {
645                                         *cp = '\0';
646                                         break;
647                                 }
648
649                                 /* store c in md_msg->fwd_st */
650                                 if (i >= iface->msdstrip)
651                                         *cp++ = c;
652                         }
653
654                         /* make sure the station number is null terminated, even if this will truncate it */
655                         mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
656                         cp = NULL;
657                         
658                         /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
659                          * up a message on this field */
660                         ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
661
662                         /* read the mwi failure cause */
663                         for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
664                                 mwi_msg->cause[i] = fgetc(iface->file);
665
666                         mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
667
668                         /* add the message to the message queue */
669                         mwi_msg->timestamp = ast_tvnow();
670                         ast_smdi_mwi_message_push(iface, mwi_msg);
671                         ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
672                         
673                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
674                 } else {
675                         ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
676                         start = 0;
677                 }
678         }
679
680         ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
681         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
682         return NULL;
683 }
684
685 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
686 {
687         ast_free(msg);
688 }
689
690 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
691 {
692         ast_free(msg);
693 }
694
695 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
696 {
697         ast_string_field_free_memory(mm);
698         ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
699         free(mm);
700 }
701
702 static void destroy_all_mailbox_mappings(void)
703 {
704         struct mailbox_mapping *mm;
705
706         ast_mutex_lock(&mwi_monitor.lock);
707         while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
708                 destroy_mailbox_mapping(mm);
709         ast_mutex_unlock(&mwi_monitor.lock);
710 }
711
712 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
713 {
714         struct mailbox_mapping *mm;
715         char *mailbox, *context;
716
717         if (!(mm = ast_calloc(1, sizeof(*mm))))
718                 return;
719         
720         if (ast_string_field_init(mm, 32)) {
721                 free(mm);
722                 return;
723         }
724
725         ast_string_field_set(mm, smdi, var->name);
726
727         context = ast_strdupa(var->value);
728         mailbox = strsep(&context, "@");
729         if (ast_strlen_zero(context))
730                 context = "default";
731
732         ast_string_field_set(mm, mailbox, mailbox);
733         ast_string_field_set(mm, context, context);
734
735         mm->iface = ASTOBJ_REF(iface);
736
737         ast_mutex_lock(&mwi_monitor.lock);
738         AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
739         ast_mutex_unlock(&mwi_monitor.lock);
740 }
741
742 /*!
743  * \note Called with the mwi_monitor.lock locked
744  */
745 static void poll_mailbox(struct mailbox_mapping *mm)
746 {
747         char buf[1024];
748         unsigned int state;
749
750         snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
751
752         state = !!ast_app_has_voicemail(mm->mailbox, NULL);
753
754         if (state != mm->cur_state) {
755                 if (state)
756                         ast_smdi_mwi_set(mm->iface, mm->smdi);
757                 else
758                         ast_smdi_mwi_unset(mm->iface, mm->smdi);
759
760                 mm->cur_state = state;
761         }
762 }
763
764 static void *mwi_monitor_handler(void *data)
765 {
766         while (!mwi_monitor.stop) {
767                 struct timespec ts = { 0, };
768                 struct timeval tv;
769                 struct mailbox_mapping *mm;
770
771                 ast_mutex_lock(&mwi_monitor.lock);
772
773                 mwi_monitor.last_poll = ast_tvnow();
774
775                 AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
776                         poll_mailbox(mm);
777
778                 /* Sleep up to the configured polling interval.  Allow unload_module()
779                  * to signal us to wake up and exit. */
780                 tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
781                 ts.tv_sec = tv.tv_sec;
782                 ts.tv_nsec = tv.tv_usec * 1000;
783                 ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
784
785                 ast_mutex_unlock(&mwi_monitor.lock);
786         }
787
788         return NULL;
789 }
790
791 static struct ast_smdi_interface *alloc_smdi_interface(void)
792 {
793         struct ast_smdi_interface *iface;
794
795         if (!(iface = ast_calloc(1, sizeof(*iface))))
796                 return NULL;
797
798         ASTOBJ_INIT(iface);
799         ASTOBJ_CONTAINER_INIT(&iface->md_q);
800         ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
801
802         ast_mutex_init(&iface->md_q_lock);
803         ast_cond_init(&iface->md_q_cond, NULL);
804
805         ast_mutex_init(&iface->mwi_q_lock);
806         ast_cond_init(&iface->mwi_q_cond, NULL);
807
808         return iface;
809 }
810
811 /*!
812  * \internal
813  * \brief Load and reload SMDI configuration.
814  * \param reload this should be 1 if we are reloading and 0 if not.
815  *
816  * This function loads/reloads the SMDI configuration and starts and stops
817  * interfaces accordingly.
818  *
819  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
820  */
821 static int smdi_load(int reload)
822 {
823         struct ast_config *conf;
824         struct ast_variable *v;
825         struct ast_smdi_interface *iface = NULL;
826         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
827         int res = 0;
828
829         /* Config options */
830         speed_t baud_rate = B9600;     /* 9600 baud rate */
831         tcflag_t paritybit = PARENB;   /* even parity checking */
832         tcflag_t charsize = CS7;       /* seven bit characters */
833         int stopbits = 0;              /* One stop bit */
834         
835         int msdstrip = 0;              /* strip zero digits */
836         long msg_expiry = SMDI_MSG_EXPIRY_TIME;
837
838         if (!(conf = ast_config_load(config_file, config_flags))) {
839                 if (reload)
840                         ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
841                 else
842                         ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
843                 return 1;
844         } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
845                 return 0;
846
847         /* Mark all interfaces that we are listening on.  We will unmark them
848          * as we find them in the config file, this way we know any interfaces
849          * still marked after we have finished parsing the config file should
850          * be stopped.
851          */
852         if (reload)
853                 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
854
855         for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
856                 if (!strcasecmp(v->name, "baudrate")) {
857                         if (!strcasecmp(v->value, "9600"))
858                                 baud_rate = B9600;
859                         else if (!strcasecmp(v->value, "4800"))
860                                 baud_rate = B4800;
861                         else if (!strcasecmp(v->value, "2400"))
862                                 baud_rate = B2400;
863                         else if (!strcasecmp(v->value, "1200"))
864                                 baud_rate = B1200;
865                         else {
866                                 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
867                                 baud_rate = B9600;
868                         }
869                 } else if (!strcasecmp(v->name, "msdstrip")) {
870                         if (!sscanf(v->value, "%d", &msdstrip)) {
871                                 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
872                                 msdstrip = 0;
873                         } else if (0 > msdstrip || msdstrip > 9) {
874                                 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
875                                 msdstrip = 0;
876                         }
877                 } else if (!strcasecmp(v->name, "msgexpirytime")) {
878                         if (!sscanf(v->value, "%ld", &msg_expiry)) {
879                                 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
880                                 msg_expiry = SMDI_MSG_EXPIRY_TIME;
881                         }
882                 } else if (!strcasecmp(v->name, "paritybit")) {
883                         if (!strcasecmp(v->value, "even"))
884                                 paritybit = PARENB;
885                         else if (!strcasecmp(v->value, "odd"))
886                                 paritybit = PARENB | PARODD;
887                         else if (!strcasecmp(v->value, "none"))
888                                 paritybit = ~PARENB;
889                         else {
890                                 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
891                                 paritybit = PARENB;
892                         }
893                 } else if (!strcasecmp(v->name, "charsize")) {
894                         if (!strcasecmp(v->value, "7"))
895                                 charsize = CS7;
896                         else if (!strcasecmp(v->value, "8"))
897                                 charsize = CS8;
898                         else {
899                                 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
900                                 charsize = CS7;
901                         }
902                 } else if (!strcasecmp(v->name, "twostopbits")) {
903                         stopbits = ast_true(v->name);
904                 } else if (!strcasecmp(v->name, "smdiport")) {
905                         if (reload) {
906                                 /* we are reloading, check if we are already
907                                  * monitoring this interface, if we are we do
908                                  * not want to start it again.  This also has
909                                  * the side effect of not updating different
910                                  * setting for the serial port, but it should
911                                  * be trivial to rewrite this section so that
912                                  * options on the port are changed without
913                                  * restarting the interface.  Or the interface
914                                  * could be restarted with out emptying the
915                                  * queue. */
916                                 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
917                                         ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
918                                         ASTOBJ_UNMARK(iface);
919                                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
920                                         continue;
921                                 }
922                         }
923                         
924                         if (!(iface = alloc_smdi_interface()))
925                                 continue;
926
927                         ast_copy_string(iface->name, v->value, sizeof(iface->name));
928
929                         iface->thread = AST_PTHREADT_NULL;
930
931                         if (!(iface->file = fopen(iface->name, "r"))) {
932                                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
933                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
934                                 continue;
935                         }
936
937                         iface->fd = fileno(iface->file);
938
939                         /* Set the proper attributes for our serial port. */
940
941                         /* get the current attributes from the port */
942                         if (tcgetattr(iface->fd, &iface->mode)) {
943                                 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
944                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
945                                 continue;
946                         }
947
948                         /* set the desired speed */
949                         if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
950                                 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
951                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
952                                 continue;
953                         }
954                         
955                         /* set the stop bits */
956                         if (stopbits)
957                                 iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
958                         else
959                                 iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
960                         
961                         /* set the parity */
962                         iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
963                         
964                         /* set the character size */
965                         iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
966                         
967                         /* commit the desired attributes */
968                         if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
969                                 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
970                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
971                                 continue;
972                         }
973
974                         /* set the msdstrip */
975                         iface->msdstrip = msdstrip;
976
977                         /* set the message expiry time */
978                         iface->msg_expiry = msg_expiry;
979
980                         /* start the listener thread */
981                         ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
982                         if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
983                                 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
984                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
985                                 continue;
986                         }
987
988                         ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
989                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
990                         ast_module_ref(ast_module_info->self);
991                 } else {
992                         ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
993                 }
994         }
995
996         destroy_all_mailbox_mappings();
997         mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
998         
999         iface = NULL;
1000
1001         for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
1002                 if (!strcasecmp(v->name, "smdiport")) {
1003                         if (iface)
1004                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1005
1006                         if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
1007                                 ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name);
1008                                 continue;
1009                         }
1010                 } else if (!strcasecmp(v->name, "pollinginterval")) {
1011                         if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) {
1012                                 ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
1013                                 mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1014                         }
1015                 } else {
1016                         if (!iface) {
1017                                 ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
1018                                 continue;
1019                         }
1020                         append_mailbox_mapping(v, iface);
1021                 }
1022         }
1023
1024         if (iface)
1025                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1026
1027         ast_config_destroy(conf);
1028         
1029         if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
1030                 && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
1031                 ast_log(LOG_ERROR, "Failed to start MWI monitoring thread.  This module will not operate.\n");
1032                 return AST_MODULE_LOAD_FAILURE;
1033         }
1034
1035         /* Prune any interfaces we should no longer monitor. */
1036         if (reload)
1037                 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
1038         
1039         ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
1040         /* TODO: this is bad, we need an ASTOBJ method for this! */
1041         if (!smdi_ifaces.head)
1042                 res = 1;
1043         ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
1044         
1045         return res;
1046 }
1047
1048 struct smdi_msg_datastore {
1049         unsigned int id;
1050         struct ast_smdi_interface *iface;
1051         struct ast_smdi_md_message *md_msg;
1052 };
1053
1054 static void smdi_msg_datastore_destroy(void *data)
1055 {
1056         struct smdi_msg_datastore *smd = data;
1057
1058         if (smd->iface)
1059                 ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
1060
1061         if (smd->md_msg)
1062                 ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
1063
1064         free(smd);
1065 }
1066
1067 static const struct ast_datastore_info smdi_msg_datastore_info = {
1068         .type = "SMDIMSG",
1069         .destroy = smdi_msg_datastore_destroy,
1070 };
1071
1072 static int smdi_msg_id;
1073
1074 /*! In milliseconds */
1075 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
1076
1077 AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS
1078         AST_APP_OPTION('t', OPT_SEARCH_TERMINAL),
1079         AST_APP_OPTION('n', OPT_SEARCH_NUMBER),
1080 END_OPTIONS );
1081
1082 static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1083 {
1084         struct ast_module_user *u;
1085         AST_DECLARE_APP_ARGS(args,
1086                 AST_APP_ARG(port);
1087                 AST_APP_ARG(search_key);
1088                 AST_APP_ARG(timeout);
1089                 AST_APP_ARG(options);
1090         );
1091         struct ast_flags options = { 0 };
1092         unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1093         int res = -1;
1094         char *parse = NULL;
1095         struct smdi_msg_datastore *smd = NULL;
1096         struct ast_datastore *datastore = NULL;
1097         struct ast_smdi_interface *iface = NULL;
1098         struct ast_smdi_md_message *md_msg = NULL;
1099
1100         u = ast_module_user_add(chan);
1101
1102         if (ast_strlen_zero(data)) {
1103                 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
1104                 goto return_error;
1105         }
1106
1107         if (!chan) {
1108                 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
1109                 goto return_error;
1110         }
1111
1112         ast_autoservice_start(chan);
1113
1114         parse = ast_strdupa(data);
1115         AST_STANDARD_APP_ARGS(args, parse);
1116
1117         if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
1118                 ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
1119                 goto return_error;
1120         }
1121
1122         if (!(iface = ast_smdi_interface_find(args.port))) {
1123                 ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
1124                 goto return_error;
1125         }
1126
1127         if (!ast_strlen_zero(args.options)) {
1128                 ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
1129         }
1130
1131         if (!ast_strlen_zero(args.timeout)) {
1132                 if (sscanf(args.timeout, "%u", &timeout) != 1) {
1133                         ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
1134                         timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1135                 }
1136         }
1137
1138         if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) {
1139                 ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after "
1140                         "waiting %u ms.\n", args.search_key, timeout);
1141                 goto return_error;
1142         }
1143
1144         if (!(smd = ast_calloc(1, sizeof(*smd))))
1145                 goto return_error;
1146
1147         smd->iface = ASTOBJ_REF(iface);
1148         smd->md_msg = ASTOBJ_REF(md_msg);
1149         smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
1150         snprintf(buf, len, "%u", smd->id);
1151
1152         if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf)))
1153                 goto return_error;
1154
1155         datastore->data = smd;
1156
1157         ast_channel_lock(chan);
1158         ast_channel_datastore_add(chan, datastore);
1159         ast_channel_unlock(chan);
1160
1161         res = 0;
1162
1163 return_error:
1164         if (iface)
1165                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1166
1167         if (md_msg)
1168                 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
1169
1170         if (smd && !datastore)
1171                 smdi_msg_datastore_destroy(smd);
1172
1173         if (parse)
1174                 ast_autoservice_stop(chan);
1175
1176         ast_module_user_remove(u);
1177
1178         return res;
1179 }
1180
1181 static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1182 {
1183         struct ast_module_user *u;
1184         int res = -1;
1185         AST_DECLARE_APP_ARGS(args,
1186                 AST_APP_ARG(id);
1187                 AST_APP_ARG(component);
1188         );
1189         char *parse;
1190         struct ast_datastore *datastore = NULL;
1191         struct smdi_msg_datastore *smd = NULL;
1192
1193         u = ast_module_user_add(chan);
1194
1195         if (!chan) {
1196                 ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
1197                 goto return_error;
1198         }
1199
1200         if (ast_strlen_zero(data)) {
1201                 ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
1202                 goto return_error;
1203         }
1204
1205         parse = ast_strdupa(data);
1206         AST_STANDARD_APP_ARGS(args, parse);
1207
1208         if (ast_strlen_zero(args.id)) {
1209                 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1210                 goto return_error;
1211         }
1212
1213         if (ast_strlen_zero(args.component)) {
1214                 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1215                 goto return_error;
1216         }
1217
1218         ast_channel_lock(chan);
1219         datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
1220         ast_channel_unlock(chan);
1221         
1222         if (!datastore) {
1223                 ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
1224                 goto return_error;
1225         }
1226
1227         smd = datastore->data;
1228
1229         if (!strcasecmp(args.component, "number")) {
1230                 ast_copy_string(buf, smd->md_msg->mesg_desk_num, len);
1231         } else if (!strcasecmp(args.component, "terminal")) {
1232                 ast_copy_string(buf, smd->md_msg->mesg_desk_term, len);
1233         } else if (!strcasecmp(args.component, "station")) {
1234                 ast_copy_string(buf, smd->md_msg->fwd_st, len);
1235         } else if (!strcasecmp(args.component, "callerid")) {
1236                 ast_copy_string(buf, smd->md_msg->calling_st, len);
1237         } else if (!strcasecmp(args.component, "type")) {
1238                 snprintf(buf, len, "%c", smd->md_msg->type);
1239         } else {
1240                 ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
1241                         args.component);
1242                 goto return_error;
1243         }
1244
1245         res = 0;
1246
1247 return_error:
1248         ast_module_user_remove(u);
1249
1250         return 0;
1251 }
1252
1253 static struct ast_custom_function smdi_msg_retrieve_function = {
1254         .name = "SMDI_MSG_RETRIEVE",
1255         .synopsis = "Retrieve an SMDI message.",
1256         .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])",
1257         .desc = 
1258         "   This function is used to retrieve an incoming SMDI message.  It returns\n"
1259         "an ID which can be used with the SMDI_MSG() function to access details of\n"
1260         "the message.  Note that this is a destructive function in the sense that\n"
1261         "once an SMDI message is retrieved using this function, it is no longer in\n"
1262         "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
1263         "channels.  The timeout for this function is optional, and the default is\n"
1264         "3 seconds.  When providing a timeout, it should be in milliseconds.\n"
1265         "   The default search is done on the forwarding station ID.  However, if\n"
1266         "you set one of the search key options in the options field, you can change\n"
1267         "this behavior.\n"
1268         "   Options:\n"
1269         "     t - Instead of searching on the forwarding station, search on the message\n"
1270         "         desk terminal.\n"
1271         "     n - Instead of searching on the forwarding station, search on the message\n"
1272         "         desk number.\n"
1273         "",
1274         .read = smdi_msg_retrieve_read,
1275 };
1276
1277 static struct ast_custom_function smdi_msg_function = {
1278         .name = "SMDI_MSG",
1279         .synopsis = "Retrieve details about an SMDI message.",
1280         .syntax = "SMDI_MSG(<message_id>,<component>)",
1281         .desc = 
1282         "   This function is used to access details of an SMDI message that was\n"
1283         "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
1284         "function.\n"
1285         "   Valid message components are:\n"
1286         "      number   - The message desk number\n"
1287         "      terminal - The message desk terminal\n"
1288         "      station  - The forwarding station\n"
1289         "      callerid - The callerID of the calling party that was forwarded\n"
1290         "      type     - The call type.  The value here is the exact character\n"
1291         "                 that came in on the SMDI link.  Typically, example values\n"
1292         "                 are: D - Direct Calls, A - Forward All Calls,\n"
1293         "                      B - Forward Busy Calls, N - Forward No Answer Calls\n"
1294         "",
1295         .read = smdi_msg_read,
1296 };
1297
1298 static int load_module(void)
1299 {
1300         int res;
1301         
1302         /* initialize our containers */
1303         memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
1304         ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
1305         
1306         ast_mutex_init(&mwi_monitor.lock);
1307         ast_cond_init(&mwi_monitor.cond, NULL);
1308
1309         ast_custom_function_register(&smdi_msg_retrieve_function);
1310         ast_custom_function_register(&smdi_msg_function);
1311
1312         /* load the config and start the listener threads*/
1313         res = smdi_load(0);
1314         if (res < 0) {
1315                 return res;
1316         } else if (res == 1) {
1317                 ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
1318                 return AST_MODULE_LOAD_DECLINE;
1319         }
1320         
1321         return AST_MODULE_LOAD_SUCCESS;
1322 }
1323
1324 static int unload_module(void)
1325 {
1326         /* this destructor stops any running smdi_read threads */
1327         ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
1328         ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
1329
1330         destroy_all_mailbox_mappings();
1331
1332         ast_mutex_lock(&mwi_monitor.lock);
1333         mwi_monitor.stop = 1;
1334         ast_cond_signal(&mwi_monitor.cond);
1335         ast_mutex_unlock(&mwi_monitor.lock);
1336
1337         if (mwi_monitor.thread != AST_PTHREADT_NULL) {
1338                 pthread_join(mwi_monitor.thread, NULL);
1339         }
1340
1341         ast_custom_function_unregister(&smdi_msg_retrieve_function);
1342         ast_custom_function_unregister(&smdi_msg_function);
1343
1344         return 0;
1345 }
1346
1347 static int reload(void)
1348 {
1349         int res;
1350
1351         res = smdi_load(1);
1352
1353         if (res < 0) {
1354                 return res;
1355         } else if (res == 1) {
1356                 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
1357                 return 0;
1358         } else
1359                 return 0;
1360 }
1361
1362 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
1363                 .load = load_module,
1364                 .unload = unload_module,
1365                 .reload = reload,
1366                );