Index: channel.c
===================================================================
--- channel.c	(revision 7745)
+++ channel.c	(working copy)
@@ -1310,6 +1310,7 @@
 	chan->generatordata = NULL;
 	chan->generator = NULL;
 	if (chan->cdr) {		/* End the CDR if it hasn't already */ 
+		ast_cdr_sethangupcause(chan);
 		ast_cdr_end(chan->cdr);
 		ast_cdr_detach(chan->cdr);	/* Post and Free the CDR */ 
 		chan->cdr = NULL;
Index: cdr.c
===================================================================
--- cdr.c	(revision 7745)
+++ cdr.c	(working copy)
@@ -256,6 +256,8 @@
 		snprintf(workspace, workspacelen, "%d", cdr->billsec);
 	else if (!strcasecmp(name, "disposition"))
 		ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
+	else if (!strcasecmp(name, "hangupcause"))
+		snprintf(workspace, workspacelen, "%d", cdr->hangupcause);
 	else if (!strcasecmp(name, "amaflags"))
 		ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
 	else if (!strcasecmp(name, "accountcode"))
@@ -280,8 +282,8 @@
 	struct varshead *headp;
 	const char *read_only[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
 				    "lastapp", "lastdata", "start", "answer", "end", "duration",
-				    "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
-				    "userfield", NULL };
+				    "billsec", "disposition", "hangupcause", "amaflags", "accountcode",
+					"uniqueid", "userfield", NULL };
 	int x;
 	
 	for(x = 0; read_only[x]; x++) {
@@ -637,6 +639,7 @@
 			ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
 
 			cdr->disposition = (c->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NOANSWER;
+			cdr->hangupcause = 0;
 			cdr->amaflags = c->amaflags ? c->amaflags :  ast_default_amaflags;
 			ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
 			/* Destination information */
@@ -752,6 +755,18 @@
 	return 0;
 }
 
+/*! Set the CDR hangupcause variable */
+void ast_cdr_sethangupcause(struct ast_channel *chan)
+{
+	struct ast_cdr *cdr = chan->cdr;
+
+	while (cdr) {
+		if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			cdr->hangupcause = chan->hangupcause;
+		cdr = cdr->next;
+	}
+}
+
 int ast_cdr_update(struct ast_channel *c)
 {
 	struct ast_cdr *cdr = c->cdr;
Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 7745)
+++ channels/chan_sip.c	(working copy)
@@ -147,6 +147,8 @@
 #define RTP 	1
 #define NO_RTP	0
 
+#define SIP_RETVAL_IGNORE    1
+
 /* Do _NOT_ make any changes to this enum, or the array following it;
    if you think you are doing the right thing, you are probably
    not doing the right thing. If you think there are changes
@@ -921,6 +923,7 @@
 static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
 static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate);
 static char *gettag(struct sip_request *req, char *header, char *tagbuf, int tagbufsize);
+static int sip_addheader(struct ast_channel *chan, void *data);
 
 /*! \brief Definition of this channel for PBX channel registration */
 static const struct ast_channel_tech sip_tech = {
@@ -979,6 +982,21 @@
 	return res;
 }
 
+/* --- sip_extract_tag: extract from and to tags from a callid ---*/
+static int sip_extract_tag(char **in) 
+{
+	char *tag;
+
+	if ((tag = strcasestr(*in, "tag="))) {
+		char *ptr;
+		tag += 4;
+		if ((ptr = strchr(tag, ';')))
+			*ptr = '\0';
+		return 0;
+	}
+	return -1;
+}
+
 /*! \brief  parse_sip_options: Parse supported header in incoming packet */
 unsigned int parse_sip_options(struct sip_pvt *pvt, char *supported)
 {
@@ -1915,9 +1933,9 @@
 	struct ast_hostent ahp;
 	struct sip_peer *p;
 	int found=0;
-	char *port;
+	char *port, *ptr, *hostp, *hostn;
 	int portno;
-	char host[MAXHOSTNAMELEN], *hostn;
+	char host[MAXHOSTNAMELEN];
 	char peer[256];
 
 	ast_copy_string(peer, opeer, sizeof(peer));
@@ -1955,7 +1973,13 @@
 				portno = tportno;
 			}
 		}
-		hp = ast_gethostbyname(hostn, &ahp);
+		if ((hostp = ast_strdupa(hostn))) {
+			if ((ptr = strchr(hostp, '?')))
+				*ptr = '\0';
+		} else
+			hostp = peer;
+		
+		hp = ast_gethostbyname(hostp, &ahp);
 		if (hp) {
 			ast_copy_string(dialog->tohost, peer, sizeof(dialog->tohost));
 			memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr));
@@ -4093,6 +4117,9 @@
 		add_header(req, "From", ot);
 		add_header(req, "To", of);
 	}
+//	if (sipmethod == SIP_MESSAGE) /* Add date header to MESSAGE */
+//		append_date(req);
+//	else
 	add_header(req, "Contact", p->our_contact);
 	copy_header(req, orig, "Call-ID");
 	add_header(req, "CSeq", tmp);
@@ -4947,6 +4974,7 @@
 	enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
 	char *pidfstate = "--";
 	char *pidfnote= "Ready";
+	struct sip_pvt *np = NULL;
 
 	memset(from, 0, sizeof(from));
 	memset(to, 0, sizeof(to));
@@ -5080,11 +5108,36 @@
 	case DIALOG_INFO_XML: /* SNOM subscribes in this format */
 		ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n");
 		ast_build_string(&t, &maxbytes, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto);
-		if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
-			ast_build_string(&t, &maxbytes, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten);
-		else
+
+		if (!ast_strlen_zero(hint)) {
+			/* check if the device is ringing and if so get the callid to enable pickup functionality (e.g. for snom phones) */
+			if ((state & AST_EXTENSION_RINGING)) {
+				struct ast_channel *chan = NULL;
+				int hintlen = strlen(hint);
+
+				while ((chan = ast_channel_walk_locked(chan)) != NULL) {
+					if (chan->_state == AST_STATE_RINGING && !strncasecmp(chan->name, "SIP", 3) && chan->tech_pvt && !strncasecmp(chan->name, hint, hintlen)) {
+						np = chan->tech_pvt;
+						ast_build_string(&t, &maxbytes, "<dialog id=\"%s\" direction=\"recipient\" call-id=\"%s\" local-tag=\"%s\" remote-tag=\"%s\">\n", p->exten, np->callid, np->tag, np->theirtag);
+						ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
+						ast_build_string(&t, &maxbytes, "<local><identity display=\"%s\">%s</identity><target uri=\"%s\"/></local>\n", p->exten, mfrom, mfrom);
+						ast_build_string(&t, &maxbytes, "<remote><identity display=\"%s\">%s</identity><target uri=\"%s\"/></remote>\n", np->fromname, mto, mto);
+
+						if (option_debug > 1)
+							ast_log(LOG_NOTICE, "Transmitting CallID in NOTIFY message - DialogID: %s  CallID: %s\n", p->exten, np->callid);
+
+						ast_mutex_unlock(&chan->lock);
+						break;
+					}
+					ast_mutex_unlock(&chan->lock);
+				}
+			}
+		}
+
+		if (!np) {
 			ast_build_string(&t, &maxbytes, "<dialog id=\"%s\">\n", p->exten);
-		ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
+			ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
+		}
 		ast_build_string(&t, &maxbytes, "</dialog>\n</dialog-info>\n");
 		break;
 	case NONE:
@@ -6589,19 +6642,44 @@
 }
 
 /*! \brief  get_sip_pvt_byid_locked: Lock interface lock and find matching pvt lock  ---*/
-static struct sip_pvt *get_sip_pvt_byid_locked(char *callid) 
+static struct sip_pvt *get_sip_pvt_byid_locked(char *callid, struct sip_request *req, char *totag, char *fromtag) 
 {
 	struct sip_pvt *sip_pvt_ptr = NULL;
+	char *real_totag = NULL, *real_fromtag = NULL;
+	int match = 1;
 	
 	/* Search interfaces and find the match */
 	ast_mutex_lock(&iflock);
 	sip_pvt_ptr = iflist;
-	while(sip_pvt_ptr) {
+	while (sip_pvt_ptr) {
 		if (!strcmp(sip_pvt_ptr->callid, callid)) {
 			/* Go ahead and lock it (and its owner) before returning */
 			ast_mutex_lock(&sip_pvt_ptr->lock);
+
+			if (req && pedanticsipchecking) {
+				if (totag) {
+					real_totag = ast_strdupa(get_header(req, "To"));
+					if (sip_extract_tag(&real_totag))
+						real_totag = NULL;
+					if (strcmp(real_totag, totag))
+						match = 0;
+				}
+				if (match && fromtag) {
+					real_fromtag = ast_strdupa(get_header(req, "From"));
+					if (sip_extract_tag(&real_fromtag))
+						real_fromtag = NULL;
+					if (strcmp(real_fromtag, fromtag))
+						match = 0;
+				}
+			}
+			
+			if (!match) {
+				ast_mutex_unlock(&sip_pvt_ptr->lock);
+				break;
+			}
+			
 			if (sip_pvt_ptr->owner) {
-				while(ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) {
+				while (ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) {
 					ast_mutex_unlock(&sip_pvt_ptr->lock);
 					usleep(1);
 					ast_mutex_lock(&sip_pvt_ptr->lock);
@@ -6622,7 +6700,7 @@
 {
 
 	char *p_refer_to = NULL, *p_referred_by = NULL, *h_refer_to = NULL, *h_referred_by = NULL, *h_contact = NULL;
-	char *replace_callid = "", *refer_to = NULL, *referred_by = NULL, *ptr = NULL;
+	char *replace_callid = "", *refer_to = NULL, *referred_by = NULL, *ptr = NULL, *replaces_header = NULL, *refer_uri;
 	struct sip_request *req = NULL;
 	struct sip_pvt *sip_pvt_ptr = NULL;
 	struct ast_channel *chan = NULL, *peer = NULL;
@@ -6667,6 +6745,8 @@
 	if (referred_by)
 		referred_by += 4;
 	
+	refer_uri = ast_strdupa(refer_to);	
+
 	if ((ptr = strchr(refer_to, '?'))) {
 		/* Search for arguments */
 		*ptr = '\0';
@@ -6674,10 +6754,7 @@
 		if (!strncasecmp(ptr, "REPLACES=", 9)) {
 			char *p;
 			replace_callid = ast_strdupa(ptr + 9);
-			/* someday soon to support invite/replaces properly!
-			   replaces_header = ast_strdupa(replace_callid); 
-			   -anthm
-			*/
+			replaces_header = ast_strdupa(replace_callid); 
 			ast_uri_decode(replace_callid);
 			if ((ptr = strchr(replace_callid, '%'))) 
 				*ptr = '\0';
@@ -6715,18 +6792,47 @@
 		ast_copy_string(sip_pvt->referred_by, "", sizeof(sip_pvt->referred_by));
 		ast_copy_string(sip_pvt->refer_contact, "", sizeof(sip_pvt->refer_contact));
 		sip_pvt->refer_call = NULL;
-		if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid))) {
+		if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid, req, NULL, NULL))) {
 			sip_pvt->refer_call = sip_pvt_ptr;
 			if (sip_pvt->refer_call == sip_pvt) {
 				ast_log(LOG_NOTICE, "Supervised transfer attempted to transfer into same call id (%s == %s)!\n", replace_callid, sip_pvt->callid);
 				sip_pvt->refer_call = NULL;
-			} else
-				return 0;
+			}
+			return 0;
 		} else {
-			ast_log(LOG_NOTICE, "Supervised transfer requested, but unable to find callid '%s'.  Both legs must reside on Asterisk box to transfer at this time.\n", replace_callid);
-			/* XXX The refer_to could contain a call on an entirely different machine, requiring an 
-	    		  INVITE with a replaces header -anthm XXX */
-			/* The only way to find out is to use the dialplan - oej */
+			/* Don't ask me =0 ?, SIP made do it! */
+			int cause = 0, res = -1;
+			struct ast_channel *ichan = NULL;
+			transmit_notify_with_sipfrag(sip_pvt, sip_pvt->ocseq);
+			if ((ptr = strchr(refer_uri, ';')))
+				*ptr = '\0';
+
+			if ((ichan = sip_request_call("SIP", sip_pvt->owner ? sip_pvt->owner->readformat : AST_FORMAT_ULAW, refer_uri, &cause))) {
+				struct ast_frame *f;
+				char *rbuf;	
+				if (sip_debug_test_pvt(sip_pvt))
+					ast_log(LOG_DEBUG, "Going hunting for a remote INVITE/Replaces at [%s] Wish me luck!\n", refer_uri);
+				if ((rbuf = alloca(strlen(replaces_header) + 10))) {
+					sprintf(rbuf, "Replaces: %s", replaces_header);
+					sip_addheader(ichan, rbuf);
+					sip_call(ichan, refer_uri, 20000);
+					ast_channel_masquerade(sip_pvt->owner, ichan);
+					if ((f = ast_read(ichan))) {
+						ast_log(LOG_NOTICE, "INVITE/Replaces successful for URI %s\n", refer_uri);
+						ast_frfree(f);
+						transmit_response(sip_pvt, "202 Accepted", req);
+						res = SIP_RETVAL_IGNORE; /* means do nothing more */
+					} else
+						res = -1;
+				} else {
+					ast_log(LOG_ERROR,"Memory Error!\n");
+					res = -1;
+				}
+
+				ast_hangup(ichan);
+			} else 
+				res = -1;
+			return res;
 		}
 	} else if (ast_exists_extension(NULL, sip_pvt->context, refer_to, 1, NULL) || !strcmp(refer_to, ast_parking_ext())) {
 		/* This is an unsupervised transfer (blind transfer) */
@@ -7007,10 +7113,12 @@
 		}
 
 		if (p->rtp) {
+			if (option_debug > 1)
 			ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 			ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 		}
 		if (p->vrtp) {
+			if (option_debug > 1)
 			ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 			ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 		}
@@ -8578,7 +8686,12 @@
 {
 	char buf[1024];
 	unsigned int event;
+	char resp = 0;
+	struct ast_frame f;
 	char *c;
+
+	if (sip_debug_test_pvt(p))
+		ast_log(LOG_NOTICE, "Receiving INFO from %s\n", p->callid);
 	
 	/* Need to check the media/type */
 	if (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay") ||
@@ -8636,6 +8749,43 @@
 		}
 		transmit_response(p, "200 OK", req);
 		return;
+	} else if ((c = get_header(req, "Record"))) {
+		/* Record button on SNOM */
+		if (!strcasecmp(c, "on")) {
+			if (sip_debug_test_pvt(p))
+				ast_log(LOG_NOTICE, "Record State 'On' from %s\n", p->exten);
+			if (!p->owner->monitor) {
+				time_t t;
+				struct tm tm;
+				char date[20];
+		
+				time(&t);
+				localtime_r(&t, &tm);
+				strftime(date, sizeof(date), "%Y%m%d_%H%M%S", &tm);
+				snprintf(buf, sizeof(buf), "/var/spool/asterisk/monitor/%s/%s", p->exten, date);
+
+				if (!(ast_monitor_start(p->owner, NULL, buf, 0))) {
+					ast_monitor_setjoinfiles(p->owner, 1); /* join the two audio files */
+					transmit_response(p, "200 OK", req); /* i don't really know how to answer to this, so the phone toggles recording on/off */
+					ast_log(LOG_NOTICE, "Recording extension: %s to file: %s\n", p->exten, buf);
+				}
+			} else {
+				if (!(ast_monitor_stop(p->owner, 0))) {
+					transmit_response(p, "200 OK", req); /* i don't really know how to answer to this, so the phone toggles recording on/off */
+					ast_log(LOG_NOTICE, "Stopped Recording extension %s\n", p->exten);
+				}
+			}		
+		} else if (!strcasecmp(c, "off")) {
+//			if (sip_debug_test_pvt(p))
+				ast_log(LOG_NOTICE, "Record State 'Off' from %s\n", p->callid);
+			if (!(ast_monitor_stop(p->owner, 0))) {
+				transmit_response(p, "200 OK", req); /* i don't really know how to answer to this, so the phone toggles recording on/off */
+				ast_log(LOG_NOTICE, "Stopped Recording extension %s\n", p->exten);
+			}
+		} else {
+			transmit_response(p, "405 Method Not Allowed", req);
+		}
+		return;
 	} else if (!strcasecmp(get_header(req, "Content-Type"), "application/media_control+xml")) {
 		/* Eh, we'll just assume it's a fast picture update for now */
 		if (p->owner)
@@ -8659,7 +8809,7 @@
 	/* if (get_msg_text(buf, sizeof(buf), req)) { */
 
 	ast_log(LOG_WARNING, "Unable to parse INFO message from %s. Content %s\n", p->callid, buf);
-	transmit_response(p, "415 Unsupported media type", req);
+	transmit_response(p, "501 Not Implemented", req);
 	return;
 }
 
@@ -10156,6 +10306,7 @@
 		}
 		peerc->cdr = NULL;
 		
+		ast_log(LOG_DEBUG, "Trying to masquerade %s and %s\n", peerb->name, peerc->name);
 		if (ast_channel_masquerade(peerb, peerc)) {
 			ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name);
 			res = -1;
@@ -10221,13 +10372,13 @@
 /*! \brief  handle_request_invite: Handle incoming INVITE request */
 static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, struct sockaddr_in *sin, int *recount, char *e)
 {
-	int res = 1;
-	struct ast_channel *c=NULL;
-	int gotdest;
-	struct ast_frame af = { AST_FRAME_NULL, };
-	char *supported;
-	char *required;
+	int res = 1, gotdest = 0;
+	struct ast_channel *c = NULL;
+	struct ast_frame af = { AST_FRAME_NULL, }, *f = NULL;
+	char *supported = NULL, *required = NULL;
 	unsigned int required_profile = 0;
+	char *ptr, *p_replaces = NULL, *replace_id = NULL;
+	struct sip_pvt *refer_pvt = NULL;
 
 	/* Find out what they support */
 	if (!p->sipoptions) {
@@ -10244,7 +10395,20 @@
 			if (!p->lastinvite)
 				ast_set_flag(p, SIP_NEEDDESTROY);	
 			return -1;
+		}
+	}
 			
+	if ((p_replaces = get_header(req, "Replaces"))) {
+		if (ast_strlen_zero(p_replaces)) {
+			p_replaces = NULL;
+		} else {
+			if (debug)
+				ast_log(LOG_DEBUG, "Found a Replaces header %s\n", get_header(req, "Replaces"));
+			replace_id = ast_strdupa(p_replaces);
+			if (strchr(replace_id, '%'))
+				ast_uri_decode(replace_id);
+			if ((ptr = strchr(replace_id, ';')))
+				*ptr = '\0';
 		}
 	}
 
@@ -10339,7 +10503,7 @@
 		extract_uri(p, req);
 		build_contact(p);
 
-		if (gotdest) {
+		if (!replace_id && gotdest) {
 			if (gotdest < 0) {
 				if (ignore)
 					transmit_response(p, "404 Not Found", req);
@@ -10366,9 +10530,43 @@
 			/* Save Record-Route for any later requests we make on this dialogue */
 			build_route(p, req, 0);
 			if (c) {
-				/* Pre-lock the call */
-				ast_mutex_lock(&c->lock);
-			}
+				if (replace_id) {
+					if ((refer_pvt = get_sip_pvt_byid_locked(replace_id, req, NULL, p->theirtag))) {
+						if (refer_pvt->owner && refer_pvt->owner->_state == AST_STATE_RINGING) {
+							transmit_response(p, "100 Trying", req);
+							ast_mutex_unlock(&refer_pvt->lock);
+							ast_channel_masquerade(refer_pvt->owner, c );
+							ast_hangup(c);
+							c = refer_pvt->owner;
+							if ((f = ast_read(c))) {
+								if (option_debug > 1)
+									ast_log(LOG_DEBUG, "SIP Call Replacement (pickup) successful for CallID: %s\n", p_replaces);
+								ast_frfree(f);
+								ast_setstate(c, AST_STATE_UP);
+							} else {
+								ast_log(LOG_WARNING, "SIP Call Replacement (pickup) not successful for CallID: %s\n", p_replaces);
+								transmit_response_with_allow(p, "403 Call Can Not Be Read From", req, 0); /* Don't know if this is the correct answer, but seems to work */
+								ast_mutex_unlock(&refer_pvt->owner->lock);
+								return 0;
+							}
+						} else {
+							if (refer_pvt->owner)
+								ast_mutex_unlock(&refer_pvt->owner->lock);
+							ast_mutex_unlock(&refer_pvt->lock);
+							ast_hangup(c);
+							ast_log(LOG_WARNING, "SIP Call Replacement (pickup) not possible. Call already answered\n");
+							transmit_response_with_allow(p, "403 Call Already Answered Or Hung Up", req, 0); /* Don't know if this is the correct answer, but seems to work */
+							return 0;
+						}
+					} else {
+						ast_hangup(c);
+						transmit_response_with_allow(p, "481 Call/Transaction Does Not Exist", req, 0);
+						return 0;
+					}
+				} else {
+					ast_mutex_lock(&c->lock); /* Pre-lock the call */
+				}
+			} 
 		}
 		
 	} else {
@@ -11037,6 +11235,10 @@
 		if (!p->lastinvite && ast_strlen_zero(p->randdata))
 			ast_set_flag(p, SIP_NEEDDESTROY);	
 		break;
+	case SIP_PUBLISH:
+		if (debug)
+			ast_log(LOG_DEBUG, "PUBLISH: from %s\n", e);
+		break;
 	default:
 		transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
 		ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", 
Index: channels/chan_agent.c
===================================================================
--- channels/chan_agent.c	(revision 7745)
+++ channels/chan_agent.c	(working copy)
@@ -21,7 +21,7 @@
  * \brief Implementation of Agents (proxy channel)
  *
  * This file is the implementation of Agents modules.
- * It is a dynamic module that is loaded by Asterisk. 
+ * It is a dynamic module that is loaded by Asterisk.
  * \par See also
  * \arg \ref Config_agent
  *
@@ -96,8 +96,10 @@
 "Asks the agent to login to the system with callback.\n"
 "The agent's callback extension is called (optionally with the specified\n"
 "context).\n"
+"You can specify the password with the AgentNo in the form: AgentNo#Password.\n"
 "The option string may contain zero or more of the following characters:\n"
-"      's' -- silent login - do not announce the login ok segment agent logged in/off\n";
+"      's' -- silent login - do not announce the login ok segment agent logged in/off\n"
+"      'i' -- silent login + ignore errors - not trying to interact with the user on errors\n";
 
 static const char descrip3[] =
 "  AgentMonitorOutgoing([options]):\n"
@@ -117,6 +119,7 @@
 "	      agentid is not known.\n"
 "             It's handy if you want to have one context for agent and non-agent calls.\n";
 
+
 static const char mandescr_agents[] =
 "Description: Will list info about all possible agents.\n"
 "Variables: NONE\n";
@@ -729,7 +732,7 @@
 	char buf[AST_MAX_BUF];
 
 	/* if there is no Caller ID, nothing to do */
-	if (ast_strlen_zero(callerid))
+	if (!callerid || ast_strlen_zero(callerid))
 		return;
 
 	snprintf(buf, sizeof(buf), "%s_%s",GETAGENTBYCALLERID, callerid);
@@ -1329,8 +1332,15 @@
 					} else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
 						/* Adjustable agent */
 						p->chan = ast_request("Local", format, p->loginchan, cause);
-						if (p->chan)
+						if (p->chan) {	
 							chan = agent_new(p, AST_STATE_DOWN);
+							manager_event(EVENT_FLAG_AGENT, "AgentLocalRelation",
+								"Channel: %s\r\n"
+								"LocalChannel: %s\r\n"
+								"Uniqueid: %s\r\n"
+								"LocalUniqueid: %s\r\n",
+								p->owner->name, p->chan->name, p->owner->uniqueid, p->chan->uniqueid);
+						}
 					}
 					if (chan) {
 						ast_mutex_unlock(&p->lock);
@@ -1394,7 +1404,7 @@
 	char *talkingtoChan = NULL;
 	char *status = NULL;
 
-	if (!ast_strlen_zero(id))
+	if (id && !ast_strlen_zero(id))
 		snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
 	astman_send_ack(s, m, "Agents will follow");
 	ast_mutex_lock(&agentlock);
@@ -1532,7 +1542,7 @@
 	int soft;
 	int ret; /* return value of agent_logoff */
 
-	if (ast_strlen_zero(agent)) {
+	if (!agent || ast_strlen_zero(agent)) {
 		astman_send_error(s, m, "No agent specified");
 		return 0;
 	}
@@ -1676,7 +1686,7 @@
 	struct localuser *u;
 	int login_state = 0;
 	char user[AST_MAX_AGENT] = "";
-	char pass[AST_MAX_AGENT];
+	char pass[AST_MAX_AGENT] = "";
 	char agent[AST_MAX_AGENT] = "";
 	char xpass[AST_MAX_AGENT] = "";
 	char *errmsg;
@@ -1692,7 +1702,8 @@
 	char agent_goodbye[AST_MAX_FILENAME_LEN];
 	int update_cdr = updatecdr;
 	char *filename = "agent-loginok";
-
+	int ignoreerrors = 0;
+	
 	LOCAL_USER_ADD(u);
 
 	if (!(parse = ast_strdupa(data))) {
@@ -1741,16 +1752,30 @@
 		if (strchr(args.options, 's')) {
 			play_announcement = 0;
 		}
+		if (strchr(args.options, 'i')) {
+	    	ignoreerrors = 1;
+	    	play_announcement = 0;
+		}
 	}
-
+			
 	if (chan->_state != AST_STATE_UP)
 		res = ast_answer(chan);
 	if (!res) {
 		if (!ast_strlen_zero(args.agent_id))
+		{
 			ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
+		
+			if ( strchr(user,'#') )
+			{
+			    strcpy(pass,strchr(user,'#')+1);
+			    *(strchr(user,'#')) = 0;
+			    max_login_tries=1;
+			}
+		}
 		else
-			res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
+			res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);		
 	}
+	
 	while (!res && (max_login_tries==0 || tries < max_login_tries)) {
 		tries++;
 		/* Check for password */
@@ -1764,7 +1789,10 @@
 		ast_mutex_unlock(&agentlock);
 		if (!res) {
 			if (!ast_strlen_zero(xpass))
-				res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
+			{
+			    if ( ast_strlen_zero(pass) )
+						res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
+			}
 			else
 				pass[0] = '\0';
 		}
@@ -1829,9 +1857,13 @@
 							if (!ast_strlen_zero(args.extension)) {
 								ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
 								res = 0;
-							} else
-								res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
-							if (ast_strlen_zero(tmpchan) || ast_exists_extension(chan, !ast_strlen_zero(context) ? context : "default", tmpchan,
+							} else {
+	    							if ( !ignoreerrors )
+									    res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
+								else
+								    res = 0;
+							}
+							if (ast_strlen_zero(tmpchan) || ast_exists_extension(chan, context && !ast_strlen_zero(context) ? context : "default", tmpchan,
 													     1, NULL))
 								break;
 							if (args.extension) {
@@ -1840,16 +1872,19 @@
 								pos = 0;
 							} else {
 								ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, !ast_strlen_zero(context) ? context : "default", p->agent);
-								res = ast_streamfile(chan, "invalid", chan->language);
-								if (!res)
-									res = ast_waitstream(chan, AST_DIGIT_ANY);
-								if (res > 0) {
-									tmpchan[0] = res;
-									tmpchan[1] = '\0';
-									pos = 1;
-								} else {
-									tmpchan[0] = '\0';
-									pos = 0;
+								if ( ignoreerrors )
+								{
+							    res = ast_streamfile(chan, "invalid", chan->language);
+							    if (!res)
+										res = ast_waitstream(chan, AST_DIGIT_ANY);
+							    if (res > 0) {
+										tmpchan[0] = res;
+										tmpchan[1] = '\0';
+										pos = 1;
+							    } else {
+										tmpchan[0] = '\0';
+										pos = 0;
+							    }
 								}
 							}
 						}
@@ -2058,6 +2093,7 @@
 		if (!p)
 			ast_mutex_unlock(&agentlock);
 
+		if ( ignoreerrors ) break;
 		if (!res && (max_login_tries==0 || tries < max_login_tries))
 			res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
 	}
@@ -2101,7 +2137,7 @@
 				res = ast_safe_sleep(chan, 1000);
 		}
 	}
-
+	
 	LOCAL_USER_REMOVE(u);
 	
 	/* We should never get here if next priority exists when in callbackmode */
@@ -2181,7 +2217,7 @@
 		else
 			snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
 
-		if (!ast_strlen_zero(wrapuptime_s)) {
+		if (wrapuptime_s && !ast_strlen_zero(wrapuptime_s)) {
 			p->wrapuptime = atoi(wrapuptime_s);
 			if (p->wrapuptime < 0)
 				p->wrapuptime = 0;
Index: channels/chan_iax2.c
===================================================================
--- channels/chan_iax2.c	(revision 7745)
+++ channels/chan_iax2.c	(working copy)
@@ -5557,8 +5557,10 @@
 	p->expire = -1;
 	/* Reset expiry value */
 	p->expiry = min_reg_expire;
-	if (!ast_test_flag(p, IAX_TEMPONLY))
+	if (!ast_test_flag(p, IAX_TEMPONLY)) {
 		ast_db_del("IAX/Registry", p->name);
+        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: RegistrationExpired\r\n", p->name);
+    }
 	register_peer_exten(p, 0);
 	ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
 	if (iax2_regfunk)
Index: apps/app_voicemail.c
===================================================================
--- apps/app_voicemail.c	(revision 7745)
+++ apps/app_voicemail.c	(working copy)
@@ -6642,6 +6642,10 @@
  				return 1;
 #endif
  		case '0':
+			if (!ast_test_flag(vmu, VM_OPERATOR)) {
+				cmd = '1';
+				return cmd;
+			}
 			if (message_exists || recorded) {
 				cmd = ast_play_and_wait(chan, "vm-saveoper");
 				if (!cmd)
Index: apps/app_queue.c
===================================================================
--- apps/app_queue.c	(revision 7745)
+++ apps/app_queue.c	(working copy)
@@ -280,6 +280,7 @@
 	int pos;			/*!< Where we are in the queue */
 	int prio;			/*!< Our priority */
 	int last_pos_said;              /*!< Last position we told the user */
+	int announce_count;             /*!< Number of times we made announce */
 	time_t last_periodic_announce_time;	/*!< The last time we played a periodic anouncement */
 	time_t last_pos;                /*!< Last time we told the user their position */
 	int opos;			/*!< Where we started in the queue */
@@ -310,6 +311,7 @@
 
 struct ast_call_queue {
 	ast_mutex_t lock;	
+
 	char name[80];			/*!< Name */
 	char moh[80];			/*!< Music On Hold class to be used */
 	char announce[80];		/*!< Announcement to play when call is answered */
@@ -328,6 +330,7 @@
 		unsigned int realtime:1;
 	int announcefrequency;          /*!< How often to announce their position */
 	int periodicannouncefrequency;	/*!< How often to play periodic announcement */
+	int firstannouncedelay;			/*!< Delay after the first announce */
 	int roundingseconds;            /*!< How many seconds do we round to? */
 	int holdtime;                   /*!< Current avg holdtime, based on recursive boxcar filter */
 	int callscompleted;             /*!< Number of queue calls completed */
@@ -335,6 +338,7 @@
 	int servicelevel;               /*!< seconds setting for servicelevel*/
 	int callscompletedinsl;         /*!< Number of calls answered with servicelevel*/
 	char monfmt[8];                 /*!< Format to use when recording calls */
+	char sound_first[80];			/*!< Sound file: an announcement only played once when no free member found (def. queue-first) */
 	char sound_next[80];            /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
 	char sound_thereare[80];        /*!< Sound file: "There are currently" (def. queue-thereare) */
 	char sound_calls[80];           /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
@@ -353,10 +357,12 @@
 	int retry;			/*!< Retry calling everyone after this amount of time */
 	int timeout;			/*!< How long to wait for an answer */
 	int weight;                     /*!< Respective weight */
+	int autopause;			/*!< Automatically pause a queue member when they do not answer */
 	
 	/* Queue strategy things */
 	int rrpos;			/*!< Round Robin - position */
 	int memberdelay;		/*!< Seconds to delay connecting member to caller */
+	int ffifo;			/*!< Fake fifo - force next call to top when first is in ringing */
 
 	struct member *members;		/*!< Head of the list of members */
 	struct queue_ent *head;		/*!< Head of the list of callers */
@@ -366,6 +372,8 @@
 static struct ast_call_queue *queues = NULL;
 AST_MUTEX_DEFINE_STATIC(qlock);
 
+static int set_member_paused(char *queuename, char *interface, int paused);
+
 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
 {
 	int i;
@@ -572,6 +580,8 @@
 	q->context[0] = '\0';
 	q->monfmt[0] = '\0';
 	q->periodicannouncefrequency = 0;
+	q->firstannouncedelay = 0;
+	ast_copy_string(q->sound_first, "queue-first", sizeof(q->sound_first));
 	ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
 	ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
 	ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
@@ -616,6 +626,8 @@
 		q->monjoin = ast_true(val);
 	} else if (!strcasecmp(param, "monitor-format")) {
 		ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
+	} else if (!strcasecmp(param, "queue-first")) {
+		ast_copy_string(q->sound_first, val, sizeof(q->sound_first));
 	} else if (!strcasecmp(param, "queue-youarenext")) {
 		ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
 	} else if (!strcasecmp(param, "queue-thereare")) {
@@ -660,12 +672,16 @@
 		ast_copy_string(q->sound_periodicannounce, val, sizeof(q->sound_periodicannounce));
 	} else if (!strcasecmp(param, "periodic-announce-frequency")) {
 		q->periodicannouncefrequency = atoi(val);
+	} else if (!strcasecmp(param, "first-announce-delay")) {
+		q->firstannouncedelay = atoi(val);	
 	} else if (!strcasecmp(param, "retry")) {
 		q->retry = atoi(val);
 		if (q->retry < 0)
 			q->retry = DEFAULT_RETRY;
 	} else if (!strcasecmp(param, "wrapuptime")) {
 		q->wrapuptime = atoi(val);
+	} else if (!strcasecmp(param, "autopause")) {
+		q->autopause = ast_true(val);
 	} else if (!strcasecmp(param, "maxlen")) {
 		q->maxlen = atoi(val);
 		if (q->maxlen < 0)
@@ -709,6 +725,8 @@
 		   we will not see any effect on use_weight until next reload. */
 	} else if (!strcasecmp(param, "timeoutrestart")) {
 		q->timeoutrestart = ast_true(val);
+	} else if (!strcasecmp(param, "fakefifo")) {
+		q->ffifo = ast_true(val);
 	} else if(failunknown) {
 		if (linenum >= 0) {
 			ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
@@ -1549,13 +1567,19 @@
 static int say_periodic_announcement(struct queue_ent *qe)
 {
 	int res = 0;
+	int delay=0;
 	time_t now;
 
 	/* Get the current time */
 	time(&now);
 
 	/* Check to see if it is time to announce */
-	if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
+	if ( qe->announce_count==0 && qe->parent->sound_first[0]!=0 )
+	    delay = qe->parent->firstannouncedelay;
+	else
+	    delay = qe->parent->periodicannouncefrequency;
+	    
+	if ((now - qe->last_periodic_announce_time) < delay)
 		return 0;
 
 	/* Stop the music on hold so we can play our own file */
@@ -1565,11 +1589,16 @@
 		ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
 
 	/* play the announcement */
-	res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
+	if ( qe->announce_count!=0 || qe->parent->sound_first[0]==0 )
+		res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
+	else
+		res = background_file(qe, qe->chan, qe->parent->sound_first);
+	qe->announce_count++;
 
 	/* Resume Music on Hold */
 	ast_moh_start(qe->chan, qe->moh);
 
+	time(&now);
 	/* update last_periodic_announce_time */
 	qe->last_periodic_announce_time = now;
 
@@ -1609,6 +1638,7 @@
 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
 {
 	char *queue = qe->parent->name;
+	char on[256] = "";
 	struct localuser *o;
 	int found;
 	int numlines;
@@ -1652,6 +1682,7 @@
 					peer = o;
 				}
 			} else if (o->chan && (o->chan == winner)) {
+				ast_copy_string(on, o->member->interface, sizeof(on));	
 				if (!ast_strlen_zero(o->chan->call_forward)) {
 					char tmpchan[256]="";
 					char *stuff;
@@ -1729,7 +1760,7 @@
 				if (f) {
 					if (f->frametype == AST_FRAME_CONTROL) {
 						switch(f->subclass) {
-		    			case AST_CONTROL_ANSWER:
+		    				case AST_CONTROL_ANSWER:
 							/* This is our guy if someone answered. */
 							if (!peer) {
 								if (option_verbose > 2)
@@ -1773,7 +1804,7 @@
 							if (!sentringing) {
 #if 0
 								ast_indicate(in, AST_CONTROL_RINGING);
-#endif								
+#endif
 								sentringing++;
 							}
 							break;
@@ -1797,6 +1828,7 @@
 				}
 			}
 			o = o->next;
+			
 		}
 		if (winner == in) {
 			f = ast_read(in);
@@ -1830,8 +1862,19 @@
 			}
 			ast_frfree(f);
 		}
-		if (!*to && (option_verbose > 2))
-			ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+		if (!*to) {
+			if (option_verbose > 2)
+					ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+			if (qe->parent->autopause) {
+				if (!set_member_paused(qe->parent->name, on, 1)) {
+					if (option_verbose > 2)	
+						ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",on, qe->parent->name);
+				} else {
+					if (option_verbose > 2)
+						ast_verbose( VERBOSE_PREFIX_3 "Pause failed on Queue Member %s in queue %s!\n",on, qe->parent->name);
+				}
+			}
+		}
 	}
 
 	return peer;
@@ -1853,7 +1896,10 @@
 	} else {
 		if (option_debug)
 			ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
-		res = 0;
+		if (!qe->parent->ffifo)
+			res = 0;
+		else
+			res = 1;
 	}
 	return res;
 }
@@ -2912,6 +2958,9 @@
 	qe.last_pos_said = 0;
 	qe.last_pos = 0;
 	qe.last_periodic_announce_time = time(NULL);
+	qe.announce_count=0;
+	if ( qe.chan->rings>=3 )
+	    qe.announce_count++;
 	if (!join_queue(queuename, &qe, &reason)) {
 		ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "",
 			      chan->cid.cid_num ? chan->cid.cid_num : "");
Index: doc/README.cdr
===================================================================
--- doc/README.cdr	(revision 7745)
+++ doc/README.cdr	(working copy)
@@ -94,6 +94,7 @@
 ${CDR(duration)}		Duration of the call.
 ${CDR(billsec)}			Duration of the call once it was answered.
 ${CDR(disposition)}		ANSWERED, NO ANSWER, BUSY
+${CDR(hangupcause)}		Channel hangup cause
 ${CDR(amaflags)}		DOCUMENTATION, BILL, IGNORE etc
 ${CDR(accountcode)}		The channel's account code.
 ${CDR(uniqueid)}		The channel's unique id.
Index: doc/README.variables
===================================================================
--- doc/README.variables	(revision 7745)
+++ doc/README.variables	(working copy)
@@ -778,6 +778,7 @@
 ${CDR(duration)}		Duration of the call.
 ${CDR(billsec)}			Duration of the call once it was answered.
 ${CDR(disposition)}		ANSWERED, NO ANSWER, BUSY
+${CDR(hangupcause)}		Channel hangup cause
 ${CDR(amaflags)}		DOCUMENTATION, BILL, IGNORE etc
 ${CDR(accountcode)}		The channel's account code.
 ${CDR(uniqueid)}		The channel's unique id.
Index: Makefile
===================================================================
--- Makefile	(revision 7745)
+++ Makefile	(working copy)
@@ -79,7 +79,7 @@
 #   *CLI> show memory allocations [filename]
 #   *CLI> show memory summary [filename]
 #
-MALLOC_DEBUG = #-include $(PWD)/include/asterisk/astmm.h
+MALLOC_DEBUG = -include $(PWD)/include/asterisk/astmm.h
 
 # Where to install asterisk after compiling
 # Default -> leave empty
Index: include/asterisk/cdr.h
===================================================================
--- include/asterisk/cdr.h	(revision 7745)
+++ include/asterisk/cdr.h	(working copy)
@@ -79,6 +79,8 @@
 	int billsec;				
 	/*! What happened to the call */
 	int disposition;			
+	/*! Why is the channel hanged up */
+	int hangupcause;
 	/*! What flags to use */
 	int amaflags;				
 	/*! What account number to use */
@@ -274,6 +276,13 @@
 extern int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield);
 extern int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield);
 
+/*! Set the CDR hangupcause variable 
+ *!
+ * \param chan Channel to set hangupcause for
+ * Copy the channel hangupcause to CDR(hangupcause)
+ * Returns nothing
+ */
+extern void ast_cdr_sethangupcause(struct ast_channel *chan);
 
 /* Update CDR on a channel */
 extern int ast_cdr_update(struct ast_channel *chan);
Index: configs/cdr_odbc.conf.sample
===================================================================
--- configs/cdr_odbc.conf.sample	(revision 7745)
+++ configs/cdr_odbc.conf.sample	(working copy)
@@ -7,6 +7,8 @@
 ;username=username
 ;password=password
 ;loguniqueid=yes
+;loguserfield=yes
+;loghangupcause=yes
 ;dispositionstring=yes
 ;table=cdr		;"cdr" is default table name
 ;usegmtime=no             ; set to "yes" to log in GMT
Index: codecs/gsm/Makefile
===================================================================
--- codecs/gsm/Makefile	(revision 7745)
+++ codecs/gsm/Makefile	(working copy)
@@ -30,7 +30,7 @@
 ######### define this, and read about the GSM_OPT_WAV49 option in the
 ######### manual page on gsm_option(3).
 
-#K6OPT	= -DK6OPT
+K6OPT	= -DK6OPT
 #K6OPT	=
 ######### Define to enable MMXTM optimizations for x86 architecture CPU's
 ######### which support MMX instructions.  This should be newer pentiums,
Index: cdr/cdr_odbc.c
===================================================================
--- cdr/cdr_odbc.c	(revision 7745)
+++ cdr/cdr_odbc.c	(working copy)
@@ -65,6 +65,8 @@
 static char *config = "cdr_odbc.conf";
 static char *dsn = NULL, *username = NULL, *password = NULL, *table = NULL;
 static int loguniqueid = 0;
+static int loguserfield = 0;
+static int loghangupcause = 0;
 static int usegmtime = 0;
 static int dispositionstring = 0;
 static int connected = 0;
@@ -84,8 +86,8 @@
 	short int ODBC_mlen;
 	int ODBC_res;
 	char ODBC_msg[200], ODBC_stat[10];
-	char sqlcmd[2048] = "", timestr[128];
-	int res = 0;
+	char sqlcmd[2048] = "", timestr[128], sqlsuffix[8];
+	int res = 0, paramnum = 0;
 	struct tm tm;
 
 	if (usegmtime) 
@@ -96,17 +98,27 @@
 	ast_mutex_lock(&odbc_lock);
 	strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
 	memset(sqlcmd,0,2048);
+	memset(sqlsuffix,0,8);
+
+	/* Create the first part of the query */
+	snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
+        "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
+        "duration,billsec,disposition,amaflags,accountcode", table);
+
 	if (loguniqueid) {
-		snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
-		"(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
-		"lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
-		"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table);
-	} else {
-		snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
-		"(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
-		"duration,billsec,disposition,amaflags,accountcode) "
-		"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table);
+		snprintf(sqlcmd + strlen(sqlcmd),sizeof(sqlcmd) - strlen(sqlcmd),",uniqueid");
+		snprintf(sqlsuffix + strlen(sqlsuffix),sizeof(sqlsuffix) - strlen(sqlsuffix),",?");
 	}
+	if (loguserfield) {
+		snprintf(sqlcmd + strlen(sqlcmd),sizeof(sqlcmd) - strlen(sqlcmd),",userfield");
+		snprintf(sqlsuffix + strlen(sqlsuffix),sizeof(sqlsuffix) - strlen(sqlsuffix),",?");
+	}
+	if (loghangupcause) {
+		snprintf(sqlcmd + strlen(sqlcmd),sizeof(sqlcmd) - strlen(sqlcmd),",hangupcause");
+		snprintf(sqlsuffix + strlen(sqlsuffix),sizeof(sqlsuffix) - strlen(sqlsuffix),",?");
+	}
+	/* add the last part */
+	snprintf(sqlcmd + strlen(sqlcmd),sizeof(sqlcmd) - strlen(sqlcmd),") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?%s)", sqlsuffix);
 
 	if (!connected) {
 		res = odbc_init();
@@ -145,28 +157,30 @@
 		return 0;
 	}
 
-	SQLBindParameter(ODBC_stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(timestr), 0, &timestr, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(timestr), 0, &timestr, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
 	if (dispositionstring)
-		SQLBindParameter(ODBC_stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
+		SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
 	else
-		SQLBindParameter(ODBC_stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 13, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
-	SQLBindParameter(ODBC_stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
+		SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
+	SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
 
-	if (loguniqueid) {
-		SQLBindParameter(ODBC_stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
-		SQLBindParameter(ODBC_stmt, 16, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
-	}
+	if (loguniqueid)
+		SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
+	if (loguserfield)
+		SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
+	if (loghangupcause)
+		SQLBindParameter(ODBC_stmt, ++paramnum, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->hangupcause, 0, NULL);
 
 	if (connected) {
 		res = odbc_do_query();
@@ -315,6 +329,32 @@
 		loguniqueid = 0;
 	}
 
+	tmp = ast_variable_retrieve(cfg,"global","loguserfield");
+	if (tmp) {
+		loguserfield = ast_true(tmp);
+		if (loguserfield) {
+			ast_log(LOG_DEBUG,"cdr_odbc: Logging userfield\n");
+		} else {
+			ast_log(LOG_DEBUG,"cdr_odbc: Not logging userfield\n");
+		}
+	} else {
+		ast_log(LOG_DEBUG,"cdr_odbc: Not logging userfield\n");
+		loguserfield = 0;
+	}
+
+	tmp = ast_variable_retrieve(cfg,"global","loghangupcause");
+	if (tmp) {
+		loghangupcause = ast_true(tmp);
+		if (loghangupcause) {
+			ast_log(LOG_DEBUG,"cdr_odbc: Logging hangupcause\n");
+		} else {
+			ast_log(LOG_DEBUG,"cdr_odbc: Not logging hangupcause\n");
+		}
+	} else {
+		ast_log(LOG_DEBUG,"cdr_odbc: Not logging hangupcause\n");
+		loghangupcause = 0;
+	}
+
 	tmp = ast_variable_retrieve(cfg,"global","usegmtime");
 	if (tmp) {
 		usegmtime = ast_true(tmp);
Index: cdr/cdr_manager.c
===================================================================
--- cdr/cdr_manager.c	(revision 7745)
+++ cdr/cdr_manager.c	(working copy)
@@ -124,13 +124,14 @@
 	    "Duration: %d\r\n"
 	    "BillableSeconds: %d\r\n"
 	    "Disposition: %s\r\n"
+	    "Hangup: %d\r\n"
 	    "AMAFlags: %s\r\n"
 	    "UniqueID: %s\r\n"
 	    "UserField: %s\r\n",
 	    cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel,
 	    cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime,
 	    cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), 
-	    ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield);
+	    cdr->hangupcause, ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield);
 	    	
 	return 0;
 }
Index: cdr/cdr_csv.c
===================================================================
--- cdr/cdr_csv.c	(revision 7745)
+++ cdr/cdr_csv.c	(working copy)
@@ -52,6 +52,7 @@
 
 /* #define CSV_LOGUNIQUEID 1 */
 /* #define CSV_LOGUSERFIELD 1 */
+/* #define CSV_LOGHANGUPCAUSE 1 */
 
 /*----------------------------------------------------
   The values are as follows:
@@ -188,6 +189,11 @@
 	/* append the user field */
 	append_string(buf, cdr->userfield,bufsize);	
 #endif
+#ifdef CSV_LOGHANGUPCAUSE
+	/* append the hangupcause field */
+	append_string(buf, cdr->hangupcause,bufsize);	
+#endif
+
 	/* If we hit the end of our buffer, log an error */
 	if (strlen(buf) < bufsize - 5) {
 		/* Trim off trailing comma */
Index: cdr/cdr_sqlite.c
===================================================================
--- cdr/cdr_sqlite.c	(revision 7745)
+++ cdr/cdr_sqlite.c	(working copy)
@@ -49,6 +49,7 @@
 
 #define LOG_UNIQUEID	0
 #define LOG_USERFIELD	0
+#define LOG_HANGUPCAUSE	0
 
 /* When you change the DATE_FORMAT, be sure to change the CHAR(19) below to something else */
 #define DATE_FORMAT "%Y-%m-%d %T"
@@ -84,6 +85,9 @@
 #if LOG_USERFIELD
 "	,userfield	VARCHAR(255)"
 #endif
+#if LOG_HANGUPCAUSE
+"	,hangupcause	INTEGER"
+#endif
 ");";
 
 static int sqlite_log(struct ast_cdr *cdr)
@@ -123,6 +127,9 @@
 #				if LOG_USERFIELD
 				",userfield"
 #				endif
+#				if LOG_HANGUPCAUSE
+				",hangupcause"
+#				endif
 			") VALUES ("
 				"'%q', '%q', '%q', '%q', "
 				"'%q', '%q', '%q', '%q', "
@@ -135,6 +142,9 @@
 #				if LOG_USERFIELD
 				",'%q'"
 #				endif
+#				if LOG_HANGUPCAUSE
+				",%d"
+#				endif
 			")", NULL, NULL, &zErr,
 				cdr->clid, cdr->src, cdr->dst, cdr->dcontext,
 				cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata,
@@ -147,6 +157,9 @@
 #				if LOG_USERFIELD
 				,cdr->userfield
 #				endif
+#				if LOG_HANGUPCAUSE
+				,cdr->hangupcause
+#				endif
 			);
 		if (res != SQLITE_BUSY && res != SQLITE_LOCKED)
 			break;
