struct ast_frame *transframe[32];
struct ast_frame *origframe;
struct ast_trans_pvt *transpath[32];
- AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
+ struct ao2_container *usercontainer;
AST_LIST_ENTRY(ast_conference) list;
/* announce_thread related data */
pthread_t announcethread;
ast_autoservice_stop(chan);
}
+static int user_no_cmp(void *obj, void *arg, int flags)
+{
+ struct ast_conf_user *user = obj;
+ int *user_no = arg;
+
+ if (user->user_no == *user_no) {
+ return (CMP_MATCH | CMP_STOP);
+ }
+
+ return 0;
+}
+
+static int user_max_cmp(void *obj, void *arg, int flags)
+{
+ struct ast_conf_user *user = obj;
+ int *max_no = arg;
+
+ if (user->user_no > *max_no) {
+ *max_no = user->user_no;
+ }
+
+ return 0;
+}
+
/*!
* \brief Find or create a conference
*
goto cnfout;
/* Make a new one */
- if (!(cnf = ast_calloc(1, sizeof(*cnf))))
+ if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
+ !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
goto cnfout;
+ }
ast_mutex_init(&cnf->playlock);
ast_mutex_init(&cnf->listenlock);
}
if (cnf) {
+ struct ao2_iterator user_iter;
+ user_iter = ao2_iterator_init(cnf->usercontainer, 0);
/* Search for the user */
- AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
+ while((usr = ao2_iterator_next(&user_iter))) {
snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
- if (!strncasecmp(word, usrno, len) && ++which > state)
+ if (!strncasecmp(word, usrno, len) && ++which > state) {
+ ao2_ref(usr, -1);
break;
+ }
+ ao2_ref(usr, -1);
}
+ ao2_iterator_destroy(&user_iter);
+ AST_LIST_UNLOCK(&confs);
+ return usr ? ast_strdup(usrno) : NULL;
}
- AST_LIST_UNLOCK(&confs);
- return usr ? ast_strdup(usrno) : NULL;
}
}
ast_free(cmdline);
return CLI_SUCCESS;
} else if (strcmp(a->argv[1], "list") == 0) {
+ struct ao2_iterator user_iter;
int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
/* List all the users in a conference */
if (AST_LIST_EMPTY(&confs)) {
}
/* Show all the users */
time(&now);
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
+ user_iter = ao2_iterator_init(cnf->usercontainer, 0);
+ while((user = ao2_iterator_next(&user_iter))) {
hr = (now - user->jointime) / 3600;
min = ((now - user->jointime) % 3600) / 60;
sec = (now - user->jointime) % 60;
user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
user->talking, hr, min, sec);
}
+ ao2_ref(user, -1);
}
+ ao2_iterator_destroy(&user_iter);
if (!concise) {
ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
}
if (conf->recordingformat) {
ast_free(conf->recordingformat);
}
+ if (conf->usercontainer) {
+ ao2_ref(conf->usercontainer, -1);
+ }
ast_mutex_destroy(&conf->playlock);
ast_mutex_destroy(&conf->listenlock);
ast_mutex_destroy(&conf->recordthreadlock);
const struct ast_conf_user *sender, struct ast_frame *f)
{
struct ast_conf_user *user;
+ struct ao2_iterator user_iter;
- AST_LIST_TRAVERSE(&conf->userlist, user, list) {
- if (user == sender)
+ user_iter = ao2_iterator_init(conf->usercontainer, 0);
+ while ((user = ao2_iterator_next(&user_iter))) {
+ if (user == sender) {
+ ao2_ref(user, -1);
continue;
+ }
if (ast_write(user->chan, f) < 0)
ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
+ ao2_ref(user, -1);
}
+ ao2_iterator_destroy(&user_iter);
}
static void sla_queue_event_full(enum sla_event_type type,
int setusercount = 0;
int confsilence = 0, totalsilence = 0;
- if (!(user = ast_calloc(1, sizeof(*user))))
+ if (!(user = ao2_alloc(sizeof(*user), NULL))) {
return ret;
+ }
/* Possible timeout waiting for marked user */
if ((confflags & CONFFLAG_WAITMARKED) &&
ast_mutex_lock(&conf->playlock);
- if (AST_LIST_EMPTY(&conf->userlist))
- user->user_no = 1;
- else
- user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
-
- if (rt_schedule && conf->maxusers)
+ if (rt_schedule && conf->maxusers) {
if (conf->users >= conf->maxusers) {
/* Sorry, but this confernce has reached the participant limit! */
if (!ast_streamfile(chan, "conf-full", chan->language))
ast_waitstream(chan, "");
ast_mutex_unlock(&conf->playlock);
- user->user_no = 0;
goto outrun;
}
+ }
- AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
+ ao2_lock(conf->usercontainer);
+ ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
+ user->user_no++;
+ ao2_link(conf->usercontainer, user);
+ ao2_unlock(conf->usercontainer);
user->chan = chan;
user->userflags = confflags;
}
break;
case '3': /* Eject last user */
+ {
+ int max_no = 0;
menu_active = 0;
- usr = AST_LIST_LAST(&conf->userlist);
- if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
- if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
+ ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
+ usr = ao2_find(conf->usercontainer, &max_no, 0);
+ if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
+ if(!ast_streamfile(chan, "conf-errormenu", chan->language))
ast_waitstream(chan, "");
- }
} else {
usr->adminflags |= ADMINFLAG_KICKME;
}
+ ao2_ref(user, -1);
ast_stopstream(chan);
- break;
+ break;
+ }
case '4':
tweak_listen_volume(user, VOL_DOWN);
break;
ast_dsp_free(dsp);
}
- if (user->user_no) { /* Only cleanup users who really joined! */
+ if (!user->user_no) {
+ ao2_ref(user, -1);
+ } else { /* Only cleanup users who really joined! */
now = ast_tvnow();
hr = (now.tv_sec - user->jointime) / 3600;
min = ((now.tv_sec - user->jointime) % 3600) / 60;
conf->markedusers--;
}
}
- /* Remove ourselves from the list */
- AST_LIST_REMOVE(&conf->userlist, user, list);
+ /* Remove ourselves from the container */
+ ao2_unlink(conf->usercontainer, user);
/* Change any states */
if (!conf->users) {
pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
}
}
- ast_free(user);
AST_LIST_UNLOCK(&confs);
return ret;
sscanf(callerident, "%30i", &cid);
if (conf && callerident) {
- AST_LIST_TRAVERSE(&conf->userlist, user, list) {
- if (cid == user->user_no)
- return user;
- }
+ user = ao2_find(conf->usercontainer, &cid, 0);
+ /* reference decremented later in admin_exec */
+ return user;
}
return NULL;
}
+static int user_set_kickme_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ user->adminflags |= ADMINFLAG_KICKME;
+ return 0;
+}
+
+static int user_set_muted_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ if (!(user->userflags & CONFFLAG_ADMIN)) {
+ user->adminflags |= ADMINFLAG_MUTED;
+ }
+ return 0;
+}
+
+static int user_set_unmuted_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
+ return 0;
+}
+
+static int user_listen_volup_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ tweak_listen_volume(user, VOL_UP);
+ return 0;
+}
+
+static int user_listen_voldown_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ tweak_listen_volume(user, VOL_DOWN);
+ return 0;
+}
+
+static int user_talk_volup_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ tweak_talk_volume(user, VOL_UP);
+ return 0;
+}
+
+static int user_talk_voldown_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ tweak_talk_volume(user, VOL_DOWN);
+ return 0;
+}
+
+static int user_reset_vol_cb(void *obj, void *unused, int flags)
+{
+ struct ast_conf_user *user = obj;
+ reset_volumes(user);
+ return 0;
+}
+
+static int user_chan_cb(void *obj, void *args, int flags)
+{
+ struct ast_conf_user *user = obj;
+ const char *channel = args;
+
+ if (!strcmp(user->chan->name, channel)) {
+ return (CMP_MATCH | CMP_STOP);
+ }
+
+ return 0;
+}
+
/*! \brief The MeetMeadmin application */
/* MeetMeAdmin(confno, command, caller) */
static int admin_exec(struct ast_channel *chan, void *data) {
ast_atomic_fetchadd_int(&cnf->refcount, 1);
- if (args.user)
+ if (args.user) {
user = find_user(cnf, args.user);
+ if (!user) {
+ ast_log(LOG_NOTICE, "Specified User not found!\n");
+ res = -2;
+ goto usernotfound;
+ }
+ }
switch (*args.command) {
case 76: /* L: Lock */
cnf->locked = 0;
break;
case 75: /* K: kick all users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list)
- user->adminflags |= ADMINFLAG_KICKME;
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
break;
case 101: /* e: Eject last user*/
- user = AST_LIST_LAST(&cnf->userlist);
+ {
+ int max_no = 0;
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
+ user = ao2_find(cnf->usercontainer, &max_no, 0);
if (!(user->userflags & CONFFLAG_ADMIN))
user->adminflags |= ADMINFLAG_KICKME;
else {
res = -1;
ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
}
+ ao2_ref(user, -1);
break;
+ }
case 77: /* M: Mute */
if (user) {
user->adminflags |= ADMINFLAG_MUTED;
}
break;
case 78: /* N: Mute all (non-admin) users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- if (!(user->userflags & CONFFLAG_ADMIN)) {
- user->adminflags |= ADMINFLAG_MUTED;
- }
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, NULL);
break;
case 109: /* m: Unmute */
- if (user) {
- user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
break;
case 110: /* n: Unmute all users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
break;
case 107: /* k: Kick user */
- if (user) {
- user->adminflags |= ADMINFLAG_KICKME;
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ user->adminflags |= ADMINFLAG_KICKME;
break;
case 118: /* v: Lower all users listen volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- tweak_listen_volume(user, VOL_DOWN);
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
break;
case 86: /* V: Raise all users listen volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- tweak_listen_volume(user, VOL_UP);
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
break;
case 115: /* s: Lower all users speaking volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- tweak_talk_volume(user, VOL_DOWN);
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
break;
case 83: /* S: Raise all users speaking volume */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- tweak_talk_volume(user, VOL_UP);
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
break;
case 82: /* R: Reset all volume levels */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
- reset_volumes(user);
- }
+ ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
break;
case 114: /* r: Reset user's volume level */
- if (user) {
- reset_volumes(user);
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ reset_volumes(user);
break;
case 85: /* U: Raise user's listen volume */
- if (user) {
- tweak_listen_volume(user, VOL_UP);
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ tweak_listen_volume(user, VOL_UP);
break;
case 117: /* u: Lower user's listen volume */
- if (user) {
- tweak_listen_volume(user, VOL_DOWN);
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ tweak_listen_volume(user, VOL_DOWN);
break;
case 84: /* T: Raise user's talk volume */
- if (user) {
- tweak_talk_volume(user, VOL_UP);
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ tweak_talk_volume(user, VOL_UP);
break;
case 116: /* t: Lower user's talk volume */
- if (user) {
- tweak_talk_volume(user, VOL_DOWN);
- } else {
- res = -2;
- ast_log(LOG_NOTICE, "Specified User not found!\n");
- }
+ tweak_talk_volume(user, VOL_DOWN);
break;
case 'E': /* E: Extend conference */
if (rt_extend_conf(args.confno)) {
break;
}
+ if (args.user) {
+ /* decrement reference from find_user */
+ ao2_ref(user, -1);
+ }
+usernotfound:
AST_LIST_UNLOCK(&confs);
dispose_conf(cnf);
AST_LIST_LOCK(&confs);
AST_LIST_TRAVERSE(&confs, conf, list) {
- AST_LIST_TRAVERSE(&conf->userlist, user, list) {
- if (!strcmp(user->chan->name, args.channel))
- break;
+ if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
+ break;
}
}
ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
break;
}
-
+ ao2_ref(user, -1);
AST_LIST_UNLOCK(&confs);
return 0;
return 0;
}
- AST_LIST_TRAVERSE(&conf->userlist, user, list)
- if (user->user_no == userno)
- break;
+ user = ao2_find(conf->usercontainer, &userno, 0);
if (!user) {
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);
+ ao2_ref(user, -1);
astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
return 0;
}
char idText[80] = "";
struct ast_conference *cnf;
struct ast_conf_user *user;
+ struct ao2_iterator user_iter;
int total = 0;
if (!ast_strlen_zero(actionid))
/* Find the right conference */
AST_LIST_LOCK(&confs);
AST_LIST_TRAVERSE(&confs, cnf, list) {
+ user_iter = ao2_iterator_init(cnf->usercontainer, 0);
/* If we ask for one particular, and this isn't it, skip it */
if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
continue;
/* Show all the users */
- AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
+ while ((user = ao2_iterator_next(&user_iter))) {
total++;
astman_append(s,
"Event: MeetmeList\r\n"
user->userflags & CONFFLAG_MONITOR ? "Listen only" : user->userflags & CONFFLAG_TALKER ? "Talk only" : "Talk and listen",
user->userflags & CONFFLAG_MARKEDUSER ? "Yes" : "No",
user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
- user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
+ user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
+ ao2_ref(user, -1);
}
+ ao2_iterator_destroy(&user_iter);
}
AST_LIST_UNLOCK(&confs);
/* Send final confirmation */