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