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