Index: freeDiameter/tests/testsess.c
===================================================================
--- freeDiameter/tests/testsess.c	(revision 5)
+++ freeDiameter/tests/testsess.c	(revision 6)
@@ -172,4 +172,34 @@
 	}
 	
+	/* Test fd_sess_reclaim */
+	{
+		struct mystate *tms;
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_reclaim( &sess1 ) );
+		CHECK( NULL, sess1 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		tms = new_state(TEST_SID, NULL);
+		CHECK( 0, fd_sess_state_store ( hdl1, sess1, &tms ) );
+		
+		CHECK( 0, fd_sess_reclaim( &sess1 ) );
+		CHECK( NULL, sess1 );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 0, new );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+		
+		CHECK( 0, fd_sess_fromsid( TEST_SID, strlen(TEST_SID), &sess1, &new ) );
+		CHECK( 1, new ? 1 : 0 );
+		
+		CHECK( 0, fd_sess_destroy( &sess1 ) );
+	}
+	
 	/* Test timeout function */
 	{
Index: include/freeDiameter/libfreeDiameter.h
===================================================================
--- include/freeDiameter/libfreeDiameter.h	(revision 5)
+++ include/freeDiameter/libfreeDiameter.h	(revision 6)
@@ -1309,8 +1309,8 @@
  *  ENOMEM	: Not enough memory to complete the operation
  */
-int fd_sess_handler_create_int ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) );
+int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) );
 /* Macro to avoid casting everywhere */
 #define fd_sess_handler_create( _handler, _cleanup ) \
-	fd_sess_handler_create_int( (_handler), (void (*)(char *, session_state *))(_cleanup) )
+	fd_sess_handler_create_internal( (_handler), (void (*)(char *, session_state *))(_cleanup) )
 
 /*
@@ -1438,4 +1438,20 @@
 int fd_sess_destroy ( struct session ** session );
 
+/*
+ * FUNCTION:	fd_sess_reclaim
+ *
+ * PARAMETERS:
+ *  session	: Pointer to a session object.
+ *
+ * DESCRIPTION: 
+ *   Destroys the resources of a session, only if no session_state is associated with it.
+ *
+ * RETURN VALUE:
+ *  0      	: The session no longer exists.
+ *  EINVAL 	: A parameter is invalid.
+ */
+int fd_sess_reclaim ( struct session ** session );
+
+
 
 
@@ -1459,7 +1475,7 @@
  *  ENOMEM	: Not enough memory to complete the operation
  */
-int fd_sess_state_store_int ( struct session_handler * handler, struct session * session, session_state ** state );
+int fd_sess_state_store_internal ( struct session_handler * handler, struct session * session, session_state ** state );
 #define fd_sess_state_store( _handler, _session, _state ) \
-	fd_sess_state_store_int( (_handler), (_session), (void *)(_state) )
+	fd_sess_state_store_internal( (_handler), (_session), (void *)(_state) )
 
 /*
@@ -1481,7 +1497,7 @@
  *  EINVAL 	: A parameter is invalid.
  */
-int fd_sess_state_retrieve_int ( struct session_handler * handler, struct session * session, session_state ** state ); 
+int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct session * session, session_state ** state ); 
 #define fd_sess_state_retrieve( _handler, _session, _state ) \
-	fd_sess_state_retrieve_int( (_handler), (_session), (void *)(_state) )
+	fd_sess_state_retrieve_internal( (_handler), (_session), (void *)(_state) )
 
 
@@ -1495,19 +1511,26 @@
 /*============================================================*/
 
-/* The dispatch process consists in passing a message to some handlers
- (typically provided by extensions) based on its content (app id, cmd code...) */
-
-/* The dispatch module has two main roles:
+/* Dispatch module (passing incoming messages to extensions registered callbacks)
+ * is split between the library and the daemon.
+ *
+ * The library provides the support for associating dispatch callbacks with
+ * dictionary objects.
+ *
+ * The daemon is responsible for calling the callbacks for a message when appropriate.
+ *
+ *
+ * The dispatch module has two main roles:
  *  - help determine if a message can be handled locally (during the routing step)
+ *        This decision involves only the application-id of the message.
  *  - pass the message to the callback(s) that will handle it (during the dispatch step)
  *
- * These are the possibilities for registering a callback:
+ * These are the possibilities for registering a so-called dispatch callback:
  *
  * -> For All messages.
  *  This callback is called for all messages that are handled locally. This should be used only
- *  internally by the daemon, or for debug purpose.
- *
- * -> by AVP value (constants).
- *  This callback will be called when a message is received and contains a certain AVP with a specified value.
+ *  for debug purpose.
+ *
+ * -> by AVP value (constants only).
+ *  This callback will be called when a message is received and contains an AVP with a specified enumerated value.
  *
  * -> by AVP.
@@ -1515,5 +1538,5 @@
  *
  * -> by command-code.
- *  This callback will be called when the message is a specific command.
+ *  This callback will be called when the message is a specific command (and 'R' flag).
  *
  * -> by application.
@@ -1521,19 +1544,83 @@
  *
  * ( by vendor: would this be useful? it may be added later)
- *
- * Note that several criteria may be selected at the same time, for example command-code AND application id.
- *
- * When a callback is called, it receives the message as parameter, and eventually a pointer to 
- * the AVP in the message when this is appropriate.
- *
- * The callback must process the message, and eventually create an answer to it. See the definition
- * bellow for more information.
- *
- * If no callback has handled the message, a default handler will be called with the effect of
- * requeuing the message for forwarding on the network to another peer (for requests, if possible), or 
- * discarding the message (for answers).
- */
-
-
+ */
+enum disp_how {
+	DISP_HOW_ANY = 1,		/* Any message. This should be only used for debug. */
+	DISP_HOW_APPID,			/* Any message with the specified application-id */
+	DISP_HOW_CC,			/* Messages of the specified command-code (request or answer). App id may be specified. */
+	DISP_HOW_AVP,			/* Messages containing a specific AVP. Command-code and App id may be specified. */
+	DISP_HOW_AVP_ENUMVAL		/* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */
+};
+/*
+ * Several criteria may be selected at the same time, for example command-code AND application id.
+ *
+ * If several callbacks are registered for the same object, their order is unspecified.
+ * The order in which the callbacks are called is:
+ *  DISP_HOW_ANY
+ *  DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP
+ *  DISP_HOW_CC
+ *  DISP_HOW_APPID
+ */
+
+/* When a callback is registered, a "when" argument is passed in addition to the disp_how value,
+ * to specify which values the criteria must match. */
+struct disp_when {
+	struct dict_object *	app_id;
+	struct dict_object *	command;
+	struct dict_object *	avp;
+	struct dict_object *	value;
+};
+
+/* Here is the details on this "when" argument, depending on the disp_how value.
+ *
+ * DISP_HOW_ANY.
+ *  In this case, "when" must be NULL.
+ *
+ * DISP_HOW_APPID.
+ *  Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION.
+ *
+ * DISP_HOW_CC.
+ *  The "command" field must be defined and point to a dictionary object of type DICT_COMMAND.
+ *  The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id.
+ *  The other fields are ignored.
+ *
+ * DISP_HOW_AVP.
+ *  The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP.
+ *  The "app_id" field may be set to restrict the messages matching to a specific app id.
+ *  The "command" field may also be set to a valid DICT_COMMAND object.
+ *  The content of the "value" field is ignored.
+ *
+ * DISP_HOW_AVP_ENUMVAL.
+ *  All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set
+ *  and points to a valid DICT_ENUMVAL object. 
+ *
+ * Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored )
+ *  field:     app_id    command     avp    value
+ * APPID :       M          0         0       0
+ * CC    :       m          M         0       0
+ * AVP   :       m          m         M       0
+ * ENUMVA:       m          m         M       M
+ */
+
+/* The callbacks that are registered have the following prototype:
+ *
+ *  int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, int * is_answer );
+ *
+ * FUNCTION:	dispatch_callback
+ *
+ * PARAMETERS:
+ *  msg 	: the received message on function entry. may be updated to answer on return (see description)
+ *  avp 	: for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP.
+ *  session	: if the message contains a Session-Id AVP, the corresponding session object.
+ *
+ * DESCRIPTION: 
+ *   Create a new AVP instance.
+ *
+ * RETURN VALUE:
+ *  0      	: The AVP is created.
+ *  EINVAL 	: A parameter is invalid.
+ *  (other standard errors may be returned, too, with their standard meaning. Example:
+ *    ENOMEM 	: Memory allocation for the new avp failed.)
+ */
 
 
Index: libfreeDiameter/sessions.c
===================================================================
--- libfreeDiameter/sessions.c	(revision 5)
+++ libfreeDiameter/sessions.c	(revision 6)
@@ -242,5 +242,5 @@
 
 /* Create a new handler */
-int fd_sess_handler_create_int ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) )
+int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) )
 {
 	struct session_handler *new;
@@ -565,8 +565,34 @@
 }
 
+/* Destroy a session if it is not used */
+int fd_sess_reclaim ( struct session ** session )
+{
+	struct session * sess;
+	
+	TRACE_ENTRY("%p", session);
+	CHECK_PARAMS( session && VALIDATE_SI(*session) );
+	
+	sess = *session;
+	*session = NULL;
+	
+	CHECK_FCT( pthread_mutex_lock( H_LOCK(sess->hash) ) );
+	CHECK_FCT( pthread_mutex_lock( &exp_lock ) );
+	if (FD_IS_LIST_EMPTY(&sess->states)) {
+		fd_list_unlink( &sess->chain_h );
+		fd_list_unlink( &sess->expire );
+		sess->eyec = 0xdead;
+		free(sess->sid);
+		free(sess);
+	}
+	CHECK_FCT( pthread_mutex_unlock( &exp_lock ) );
+	CHECK_FCT( pthread_mutex_unlock( H_LOCK(sess->hash) ) );
+	
+	return 0;
+}
+
 
 
 /* Save a state information with a session */
-int fd_sess_state_store_int ( struct session_handler * handler, struct session * session, session_state ** state )
+int fd_sess_state_store_internal ( struct session_handler * handler, struct session * session, session_state ** state )
 {
 	struct state *new;
@@ -617,5 +643,5 @@
 
 /* Get the data back */
-int fd_sess_state_retrieve_int ( struct session_handler * handler, struct session * session, session_state ** state )
+int fd_sess_state_retrieve_internal ( struct session_handler * handler, struct session * session, session_state ** state )
 {
 	struct fd_list * li;
