Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 714)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 715)
@@ -21,6 +21,13 @@
 
 #define THIS_FILE	"pjsua.c"
+#define NO_LIMIT	(int)0x7FFFFFFF
 
 //#define STEREO_DEMO
+
+/* Call specific data */
+struct call_data
+{
+    pj_timer_entry	    timer;
+};
 
 
@@ -41,4 +48,6 @@
     unsigned		    buddy_cnt;
     pjsua_buddy_config	    buddy_cfg[PJSUA_MAX_BUDDIES];
+
+    struct call_data	    call_data[PJSUA_MAX_CALLS];
 
     pj_pool_t		   *pool;
@@ -83,9 +92,9 @@
     puts  ("");
     puts  ("General options:");
+    puts  ("  --config-file=file  Read the config/arguments from file.");
     puts  ("  --help              Display this help screen");
     puts  ("  --version           Display version info");
     puts  ("");
     puts  ("Logging options:");
-    puts  ("  --config-file=file  Read the config/arguments from file.");
     puts  ("  --log-file=fname    Log to filename (default stderr)");
     puts  ("  --log-level=N       Set log max level to N (0(none) to 6(trace)) (default=5)");
@@ -145,7 +154,6 @@
     puts  ("  --max-calls=N       Maximum number of concurrent calls (default:4, max:255)");
     puts  ("  --thread-cnt=N      Number of worker threads (default:1)");
-    /*
     puts  ("  --duration=SEC      Set maximum call duration (default:no limit)");
-    */
+
     puts  ("");
     fflush(stdout);
@@ -168,5 +176,5 @@
     pjsua_transport_config_default(&cfg->rtp_cfg);
     cfg->rtp_cfg.port = 4000;
-    cfg->duration = (unsigned)-1;
+    cfg->duration = NO_LIMIT;
     cfg->wav_id = PJSUA_INVALID_ID;
     cfg->wav_port = PJSUA_INVALID_ID;
@@ -622,9 +630,9 @@
 	    }
 	    break;
+	*/
 
 	case OPT_DURATION:
 	    cfg->duration = my_atoi(pj_optarg);
 	    break;
-	*/
 
 	case OPT_THREAD_CNT:
@@ -708,5 +716,6 @@
 	    cfg->cfg.max_calls = my_atoi(pj_optarg);
 	    if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
-		PJ_LOG(1,(THIS_FILE,"Too many calls for max-calls (1-%d)",
+		PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
+				    "compile time limit (PJSUA_MAX_CALLS=%d)",
 			  PJSUA_MAX_CALLS));
 		return -1;
@@ -1043,5 +1052,5 @@
 
     /* Uas-duration. */
-    if (config->duration != (unsigned)-1) {
+    if (config->duration != NO_LIMIT) {
 	pj_ansi_sprintf(line, "--duration %d\n",
 			config->duration);
@@ -1172,4 +1181,31 @@
 
 
+/* Callback from timer when the maximum call duration has been
+ * exceeded.
+ */
+static void call_timeout_callback(pj_timer_heap_t *timer_heap,
+				  struct pj_timer_entry *entry)
+{
+    pjsua_call_id call_id = entry->id;
+    pjsua_msg_data msg_data;
+    pjsip_generic_string_hdr warn;
+    pj_str_t hname = pj_str("Warning");
+    pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    /* Add warning header */
+    pjsua_msg_data_init(&msg_data);
+    pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
+    pj_list_push_back(&msg_data.hdr_list, &warn);
+
+    /* Call duration has been exceeded; disconnect the call */
+    PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
+			 "for call %d, disconnecting the call",
+			 app_config.duration, call_id));
+    entry->id = PJSUA_INVALID_ID;
+    pjsua_call_hangup(call_id, 200, NULL, &msg_data);
+}
+
 
 /*
@@ -1186,4 +1222,13 @@
     if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
 
+	/* Cancel duration timer, if any */
+	if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
+	    struct call_data *cd = &app_config.call_data[call_id];
+	    pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
+
+	    cd->timer.id = PJSUA_INVALID_ID;
+	    pjsip_endpt_cancel_timer(endpt, &cd->timer);
+	}
+
 	PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]", 
 		  call_id,
@@ -1196,4 +1241,18 @@
 
     } else {
+
+	if (app_config.duration!=NO_LIMIT && 
+	    call_info.state == PJSIP_INV_STATE_CONFIRMED) 
+	{
+	    /* Schedule timer to hangup call after the specified duration */
+	    struct call_data *cd = &app_config.call_data[call_id];
+	    pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
+	    pj_time_val delay;
+
+	    cd->timer.id = call_id;
+	    delay.sec = app_config.duration;
+	    delay.msec = 0;
+	    pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
+	}
 
 	PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s", 
@@ -2365,4 +2424,10 @@
 #endif
 
+    /* Initialize calls data */
+    for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
+	app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
+	app_config.call_data[i].timer.cb = &call_timeout_callback;
+    }
+
     /* Optionally registers WAV file */
     if (app_config.wav_file.slen) {
Index: pjproject/trunk/pjsip/include/pjsip/sip_msg.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip/sip_msg.h	(revision 714)
+++ pjproject/trunk/pjsip/include/pjsip/sip_msg.h	(revision 715)
@@ -880,4 +880,5 @@
 				 const pj_str_t *hvalue);
 
+
 /**
  * Initialize a preallocated memory with the header structure. This function
@@ -903,4 +904,21 @@
 			       const pj_str_t *hvalue);
 
+
+/**
+ * Construct a generic string header without allocating memory from the pool.
+ * This function is useful to create a temporary header which life-time is
+ * very short (for example, creating the header in the stack to be passed
+ * as argument to a function which will copy the header).
+ *
+ * @param pool	    The pool.
+ * @param hname	    The header name to be assigned to the header, or NULL to
+ *		    assign the header name with some string.
+ * @param hvalue    Optional string to be assigned as the value.
+ *
+ * @return	    The header, or THROW exception.
+ */
+PJ_DECL(void) pjsip_generic_string_hdr_init2(pjsip_generic_string_hdr *h,
+					     pj_str_t *hname,
+					     pj_str_t *hvalue);
 
 
Index: pjproject/trunk/pjsip/src/pjsip/sip_msg.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_msg.c	(revision 714)
+++ pjproject/trunk/pjsip/src/pjsip/sip_msg.c	(revision 715)
@@ -506,4 +506,23 @@
 };
 
+
+PJ_DEF(void) pjsip_generic_string_hdr_init2(pjsip_generic_string_hdr *hdr,
+					    pj_str_t *hname,
+					    pj_str_t *hvalue)
+{
+    init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr);
+    if (hname) {
+	hdr->name = *hname;
+	hdr->sname = *hname;
+    }
+    if (hvalue) {
+	hdr->hvalue = *hvalue;
+    } else {
+	hdr->hvalue.ptr = NULL;
+	hdr->hvalue.slen = 0;
+    }
+}
+
+
 PJ_DEF(pjsip_generic_string_hdr*) 
 pjsip_generic_string_hdr_init( pj_pool_t *pool,
@@ -513,17 +532,19 @@
 {
     pjsip_generic_string_hdr *hdr = mem;
-
-    init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr);
+    pj_str_t dup_hname, dup_hval;
+
     if (hnames) {
-	pj_strdup(pool, &hdr->name, hnames);
-	hdr->sname = hdr->name;
-    }
+	pj_strdup(pool, &dup_hname, hnames);
+    } else {
+	dup_hname.slen = 0;
+    }
+
     if (hvalue) {
-	pj_strdup(pool, &hdr->hvalue, hvalue);
+	pj_strdup(pool, &dup_hval, hvalue);
     } else {
-	hdr->hvalue.ptr = NULL;
-	hdr->hvalue.slen = 0;
-    }
-
+	dup_hval.slen = 0;
+    }
+
+    pjsip_generic_string_hdr_init2(hdr, &dup_hname, &dup_hval);
     return hdr;
 }
