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