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