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