--- ../cleancvs/asterisk/apps/app_meetme.c      2004-04-29 04:30:14.000000000 +0
200
+++ apps/app_meetme.c   2004-05-06 15:54:37.000000000 +0200
@@ -37,9 +37,11 @@

 static char *app = "MeetMe";
 static char *app2 = "MeetMeCount";
+static char *app3 = "MeetMeAdmin";

 static char *synopsis = "Simple MeetMe conference bridge";
 static char *synopsis2 = "MeetMe participant count";
+static char *synopsis3 = "Send Admin Commands to a conference";

 static char *descrip =
 "  MeetMe(confno[,[options][,pin]]): Enters the user into a specified MeetMe co
nference.\n"
@@ -58,6 +60,7 @@
 "      'v' -- video mode\n"
 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
 "      'M' -- enable music on hold when the conference has a single caller\n"
+"      'x' -- exit the conference if the last marked user left\n"
 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
 "            Default: conf-background.agi\n"
 "             (Note: This does not work with non-Zap channels in the same confe
rence)\n"
@@ -71,6 +74,8 @@
 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";

+static char *descrip3 = "";
+
 STANDARD_LOCAL_USER;

 LOCAL_USER_DECL;
@@ -80,12 +85,31 @@
        int fd;                         /* Announcements fd */
        int zapconf;                    /* Zaptel Conf # */
        int users;                      /* Number of active users */
+        int markedusers;                 /* Number of marked users */
+        struct ast_conf_user *firstuser;  /* Pointer to the first user struct *
/
+        struct ast_conf_user *lastuser;   /* Pointer to the last user struct */
        time_t start;                   /* Start time (s) */
        int isdynamic;                  /* Created on the fly? */
+        int locked;                      /* Is the conference locked? */
        char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN
 */
        struct ast_conference *next;
 } *confs;

+struct ast_conf_user {
+       int user_no;                     /* User Number */
+       struct ast_conf_user *prevuser;  /* Pointer to the previous user */
+       struct ast_conf_user *nextuser;  /* Pointer to the next user */
+       int userflags;                   /* Flags as set in the conference */
+       int adminflags;                  /* Flags set by the Admin */
+       char channame[AST_CHANNEL_NAME]; /* Name of the connected channel */
+       char usrvalue[50];               /* Custom User Value */
+       time_t jointime;                 /* Time the user joined the conference
*/
+};
+
+#define ADMINFLAG_MUTED (1 << 1)       /* User is listening at the moment */
+#define ADMINFLAG_KICKME (1 << 2)      /* User is able to talk*/
+
+
 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;

 #include "enter.h"
@@ -105,6 +129,7 @@
 #define CONFFLAG_VIDEO (1 << 7)                /* Set to enable video mode */
 #define CONFFLAG_AGI (1 << 8)          /* Set to run AGI Script in Background *
/
 #define CONFFLAG_MOH (1 << 9)          /* Set to have music on hold when */
+#define CONFFLAG_ADMINEXIT (1 << 10)    /* If set the MeetMe will return if all
 marked with this flag left */


 static int careful_write(int fd, unsigned char *data, int len)
@@ -185,9 +210,13 @@
                                cnf = NULL;
                                goto cnfout;
                        }
+                        /* Fill the conference struct */
                        cnf->start = time(NULL);
                        cnf->zapconf = ztc.confno;
                        cnf->isdynamic = dynamic;
+                        cnf->firstuser = NULL;
+                        cnf->lastuser = NULL;
+                        cnf->locked = 0;
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Created ZapTel con
ference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
                        cnf->next = confs;
@@ -244,6 +273,7 @@
 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int
confflags)
 {
        struct ast_conference *prev=NULL, *cur;
+        struct ast_conf_user user;
        int fd;
        struct zt_confinfo ztc;
        struct ast_frame *f;
@@ -264,12 +294,54 @@
        struct ast_app *app;
        char *agifile;
        char *agifiledefault = "conf-background.agi";
+        char choice[2] = "";

        ZT_BUFFERINFO bi;
        char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
        char *buf = __buf + AST_FRIENDLY_OFFSET;

+       if (conf->locked) {
+               /* Sorry, but this confernce is locked! */
+               if (!ast_streamfile(chan, "conf-locked", chan->language))
+                       ast_waitstream(chan, "");
+               goto outrun;
+       }
+
        conf->users++;
+        if (confflags & CONFFLAG_ADMINEXIT) {
+               if (conf->markedusers == -1) {
+                       conf->markedusers = 1;
+               } else {
+                       conf->markedusers++;
+               }
+        }
+
+        if (conf->firstuser == NULL) {
+               /* Fill the first new User struct */
+                user.user_no = 1;
+                user.nextuser = NULL;
+                user.prevuser = NULL;
+               conf->firstuser = &user;
+               conf->lastuser = &user;
+        } else {
+               /* Fill the new user struct */
+               user.user_no = conf->lastuser->user_no + 1;
+                user.prevuser = conf->lastuser;
+                user.nextuser = NULL;
+                if (conf->lastuser->nextuser != NULL) {
+                       ast_log(LOG_WARNING, "Error in User Management!\n");
+                       goto outrun;
+                } else {
+                       conf->lastuser->nextuser = &user;
+                       conf->lastuser = &user;
+                }
+        }
+        strncpy(user.usrvalue, "test", sizeof(user.usrvalue));
+       strncpy(user.channame, chan->name, AST_CHANNEL_NAME);
+       user.userflags = confflags;
+
+        ast_log(LOG_WARNING, "USER: %.8X\n", &user);
+
        if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
                if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
                        ast_waitstream(chan, "");
@@ -417,6 +489,10 @@
                outfd = -1;
                ms = -1;
                c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+
+                /* Update the struct with the actual confflags */
+                user.userflags = confflags;
+
                /* trying to add moh for single person conf */
                if (confflags & CONFFLAG_MOH) {
                        if (conf->users == 1) {
@@ -431,7 +507,37 @@
                                }
                        }
                }
-               /* end modifications */
+
+                /* Leave if the last marked user left */
+                if ((confflags & CONFFLAG_ADMINEXIT) && (conf->markedusers == 0
)) {
+                       ret = 0;
+                       break;
+                }
+
+               /* Check if the admin changed my modes */
+               if (user.adminflags) {
+                       /* Set the new modes */
+                       if ((user.adminflags & ADMINFLAG_MUTED) && (ztc.confmode
 & ZT_CONF_TALKER)) {
+                               ztc.confmode ^= ZT_CONF_TALKER;
+                               if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                                       ast_log(LOG_WARNING, "Error setting conf
erence - Un/Mute \n");
+                                       close(fd);
+                                       goto outrun;
+                               }
+                       }
+                       if (!(user.adminflags & ADMINFLAG_MUTED) && !(confflags
& CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
+                               ztc.confmode |= ZT_CONF_TALKER;
+                               if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                                       ast_log(LOG_WARNING, "Error setting conf
erence - Un/Mute \n");
+                                       close(fd);
+                                       goto outrun;
+                               }
+                       }
+                       if (user.adminflags & ADMINFLAG_KICKME) {
+                               close(fd);
+                               goto outrun;
+                       }
+               }

                if (c) {
                        if (c->fds[0] != origfd) {
@@ -450,12 +556,61 @@
                                ret = 0;
                                break;
                        } else if ((f->frametype == AST_FRAME_DTMF) && (f->subcl
ass == '*') && (confflags & CONFFLAG_STARMENU)) {
+                                       if (musiconhold) {
+                                               ast_moh_stop(chan);
+                                       }
                                        if ((confflags & CONFFLAG_ADMIN)) {
-                                       /* Do admin stuff here */
+                                               /* Admin menu */
+                                               ast_app_getdata(chan, "conf-admi
nmenu", choice, 1, 1000);
+                                               ast_log(LOG_WARNING, "DEBUG: %i"
, atoi(choice));
+                                               switch(atoi(choice)) {
+                                                       case 1: /* Un/Mute */
+                                                               if (ztc.confmode
 & ZT_CONF_TALKER) {
+                                                                       ztc.conf
mode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+                                                                       confflag
s |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
                                        } else {
-                                       /* Do user menu here */
+                                                                       ztc.conf
mode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+                                                                       confflag
s ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
+                                                               }
+                                                               if (ioctl(fd, ZT
_SETCONF, &ztc)) {
+                                                                       ast_log(
LOG_WARNING, "Error setting conference - Un/Mute \n");
+                                                                       close(fd
);
+                                                                       goto out
run;
+                                                               }
+                                                               break;
+                                                       case 2: /* Lock the Conf
erence */
+                                                               conf->locked = 1
;
+                                                               break;
+                                               }
+                                        } else {
+                                               /* User menu */
+                                               ast_log(LOG_WARNING, "GOT TO THE
 USER MENU");
+                                               ast_app_getdata(chan, "conf-user
menu", choice, 1, 0);
+                                                       switch(atoi(choice)) {
+                                                               case 1: /* Un/Mu
te */
+                                                                       if (user
.adminflags & ADMINFLAG_MUTED) {
+                                                                              i
f (!ast_streamfile(chan, "conf-notallowed", chan->language))
+                                                                              a
st_waitstream(chan, "");
+                                                                       } else {
+                                                                       if (ztc.
confmode & ZT_CONF_TALKER) {
+                                                                              z
tc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+                                                                              c
onfflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
+                                                                       } else {
+                                                                              z
tc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+                                                                              c
onfflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
+                                                                       }
+                                                                       if (ioct
l(fd, ZT_SETCONF, &ztc)) {
+                                                                              a
st_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+                                                                              c
lose(fd);
+                                                                              g
oto outrun;
+                                                                       }
+                                                               }
+                                                                       break;
+                                                       }
+                                        }
+                                       if (musiconhold) {
+                                               ast_moh_start(chan, NULL);
                                        }
-
                        } else if (fd != chan->fds[0]) {
                                if (f->frametype == AST_FRAME_VOICE) {
                                        if (f->subclass == AST_FORMAT_ULAW) {
@@ -523,7 +678,21 @@
                        ast_log(LOG_WARNING, "Conference not found\n");
                close(conf->fd);
                free(conf);
+        } else {
+               /* Remove the user struct */
+               if (&user == conf->firstuser) {
+                       conf->firstuser->nextuser->prevuser = NULL;
+                       conf->firstuser = conf->firstuser->nextuser;
+               } else if (&user == conf->lastuser){
+                       conf->lastuser->prevuser->nextuser = NULL;
+                       conf->lastuser = conf->lastuser->prevuser;
+               } else {
+                       user.nextuser->prevuser = user.prevuser;
+                       user.prevuser->nextuser = user.nextuser;
+               }
        }
+        /* Return the number of seconds the user was in the conf */
+        pbx_builtin_setvar_helper(chan, "MEETMESECS", (char *) (user.jointime -
 time(NULL)));
        ast_mutex_unlock(&conflock);
        return ret;
 }
@@ -681,6 +850,8 @@
                        confflags |= CONFFLAG_QUIET;
                if (strchr(inflags, 'M'))
                        confflags |= CONFFLAG_MOH;
+                if (strchr(inflags, 'x'))
+                        confflags |= CONFFLAG_ADMINEXIT;
                if (strchr(inflags, 'b'))
                        confflags |= CONFFLAG_AGI;
                if (strchr(inflags, 'd'))
@@ -854,10 +1025,101 @@
        return res;
 }

+static struct ast_conf_user* find_user(struct ast_conference *conf, char *calle
rident) {
+       struct ast_conf_user *user = NULL;
+       if (conf && callerident) {
+               user = conf->firstuser;
+               while(user) {
+                       if (strcmp(user->channame, callerident))
+                               return user;
+                       if (user->nextuser == NULL)
+                               return NULL;
+                       user = user->nextuser;
+               }
+       }
+       return NULL;
+}
+
+static int admin_exec(struct ast_channel *chan, void *data) {
+       /* MeetMeAdmin(confno, command, caller) */
+       char *params, *command = NULL, *caller = NULL, *conf = NULL;
+       struct ast_conference *cnf;
+       struct ast_conf_user *user = NULL;
+
+       ast_mutex_lock(&conflock);
+       /* The param has the conference number the user and the command to execu
te */
+       if (data && strlen(data)) {
+               params = ast_strdupa((char *) data);
+               conf = strsep(&params, "|");
+               command = strsep(&params, "|");
+               caller = strsep(&params, "|");
+
+               ast_mutex_lock(&conflock);
+                cnf = confs;
+                while (cnf) {
+                        if (strcmp(cnf->confno, conf))
+                               break;
+                       cnf = cnf->next;
+                }
+                ast_mutex_unlock(&conflock);
+
+                if (caller)
+                       user = find_user(cnf, caller);
+
+               if (cnf) {
+                       switch((int) (*command)) {
+                               case 76: /* L: Lock */
+                                       cnf->locked = 1;
+                                       break;
+                               case 108: /* l: Unlock */
+                                       cnf->locked = 0;
+                                       break;
+                               case 75: /* K: kick all users*/
+                                       user = cnf->firstuser;
+                                       while(user) {
+                                               user->adminflags |= ADMINFLAG_KI
CKME;
+                                               if (user->nextuser) {
+                                                       user = user->nextuser;
+                                               } else {
+                                                       break;
+                                               }
+                                       }
+                                       break;
+                               case 77: /* M: Mute */
+                                       if (user) {
+                                               user->adminflags |= ADMINFLAG_MU
TED;
+                                       } else {
+                                               ast_log(LOG_NOTICE, "Specified U
ser not found!");
+                                       }
+                                       break;
+                               case 109: /* m: Unmute */
+                                       if (user) {
+                                               user->adminflags ^= ADMINFLAG_MU
TED;
+                                       } else {
+                                               ast_log(LOG_NOTICE, "Specified U
ser not found!");
+                                       }
+                                       break;
+                               case 107: /* k: Kick user */
+                                       if (user) {
+                                               user->adminflags |= ADMINFLAG_KI
CKME;
+                                       } else {
+                                               ast_log(LOG_NOTICE, "Specified U
ser not found!");
+                                       }
+                                       break;
+                       }
+               } else {
+                       ast_log(LOG_NOTICE, "Conference Number not found\n");
+               }
+       }
+       ast_mutex_unlock(&conflock);
+       return 0;
+}
+
 int unload_module(void)
 {
        STANDARD_HANGUP_LOCALUSERS;
        ast_cli_unregister(&cli_show_confs);
+        ast_unregister_application(app3);
        ast_unregister_application(app2);
        return ast_unregister_application(app);
 }
@@ -865,6 +1127,7 @@
 int load_module(void)
 {
        ast_cli_register(&cli_show_confs);
+        ast_register_application(app3, admin_exec, synopsis3, descrip3);
        ast_register_application(app2, count_exec, synopsis2, descrip2);
        return ast_register_application(app, conf_exec, synopsis, descrip);
 }
[root@uhura asterisk]# cat ../meetme_EXIT_patchv2.diff
--- ../cleancvs/asterisk/apps/app_meetme.c      2004-04-29 04:30:14.000000000 +0200
+++ apps/app_meetme.c   2004-05-06 15:54:37.000000000 +0200
@@ -37,9 +37,11 @@

 static char *app = "MeetMe";
 static char *app2 = "MeetMeCount";
+static char *app3 = "MeetMeAdmin";

 static char *synopsis = "Simple MeetMe conference bridge";
 static char *synopsis2 = "MeetMe participant count";
+static char *synopsis3 = "Send Admin Commands to a conference";

 static char *descrip =
 "  MeetMe(confno[,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
@@ -58,6 +60,7 @@
 "      'v' -- video mode\n"
 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
 "      'M' -- enable music on hold when the conference has a single caller\n"
+"      'x' -- exit the conference if the last marked user left\n"
 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
 "            Default: conf-background.agi\n"
 "             (Note: This does not work with non-Zap channels in the same conference)\n"
@@ -71,6 +74,8 @@
 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";

+static char *descrip3 = "";
+
 STANDARD_LOCAL_USER;

 LOCAL_USER_DECL;
@@ -80,12 +85,31 @@
        int fd;                         /* Announcements fd */
        int zapconf;                    /* Zaptel Conf # */
        int users;                      /* Number of active users */
+        int markedusers;                 /* Number of marked users */
+        struct ast_conf_user *firstuser;  /* Pointer to the first user struct */
+        struct ast_conf_user *lastuser;   /* Pointer to the last user struct */
        time_t start;                   /* Start time (s) */
        int isdynamic;                  /* Created on the fly? */
+        int locked;                      /* Is the conference locked? */
        char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN */
        struct ast_conference *next;
 } *confs;

+struct ast_conf_user {
+       int user_no;                     /* User Number */
+       struct ast_conf_user *prevuser;  /* Pointer to the previous user */
+       struct ast_conf_user *nextuser;  /* Pointer to the next user */
+       int userflags;                   /* Flags as set in the conference */
+       int adminflags;                  /* Flags set by the Admin */
+       char channame[AST_CHANNEL_NAME]; /* Name of the connected channel */
+       char usrvalue[50];               /* Custom User Value */
+       time_t jointime;                 /* Time the user joined the conference */
+};
+
+#define ADMINFLAG_MUTED (1 << 1)       /* User is listening at the moment */
+#define ADMINFLAG_KICKME (1 << 2)      /* User is able to talk*/
+
+
 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;

 #include "enter.h"
@@ -105,6 +129,7 @@
 #define CONFFLAG_VIDEO (1 << 7)                /* Set to enable video mode */
 #define CONFFLAG_AGI (1 << 8)          /* Set to run AGI Script in Background */
 #define CONFFLAG_MOH (1 << 9)          /* Set to have music on hold when */
+#define CONFFLAG_ADMINEXIT (1 << 10)    /* If set the MeetMe will return if all marked with this flag left */


 static int careful_write(int fd, unsigned char *data, int len)
@@ -185,9 +210,13 @@
                                cnf = NULL;
                                goto cnfout;
                        }
+                        /* Fill the conference struct */
                        cnf->start = time(NULL);
                        cnf->zapconf = ztc.confno;
                        cnf->isdynamic = dynamic;
+                        cnf->firstuser = NULL;
+                        cnf->lastuser = NULL;
+                        cnf->locked = 0;
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Created ZapTel conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
                        cnf->next = confs;
@@ -244,6 +273,7 @@
 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
 {
        struct ast_conference *prev=NULL, *cur;
+        struct ast_conf_user user;
        int fd;
        struct zt_confinfo ztc;
        struct ast_frame *f;
@@ -264,12 +294,54 @@
        struct ast_app *app;
        char *agifile;
        char *agifiledefault = "conf-background.agi";
+        char choice[2] = "";

        ZT_BUFFERINFO bi;
        char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
        char *buf = __buf + AST_FRIENDLY_OFFSET;

+       if (conf->locked) {
+               /* Sorry, but this confernce is locked! */
+               if (!ast_streamfile(chan, "conf-locked", chan->language))
+                       ast_waitstream(chan, "");
+               goto outrun;
+       }
+
        conf->users++;
+        if (confflags & CONFFLAG_ADMINEXIT) {
+               if (conf->markedusers == -1) {
+                       conf->markedusers = 1;
+               } else {
+                       conf->markedusers++;
+               }
+        }
+
+        if (conf->firstuser == NULL) {
+               /* Fill the first new User struct */
+                user.user_no = 1;
+                user.nextuser = NULL;
+                user.prevuser = NULL;
+               conf->firstuser = &user;
+               conf->lastuser = &user;
+        } else {
+               /* Fill the new user struct */
+               user.user_no = conf->lastuser->user_no + 1;
+                user.prevuser = conf->lastuser;
+                user.nextuser = NULL;
+                if (conf->lastuser->nextuser != NULL) {
+                       ast_log(LOG_WARNING, "Error in User Management!\n");
+                       goto outrun;
+                } else {
+                       conf->lastuser->nextuser = &user;
+                       conf->lastuser = &user;
+                }
+        }
+        strncpy(user.usrvalue, "test", sizeof(user.usrvalue));
+       strncpy(user.channame, chan->name, AST_CHANNEL_NAME);
+       user.userflags = confflags;
+
+        ast_log(LOG_WARNING, "USER: %.8X\n", &user);
+
        if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
                if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
                        ast_waitstream(chan, "");
@@ -417,6 +489,10 @@
                outfd = -1;
                ms = -1;
                c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+
+                /* Update the struct with the actual confflags */
+                user.userflags = confflags;
+
                /* trying to add moh for single person conf */
                if (confflags & CONFFLAG_MOH) {
                        if (conf->users == 1) {
@@ -431,7 +507,37 @@
                                }
                        }
                }
-               /* end modifications */
+
+                /* Leave if the last marked user left */
+                if ((confflags & CONFFLAG_ADMINEXIT) && (conf->markedusers == 0)) {
+                       ret = 0;
+                       break;
+                }
+
+               /* Check if the admin changed my modes */
+               if (user.adminflags) {
+                       /* Set the new modes */
+                       if ((user.adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
+                               ztc.confmode ^= ZT_CONF_TALKER;
+                               if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                                       ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+                                       close(fd);
+                                       goto outrun;
+                               }
+                       }
+                       if (!(user.adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
+                               ztc.confmode |= ZT_CONF_TALKER;
+                               if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                                       ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+                                       close(fd);
+                                       goto outrun;
+                               }
+                       }
+                       if (user.adminflags & ADMINFLAG_KICKME) {
+                               close(fd);
+                               goto outrun;
+                       }
+               }

                if (c) {
                        if (c->fds[0] != origfd) {
@@ -450,12 +556,61 @@
                                ret = 0;
                                break;
                        } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) {
+                                       if (musiconhold) {
+                                               ast_moh_stop(chan);
+                                       }
                                        if ((confflags & CONFFLAG_ADMIN)) {
-                                       /* Do admin stuff here */
+                                               /* Admin menu */
+                                               ast_app_getdata(chan, "conf-adminmenu", choice, 1, 1000);
+                                               ast_log(LOG_WARNING, "DEBUG: %i", atoi(choice));
+                                               switch(atoi(choice)) {
+                                                       case 1: /* Un/Mute */
+                                                               if (ztc.confmode & ZT_CONF_TALKER) {
+                                                                       ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+                                                                       confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
                                        } else {
-                                       /* Do user menu here */
+                                                                       ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+                                                                       confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
+                                                               }
+                                                               if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                                                                       ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+                                                                       close(fd);
+                                                                       goto outrun;
+                                                               }
+                                                               break;
+                                                       case 2: /* Lock the Conference */
+                                                               conf->locked = 1;
+                                                               break;
+                                               }
+                                        } else {
+                                               /* User menu */
+                                               ast_log(LOG_WARNING, "GOT TO THE USER MENU");
+                                               ast_app_getdata(chan, "conf-usermenu", choice, 1, 0);
+                                                       switch(atoi(choice)) {
+                                                               case 1: /* Un/Mute */
+                                                                       if (user.adminflags & ADMINFLAG_MUTED) {
+                                                                               if (!ast_streamfile(chan, "conf-notallowed", chan->language))
+                                                                               ast_waitstream(chan, "");
+                                                                       } else {
+                                                                       if (ztc.confmode & ZT_CONF_TALKER) {
+                                                                               ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
+                                                                               confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
+                                                                       } else {
+                                                                               ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+                                                                               confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
+                                                                       }
+                                                                       if (ioctl(fd, ZT_SETCONF, &ztc)) {
+                                                                               ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
+                                                                               close(fd);
+                                                                               goto outrun;
+                                                                       }
+                                                               }
+                                                                       break;
+                                                       }
+                                        }
+                                       if (musiconhold) {
+                                               ast_moh_start(chan, NULL);
                                        }
-
                        } else if (fd != chan->fds[0]) {
                                if (f->frametype == AST_FRAME_VOICE) {
                                        if (f->subclass == AST_FORMAT_ULAW) {
@@ -523,7 +678,21 @@
                        ast_log(LOG_WARNING, "Conference not found\n");
                close(conf->fd);
                free(conf);
+        } else {
+               /* Remove the user struct */
+               if (&user == conf->firstuser) {
+                       conf->firstuser->nextuser->prevuser = NULL;
+                       conf->firstuser = conf->firstuser->nextuser;
+               } else if (&user == conf->lastuser){
+                       conf->lastuser->prevuser->nextuser = NULL;
+                       conf->lastuser = conf->lastuser->prevuser;
+               } else {
+                       user.nextuser->prevuser = user.prevuser;
+                       user.prevuser->nextuser = user.nextuser;
+               }
        }
+        /* Return the number of seconds the user was in the conf */
+        pbx_builtin_setvar_helper(chan, "MEETMESECS", (char *) (user.jointime - time(NULL)));
        ast_mutex_unlock(&conflock);
        return ret;
 }
@@ -681,6 +850,8 @@
                        confflags |= CONFFLAG_QUIET;
                if (strchr(inflags, 'M'))
                        confflags |= CONFFLAG_MOH;
+                if (strchr(inflags, 'x'))
+                        confflags |= CONFFLAG_ADMINEXIT;
                if (strchr(inflags, 'b'))
                        confflags |= CONFFLAG_AGI;
                if (strchr(inflags, 'd'))
@@ -854,10 +1025,101 @@
        return res;
 }

+static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
+       struct ast_conf_user *user = NULL;
+       if (conf && callerident) {
+               user = conf->firstuser;
+               while(user) {
+                       if (strcmp(user->channame, callerident))
+                               return user;
+                       if (user->nextuser == NULL)
+                               return NULL;
+                       user = user->nextuser;
+               }
+       }
+       return NULL;
+}
+
+static int admin_exec(struct ast_channel *chan, void *data) {
+       /* MeetMeAdmin(confno, command, caller) */
+       char *params, *command = NULL, *caller = NULL, *conf = NULL;
+       struct ast_conference *cnf;
+       struct ast_conf_user *user = NULL;
+
+       ast_mutex_lock(&conflock);
+       /* The param has the conference number the user and the command to execute */
+       if (data && strlen(data)) {
+               params = ast_strdupa((char *) data);
+               conf = strsep(&params, "|");
+               command = strsep(&params, "|");
+               caller = strsep(&params, "|");
+
+               ast_mutex_lock(&conflock);
+                cnf = confs;
+                while (cnf) {
+                        if (strcmp(cnf->confno, conf))
+                               break;
+                       cnf = cnf->next;
+                }
+                ast_mutex_unlock(&conflock);
+
+                if (caller)
+                       user = find_user(cnf, caller);
+
+               if (cnf) {
+                       switch((int) (*command)) {
+                               case 76: /* L: Lock */
+                                       cnf->locked = 1;
+                                       break;
+                               case 108: /* l: Unlock */
+                                       cnf->locked = 0;
+                                       break;
+                               case 75: /* K: kick all users*/
+                                       user = cnf->firstuser;
+                                       while(user) {
+                                               user->adminflags |= ADMINFLAG_KICKME;
+                                               if (user->nextuser) {
+                                                       user = user->nextuser;
+                                               } else {
+                                                       break;
+                                               }
+                                       }
+                                       break;
+                               case 77: /* M: Mute */
+                                       if (user) {
+                                               user->adminflags |= ADMINFLAG_MUTED;
+                                       } else {
+                                               ast_log(LOG_NOTICE, "Specified User not found!");
+                                       }
+                                       break;
+                               case 109: /* m: Unmute */
+                                       if (user) {
+                                               user->adminflags ^= ADMINFLAG_MUTED;
+                                       } else {
+                                               ast_log(LOG_NOTICE, "Specified User not found!");
+                                       }
+                                       break;
+                               case 107: /* k: Kick user */
+                                       if (user) {
+                                               user->adminflags |= ADMINFLAG_KICKME;
+                                       } else {
+                                               ast_log(LOG_NOTICE, "Specified User not found!");
+                                       }
+                                       break;
+                       }
+               } else {
+                       ast_log(LOG_NOTICE, "Conference Number not found\n");
+               }
+       }
+       ast_mutex_unlock(&conflock);
+       return 0;
+}
+
 int unload_module(void)
 {
        STANDARD_HANGUP_LOCALUSERS;
        ast_cli_unregister(&cli_show_confs);
+        ast_unregister_application(app3);
        ast_unregister_application(app2);
        return ast_unregister_application(app);
 }
@@ -865,6 +1127,7 @@
 int load_module(void)
 {
        ast_cli_register(&cli_show_confs);
+        ast_register_application(app3, admin_exec, synopsis3, descrip3);
        ast_register_application(app2, count_exec, synopsis2, descrip2);
        return ast_register_application(app, conf_exec, synopsis, descrip);
 }
