#include "asterisk/translate.h"
 #include "asterisk/ulaw.h"
 #include "asterisk/astobj.h"
+#include "asterisk/astobj2.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/dial.h"
 #include "asterisk/causes.h"
 #define MAX_CONFNUM 80
 #define MAX_PIN     80
 
+enum announcetypes {
+       CONF_HASJOIN,
+       CONF_HASLEFT
+};
+
+struct announce_listitem {
+       AST_LIST_ENTRY(announce_listitem) entry;
+       char namerecloc[PATH_MAX];                              /*!< Name Recorded file Location */
+       char language[MAX_LANGUAGE];
+       struct ast_channel *confchan;
+       int confusers;
+       enum announcetypes announcetype;
+};
+
 /*! \brief The MeetMe Conference object */
 struct ast_conference {
        ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
        unsigned int isdynamic:1;               /*!< Created on the fly? */
        unsigned int locked:1;                  /*!< Is the conference locked? */
        pthread_t recordthread;                 /*!< thread for recording */
-       ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
+       ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
        pthread_attr_t attr;                    /*!< thread attribute */
        const char *recordingfilename;          /*!< Filename to record the Conference into */
        const char *recordingformat;            /*!< Format to record the Conference in */
        struct ast_trans_pvt *transpath[32];
        AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
        AST_LIST_ENTRY(ast_conference) list;
+       /* announce_thread related data */
+       pthread_t announcethread;
+       ast_mutex_t announcethreadlock;
+       unsigned int announcethread_stop:1;
+       ast_cond_t announcelist_addition;
+       AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
+       ast_mutex_t announcelistlock;
 };
 
 static AST_LIST_HEAD_STATIC(confs, ast_conference);
        ast_mutex_init(&cnf->listenlock);
        cnf->recordthread = AST_PTHREADT_NULL;
        ast_mutex_init(&cnf->recordthreadlock);
+       cnf->announcethread = AST_PTHREADT_NULL;
+       ast_mutex_init(&cnf->announcethreadlock);
        ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
        ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
        ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
 static int conf_free(struct ast_conference *conf)
 {
        int x;
+       struct announce_listitem *item;
        
        AST_LIST_REMOVE(&confs, conf, list);
 
                if (conf->transpath[x])
                        ast_translator_free_path(conf->transpath[x]);
        }
+       if (conf->announcethread != AST_PTHREADT_NULL) {
+               ast_mutex_lock(&conf->announcelistlock);
+               conf->announcethread_stop = 1;
+               ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
+               ast_cond_signal(&conf->announcelist_addition);
+               ast_mutex_unlock(&conf->announcelistlock);
+               pthread_join(conf->announcethread, NULL);
+       
+               while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
+                       ast_filedelete(item->namerecloc, NULL);
+                       ao2_ref(item, -1);
+               }
+               ast_mutex_destroy(&conf->announcelistlock);
+       }
        if (conf->origframe)
                ast_frfree(conf->origframe);
        if (conf->lchan)
        ast_mutex_destroy(&conf->playlock);
        ast_mutex_destroy(&conf->listenlock);
        ast_mutex_destroy(&conf->recordthreadlock);
+       ast_mutex_destroy(&conf->announcethreadlock);
+
        free(conf);
 
        return 0;
        return res;
 }
 
+static const char *get_announce_filename(enum announcetypes type)
+{
+       switch (type) {
+       case CONF_HASLEFT:
+               return "conf-hasleft";
+               break;
+       case CONF_HASJOIN:
+               return "conf-hasjoin";
+               break;
+       default:
+               return "";
+       }
+}
+
+static void *announce_thread(void *data)
+{
+       struct announce_listitem *current;
+       struct ast_conference *conf = data;
+       int res;
+       char filename[PATH_MAX] = "";
+       AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
+       AST_LIST_HEAD_INIT_NOLOCK(&local_list);
+
+       while (!conf->announcethread_stop) {
+               ast_mutex_lock(&conf->announcelistlock);
+               if (conf->announcethread_stop) {
+                       ast_mutex_unlock(&conf->announcelistlock);
+                       break;
+               }
+               if (AST_LIST_EMPTY(&conf->announcelist))
+                       ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
+
+               AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
+               AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
+
+               ast_mutex_unlock(&conf->announcelistlock);
+               if (conf->announcethread_stop) {
+                       break;
+               }
+
+               for (; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
+                       ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
+                       if (!ast_fileexists(current->namerecloc, NULL, NULL))
+                               continue;
+                       if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
+                               if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
+                                       res = ast_waitstream(current->confchan, "");
+                               if (!res) {
+                                       ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
+                                       if (!ast_streamfile(current->confchan, filename, current->language))
+                                               ast_waitstream(current->confchan, "");
+                               }
+                       }
+                       if (current->announcetype == CONF_HASLEFT) {
+                               ast_filedelete(current->namerecloc, NULL);
+                       }
+               }
+       }
+
+       /* thread marked to stop, clean up */
+       while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
+               ast_filedelete(current->namerecloc, NULL);
+               ao2_ref(current, -1);
+       }
+       return NULL;
+}
 
 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
 {
        }
        ast_mutex_unlock(&conf->recordthreadlock);
 
+       ast_mutex_lock(&conf->announcethreadlock);
+       if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
+               ast_mutex_init(&conf->announcelistlock);
+               AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
+               ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
+       }
+       ast_mutex_unlock(&conf->announcethreadlock);
+
        time(&user->jointime);
 
        if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
        ztc.chan = 0;   
        ztc.confno = conf->zapconf;
 
-       ast_mutex_lock(&conf->playlock);
-
        if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
-               if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
-                       if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
-                               ast_waitstream(conf->chan, "");
-                       if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
-                               ast_waitstream(conf->chan, "");
+               struct announce_listitem *item;
+               if (!(item = ao2_alloc(sizeof(*item), NULL)))
+                       return -1;
+               ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
+               ast_copy_string(item->language, chan->language, sizeof(item->language));
+               item->confchan = conf->chan;
+               item->confusers = conf->users;
+               item->announcetype = CONF_HASJOIN;
+               ast_mutex_lock(&conf->announcelistlock);
+               ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
+               AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
+               ast_cond_signal(&conf->announcelist_addition);
+               ast_mutex_unlock(&conf->announcelistlock);
+
+               while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
+                       ;
                }
+               ao2_ref(item, -1);
        }
 
        if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
        if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
                ast_log(LOG_WARNING, "Error setting conference\n");
                close(fd);
-               ast_mutex_unlock(&conf->playlock);
                goto outrun;
        }
        ast_log(LOG_DEBUG, "Placed channel %s in %s conf %d\n", chan->name, dahdi_chan_name, conf->zapconf);
                                conf_play(chan, conf, ENTER);
        }
 
-       ast_mutex_unlock(&conf->playlock);
-
        conf_flush(fd, chan);
 
        if (confflags & CONFFLAG_AGI) {
 
        reset_volumes(user);
 
-       AST_LIST_LOCK(&confs);
        if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
                conf_play(chan, conf, LEAVE);
 
-       if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
-               if (ast_fileexists(user->namerecloc, NULL, NULL)) {
-                       if ((conf->chan) && (conf->users > 1)) {
-                               if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
-                                       ast_waitstream(conf->chan, "");
-                               if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
-                                       ast_waitstream(conf->chan, "");
-                       }
-                       ast_filedelete(user->namerecloc, NULL);
-               }
+       if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
+               struct announce_listitem *item;
+               if (!(item = ao2_alloc(sizeof(*item), NULL)))
+                       return -1;
+               ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
+               ast_copy_string(item->language, chan->language, sizeof(item->language));
+               item->confchan = conf->chan;
+               item->confusers = conf->users;
+               item->announcetype = CONF_HASLEFT;
+               ast_mutex_lock(&conf->announcelistlock);
+               AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
+               ast_cond_signal(&conf->announcelist_addition);
+               ast_mutex_unlock(&conf->announcelistlock);
+       } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
+               /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
+               ast_filedelete(user->namerecloc, NULL);
        }
-       AST_LIST_UNLOCK(&confs);
 
  outrun:
        AST_LIST_LOCK(&confs);