diff --git a/CHANGES b/CHANGES
index 9b4f755d64..69834642ef 100644
--- a/CHANGES
+++ b/CHANGES
@@ -93,6 +93,13 @@ Application Changes
  * PrivacyManager now takes an option where you can specify a context where the 
    given number will be matched. This way you have more control over who is allowed
    and it stops the people who blindly enter 10 digits.
+ * ForkCDR has new options: 'a' updates the answer time on the new CDR; 'A' locks
+   answer times, disposition, on orig CDR against updates; 'D' Copies the disposition
+   from the orig CDR to the new CDR after reset; 'e' sets the 'end' time on the
+   original CDR; 'R' prevents the new CDR from being reset; 's(var=val)' adds/changes
+   the 'var' variable on the original CDR; 'T' forces ast_cdr_end(), ast_cdr_answer(),
+   _busy(), failed(), etc, to all obey the LOCKED flag on cdr's in the chain, and
+   also the ast_cdr_setvar() func.
 
 SIP Changes
 -----------
diff --git a/apps/app_forkcdr.c b/apps/app_forkcdr.c
index 8d01a829c4..4c6e620fb7 100644
--- a/apps/app_forkcdr.c
+++ b/apps/app_forkcdr.c
@@ -42,21 +42,82 @@ static char *synopsis =
 "Forks the Call Data Record";
 static char *descrip = 
 "  ForkCDR([options]):  Causes the Call Data Record to fork an additional\n"
-"cdr record starting from the time of the fork call\n"
+"cdr record starting from the time of the fork call. This new cdr record will\n"
+"be linked to end of the list of cdr records attached to the channel. The original CDR is\n"
+"has a LOCKED flag set, which forces most cdr operations to skip it, except\n"
+"for the functions that set the answer and end times, which ignore the LOCKED\n"
+"flag. This allows all the cdr records in the channel to be 'ended' together\n"
+"when the channel is closed.\n"
+"The CDR() func (when setting CDR values) normally ignores the LOCKED flag also,\n"
+"but has options to vary its behavior. The 'T' option (described below), can\n"
+"override this behavior, but beware the risks.\n"
+"\n"
+"Detailed Behavior Description:\n"
+"First, this app finds the last cdr record in the list, and makes\n"
+"a copy of it. This new copy will be the newly forked cdr record.\n"
+"Next, this new record is linked to the end of the cdr record list.\n"
+"Next, The new cdr record is RESET (unless you use an option to prevent this)\n"
+"This means that:\n"
+"   1. All flags are unset on the cdr record\n"
+"   2. the start, end, and answer times are all set to zero.\n"
+"   3. the billsec and duration fields are set to zero.\n"
+"   4. the start time is set to the current time.\n"
+"   5. the disposition is set to NULL.\n"
+"Next, unless you specified the 'v' option, all variables will be\n"
+"removed from the original cdr record. Thus, the 'v' option allows\n"
+"any CDR variables to be replicated to all new forked cdr records.\n"
+"Without the 'v' option, the variables on the original are effectively\n"
+"moved to the new forked cdr record.\n"
+"Next, if the 's' option is set, the provided variable and value\n"
+"are set on the original cdr record.\n"
+"Next, if the 'a' option is given, and the original cdr record has an\n"
+"answer time set, then the new forked cdr record will have its answer\n"
+"time set to its start time. If the old answer time were carried forward,\n"
+"the answer time would be earlier than the start time, giving strange\n"
+"duration and billsec times.\n"
+"Next, if the 'd' option was specified, the disposition is copied from\n"
+"the original cdr record to the new forked cdr.\n"
+"Next, if the 'D' option was specified, the destination channel field\n"
+"in the new forked CDR is erased.\n"
+"Next, if the 'e' option was specified, the 'end' time for the original\n"
+"cdr record is set to the current time. Future hang-up or ending events\n"
+"will not override this time stamp.\n"
+"Next, If the 'A' option is specified, the original cdr record will have\n"
+"it ANS_LOCKED flag set, which prevent future call dispostion events\n"
+"from updating the original cdr record's disposition. Normally, an\n"
+"'ANSWERED' event would mark all cdr records in the chain as 'ANSWERED'.\n"
+"Next, if the 'T' option is specified, the original cdr record will have\n"
+"its 'DONT_TOUCH' flag set, which will force the cdr_answer, cdr_end, and\n"
+"cdr_setvar functions to leave that cdr record alone.\n"
+"And, last but not least, the original cdr record has its LOCKED flag\n"
+"set. Almost all internal CDR functions (except for the funcs that set\n"
+"the end, and answer times, and set a variable) will honor this flag\n"
+"and leave a LOCKED cdr record alone.\n"
+"This means that the newly created forked cdr record will affected\n"
+"by events transpiring within Asterisk, with the previously noted\n"
+"exceptions.\n"
 "  Options:\n"
-"    a  - update the answer time on the NEW CDR just after it's been inited..\n"
+"    a - update the answer time on the NEW CDR just after it's been inited..\n"
 "         The new CDR may have been answered already, the reset that forkcdr.\n"
-"         does will erase the answer time. This will bring it back, but.\n"
+"         does will erase the answer time. This will bring it back, but\n"
 "         the answer time will be a copy of the fork/start time. It will.\n"
 "         only do this if the initial cdr was indeed already answered..\n"
-"    D -  Copy the disposition forward from the old cdr, after the .\n"
+"    A - Lock the original CDR against the answer time being updated.\n"
+"         This will allow the disposition on the original CDR to remain the same.\n"
+"    d - Copy the disposition forward from the old cdr, after the .\n"
 "         init..\n"
-"    d -  Clear the dstchannel on the new CDR after reset..\n"
-"    e -  end the original CDR. Do this after all the necc. data.\n"
+"    D - Clear the dstchannel on the new CDR after reset..\n"
+"    e - end the original CDR. Do this after all the necc. data.\n"
 "         is copied from the original CDR to the new forked CDR..\n"
 "    R -  do NOT reset the new cdr..\n"
 "    s(name=val) - Set the CDR var 'name' in the original CDR, with value.\n"
 "                  'val'.\n"
+"    T -  Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end\n"
+"          cdr funcs will obey this flag; normally they don't honor the LOCKED\n"
+"          flag set on the original CDR record.\n"
+"          Beware-- using this flag may cause CDR's not to have their end times\n"
+"          updated! It is suggested that if you specify this flag, you might\n"
+"          wish to use the 'e' flag as well!\n"
 "    v  - When the new CDR is forked, it gets a copy of the vars attached\n"
 "         to the current CDR. The vars attached to the original CDR are removed\n"
 "         unless this option is specified.\n";
@@ -70,6 +131,8 @@ enum {
 	OPT_NORESET =           (1 << 4),
 	OPT_KEEPVARS =          (1 << 5),
 	OPT_VARSET =            (1 << 6),
+	OPT_ANSLOCK =           (1 << 7),
+	OPT_DONTOUCH =          (1 << 8),
 };
 
 enum {
@@ -80,11 +143,13 @@ enum {
 
 AST_APP_OPTIONS(forkcdr_exec_options, {
 	AST_APP_OPTION('a', OPT_SETANS),
+	AST_APP_OPTION('A', OPT_ANSLOCK),
 	AST_APP_OPTION('d', OPT_SETDISP),
 	AST_APP_OPTION('D', OPT_RESETDEST),
 	AST_APP_OPTION('e', OPT_ENDCDR),
 	AST_APP_OPTION('R', OPT_NORESET),
 	AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET),
+	AST_APP_OPTION('T', OPT_DONTOUCH),
 	AST_APP_OPTION('v', OPT_KEEPVARS),
 });
 
@@ -132,6 +197,12 @@ static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, ch
 	if (ast_test_flag(&optflags, OPT_ENDCDR))
 		ast_cdr_end(cdr);
 
+	if (ast_test_flag(&optflags, OPT_ANSLOCK))
+		ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED);
+	
+	if (ast_test_flag(&optflags, OPT_DONTOUCH))
+		ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH);
+		
 	ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
 }
 
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index a1004e679c..3ea1a30083 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -40,11 +40,13 @@ enum {
 	OPT_RECURSIVE = (1 << 0),
 	OPT_UNPARSED = (1 << 1),
 	OPT_LAST = (1 << 2),
+	OPT_SKIPLOCKED = (1 << 3),
 } cdr_option_flags;
 
 AST_APP_OPTIONS(cdr_func_options, {
 	AST_APP_OPTION('l', OPT_LAST),
 	AST_APP_OPTION('r', OPT_RECURSIVE),
+	AST_APP_OPTION('s', OPT_SKIPLOCKED),
 	AST_APP_OPTION('u', OPT_UNPARSED),
 });
 
@@ -74,6 +76,10 @@ static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
 		while (cdr->next)
 			cdr = cdr->next;
 
+	if (ast_test_flag(&flags, OPT_SKIPLOCKED))
+		while (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED) && cdr->next)
+			cdr = cdr->next;
+
 	ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
 		       ast_test_flag(&flags, OPT_RECURSIVE),
 			   ast_test_flag(&flags, OPT_UNPARSED));
@@ -84,6 +90,7 @@ static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
 static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
 		     const char *value)
 {
+	struct ast_cdr *cdr = chan ? chan->cdr : NULL;
 	struct ast_flags flags = { 0 };
 	AST_DECLARE_APP_ARGS(args,
 			     AST_APP_ARG(variable);
@@ -93,19 +100,26 @@ static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
 	if (ast_strlen_zero(parse) || !value || !chan)
 		return -1;
 
+	if (!cdr)
+		return -1;
+
 	AST_STANDARD_APP_ARGS(args, parse);
 
 	if (!ast_strlen_zero(args.options))
 		ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
 
-	if (!strcasecmp(args.variable, "accountcode"))
+	if (ast_test_flag(&flags, OPT_LAST))
+		while (cdr->next)
+			cdr = cdr->next;
+
+	if (!strcasecmp(args.variable, "accountcode"))  /* the 'l' flag doesn't apply to setting the accountcode, userfield, or amaflags */
 		ast_cdr_setaccount(chan, value);
 	else if (!strcasecmp(args.variable, "userfield"))
 		ast_cdr_setuserfield(chan, value);
 	else if (!strcasecmp(args.variable, "amaflags"))
 		ast_cdr_setamaflags(chan, value);
-	else if (chan->cdr)
-		ast_cdr_setvar(chan->cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE));
+	else
+		ast_cdr_setvar(cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE));
 		/* No need to worry about the u flag, as all fields for which setting
 		 * 'u' would do anything are marked as readonly. */
 
@@ -122,6 +136,8 @@ static struct ast_custom_function cdr_function = {
 "Options:\n"
 "  'l' uses the most recent CDR on a channel with multiple records\n"
 "  'r' searches the entire stack of CDRs on the channel\n"
+"  's' skips any CDR's that are marked 'LOCKED' due to forkCDR() calls.\n"
+"      (on setting/writing CDR vars only)\n"
 "  'u' retrieves the raw, unprocessed value\n"
 "  For example, 'start', 'answer', and 'end' will be retrieved as epoch\n"
 "  values, when the 'u' option is passed, but formatted as YYYY-MM-DD HH:MM:SS\n"
@@ -139,6 +155,8 @@ static struct ast_custom_function cdr_function = {
 "  a name not on the above list, and create your own\n"
 "  variable, whose value can be changed with this function,\n"
 "  and this variable will be stored on the cdr.\n"
+"  For setting CDR values, the 'l' flag does not apply to\n"
+"  setting the accountcode, userfield, or amaflags.\n"
 "   raw values for disposition:\n"
 "       1 = NO ANSWER\n"
 "       2 = BUSY\n"
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index fbab3f53fe..addef04eaa 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -24,11 +24,13 @@
 #define _ASTERISK_CDR_H
 
 #include <sys/time.h>
-#define AST_CDR_FLAG_KEEP_VARS			(1 << 0)
+#define AST_CDR_FLAG_KEEP_VARS		(1 << 0)
 #define AST_CDR_FLAG_POSTED			(1 << 1)
 #define AST_CDR_FLAG_LOCKED			(1 << 2)
 #define AST_CDR_FLAG_CHILD			(1 << 3)
-#define AST_CDR_FLAG_POST_DISABLED		(1 << 4)
+#define AST_CDR_FLAG_POST_DISABLED	(1 << 4)
+#define AST_CDR_FLAG_ANSLOCKED      (1 << 5)
+#define AST_CDR_FLAG_DONT_TOUCH     (1 << 6)
 #define AST_CDR_FLAG_POST_ENABLE                (1 << 5)
 
 /*! \name CDR Flags */
@@ -44,11 +46,11 @@
 /*@{ */
 #define AST_CDR_OMIT				(1)
 #define AST_CDR_BILLING				(2)
-#define AST_CDR_DOCUMENTATION			(3)
+#define AST_CDR_DOCUMENTATION		(3)
 /*@} */
 
 #define AST_MAX_USER_FIELD			256
-#define AST_MAX_ACCOUNT_CODE			20
+#define AST_MAX_ACCOUNT_CODE		20
 
 /* Include channel.h after relevant declarations it will need */
 #include "asterisk/channel.h"
@@ -196,12 +198,17 @@ void ast_cdr_answer(struct ast_cdr *cdr);
  * \brief A call wasn't answered 
  * \param cdr the cdr you wish to associate with the call
  * Marks the channel disposition as "NO ANSWER"
+ * Will skip CDR's in chain with ANS_LOCK bit set. (see
+ * forkCDR() application.
  */
 extern void ast_cdr_noanswer(struct ast_cdr *cdr);
 
 /*! 
  * \brief Busy a call 
  * \param cdr the cdr you wish to associate with the call
+ * Marks the channel disposition as "BUSY"
+ * Will skip CDR's in chain with ANS_LOCK bit set. (see
+ * forkCDR() application.
  * Returns nothing
  */
 void ast_cdr_busy(struct ast_cdr *cdr);
@@ -209,6 +216,9 @@ void ast_cdr_busy(struct ast_cdr *cdr);
 /*! 
  * \brief Fail a call 
  * \param cdr the cdr you wish to associate with the call
+ * Marks the channel disposition as "FAILED"
+ * Will skip CDR's in chain with ANS_LOCK bit set. (see
+ * forkCDR() application.
  * Returns nothing
  */
 void ast_cdr_failed(struct ast_cdr *cdr);
diff --git a/main/cdr.c b/main/cdr.c
index 0a91f74fd5..bbc0c6ec45 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -306,6 +306,8 @@ int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int
 	}
 
 	for (; cdr; cdr = recur ? cdr->next : NULL) {
+		if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			continue;
 		headp = &cdr->varshead;
 		AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
 			if (!strcasecmp(ast_var_name(newvariable), name)) {
@@ -689,6 +691,10 @@ void ast_cdr_answer(struct ast_cdr *cdr)
 {
 
 	for (; cdr; cdr = cdr->next) {
+		if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) 
+			continue;
+		if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			continue;
 		check_post(cdr);
 		if (cdr->disposition < AST_CDR_ANSWERED)
 			cdr->disposition = AST_CDR_ANSWERED;
@@ -701,6 +707,10 @@ void ast_cdr_busy(struct ast_cdr *cdr)
 {
 
 	for (; cdr; cdr = cdr->next) {
+		if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) 
+			continue;
+		if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			continue;
 		if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
 			check_post(cdr);
 			if (cdr->disposition < AST_CDR_BUSY)
@@ -712,6 +722,11 @@ void ast_cdr_busy(struct ast_cdr *cdr)
 void ast_cdr_failed(struct ast_cdr *cdr)
 {
 	for (; cdr; cdr = cdr->next) {
+		if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) 
+			continue;
+		if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			continue;
+		check_post(cdr);
 		if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
 			check_post(cdr);
 			if (cdr->disposition < AST_CDR_FAILED)
@@ -725,6 +740,10 @@ void ast_cdr_noanswer(struct ast_cdr *cdr)
 	char *chan; 
 
 	while (cdr) {
+		if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) 
+			continue;
+		if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			continue;
 		if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
 			chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
 			if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
@@ -847,6 +866,8 @@ int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
 void ast_cdr_end(struct ast_cdr *cdr)
 {
 	for ( ; cdr ; cdr = cdr->next) {
+		if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+			continue;
 		check_post(cdr);
 		if (ast_tvzero(cdr->end))
 			cdr->end = ast_tvnow();