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