/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
* Author: Alexandre Westfahl <awestfahl@freediameter.net>						 *
*													 *
* Copyright (c) 2010, Alexandre Westfahl, Teraoka Laboratory (Keio University), and the WIDE Project. 	 *		
*													 *
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the Teraoka Laboratory nor the 							 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of Teraoka Laboratory 						 *
*   													 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/
#include "app_sip.h"

struct sess_state
{
	char *nonce;
};


int app_sip_MAR_cb( struct msg ** msg, struct avp * paramavp, struct session * sess, void * opaque, enum disp_action * act)
{
	struct msg *ans, *qry;
	struct avp *avp, *a2, *authdataitem;
	//struct msg_hdr * header = NULL;
	struct avp_hdr * avphdr=NULL, *avpheader=NULL, *avpheader_auth=NULL,*digestheader=NULL;
	union avp_value val;
	int found_cnonce=0;
	struct avp * tempavp=NULL,*sipAuthentication=NULL,*sipAuthenticate=NULL;
	char * result = NULL;
	char password[51];
	int idx=0, number_of_auth_items=0,i=0, ret=0;
	//Flags and variables for Database
	int sipurinotstored=0, authenticationpending=0; 
	size_t querylen=0, usernamelen=0;
	char *query=NULL;
	unsigned char *username=NULL;
	
	//The nonce we will store and retrieve in session
	struct sess_state *storednonce=NULL;
	
	
	TRACE_ENTRY("%p %p %p %p", msg, paramavp, sess, act);
	
	if (msg == NULL)
		return EINVAL;

	
	/* Create answer header */
	qry = *msg;
	CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
	ans = *msg;
	


	/* Add the appropriate command code & Auth-Application-Id 
	{
		
		CHECK_FCT( fd_msg_hdr ( *msg, &header ) );
		header->msg_flags = CMD_FLAG_PROXIABLE;
		header->msg_code = 286;
		header->msg_appl = 6;
		
		
		// Add the Auth-Application-Id 
		{
			CHECK_FCT( fd_msg_avp_new ( sip_dict.Auth_Application_Id, 0, &avp ) );
			ASSERT(avp);
			val.i32 = header->msg_appl;
			CHECK_FCT( fd_msg_avp_setvalue ( avp, &val ) );
			CHECK_FCT( fd_msg_avp_add ( ans, MSG_BRW_LAST_CHILD, avp) );
		}
	}*/
	
	
	/* Add the Auth-Session-State AVP */
	{
		
		CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.Auth_Session_State, &avp) );
		CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
		
		CHECK_FCT( fd_msg_avp_new ( sip_dict.Auth_Session_State, 0, &avp ) );
		CHECK_FCT( fd_msg_avp_setvalue( avp, avphdr->avp_value ) );
		CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, avp ) );
	}

	
	
	/* Check if method is REGISTER then User-Name must be present */
	{
		
		CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Method, &avp) );
		CHECK_FCT( fd_msg_avp_hdr( avp, &avpheader ));
		

		char *method=NULL;
		
		CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.User_Name, &avp) );
		if(avp!=NULL)
		{
			
			int not_found=1;
			MYSQL_RES *res=NULL;
			MYSQL_ROW row;
			
			CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
			
			
			
			
			if((strncmp((const char *)avpheader->avp_value->os.data,"REGISTER",avpheader->avp_value->os.len)==0))
			{
				not_found=1;
				
				//TODO TODO TODO TODO TODO TODO TODO: maybe doesn't work!!'
				ret=get_password(avphdr->avp_value->os.data, avphdr->avp_value->os.len, (char *)&password);
				
				if(ret>1)
				{
					//We couldn't make the request
					result="DIAMETER_UNABLE_TO_COMPLY";
					goto out;
				}
				not_found=ret;
				
				if(not_found)
				{
					TRACE_DEBUG(FULL,"The user %s doesn't exist!", avphdr->avp_value->os.data);
					result="DIAMETER_ERROR_USER_UNKNOWN";
					free(username);	
					goto out;
				}
			
			
				//We allocate the double size of username because at worst it can be all quotes
				username=malloc(avphdr->avp_value->os.len*2+1);
				//We purify username not to have forbidden characters
				usernamelen=mysql_real_escape_string(conn, (char *)username, (const char *)avphdr->avp_value->os.data, avphdr->avp_value->os.len);
				
				//Now that we know the user exist, we get the list of AOR owned by this user
				querylen=SQL_GETSIPAOR_LEN + usernamelen;
				query = malloc(querylen+2);
				snprintf(query, querylen+1, SQL_GETSIPAOR, username);
			
				//We make the query
				request_mysql(query);
				res=mysql_use_result(conn);
				if(res==NULL)
				{
					//We couldn't make the request
					result="DIAMETER_UNABLE_TO_COMPLY";
					goto out;
				}
			
				//We retrieve the SIP-AOR from AVP to check if the user can us it
				CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_AOR, &avp) );
				CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
			
				not_found=1;
				while ((row = mysql_fetch_row(res)) != NULL)
				{
					if(strncmp((const char *)avphdr->avp_value->os.data,row[0],avphdr->avp_value->os.len)==0)
					{
						not_found=0;
						break;
					}
				}
				mysql_free_result(res);
				free(query);
      				
				if(not_found)
				{
					TRACE_DEBUG(FULL,"The user %s can't use this SIP-AOR!",username);
					result="DIAMETER_ERROR_IDENTITIES_DONT_MATCH";
					free(username);	
					goto out;
				}
				
			}

			CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Server_URI, &avp) );
			CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr ));
			
			if(avphdr!=NULL)
			{
				unsigned char *sipuri=NULL;
				int sipurilen=0;

				//We allocate the double size of SIP-URI because at worst it can be all quotes
				CHECK_MALLOC(sipuri=malloc(avphdr->avp_value->os.len*2+1));
				//We purify SIP-URI not to have forbidden characters
				sipurilen=mysql_real_escape_string(conn, (char *)sipuri, (const char *)avphdr->avp_value->os.data, avphdr->avp_value->os.len);
				
				
				//We get the SIP-URI assignated to the user
				querylen=SQL_GETSIPURI_LEN + usernamelen;
				CHECK_MALLOC(query = malloc(querylen+2));
				snprintf(query, querylen+1, SQL_GETSIPURI, username);

				//We make the query
				request_mysql(query);
				res=mysql_use_result(conn);
				if(res==NULL)
				{
					//We couldn't make the request
					result="DIAMETER_UNABLE_TO_COMPLY";
					goto out;
				}
				not_found=1;
				while ((row = mysql_fetch_row(res)) != NULL)
				{
					if(strncmp((const char *)avphdr->avp_value->os.data,row[0],avphdr->avp_value->os.len)==0)
					{
						not_found=0;
						break;
					}
				}
				mysql_free_result(res);
				free(query);

				if(not_found)
				{
					//Temporary
					set_sipserver_uri(username, usernamelen, sipuri,sipurilen);
					
					
					set_pending_flag(username, usernamelen);
					
					
					authenticationpending=1;
				}
				free(sipuri);
				
			}
			else
				sipurinotstored=1;

		}
		else
		{
			result="DIAMETER_USER_NAME_REQUIRED";
			goto out;
		}

			
		free(method);

	}

	
	//TODO: remove loop for authdataitem because RFC say only one (wait for answer from Garcia)
	// How many Auth Data Items?
	CHECK_FCT( fd_msg_search_avp ( qry, sip_dict.SIP_Number_Auth_Items, &avp) );
	CHECK_FCT( fd_msg_avp_hdr( avp, &avpheader )  );
	
	
	if(avp!=NULL)
	{
		CHECK_FCT(fd_msg_search_avp ( qry, sip_dict.SIP_Auth_Data_Item, &avp));
		CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
		
		if(avp!=NULL)
		{
			//First is Authentication Scheme
			CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_FIRST_CHILD, &avp, NULL) );
			CHECK_FCT( fd_msg_avp_hdr( avp, &avphdr )  );
			
			//Digest-Authentication?
			if(avphdr->avp_value->i32==0)
			{
				
				for(idx=0;idx<avpheader->avp_value->i32;idx++)
				{
					//We look for SIP Auth items
					CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL) );
					
					if(avp!=NULL)
					{
						
						CHECK_FCT( fd_msg_avp_hdr( avp,&avphdr ));
		
						if(avphdr->avp_code==380) //We only create Auth-Data-Item to answer Auth-Data-Item
						{
							/* Add the Auth-Data-Item AVP */
							CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Auth_Data_Item, 0, &authdataitem ) );
					
							/* Add the Authentication Scheme AVP */
							{
								CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authentication_Scheme, 0, &a2 ) );
								val.i32=0; //We only know Digest Authentication
								CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
								CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, a2 ) );
							}
							
							//We need to know if there is a Cnonce attribute (only in the second MAR request)
							
							//CHECK_FCT(fd_msg_browse ( avp, MSG_BRW_WALK, &avp, NULL) );
							
							
							CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_CNonce, &a2 ));
							
							if(a2!=NULL)
								found_cnonce=1;
							else
								found_cnonce=0;
								
							if(!found_cnonce)
							{
								/*
								We are in the case of first access request so we need to challenge the user.
								*/
								TRACE_DEBUG(FULL,"First Authorization in progress...");

								/* Create a new session */ //this create a new session Id !!!
								//CHECK_FCT_DO( fd_sess_new( &sess, fd_g_config->cnf_diamid, "diamsip", 7), goto out );
								

								/* Create the SIP-Authenticate AVP */
								{
									CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authenticate, 0, &sipAuthenticate ) );
								}						

								/* Add the Digest QOP AVP */
								{
									CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_QOP, 0, &a2 ) );
									val.os.data=(unsigned char *)"auth";
									val.os.len=strlen((const char *)val.os.data);
									CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
									CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
								}
								/* Add the Digest Nonce AVP */
								{
									uint8_t buffer[NONCE_SIZE];
									char nonce[NONCE_SIZE * 2 + 1];
							
								
									gcry_create_nonce ((uint8_t *)buffer, sizeof(buffer));
							
									for(i=0;i<NONCE_SIZE;i++)
										sprintf(&nonce[2 * i], "%2.2hhx", buffer[i]);
								
									CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Nonce, 0, &a2 ) );
									
									
									//We store the nonce (storednonce structure) inside the session
									storednonce=malloc(sizeof(struct sess_state));
									memset(storednonce,0,sizeof(struct sess_state));
									CHECK_MALLOC(storednonce->nonce=malloc(NONCE_SIZE*2+1));
									memcpy(storednonce->nonce,(char *)nonce,NONCE_SIZE*2+1);
									CHECK_FCT( fd_sess_state_store ( ds_sess_hdl, sess, &storednonce ));  
									
									val.os.data=(unsigned char *)nonce;
									val.os.len=NONCE_SIZE * 2;
									
									CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
									CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
								}
								/* Add the Digest Algorithm AVP */
								{
									CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Algorithm, 0, &a2 ) );
									val.os.data=(unsigned char *)"MD5";
									val.os.len=strlen((const char *)val.os.data);
									CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
									CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
							
								}
								/* Add the Digest Realm AVP */
								{
									tempavp=avp;
								
									avpheader_auth=walk_digest(tempavp, 104);
									if(avpheader_auth!=NULL)
									{
										CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Realm, 0, &a2 ) );
										CHECK_FCT( fd_msg_avp_setvalue( a2, avpheader_auth->avp_value ) );
										CHECK_FCT( fd_msg_avp_add( sipAuthenticate, MSG_BRW_LAST_CHILD, a2 ) );
								
									}
								}
						
						
								//We add SIP Authenticate to Auth Data Item
								CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, sipAuthenticate ) );
								//We add Auth Data Item to Answer
								CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, authdataitem ) );
						
								number_of_auth_items++;
								if(sipurinotstored)
									result="DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED";
								else
									result="DIAMETER_MULTI_ROUND_AUTH";
								found_cnonce=0;
							}
							else
							{
								/*
								We are in the case of access request after challenge so we need to check credentials.
								*/
								TRACE_DEBUG(FULL,"Authentication after challenge");
								
								/* Search the session, retrieve its data */
								{
									//int new=0;
									
									//TRACE_DEBUG(FULL,"new: *%d*",new);
									//ASSERT( new == 0 );
									CHECK_FCT( fd_sess_state_retrieve( ds_sess_hdl, sess, &storednonce ));
									if(storednonce ==NULL)
									{
										result="DIAMETER_UNABLE_TO_COMPLY";
												
										if(username!=NULL)
											free(username);
										goto out;
									}
								}
								
								/* Create the SIP-Authentication-Info AVP */
								{
									CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Authentication_Info, 0, &sipAuthentication ) );
								}
								
								
							
								/* Add the Digest response Auth AVP */
								{
									//uint8_t bufferresp[DIGEST_LEN];
									//char response[DIGEST_LEN*2+1];
									
									
									//We extract all the data we need
									tempavp=avp;
									
									char * digest_username=NULL, *digest_uri=NULL, *digest_response=NULL, *digest_realm=NULL, *digest_nonce=NULL, *digest_method=NULL, *digest_qop=NULL, *digest_algorithm=NULL, *digest_cnonce=NULL, *digest_noncecount=NULL;
									
									
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Nonce, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											
											digest_nonce = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_nonce, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_nonce[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_nonce);	
											TRACE_DEBUG(FULL,"Stored Nonce:*%s*",storednonce->nonce);
											
											if(strcmp(digest_nonce,storednonce->nonce)!=0)
											{
												free(digest_nonce);
												free(storednonce->nonce);
												free(storednonce);
												result="DIAMETER_UNABLE_TO_COMPLY";
												
												if(username!=NULL)
													free(username);
												goto out;
											}
											
										}
											
									}
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Response, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_response = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_response, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_response[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_response);	
										}
										
									}
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Realm, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_realm = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_realm, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_realm[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_realm);	
										}
									}
									
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Method, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_method = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_method, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_method[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_method);	
										}
									}
									else
										digest_method="";
									
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_URI, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_uri = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_uri, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_uri[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_uri);	
										}
									}
									
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_QOP, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_qop = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_qop, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_qop[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_qop);	
										}
									}
									else
										digest_qop=NULL;
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Algorithm, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_algorithm = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_algorithm, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_algorithm[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_algorithm);	
										}
									}
									else
										digest_algorithm=NULL;
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_CNonce, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_cnonce = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_cnonce, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_cnonce[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_cnonce);	
										}
									}
									else
										digest_cnonce="";
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Nonce_Count, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_noncecount = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_noncecount, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_noncecount[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_noncecount);	
										}
									}
									else
										digest_noncecount="";
									CHECK_FCT(fd_msg_search_avp (avp, sip_dict.Digest_Username, &a2 ));
									if(a2!=NULL)
									{
										CHECK_FCT( fd_msg_avp_hdr( a2, &digestheader )  );
										if(digestheader!=NULL)
										{
											digest_username = malloc(digestheader->avp_value->os.len + 1);
											memcpy(digest_username, digestheader->avp_value->os.data,
											digestheader->avp_value->os.len);
											digest_username[digestheader->avp_value->os.len]='\0';
											TRACE_DEBUG(FULL,"Element:*%s*",digest_username);	
										}
									}
									//TODO: replace by authentication function
									
									HASHHEX HA1;
									HASHHEX HA2 = "";
									HASHHEX response, responseauth;

									
									DigestCalcHA1(digest_algorithm, digest_username, digest_realm, password, digest_nonce,digest_cnonce, HA1);
      								DigestCalcResponse(HA1, digest_nonce, digest_noncecount, digest_cnonce, digest_qop,digest_method, digest_uri, HA2, response);
      									
		
									// We check that the Digest-Response is the same (UA, Diameter)
									if(strcmp(response,digest_response)!=0)
									{
										TRACE_DEBUG(FULL,"Response calculated by Diameter server:%s",response);
										TRACE_DEBUG(FULL,"Response calculated by UA:%s",digest_response);
										TRACE_DEBUG(INFO,"Digest-Response does not match!");
										result="DIAMETER_UNABLE_TO_COMPLY";
										free(digest_algorithm);
										free(digest_cnonce);
										free(digest_nonce);
										free(digest_noncecount);
										free(digest_method);
										free(digest_username);
										free(digest_uri);
										free(digest_qop);
										free(digest_response);
										free(digest_realm);
										free(storednonce->nonce);
										free(storednonce);
										if(username!=NULL)
											free(username);
										goto out;
										
									}
									//We calculate Digest_Response_Auth
									DigestCalcResponseAuth(HA1, digest_nonce, digest_noncecount, digest_cnonce, digest_qop,digest_method, digest_uri, HA2, responseauth);
									
									TRACE_DEBUG(FULL,"Response calculated by Diameter server:%s",response);
										TRACE_DEBUG(FULL,"Response calculated by UA:%s",digest_response);
									if(strcmp(digest_qop,"auth-int")==0)
									{
										//Digest-HA1 MUST be used instead of Digest-Response-Auth if Digest-Qop is 'auth-int'.
										CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_HA1, 0, &a2 ) );
										val.os.data=(unsigned char *)HA1;
										val.os.len=HASHHEXLEN+1;
										CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
										CHECK_FCT( fd_msg_avp_add( sipAuthentication, MSG_BRW_LAST_CHILD, a2 ) );
									}
									else
									{
										//Digest-Response-Auth MUST be used instead of Digest-HA1 if Digest-Qop is 'auth'.
										CHECK_FCT( fd_msg_avp_new ( sip_dict.Digest_Response_Auth, 0, &a2 ) );
										val.os.data=(unsigned char *)responseauth;
										val.os.len=DIGEST_LEN*2;
										CHECK_FCT( fd_msg_avp_setvalue( a2, &val ) );
										CHECK_FCT( fd_msg_avp_add( sipAuthentication, MSG_BRW_LAST_CHILD, a2 ) );
									}
									free(digest_algorithm);
									free(digest_cnonce);
									free(digest_nonce);
									free(digest_noncecount);
									free(digest_method);
									free(digest_username);
									free(digest_uri);
									free(digest_qop);
									free(digest_response);
									free(digest_realm);
									free(storednonce->nonce);
									free(storednonce);
								
									number_of_auth_items++;
								}	
								
								
								//We add SIP Authentication-Info to Auth Data Item
								CHECK_FCT( fd_msg_avp_add( authdataitem, MSG_BRW_LAST_CHILD, sipAuthentication ) );
								//We add Auth Data Item to Answer
								CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, authdataitem ) );
								
								
								if(username!=NULL && authenticationpending)
								{
									//We clear the pending flag
									clear_pending_flag(username, usernamelen);
								}
								
								if(sipurinotstored)
									result="DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED";
								else
									result="DIAMETER_SUCCESS";
								found_cnonce=0;
							}
						}
					}
					else
						TRACE_DEBUG(INFO,"No auth data items!");
				}
				/*Add SIP_Number_Auth_Items  AVP */
				{
					CHECK_FCT( fd_msg_avp_new ( sip_dict.SIP_Number_Auth_Items, 0, &avp ) );
					val.i32 = number_of_auth_items;
					CHECK_FCT( fd_msg_avp_setvalue ( avp, &val ) );
					CHECK_FCT( fd_msg_avp_add ( ans, MSG_BRW_LAST_CHILD, avp) );
				}
				
				
			}
			else
			{
				TRACE_DEBUG(INFO,"We only support DIGEST for now, unable to comply");
				result="DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED";
				if(username!=NULL)
					free(username);
				goto out;
			}
		}
	}
	else
	{
		//TODO: remove this because Number_Auth_Items is not compulsory
		TRACE_DEBUG(FULL,"Number-Auth-Items is not included.");
		result="DIAMETER_UNABLE_TO_COMPLY";
		if(username!=NULL)
			free(username);
		goto out;
	}
	
	if(username!=NULL)
		free(username);
	

out:
	/* Set the Origin-Host, Origin-Realm, Result-Code AVPs */
	CHECK_FCT( fd_msg_rescode_set( ans, result, NULL, NULL, 1 ) );
	
	
	/* Send the answer */
	CHECK_FCT( fd_msg_send( msg, NULL, NULL ) );
	
	
	return 0;
}
