[asterisk-commits] branch group/autoconf_and_menuselect r20760 - in /team/group/autoconf_and_men...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sun Apr 16 18:52:08 MST 2006


Author: russell
Date: Sun Apr 16 20:52:05 2006
New Revision: 20760

URL: http://svn.digium.com/view/asterisk?rev=20760&view=rev
Log:
resolve conflicts, re-enable automerge

Modified:
    team/group/autoconf_and_menuselect/   (props changed)
    team/group/autoconf_and_menuselect/channel.c
    team/group/autoconf_and_menuselect/channels/chan_skinny.c
    team/group/autoconf_and_menuselect/pbx.c
    team/group/autoconf_and_menuselect/res/res_features.c

Propchange: team/group/autoconf_and_menuselect/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/group/autoconf_and_menuselect/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Sun Apr 16 20:52:05 2006
@@ -1,1 +1,1 @@
-/trunk:1-20546
+/trunk:1-20759

Modified: team/group/autoconf_and_menuselect/channel.c
URL: http://svn.digium.com/view/asterisk/team/group/autoconf_and_menuselect/channel.c?rev=20760&r1=20759&r2=20760&view=diff
==============================================================================
--- team/group/autoconf_and_menuselect/channel.c (original)
+++ team/group/autoconf_and_menuselect/channel.c Sun Apr 16 20:52:05 2006
@@ -1258,13 +1258,15 @@
 {
 	struct ast_frame *translated_frame = NULL;
 	struct ast_channel_spy *spy;
-	struct ast_channel_spy_queue *queue;
 	struct channel_spy_trans *trans;
-	struct ast_frame *last;
 
 	trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
 
 	AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
+		struct ast_frame *last;
+		struct ast_frame *f1;	/* the frame to append */
+		struct ast_channel_spy_queue *queue;
+
 		ast_mutex_lock(&spy->lock);
 
 		queue = (dir == SPY_READ) ? &spy->read_queue : &spy->write_queue;
@@ -1294,12 +1296,7 @@
 					break;
 				}
 			}
-
-			for (last = queue->head; last && last->next; last = last->next);
-			if (last)
-				last->next = ast_frdup(translated_frame);
-			else
-				queue->head = ast_frdup(translated_frame);
+			f1 = translated_frame;
 		} else {
 			if (f->subclass != queue->format) {
 				ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
@@ -1308,13 +1305,17 @@
 				ast_mutex_unlock(&spy->lock);
 				continue;
 			}
-
-			for (last = queue->head; last && last->next; last = last->next);
-			if (last)
-				last->next = ast_frdup(f);
-			else
-				queue->head = ast_frdup(f);
-		}
+			f1 = f;
+		}
+		/* duplicate and append f1 to the tail */
+		f1 = ast_frdup(f1);
+
+		for (last = queue->head; last && last->next; last = last->next)
+			;
+		if (last)
+			last->next = f1;
+		else
+			queue->head = f1;
 
 		queue->samples += f->samples;
 
@@ -1815,23 +1816,24 @@
 	int blah;
 	int prestate;
 
+	/* this function is very long so make sure there is only one return
+	 * point at the end (there is only one exception to this).
+	 */
 	ast_channel_lock(chan);
 	if (chan->masq) {
 		if (ast_do_masquerade(chan)) {
 			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
-			f = NULL;
-		} else
+		} else {
 			f =  &ast_null_frame;
-		ast_channel_unlock(chan);
-		return f;
+		}
+		goto done;
 	}
 
 	/* Stop if we're a zombie or need a soft hangup */
 	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
 		if (chan->generator)
 			ast_deactivate_generator(chan);
-		ast_channel_unlock(chan);
-		return NULL;
+		goto done;
 	}
 	prestate = chan->_state;
 
@@ -1839,16 +1841,17 @@
 		/* We have DTMF that has been deferred.  Return it now */
 		chan->dtmff.frametype = AST_FRAME_DTMF;
 		chan->dtmff.subclass = chan->dtmfq[0];
-		/* Drop first digit */
+		/* Drop first digit from the buffer */
 		memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
-		ast_channel_unlock(chan);
-		return &chan->dtmff;
+		f = &chan->dtmff;
+		goto done;
 	}
 	
 	/* Read and ignore anything on the alertpipe, but read only
 	   one sizeof(blah) per frame that we send from it */
 	if (chan->alertpipe[0] > -1)
 		read(chan->alertpipe[0], &blah, sizeof(blah));
+
 #ifdef HAVE_ZAPTEL
 	if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
 		int res;
@@ -1881,20 +1884,22 @@
 				chan->timingdata = NULL;
 				ast_channel_unlock(chan);
 			}
+			/* cannot 'goto done' because the channel is already unlocked */
 			return &ast_null_frame;
 		} else
 			ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
 	} else
 #endif
-	/* Check for AST_GENERATOR_FD if not null.  If so, call generator with -1
-	   arguments now so it can do whatever it needs to. */
 	if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
+		/* if the AST_GENERATOR_FD is set, call the generator with args
+		 * set to -1 so it can do whatever it needs to.
+		 */
 		void *tmp = chan->generatordata;
 		chan->generatordata = NULL;     /* reset to let ast_write get through */
 		chan->generator->generate(chan, tmp, -1, -1);
 		chan->generatordata = tmp;
-		ast_channel_unlock(chan);
-		return &ast_null_frame;
+		f = &ast_null_frame;
+		goto done;
 	}
 
 	/* Check for pending read queue */
@@ -2006,9 +2011,8 @@
 				/* Run generator sitting on the line if timing device not available
 				* and synchronous generation of outgoing frames is necessary       */
 				if (chan->generatordata &&  !ast_internal_timing_enabled(chan)) {
-					void *tmp;
+					void *tmp = chan->generatordata;
 					int res;
-					int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
 
 					if (chan->timingfunc) {
 						if (option_debug > 1)
@@ -2016,10 +2020,8 @@
 						ast_settimeout(chan, 0, NULL, NULL);
 					}
 
-					tmp = chan->generatordata;
-					chan->generatordata = NULL;
-					generate = chan->generator->generate;
-					res = generate(chan, tmp, f->datalen, f->samples);
+					chan->generatordata = NULL;	/* reset, to let writes go through */
+					res = chan->generator->generate(chan, tmp, f->datalen, f->samples);
 					chan->generatordata = tmp;
 					if (res) {
 						if (option_debug > 1)
@@ -2053,8 +2055,9 @@
 		chan->fin &= 0x80000000;
 	else
 		chan->fin++;
+
+done:
 	ast_mutex_unlock(&chan->lock);
-
 	return f;
 }
 

Modified: team/group/autoconf_and_menuselect/channels/chan_skinny.c
URL: http://svn.digium.com/view/asterisk/team/group/autoconf_and_menuselect/channels/chan_skinny.c?rev=20760&r1=20759&r2=20760&view=diff
==============================================================================
--- team/group/autoconf_and_menuselect/channels/chan_skinny.c (original)
+++ team/group/autoconf_and_menuselect/channels/chan_skinny.c Sun Apr 16 20:52:05 2006
@@ -121,10 +121,10 @@
  * Protocol Messages *
  *********************/
 /* message types */
-#define	KEEP_ALIVE_MESSAGE 0x0000
+#define KEEP_ALIVE_MESSAGE 0x0000
 /* no additional struct */
 
-#define	REGISTER_MESSAGE 0x0001
+#define REGISTER_MESSAGE 0x0001
 typedef struct register_message {
 	char name[16];
 	int userId;
@@ -150,7 +150,7 @@
 #define OFFHOOK_MESSAGE 0x0006
 #define ONHOOK_MESSAGE 0x0007
 
-#define	CAPABILITIES_RES_MESSAGE 0x0010
+#define CAPABILITIES_RES_MESSAGE 0x0010
 typedef struct station_capabilities {
 	int codec;
 	int frames;
@@ -170,30 +170,30 @@
 	int speedDialNumber;
 } speed_dial_stat_req_message;
 
-#define	LINE_STATE_REQ_MESSAGE 0x000B
+#define LINE_STATE_REQ_MESSAGE 0x000B
 typedef struct line_state_req_message {
 	int lineNumber;
 } line_state_req_message;
 
-#define	TIME_DATE_REQ_MESSAGE 0x000D
-#define	VERSION_REQ_MESSAGE 0x000F
+#define TIME_DATE_REQ_MESSAGE 0x000D
 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
+#define VERSION_REQ_MESSAGE 0x000F
 #define SERVER_REQUEST_MESSAGE 0x0012
 #define ALARM_MESSAGE 0x0020
 
-#define OPEN_RECIEVE_CHANNEL_ACK_MESSAGE 0x0022
-typedef struct open_recieve_channel_ack_message {
+#define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
+typedef struct open_receive_channel_ack_message {
 	int status;
 	char ipAddr[4];
 	int port;
 	int passThruId;
-} open_recieve_channel_ack_message;
-
-#define	SOFT_KEY_SET_REQ_MESSAGE 0x0025
+} open_receive_channel_ack_message;
+
+#define SOFT_KEY_SET_REQ_MESSAGE 0x0025
 #define UNREGISTER_MESSAGE 0x0027
-#define	SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
-
-#define	REGISTER_ACK_MESSAGE 0x0081
+#define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
+
+#define REGISTER_ACK_MESSAGE 0x0081
 typedef struct register_ack_message {
 	int keepAlive;
 	char dateTemplate[6];
@@ -202,7 +202,7 @@
 	char res2[4];
 } register_ack_message;
 
-#define	START_TONE_MESSAGE 0x0082
+#define START_TONE_MESSAGE 0x0082
 typedef struct start_tone_message {
 	int tone;
 } start_tone_message;
@@ -290,31 +290,6 @@
 	int milliseconds;
 	int timestamp;
 } definetimedate_message;
-
-#define DISPLAYTEXT_MESSAGE 0x0099
-typedef struct displaytext_message {
-	char text[40];
-} displaytext_message;
-
-#define CLEAR_DISPLAY_MESSAGE 0x009A
-
-#define	REGISTER_REJ_MESSAGE 0x009D
-typedef struct register_rej_message {
-	char errMsg[33];
-} register_rej_message;
-
-#define CAPABILITIES_REQ_MESSAGE 0x009B
-
-#define SERVER_RES_MESSAGE 0x009E
-typedef struct server_identifier {
-	char serverName[48];
-} server_identifier;
-
-typedef struct server_res_message {
-	server_identifier server[5];
-	int serverListenPort[5];
-	int serverIpAddr[5];
-} server_res_message;
 
 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
 
@@ -477,30 +452,54 @@
 	button_definition definition[42];
 } button_template_res_message;
 
-#define	VERSION_RES_MESSAGE 0x0098
+#define VERSION_RES_MESSAGE 0x0098
 typedef struct version_res_message {
 	char version[16];
 } version_res_message;
 
-#define	KEEP_ALIVE_ACK_MESSAGE 0x0100
-
-#define OPEN_RECIEVE_CHANNEL_MESSAGE 0x0105
-typedef struct open_recieve_channel_message {
+#define DISPLAYTEXT_MESSAGE 0x0099
+typedef struct displaytext_message {
+	char text[40];
+} displaytext_message;
+
+#define CLEAR_DISPLAY_MESSAGE 0x009A
+#define CAPABILITIES_REQ_MESSAGE 0x009B
+
+#define REGISTER_REJ_MESSAGE 0x009D
+typedef struct register_rej_message {
+	char errMsg[33];
+} register_rej_message;
+
+#define SERVER_RES_MESSAGE 0x009E
+typedef struct server_identifier {
+	char serverName[48];
+} server_identifier;
+
+typedef struct server_res_message {
+	server_identifier server[5];
+	int serverListenPort[5];
+	int serverIpAddr[5];
+} server_res_message;
+
+#define KEEP_ALIVE_ACK_MESSAGE 0x0100
+
+#define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
+typedef struct open_receive_channel_message {
 	int conferenceId;
 	int partyId;
 	int packets;
 	int capability;
 	int echo;
 	int bitrate;
-} open_recieve_channel_message;
-
-#define CLOSE_RECIEVE_CHANNEL_MESSAGE 0x0106
-typedef struct close_recieve_channel_message {
+} open_receive_channel_message;
+
+#define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
+typedef struct close_receive_channel_message {
 	int conferenceId;
 	int partyId;
-} close_recieve_channel_message;
-
-#define	SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
+} close_receive_channel_message;
+
+#define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
 
 typedef struct soft_key_template_definition {
 	char softKeyLabel[16];
@@ -535,7 +534,7 @@
 	soft_key_template_definition softKeyTemplateDefinition[32];
 } soft_key_template;
 
-#define	SOFT_KEY_SET_RES_MESSAGE 0x0109
+#define SOFT_KEY_SET_RES_MESSAGE 0x0109
 static const char *soft_key_set_hack = {
 	"\x01\x02\x05\x03\x09\x0a\x0b\x10\x11\x12\x04\x0e\x0d\x00\x00\x00"
 	"\x2d\x01\x2e\x01\x31\x01\x2f\x01\x35\x01\x36\x01\x37\x01\x3c\x01"
@@ -660,9 +659,9 @@
 		call_info_message callinfo;
 		start_media_transmission_message startmedia;
 		stop_media_transmission_message stopmedia;
-		open_recieve_channel_message openrecievechannel;
-		open_recieve_channel_ack_message openrecievechannelack;
-		close_recieve_channel_message closerecievechannel;
+		open_receive_channel_message openreceivechannel;
+		open_receive_channel_ack_message openreceivechannelack;
+		close_receive_channel_message closereceivechannel;
 		display_notify_message displaynotify;
 		dialed_number_message dialednumber;
 	} data;
@@ -1056,10 +1055,10 @@
 		req->data.activatecallplane.lineInstance = 0;
 		transmit_response(s, req);
 		memset(req, 0, memsize);
-		req->len = htolel(sizeof(close_recieve_channel_message)+4);
-		req->e = htolel(CLOSE_RECIEVE_CHANNEL_MESSAGE);
-		req->data.closerecievechannel.conferenceId = 0;
-		req->data.closerecievechannel.partyId = 0;
+		req->len = htolel(sizeof(close_receive_channel_message)+4);
+		req->e = htolel(CLOSE_RECEIVE_CHANNEL_MESSAGE);
+		req->data.closereceivechannel.conferenceId = 0;
+		req->data.closereceivechannel.partyId = 0;
 		transmit_response(s, req);
 		memset(req, 0, memsize);
 		req->len = htolel(sizeof(stop_media_transmission_message)+4);
@@ -1106,19 +1105,19 @@
 	skinny_req *req;
 	struct skinny_line *l = s->device->lines;
 
-	req = req_alloc(sizeof(struct open_recieve_channel_message));
+	req = req_alloc(sizeof(struct open_receive_channel_message));
 	if (!req) {
 		ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
 		return;
 	}
-	req->len = htolel(sizeof(struct open_recieve_channel_message));
-	req->e = htolel(OPEN_RECIEVE_CHANNEL_MESSAGE);
-	req->data.openrecievechannel.conferenceId = 0;
-	req->data.openrecievechannel.partyId = 0;
-	req->data.openrecievechannel.packets = htolel(20);
-	req->data.openrecievechannel.capability = htolel(convert_cap(l->capability));
-	req->data.openrecievechannel.echo = 0;
-	req->data.openrecievechannel.bitrate = 0;
+	req->len = htolel(sizeof(struct open_receive_channel_message));
+	req->e = htolel(OPEN_RECEIVE_CHANNEL_MESSAGE);
+	req->data.openreceivechannel.conferenceId = 0;
+	req->data.openreceivechannel.partyId = 0;
+	req->data.openreceivechannel.packets = htolel(20);
+	req->data.openreceivechannel.capability = htolel(convert_cap(l->capability));
+	req->data.openreceivechannel.echo = 0;
+	req->data.openreceivechannel.bitrate = 0;
 	transmit_response(s, req);
 }
 
@@ -2397,43 +2396,43 @@
 			   and dirty redial, feeding the frames we last got into the queue
 			   function */
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Redial(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Redial(%d)\n", stimulusInstance);
 			}
 			break;
 		case STIMULUS_SPEEDDIAL:
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: SpeedDial(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: SpeedDial(%d)\n", stimulusInstance);
 			}
 			break;
 		case STIMULUS_HOLD:
 			/* start moh? set RTP to 0.0.0.0? */
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Hold(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Hold(%d)\n", stimulusInstance);
 			}
 			break;
 		case STIMULUS_TRANSFER:
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Transfer(%d)\n", stimulusInstance);
 			}
 			transmit_tone(s, SKINNY_DIALTONE);
 			/* XXX figure out how to transfer */
 			break;
 		case STIMULUS_CONFERENCE:
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Transfer(%d)\n", stimulusInstance);
 			}
 			transmit_tone(s, SKINNY_DIALTONE);
 			/* XXX determine the best way to pull off a conference.  Meetme? */
 			break;
 		case STIMULUS_VOICEMAIL:
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Voicemail(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Voicemail(%d)\n", stimulusInstance);
 			}
 			/* XXX Find and dial voicemail extension */
 			break;
 		case STIMULUS_CALLPARK:
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Park Call(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Park Call(%d)\n", stimulusInstance);
 			}
 			/* XXX Park the call */
 			break;
@@ -2460,18 +2459,18 @@
 		case STIMULUS_FORWARDNOANSWER:
 			/* Gonna be fun, not */
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Forward (%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Forward (%d)\n", stimulusInstance);
 			}
 			break;
 		case STIMULUS_DISPLAY:
 			/* Not sure what this is */
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Display(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Display(%d)\n", stimulusInstance);
 			}
 			break;
 		case STIMULUS_LINE:
 			if (skinnydebug) {
-				ast_verbose("Recieved Stimulus: Line(%d)\n", stimulusInstance);
+				ast_verbose("Received Stimulus: Line(%d)\n", stimulusInstance);
 			}
 			sub = find_subchannel_by_line(s->device->lines);
 			/* turn the speaker on */
@@ -2496,7 +2495,7 @@
 		break;
 	case SERVER_REQUEST_MESSAGE:
 		if (skinnydebug) {
-			ast_verbose("Recieved Server Request\n");
+			ast_verbose("Received Server Request\n");
 		}
 		memset(req, 0, SKINNY_MAX_PACKET);
 		req->len = htolel(sizeof(server_res_message)+4);
@@ -2566,7 +2565,7 @@
 		break;
 	case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
 		if (skinnydebug) {
-			ast_verbose("Recieved SoftKey Template Request\n");
+			ast_verbose("Received SoftKey Template Request\n");
 		}
 		memset(req, 0, SKINNY_MAX_PACKET);
 		req->len = htolel(sizeof(soft_key_template)+4);
@@ -2775,18 +2774,18 @@
 			}
 		}
 		break;
-	case OPEN_RECIEVE_CHANNEL_ACK_MESSAGE:
+	case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
 		if (skinnydebug) {
-			ast_verbose("Recieved Open Recieve Channel Ack\n");
-		}
-		status = letohl(req->data.openrecievechannelack.status);
+			ast_verbose("Received Open Receive Channel Ack\n");
+		}
+		status = letohl(req->data.openreceivechannelack.status);
 		if (status) {
-			ast_log(LOG_ERROR, "Open Recieve Channel Failure\n");
+			ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
 			break;
 		}
 		/* ENDIAN */
-		memcpy(addr, req->data.openrecievechannelack.ipAddr, sizeof(addr));
-		port = htolel(req->data.openrecievechannelack.port);
+		memcpy(addr, req->data.openreceivechannelack.ipAddr, sizeof(addr));
+		port = htolel(req->data.openreceivechannelack.port);
 		sin.sin_family = AF_INET;
 		/* I smell endian problems */
 		memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));

Modified: team/group/autoconf_and_menuselect/pbx.c
URL: http://svn.digium.com/view/asterisk/team/group/autoconf_and_menuselect/pbx.c?rev=20760&r1=20759&r2=20760&view=diff
==============================================================================
--- team/group/autoconf_and_menuselect/pbx.c (original)
+++ team/group/autoconf_and_menuselect/pbx.c Sun Apr 16 20:52:05 2006
@@ -1025,7 +1025,6 @@
 {
 	struct ast_custom_function *acf;
 	int count_acf = 0;
-	int print_acf = 0;
 	int like = 0;
 
 	if (argc == 4 && (!strcmp(argv[2], "like")) ) {
@@ -1037,18 +1036,8 @@
 	ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
 	
 	for (acf = acf_root ; acf; acf = acf->next) {
-		print_acf = 0;
-		if (like) {
-			if (strstr(acf->name, argv[3])) {
-				print_acf = 1;
-				count_acf++;
-			}
-		} else {
-			print_acf = 1;
+		if (!like || strstr(acf->name, argv[3])) {
 			count_acf++;
-		} 
-
-		if (print_acf) {
 			ast_cli(fd, "%-20.20s  %-35.35s  %s\n", acf->name, acf->syntax, acf->synopsis);
 		}
 	}
@@ -1067,7 +1056,8 @@
 	char stxtitle[40], *syntax = NULL;
 	int synopsis_size, description_size, syntax_size;
 
-	if (argc < 3) return RESULT_SHOWUSAGE;
+	if (argc < 3)
+		return RESULT_SHOWUSAGE;
 
 	if (!(acf = ast_custom_function_find(argv[2]))) {
 		ast_cli(fd, "No function by that name registered.\n");
@@ -1159,7 +1149,7 @@
 
 int ast_custom_function_unregister(struct ast_custom_function *acf) 
 {
-	struct ast_custom_function *acfptr, *lastacf = NULL;
+	struct ast_custom_function *cur, *prev = NULL;
 	int res = -1;
 
 	if (!acf)
@@ -1171,22 +1161,20 @@
 		return -1;
 	}
 
-	for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
-		if (acfptr == acf) {
-			if (lastacf) {
-				lastacf->next = acf->next;
-			} else {
+	for (cur = acf_root; cur; prev = cur, cur = cur->next) {
+		if (cur == acf) {
+			if (prev)
+				prev->next = acf->next;
+			else
 				acf_root = acf->next;
-			}
 			res = 0;
 			break;
 		}
-		lastacf = acfptr;
 	}
 
 	ast_mutex_unlock(&acflock);
 
-	if (!res && (option_verbose > 1))
+	if (!res && option_verbose > 1)
 		ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
 
 	return res;

Modified: team/group/autoconf_and_menuselect/res/res_features.c
URL: http://svn.digium.com/view/asterisk/team/group/autoconf_and_menuselect/res/res_features.c?rev=20760&r1=20759&r2=20760&view=diff
==============================================================================
--- team/group/autoconf_and_menuselect/res/res_features.c (original)
+++ team/group/autoconf_and_menuselect/res/res_features.c Sun Apr 16 20:52:05 2006
@@ -159,6 +159,14 @@
 	struct ast_channel *peer;
 };
 
+/* store context, priority and extension */
+static void set_c_e_p(struct ast_channel *chan, const char *ctx, const char *ext, int pri)
+{
+	ast_copy_string(chan->context, ctx, sizeof(chan->context));
+	ast_copy_string(chan->exten, ext, sizeof(chan->exten));
+	chan->priority = pri;
+}
+
 static void check_goto_on_transfer(struct ast_channel *chan) 
 {
 	struct ast_channel *xferchan;
@@ -212,7 +220,7 @@
 	ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
 	ast_hangup(tobj->chan);
 	ast_hangup(tobj->peer);
-	tobj->chan = tobj->peer = NULL;
+	bzero(tobj, sizeof(*tobj)); /* XXX for safety */
 	free(tobj);
 	return NULL;
 }
@@ -230,8 +238,6 @@
 	memset(&sched, 0, sizeof(sched));
 	pthread_setschedparam(thread, SCHED_RR, &sched);
 }
-
-
 
 static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
 {
@@ -294,10 +300,7 @@
 	}
 	pu->start = ast_tvnow();
 	pu->parkingnum = x;
-	if (timeout > 0)
-		pu->parkingtime = timeout;
-	else
-		pu->parkingtime = parkingtime;
+	pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
 	if (extout)
 		*extout = x;
 	if (peer) 
@@ -375,9 +378,7 @@
 		ast_channel_masquerade(chan, rchan);
 
 		/* Setup the extensions and such */
-		ast_copy_string(chan->context, rchan->context, sizeof(chan->context));
-		ast_copy_string(chan->exten, rchan->exten, sizeof(chan->exten));
-		chan->priority = rchan->priority;
+		set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
 
 		/* Make the masq execute */
 		f = ast_read(chan);
@@ -403,6 +404,39 @@
 #define FEATURE_SENSE_CHAN	(1 << 0)
 #define FEATURE_SENSE_PEER	(1 << 1)
 
+/*
+ * if the file name is non-empty, try to play it.
+ * Return 0 if success, -1 if error, digit if interrupted by a digit.
+ * If digits == "" then we can simply check for non-zero.
+ *
+ * XXX there are probably many replicas of this function in the source tree,
+ * that should be merged.
+ */
+static int stream_and_wait(struct ast_channel *chan, const char *file, const char *language, const char *digits)
+{
+	int res = 0;
+	if (!ast_strlen_zero(file)) {
+		res =  ast_streamfile(chan, file, language);
+		if (!res)
+			res = ast_waitstream(chan, digits);
+	}
+	return res;
+}
+ 
+/*
+ * set caller and callee according to the direction
+ */
+static void set_peers(struct ast_channel **caller, struct ast_channel **callee,
+	struct ast_channel *peer, struct ast_channel *chan, int sense)
+{
+	if (sense == FEATURE_SENSE_PEER) {
+		*caller = peer;
+		*callee = chan;
+	} else {
+		*callee = peer;
+		*caller = chan;
+	}
+}
 
 static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
 {
@@ -411,36 +445,26 @@
 	size_t len;
 	struct ast_channel *caller_chan = NULL, *callee_chan = NULL;
 
-
-	if(sense == 2) {
-		caller_chan = peer;
-		callee_chan = chan;
-	} else {
-		callee_chan = peer;
-		caller_chan = chan;
-	}
-	
 	if (!monitor_ok) {
 		ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
 		return -1;
 	}
 
-	if (!monitor_app) { 
-		if (!(monitor_app = pbx_findapp("Monitor"))) {
-			monitor_ok=0;
-			ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
-			return -1;
-		}
-	}
+	if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
+		monitor_ok=0;
+		ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
+		return -1;
+	}
+
+	set_peers(&caller_chan, &callee_chan, peer, chan, sense);
+
 	if (!ast_strlen_zero(courtesytone)) {
 		if (ast_autoservice_start(callee_chan))
 			return -1;
-		if (!ast_streamfile(caller_chan, courtesytone, caller_chan->language)) {
-			if (ast_waitstream(caller_chan, "") < 0) {
-				ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
-				ast_autoservice_stop(callee_chan);
-				return -1;
-			}
+		if (stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) {
+			ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+			ast_autoservice_stop(callee_chan);
+			return -1;
 		}
 		if (ast_autoservice_stop(callee_chan))
 			return -1;
@@ -505,70 +529,65 @@
 	return FEATURE_RETURN_HANGUP;
 }
 
+static int finishup(struct ast_channel *chan)
+{
+        int res;
+  
+        ast_moh_stop(chan);
+        res = ast_autoservice_stop(chan);
+        ast_indicate(chan, AST_CONTROL_UNHOLD);
+        return res;
+}
+
+static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
+{
+        const char *s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
+        if (ast_strlen_zero(s))
+                s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
+        if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
+                s = transferer->macrocontext;
+        if (ast_strlen_zero(s))
+                s = transferer->context;
+        return s;  
+}
+
 static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
 {
 	struct ast_channel *transferer;
 	struct ast_channel *transferee;
 	const char *transferer_real_context;
-	char newext[256];
+	char xferto[256];
 	int res;
 
-	if (sense == FEATURE_SENSE_PEER) {
-		transferer = peer;
-		transferee = chan;
-	} else {
-		transferer = chan;
-		transferee = peer;
-	}
-	if (!(transferer_real_context = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
-	   !(transferer_real_context = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
-		/* Use the non-macro context to transfer the call */
-		if (!ast_strlen_zero(transferer->macrocontext))
-			transferer_real_context = transferer->macrocontext;
-		else
-			transferer_real_context = transferer->context;
-	}
-	/* Start autoservice on chan while we talk
-	   to the originator */
+	set_peers(&transferer, &transferee, peer, chan, sense);
+	transferer_real_context = real_ctx(transferer, transferee);
+	/* Start autoservice on chan while we talk to the originator */
 	ast_indicate(transferee, AST_CONTROL_HOLD);
 	ast_autoservice_start(transferee);
 	ast_moh_start(transferee, NULL);
 
-	memset(newext, 0, sizeof(newext));
+	memset(xferto, 0, sizeof(xferto));
 	
 	/* Transfer */
-	if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
-		ast_moh_stop(transferee);
-		ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+	res = stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
+	if (res < 0) {
+		finishup(transferee);
+		return -1; /* error ? */
+	} else if (res > 0) {	/* If they've typed a digit already, handle it */
+		xferto[0] = (char) res;
+	}
+
+	ast_stopstream(transferer);
+	res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
+	if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
+		finishup(transferee);
 		return res;
 	}
-	if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
-		ast_moh_stop(transferee);
-		ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
-		return res;
-	} else if (res > 0) {
-		/* If they've typed a digit already, handle it */
-		newext[0] = (char) res;
-	}
-
-	ast_stopstream(transferer);
-	res = ast_app_dtget(transferer, transferer_real_context, newext, sizeof(newext), 100, transferdigittimeout);
-	if (res < 0) {
-		ast_moh_stop(transferee);
-		ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
-		return res;
-	}
-	if (!strcmp(newext, ast_parking_ext())) {
-		ast_moh_stop(transferee);
-
-		res = ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+	if (!strcmp(xferto, ast_parking_ext())) {
+		res = finishup(transferee);
 		if (res)
 			res = -1;
-		else if (!ast_park_call(transferee, transferer, 0, NULL)) {
+		else if (!ast_park_call(transferee, transferer, 0, NULL)) {	/* success */
 			/* We return non-zero, but tell the PBX not to hang the channel when
 			   the thread dies -- We have to be careful now though.  We are responsible for 
 			   hanging up the channel, else it will never be hung up! */
@@ -578,47 +597,34 @@
 			ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
 		}
 		/* XXX Maybe we should have another message here instead of invalid extension XXX */
-	} else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
+	} else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
 		pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
 		pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
-		ast_moh_stop(transferee);
-		res=ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+		res=finishup(transferee);
 		if (!transferee->pbx) {
 			/* Doh!  Use our handy async_goto functions */
 			if (option_verbose > 2) 
 				ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
-								,transferee->name, newext, transferer_real_context);
-			if (ast_async_goto(transferee, transferer_real_context, newext, 1))
+								,transferee->name, xferto, transferer_real_context);
+			if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
 				ast_log(LOG_WARNING, "Async goto failed :-(\n");
 			res = -1;
 		} else {
 			/* Set the channel's new extension, since it exists, using transferer context */
-			ast_copy_string(transferee->exten, newext, sizeof(transferee->exten));
-			ast_copy_string(transferee->context, transferer_real_context, sizeof(transferee->context));
-			transferee->priority = 0;
+			set_c_e_p(transferee, transferer_real_context, xferto, 0);
 		}
 		check_goto_on_transfer(transferer);
 		return res;
 	} else {
 		if (option_verbose > 2)	
-			ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
-	}
-	if (!ast_strlen_zero(xferfailsound))
-		res = ast_streamfile(transferer, xferfailsound, transferee->language);
-	else
-		res = 0;
-	if (res) {
-		ast_moh_stop(transferee);
-		ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
-		return res;
-	}
-	res = ast_waitstream(transferer, AST_DIGIT_ANY);
+			ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
+	}
+	if (stream_and_wait(transferer, xferfailsound, transferee->language, AST_DIGIT_ANY) < 0 ) {
+		finishup(transferee);
+		return -1;
+	}
 	ast_stopstream(transferer);
-	ast_moh_stop(transferee);
-	res = ast_autoservice_stop(transferee);
-	ast_indicate(transferee, AST_CONTROL_UNHOLD);
+	res = finishup(transferee);
 	if (res) {
 		if (option_verbose > 1)
 			ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
@@ -636,58 +642,59 @@
 	struct ast_bridge_config bconfig;
 	const char *transferer_real_context;
 	char xferto[256],dialstr[265];
-	char *cid_num;
-	char *cid_name;
 	int res;
 	struct ast_frame *f = NULL;
 	struct ast_bridge_thread_obj *tobj;
 
 	ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) XXX\n", chan->name, peer->name, sense);
-	if (sense == FEATURE_SENSE_PEER) {
-		transferer = peer;
-		transferee = chan;
-	} else {
-		transferer = chan;
-		transferee = peer;
-	}
-	if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
-	   !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
-		/* Use the non-macro context to transfer the call */
-		if (!ast_strlen_zero(transferer->macrocontext))
-			transferer_real_context = transferer->macrocontext;
-		else
-			transferer_real_context = transferer->context;
-	}
-	/* Start autoservice on chan while we talk
-	   to the originator */
+	set_peers(&transferer, &transferee, peer, chan, sense);
+        transferer_real_context = real_ctx(transferer, transferee);
+	/* Start autoservice on chan while we talk to the originator */
 	ast_indicate(transferee, AST_CONTROL_HOLD);
 	ast_autoservice_start(transferee);
 	ast_moh_start(transferee, NULL);
 	memset(xferto, 0, sizeof(xferto));
 	/* Transfer */
-	if ((res = ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
-		ast_moh_stop(transferee);
-		ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+	res = stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
+	if (res < 0) {
+		finishup(transferee);
 		return res;
-	}
-	if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
-		ast_moh_stop(transferee);
-		ast_autoservice_stop(transferee);
-		ast_indicate(transferee, AST_CONTROL_UNHOLD);
-		return res;
-	} else if(res > 0) {
-		/* If they've typed a digit already, handle it */
+	} else if (res > 0) /* If they've typed a digit already, handle it */
 		xferto[0] = (char) res;
-	}
-	if ((ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout))) {
-		cid_num = transferer->cid.cid_num;
-		cid_name = transferer->cid.cid_name;
-		if (ast_exists_extension(transferer, transferer_real_context,xferto, 1, cid_num)) {
+
+	/* this is specific of atxfer */
+	res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
+        if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
+                finishup(transferee);
+                return res;
+        }
+	if (res == 0) {
+		ast_log(LOG_WARNING, "Did not read data.\n");
+		finishup(transferee);
+		if (stream_and_wait(transferer, "beeperr", transferer->language, ""))
+			return -1;
+		return FEATURE_RETURN_SUCCESS;
+	}
+	/* valid extension, res == 1 */
+	{
+		if (!ast_exists_extension(transferer, transferer_real_context,xferto, 1, transferer->cid.cid_num)) {
+			ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
+			finishup(transferee);
+			if (stream_and_wait(transferer, "beeperr", transferer->language, ""))
+				return -1;
+		} else {
 			snprintf(dialstr, sizeof(dialstr), "%s@%s/n", xferto, transferer_real_context);
-			newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats), dialstr, 15000, &outstate, cid_num, cid_name);
+			newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats), dialstr, 15000, &outstate, transferer->cid.cid_num, transferer->cid.cid_name);
 			ast_indicate(transferer, -1);
-			if (newchan) {
+			if (!newchan) {
+				finishup(transferee);
+				/* any reason besides user requested cancel and busy triggers the failed sound */
+				if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
+						stream_and_wait(transferer, xferfailsound, transferer->language, ""))
+					return -1;
+				return FEATURE_RETURN_SUCCESS;
+			}
+			{
 				res = ast_channel_make_compatible(transferer, newchan);
 				if (res < 0) {
 					ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferer->name, newchan->name);
@@ -704,14 +711,9 @@
 						ast_frfree(f);
 						f = NULL;
 					}
-					if (!ast_strlen_zero(xfersound) && !ast_streamfile(transferer, xfersound, transferer->language)) {
-						if (ast_waitstream(transferer, "") < 0) {
-							ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
-						}
-					}
-					ast_moh_stop(transferee);
-					ast_autoservice_stop(transferee);
-					ast_indicate(transferee, AST_CONTROL_UNHOLD);
+					if (stream_and_wait(transferer, xfersound, transferer->language, ""))
+						ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+					finishup(transferee);
 					transferer->_softhangup = 0;
 					return FEATURE_RETURN_SUCCESS;
 				}
@@ -766,10 +768,8 @@
 					tobj->peer = newchan;
 					tobj->bconfig = *config;
 	
-					if (!ast_strlen_zero(xfersound) && !ast_streamfile(newchan, xfersound, newchan->language) &&
-							ast_waitstream(newchan, "") < 0) {
+					if (stream_and_wait(newchan, xfersound, newchan->language, ""))
 						ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
-					}
 					ast_bridge_call_thread_launch(tobj);
 				} else {
 					ast_hangup(xferchan);
@@ -777,39 +777,10 @@
 				}
 				return -1;
 				
-			} else {
-				ast_moh_stop(transferee);
-				ast_autoservice_stop(transferee);
-				ast_indicate(transferee, AST_CONTROL_UNHOLD);
-				/* any reason besides user requested cancel and busy triggers the failed sound */
-				if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY && !ast_strlen_zero(xferfailsound)) {
-					res = ast_streamfile(transferer, xferfailsound, transferer->language);
-					if (!res && (ast_waitstream(transferer, "") < 0)) {
-						return -1;
-					}
-				}
-				return FEATURE_RETURN_SUCCESS;
 			}
-		} else {
-			ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
-			ast_moh_stop(transferee);
-			ast_autoservice_stop(transferee);
-			ast_indicate(transferee, AST_CONTROL_UNHOLD);
-			res = ast_streamfile(transferer, "beeperr", transferer->language);
-			if (!res && (ast_waitstream(transferer, "") < 0)) {
-				return -1;
-			}
-		}
-	}  else {
-		ast_log(LOG_WARNING, "Did not read data.\n");
-		res = ast_streamfile(transferer, "beeperr", transferer->language);
-		if (ast_waitstream(transferer, "") < 0) {
-			return -1;
-		}
-	}
-	ast_moh_stop(transferee);
-	ast_autoservice_stop(transferee);
-	ast_indicate(transferee, AST_CONTROL_UNHOLD);
+		}
+	}
+	finishup(transferee);
 
 	return FEATURE_RETURN_SUCCESS;
 }
@@ -848,7 +819,8 @@
 /*! \brief unregister feature from feature_list */
 void ast_unregister_feature(struct ast_call_feature *feature)
 {
-	if (!feature) return;
+	if (!feature)
+		return;
 
 	AST_LIST_LOCK(&feature_list);
 	AST_LIST_REMOVE(&feature_list,feature,feature_entry);
@@ -902,9 +874,7 @@
 	
 	app = pbx_findapp(feature->app);
 	if (app) {
-		struct ast_channel *work = chan;
-		if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLEE))
-			work = peer;
+		struct ast_channel *work = ast_test_flag(feature,AST_FEATURE_FLAG_CALLEE) ? peer : chan;
 		res = pbx_exec(work, app, feature->app_args);
 		if (res < 0)
 			return res; 
@@ -913,7 +883,7 @@
 		return -2;
 	}
 	
-	return FEATURE_RETURN_SUCCESS;
+	return FEATURE_RETURN_SUCCESS;	/* XXX should probably return res */
 }
 
 static void unmap_features(void)
@@ -1018,26 +988,23 @@
 			char *tok;
 			struct ast_call_feature *feature;
 
-			if (!tmp) {
+			if (!tmp)	/* no memory */
 				return;
-			}
 
 			/* while we have a feature */
 			while (NULL != (tok = strsep(&tmp, "#"))) {
-				if ((feature = find_feature(tok))) {
-					if (ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
-						if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLER))
-							ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
-						if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLEE))
-							ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
-					}
+				if ((feature = find_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {

[... 529 lines stripped ...]


More information about the asterisk-commits mailing list