Bug 6526 - Add a reference count to avoid crash
[asterisk/asterisk.git] / apps / app_meetme.c
index a5d184b..86615a7 100644 (file)
@@ -148,6 +148,7 @@ struct ast_conference {
        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 refcount;                           /* reference count of usage */
        unsigned int recording:2;                               /* recording status */
        unsigned int isdynamic:1;                               /* Created on the fly? */
        unsigned int locked:1;                          /* Is the conference locked? */
@@ -453,7 +454,7 @@ static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int
                ast_autoservice_stop(chan);
 }
 
-static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
+static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
 {
        struct ast_conference *cnf;
        struct zt_confinfo ztc;
@@ -473,6 +474,7 @@ static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin
                        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));
+                       cnf->refcount = 0;
                        cnf->markedusers = 0;
                        cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
                        if (cnf->chan) {
@@ -529,6 +531,9 @@ static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin
                } 
        }
  cnfout:
+       if (cnf){ 
+               cnf->refcount += refcount;
+       }
        AST_LIST_UNLOCK(&confs);
        return cnf;
 }
@@ -859,6 +864,12 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
        char *buf = __buf + AST_FRIENDLY_OFFSET;
 
        if (!(user = ast_calloc(1, sizeof(*user)))) {
+               AST_LIST_LOCK(&confs);
+               conf->refcount--;
+               if (!conf->refcount){
+                       conf_free(conf);
+               }
+               AST_LIST_UNLOCK(&confs);
                return ret;
        }
 
@@ -1671,14 +1682,17 @@ bailoutandtrynormal:
                          "<no name>", hr, min, sec);
 
                conf->users--;
+               conf->refcount--;
                /* Update table */
                snprintf(members, sizeof(members), "%d", conf->users);
                ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
                if (confflags & CONFFLAG_MARKEDUSER) 
                        conf->markedusers--;
                if (!conf->users) {
-                       /* No more users -- close this one out */
-                       conf_free(conf);
+                       /* close this one when no more users and no references*/
+                       if (!conf->refcount){
+                               conf_free(conf);
+                       }
                } else {
                        /* Remove the user struct */ 
                        if (user == conf->firstuser) {
@@ -1721,7 +1735,7 @@ bailoutandtrynormal:
 /*
   This function looks for a conference via the RealTime module
 */
-static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
+static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin, int refcount)
 {
 
        struct ast_variable *var;
@@ -1733,6 +1747,9 @@ static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char
                if (!strcmp(confno, cnf->confno)) 
                        break;
        }
+       if (cnf){
+               cnf->refcount += refcount;
+       }
        AST_LIST_UNLOCK(&confs);
 
        if (!cnf) {
@@ -1757,14 +1774,14 @@ static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char
                }
                ast_variables_destroy(var);
 
-               cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic);
+               cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
        }
 
        return cnf;
 }
 
 
-static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
+static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin, int refcount)
 {
        struct ast_config *cfg;
        struct ast_variable *var;
@@ -1782,6 +1799,9 @@ static struct ast_conference *find_conf(struct ast_channel *chan, char *confno,
                if (!strcmp(confno, cnf->confno)) 
                        break;
        }
+       if (cnf){
+               cnf->refcount += refcount;
+       }
        AST_LIST_UNLOCK(&confs);
 
        if (!cnf) {
@@ -1794,9 +1814,9 @@ static struct ast_conference *find_conf(struct ast_channel *chan, char *confno,
                                        if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
                                                return NULL;
                                }
-                               cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
+                               cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
                        } else {
-                               cnf = build_conf(confno, "", "", make, dynamic);
+                               cnf = build_conf(confno, "", "", make, dynamic, refcount);
                        }
                } else {
                        /* Check the config */
@@ -1818,14 +1838,14 @@ static struct ast_conference *find_conf(struct ast_channel *chan, char *confno,
                                        /* Bingo it's a valid conference */
                                        if (args.pin) {
                                                if (args.pinadmin)
-                                                       cnf = build_conf(args.confno, args.pin, args.pinadmin, make, dynamic);
+                                                       cnf = build_conf(args.confno, args.pin, args.pinadmin, make, dynamic, refcount);
                                                else
-                                                       cnf = build_conf(args.confno, args.pin, "", make, dynamic);
+                                                       cnf = build_conf(args.confno, args.pin, "", make, dynamic, refcount);
                                        } else {
                                                if (args.pinadmin)
-                                                       cnf = build_conf(args.confno, "", args.pinadmin, make, dynamic);
+                                                       cnf = build_conf(args.confno, "", args.pinadmin, make, dynamic, refcount);
                                                else
-                                                       cnf = build_conf(args.confno, "", "", make, dynamic);
+                                                       cnf = build_conf(args.confno, "", "", make, dynamic, refcount);
                                        }
                                        break;
                                }
@@ -1874,7 +1894,7 @@ static int count_exec(struct ast_channel *chan, void *data)
 
        AST_STANDARD_APP_ARGS(args, localdata);
        
-       conf = find_conf(chan, args.confno, 0, 0, NULL);
+       conf = find_conf(chan, args.confno, 0, 0, NULL, 0);
        if (conf)
                count = conf->users;
        else
@@ -2060,9 +2080,9 @@ static int conf_exec(struct ast_channel *chan, void *data)
                }
                if (!ast_strlen_zero(confno)) {
                        /* Check the validity of the conference */
-                       cnf = find_conf(chan, confno, 1, dynamic, the_pin);
+                       cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1);
                        if (!cnf) {
-                               cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin);
+                               cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1);
                        }
                        if (!cnf) {
                                res = ast_streamfile(chan, "conf-invalid", chan->language);
@@ -2104,8 +2124,15 @@ static int conf_exec(struct ast_channel *chan, void *data)
                                                                res = ast_streamfile(chan, "conf-invalidpin", chan->language);
                                                                if (!res)
                                                                        ast_waitstream(chan, AST_DIGIT_ANY);
-                                                               if (res < 0)
+                                                               if (res < 0) {
+                                                                       AST_LIST_LOCK(&confs);
+                                                                       cnf->refcount--;
+                                                                       if (!cnf->refcount){
+                                                                               conf_free(cnf);
+                                                                       }
+                                                                       AST_LIST_UNLOCK(&confs);
                                                                        break;
+                                                               }
                                                                pin[0] = res;
                                                                pin[1] = '\0';
                                                                res = -1;
@@ -2118,8 +2145,9 @@ static int conf_exec(struct ast_channel *chan, void *data)
                                                        allowretry = 0;
                                                        /* see if we need to get rid of the conference */
                                                        AST_LIST_LOCK(&confs);
-                                                       if (!cnf->users) {
-                                                               conf_free(cnf); 
+                                                       cnf->refcount--;
+                                                       if (!cnf->refcount) {
+                                                               conf_free(cnf);
                                                        }
                                                        AST_LIST_UNLOCK(&confs);
                                                        break;