Index: apps/app_meetme.c
===================================================================
--- apps/app_meetme.c	(revision 307508)
+++ apps/app_meetme.c	(working copy)
@@ -477,6 +477,13 @@
 			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
 			<parameter name="Meetme" required="true" />
 			<parameter name="Usernum" required="true" />
+			<parameter name="AnnounceToUser" required="false">
+				<optionlist>
+					<option name="yes">
+						<para>Play a track to the user that they have been muted</para>
+					</option>
+				</optionlist>
+			</parameter>
 		</syntax>
 		<description>
 		</description>
@@ -489,6 +496,13 @@
 			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
 			<parameter name="Meetme" required="true" />
 			<parameter name="Usernum" required="true" />
+			<parameter name="AnnounceToUser" required="false">
+				<optionlist>
+					<option name="yes">
+						<para>Play a track to the user that they have been unmuted</para>
+					</option>
+				</optionlist>
+			</parameter>
 		</syntax>
 		<description>
 		</description>
@@ -779,13 +793,14 @@
 	char usrvalue[50];                      /*!< Custom User Value */
 	char namerecloc[PATH_MAX];		/*!< Name Recorded file Location */
 	time_t jointime;                        /*!< Time the user joined the conference */
- 	time_t kicktime;                        /*!< Time the user will be kicked from the conference */
- 	struct timeval start_time;              /*!< Time the user entered into the conference */
- 	long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
- 	long play_warning;                      /*!< Play a warning when 'y' ms are left */
- 	long warning_freq;                      /*!< Repeat the warning every 'z' ms */
- 	const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
- 	const char *end_sound;                  /*!< File to play when time is up. */
+	time_t kicktime;                        /*!< Time the user will be kicked from the conference */
+	struct timeval start_time;              /*!< Time the user entered into the conference */
+	long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
+	long play_warning;                      /*!< Play a warning when 'y' ms are left */
+	long warning_freq;                      /*!< Repeat the warning every 'z' ms */
+	const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
+	const char *end_sound;                  /*!< File to play when time is up. */
+	char *musicclass;			/*!< Music class for this specific user, for this specific meetme.  Must be free()'d on user destroy */
 	struct volume talk;
 	struct volume listen;
 	AST_LIST_ENTRY(ast_conf_user) list;
@@ -994,6 +1009,7 @@
 
 static int admin_exec(struct ast_channel *chan, const char *data);
 static void *recordthread(void *args);
+static void ast_conf_user_ao2_destructor(void *conf_user_ao2);
 
 static const char *istalking(int x)
 {
@@ -2000,6 +2016,17 @@
 	return res;
 }
 
+/*! \brief
+ * Destructor for conference user data.  Conference user data is allocated in conf_run()
+ */
+static void ast_conf_user_ao2_destructor(void *conf_user_ao2) {
+	struct ast_conf_user *user = (struct ast_conf_user *) conf_user_ao2;
+
+	if (user->musicclass) {
+		ast_free(user->musicclass);
+	}
+}
+
 static int rt_extend_conf(const char *confno)
 {
 	char currenttime[32];
@@ -2285,7 +2312,7 @@
 	}
 	ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
 
-	if (!(user = ao2_alloc(sizeof(*user), NULL))) {
+	if (!(user = ao2_alloc(sizeof(*user), ast_conf_user_ao2_destructor))) {
 		goto conf_run_cleanup;
 	}
 
@@ -2422,22 +2449,23 @@
 	ast_mutex_unlock(&conf->announcethreadlock);
 
 	time(&user->jointime);
-	
+
 	user->timelimit = timelimit;
 	user->play_warning = play_warning;
 	user->warning_freq = warning_freq;
 	user->warning_sound = warning_sound;
-	user->end_sound = end_sound;	
-	
+	user->end_sound = end_sound;
+	user->musicclass = ast_strdup(optargs[OPT_ARG_MOH_CLASS]);
+
 	if (calldurationlimit > 0) {
 		time(&user->kicktime);
 		user->kicktime = user->kicktime + calldurationlimit;
 	}
-	
+
 	if (ast_tvzero(user->start_time))
 		user->start_time = ast_tvnow();
 	time_left_ms = user->timelimit;
-	
+
 	if (user->timelimit) {
 		nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
 		nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
@@ -4769,7 +4797,10 @@
 	struct ast_conf_user *user;
 	const char *confid = astman_get_header(m, "Meetme");
 	char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
+	char *announce = ast_strdupa(astman_get_header(m, "AnnounceToUser"));
 	int userno;
+	int musiconhold = 0;
+	int announce_to_user_bool = 0;
 
 	if (ast_strlen_zero(confid)) {
 		astman_send_error(s, m, "Meetme conference not specified");
@@ -4809,11 +4840,62 @@
 		return 0;
 	}
 
-	if (mute)
-		user->adminflags |= ADMINFLAG_MUTED;	/* request user muting */
-	else
+	if (!strcasecmp(announce, "yes")) {
+		announce_to_user_bool = 1;
+	}
+
+	if (announce_to_user_bool) {
+		musiconhold = ast_test_flag(user->chan, AST_FLAG_MOH);
+		ast_mutex_lock(&conf->listenlock); /* We need to take over for a little bit, since we'll be playing a track */
+
+		if (musiconhold) {
+			ast_moh_stop(user->chan);
+		}
+	}
+
+	if (mute) {
+		user->adminflags |= ADMINFLAG_MUTED;
+
+		if (announce_to_user_bool) {
+			/* we mute the user before playing the track (otherwise we get bad audio mixing) */
+
+			if (!ast_streamfile(user->chan, "conf-muted", user->chan->language)) {
+				ast_waitstream(user->chan, "");
+			}
+		}
+
+		/* The not talking detection doesn't kick in if the user was talking while being muted */
+		manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
+			"Channel: %s\r\n"
+			"Uniqueid: %s\r\n"
+			"Meetme: %s\r\n"
+			"Usernum: %d\r\n"
+			"Status: off\r\n",
+			user->chan->name, user->chan->uniqueid, conf->confno, user->user_no);
+
+		user->talking = 0;
+	} else {
+		if (announce_to_user_bool) {
+			user->adminflags |= ADMINFLAG_MUTED;	/* mute user before playing track (otherwise we get bad audio mixing) */
+
+			if (!ast_streamfile(user->chan, "conf-unmuted", user->chan->language)) {
+				ast_waitstream(user->chan, "");
+			}
+
+			/* We unmute the user after playing the track (otherwise we get bad audio mixing) */
+		}
+
 		user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);	/* request user unmuting */
+	}
 
+	if (announce_to_user_bool) {
+		if (musiconhold) {
+			conf_start_moh(user->chan, user->musicclass);
+		}
+
+		ast_mutex_unlock(&conf->listenlock);
+	}
+
 	AST_LIST_UNLOCK(&confs);
 
 	ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid);
