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