--- app_queue.c.save	2003-10-28 20:39:20.000000000 +1100
+++ app_queue.c.new	2003-10-30 17:23:15.000000000 +1100
@@ -80,6 +80,19 @@
 "  The optionnal URL will be sent to the called party if the channel supports\n"
 "it.\n";
 
+/* Campon desriptions */
+static char *campon_app = "Campon";
+static char *campon_synopsis = "Camp a call for an extension";
+static char *campon_descrip =
+"  Campon(technology/number[|options):\n"
+"Camps an incoming call onta a particular extension.\n"
+"  This application returns -1 if the originating channel hangs up, or if the\n"
+"call is bridged and  either of the parties in the bridge terminate the call.\n"
+"Returns 0 if caller exists by pressing 1 or 2.\n"
+"The option string may contain zero or more of the following characters:\n"
+"      't' -- allow the called user transfer the calling user\n"
+"      'T' -- to allow the calling user to transfer the call.\n";
+
 // [PHM 06/26/03]
 static char *app_aqm = "AddQueueMember" ;
 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
@@ -131,6 +144,7 @@
 	char context[80];		/* Context when user exits queue */
 	int pos;					/* Where we are in the queue */
 	time_t start;				/* When we started holding */
+	time_t recall;				/* recall time for campon message */
 	struct ast_channel *chan;	/* Our channel */
 	struct queue_ent *next;		/* The next queue entry */
 };
@@ -1186,6 +1200,233 @@
 	return res;
 }
 
+/* Campon function - play file and wait for digit response */
+static int campon_message(struct ast_channel *chan, char *fname)
+{
+	int	d;
+
+	d = ast_streamfile(chan, fname, chan->language);
+	if ( !d ) {
+		d = ast_waitstream(chan, AST_DIGIT_ANY);
+	}
+	if ( !d ) {
+		d = ast_waitfordigit(chan, 5000);
+	}
+	return d;
+}
+
+/* Campon on exec - called from dialplan, copied from queue_exec and 
+   reload_queue. Uses the queue function to campon the call, with recall
+   every 45 seconds to replay another message to allow caller to exit
+   by pressing 1 or 2. Dynamically creates a queue if one does not exist */
+static int campon_exec(struct ast_channel *chan, void *data)
+{
+	int res=-1;
+	int new;
+	struct localuser *u;
+	char *queuename;
+	char info[512];
+	char *options = NULL;
+	struct ast_call_queue *q;
+	struct member *prev, *cur;
+	char *tmp;
+	int		timeout;
+	char	mohname[32];
+	char	destination[32];
+	int		d;
+	time_t	curr_time;
+	
+	/* Our queue entry */
+	struct queue_ent qe;
+	
+	if (!data) {
+		ast_log(LOG_WARNING, "CAMPON requires an argument (technology/number|optional timeout|optional URL)\n");
+		return -1;
+	}
+
+	/* play campon message */
+	if ( chan->_state != AST_STATE_UP )
+		ast_answer(chan);
+	d = campon_message(chan, "campon");
+	if ( d == '1' ) {
+		return 0;
+	}
+	else if ( d == '2' && ast_exists_extension(chan, chan->context, chan->exten,
+				chan->priority + 101, chan->callerid) ) {
+		chan->priority += 100;
+		return 0;
+	}
+	
+	LOCAL_USER_ADD(u);
+	
+	/* Parse our arguments XXX Check for failure XXX */
+	strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
+	queuename = info;
+	if (queuename) {
+		options = strchr(queuename, '|');
+		if (options) {
+			*options = '\0';
+			options++;
+		}
+	}
+	strcpy(destination, queuename);
+	/* get campon options for timeout and moh */
+	q = queues;
+	while (q) {
+		if ( !strcmp(q->name, "campon") )
+			break;
+		q = q->next;
+	}
+	if (q) {
+		/* found an entry for [campon] in queues.conf */
+		timeout = q->timeout;
+		strcpy(mohname, q->moh);
+	}
+	else {
+		/* use default values */
+		timeout = 45;
+		strcpy(mohname, "default");
+	}
+	/* make sure queue entry for this phone/extension exists otherwise create */
+	q = queues;
+	while (q) {
+		if ( !strcmp(q->name, queuename) )
+			break;
+		q = q->next;
+	}
+	if (!q) {
+		/* Make one then */
+		q = malloc(sizeof(struct ast_call_queue));
+		if (q) {
+			/* Initialize it */
+			memset(q, 0, sizeof(struct ast_call_queue));
+			ast_mutex_init(&q->lock);
+			strncpy(q->name, queuename, sizeof(q->name));
+			new = 1;
+		} else new = 0;
+	} else
+		new = 0;
+	if (q) {
+		if (!new) 
+			ast_mutex_lock(&q->lock);
+		/* Re-initialize the queue */
+		q->dead = 0;
+		q->retry = 0;
+		q->timeout = -1;
+		q->maxlen = 0;
+		free_members(q, 0);
+		strcpy(q->moh, mohname);
+		strcpy(q->announce, "");
+		strcpy(q->context, "");
+		cur = malloc(sizeof(struct member));
+		if ( cur ) {
+			memset(cur, 0, sizeof(struct member));
+			strncpy(cur->tech, queuename, sizeof(cur->tech) - 1);
+			if ((tmp = strchr(cur->tech, ','))) {
+				*tmp = '\0';
+				tmp++;
+				cur->penalty = atoi(tmp);
+				if (cur->penalty < 0)
+					cur->penalty = 0;
+			}
+			if ((tmp = strchr(cur->tech, '/')))
+				*tmp = '\0';
+			if ((tmp = strchr(queuename, '/'))) {
+				tmp++;
+				strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
+				if ((tmp = strchr(cur->loc, ',')))
+					*tmp = '\0';
+			}
+		}
+		q->members = cur;
+		prev = cur;
+		if (q->retry < 1)
+			q->retry = DEFAULT_RETRY;
+		if (q->timeout < 0)
+			q->timeout = DEFAULT_TIMEOUT;
+		if (q->maxlen < 0)
+			q->maxlen = 0;
+		if (!new) 
+			ast_mutex_unlock(&q->lock);
+		if (new) {
+			q->next = queues;
+			queues = q;
+		}
+	}
+
+	/* Setup our queue entry */
+	memset(&qe, 0, sizeof(qe));
+	qe.chan = chan;
+	qe.start = time(NULL);
+	if (!join_queue(destination, &qe)) {
+		/* Start music on hold */
+		ast_moh_start(chan, qe.moh);
+		for (;;) {
+			res = wait_our_turn(&qe);
+			/* If they hungup, return immediately */
+			if (res < 0) {
+				if (option_verbose > 2) {
+					ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
+					res = -1;
+				}
+				break;
+			}
+			if (!res)
+				break;
+			if (valid_exit(&qe, res))
+				break;
+		}
+		if (!res) {
+			qe.recall = time(NULL) + timeout;
+			for (;;) {
+				res = try_calling(&qe, options, NULL, NULL);
+				if (res)
+					break;
+				res = wait_a_bit(&qe);
+				curr_time = time(NULL);
+				if ( curr_time >= qe.recall ) {
+					ast_moh_stop(chan);
+					d = campon_message(chan, "campon-repeat");
+					if ( d == '1' ) {
+						res = 0;
+						break;
+					}
+					else if ( d == '2' && ast_exists_extension(chan, 
+								chan->context, chan->exten,
+								chan->priority + 101, chan->callerid) ) {
+						chan->priority += 100;
+						return 0;
+					}
+					else {
+						ast_moh_start(chan, qe.moh);
+						qe.recall = time(NULL) + 45;
+					}
+				}
+				if (res < 0) {
+					if (option_verbose > 2) {
+						ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
+						res = -1;
+					}
+					break;
+				}
+				if (res && valid_exit(&qe, res))
+					break;
+			}
+		}
+		/* Don't allow return code > 0 */
+		if (res > 0 && res != AST_PBX_KEEPALIVE) {
+			res = 0;	
+			ast_moh_stop(chan);
+		}
+		leave_queue(&qe);
+	} else {
+		ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
+		res =  0;
+	}
+	LOCAL_USER_REMOVE(u);
+	return res;
+}
+
 static void reload_queues(void)
 {
 	struct ast_call_queue *q, *ql, *qn;
@@ -1468,6 +1709,7 @@
 int load_module(void)
 {
 	int res;
+	res = ast_register_application(campon_app, campon_exec, campon_synopsis, campon_descrip);
 	res = ast_register_application(app, queue_exec, synopsis, descrip);
 	if (!res) {
 		ast_cli_register(&cli_show_queues);
