/* $Id$
 *
 * Copyright (C) 2006-2007 VozTelecom Sistemas S.L
 *
 * This file is part of openser, a free SIP server.
 *
 * openser is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * openser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * =====================================================================================
 * 
 *        Filename:  encode_digest.c
 * 
 *     Description:  functions to encode/decode/print Digest headers ([proxy,www]-[authenticate,require])
 * 
 *         Version:  1.0
 *         Created:  20/11/05 04:24:55 CET
 *        Revision:  none
 *        Compiler:  gcc
 * 
 *          Author:  Elias Baixas (EB), elias@conillera.net
 *         Company:  VozTele.com
 * 
 * =====================================================================================
 */
#define _GNU_SOURCE
#include <stdio.h>

#include "../../parser/digest/digest_parser.h"
#include "../../parser/parse_uri.h"
#include "../../parser/digest/digest.h"
#include "encode_digest.h"
#include "xaddress.h"
#include "encode_header.h"
#include "encode_uri.h"

#define HAS_NAME_F		0x01
#define HAS_REALM_F		0x02
#define HAS_NONCE_F		0x04
#define HAS_URI_F		0x08
#define HAS_RESPONSE_F		0x10
#define HAS_ALG_F		0x20
#define HAS_CNONCE_F		0x40
#define HAS_OPAQUE_F		0x80
#define HAS_QoP_F		0x01
#define HAS_NC_F		0x02
/*
 * encodes a digest header body.
 * encoding is:
 * 1: flags
 * HAS_NAME_F		0x01
 * HAS_REALM_F		0x02
 * HAS_NONCE_F		0x04
 * HAS_URI_F		0x08
 * HAS_RESPONSE_F	0x10
 * HAS_ALG_F		0x20
 * HAS_CNONCE_F		0x40
 * HAS_OPAQUE_F		0x80
 * 1: flags
 * HAS_QoP_F		0x01
 * HAS_NC_F		0x02
 * 2: hdr-start based pointer to where the scheme starts + length of the scheme (must be Digest).
 * 
 * for each field present, there are 2 bytes, one pointing the place where it starts, 
 * the next signaling how long this field is. The URI is a special case, and is composed of 1
 * byte telling how long is the URI structure, and then the encoded URI structure.
 */

int encode_digest(char *hdrstart,int hdrlen,dig_cred_t *digest,unsigned char *where)
{
   int i=2,j=0;/* 2*flags */
   unsigned char flags1=0,flags2=0;
   struct sip_uri sipuri;

   if(digest->username.whole.s && digest->username.whole.len){
      flags1|=HAS_NAME_F;
      where[i++]=(unsigned char)(digest->username.whole.s-hdrstart);
      where[i++]=(unsigned char)digest->username.whole.len;
   }
   if(digest->realm.s && digest->realm.len){
      flags1|=HAS_REALM_F;
      where[i++]=(unsigned char)(digest->realm.s-hdrstart);
      where[i++]=(unsigned char)digest->realm.len;
   }
   if(digest->nonce.s && digest->nonce.len){
      flags1|=HAS_NONCE_F;
      where[i++]=(unsigned char)(digest->nonce.s-hdrstart);
      where[i++]=(unsigned char)digest->nonce.len;
   }
   if(digest->uri.s && digest->uri.len){
      memset(&sipuri,0,sizeof(struct sip_uri));
      flags1|=HAS_URI_F;
      if (parse_uri(digest->uri.s, digest->uri.len,&sipuri) < 0 ) {
	 LOG(L_ERR, "Bad URI in address\n");
	 return -1;
      }else{
	 if((j=encode_uri2(hdrstart,hdrlen,digest->uri,&sipuri,&where[i+1]))<0){
	    LOG(L_ERR, "Error encoding the URI\n");
	    return -1;
	 }else{
	    where[i]=(unsigned char)j;
	    i+=(j+1);
	 }
      }
   }
   if(digest->response.s && digest->response.len){
      flags1|=HAS_RESPONSE_F;
      where[i++]=(unsigned char)(digest->response.s-hdrstart);
      where[i++]=(unsigned char)digest->response.len;
   }
   if(digest->alg.alg_str.s && digest->alg.alg_str.len){
      flags1|=HAS_ALG_F;
      where[i++]=(unsigned char)(digest->alg.alg_str.s-hdrstart);
      where[i++]=(unsigned char)digest->alg.alg_str.len;
   }
   if(digest->cnonce.s && digest->cnonce.len){
      flags1|=HAS_CNONCE_F;
      where[i++]=(unsigned char)(digest->cnonce.s-hdrstart);
      where[i++]=(unsigned char)digest->cnonce.len;
   }
   if(digest->opaque.s && digest->opaque.len){
      flags1|=HAS_OPAQUE_F;
      where[i++]=(unsigned char)(digest->opaque.s-hdrstart);
      where[i++]=(unsigned char)digest->opaque.len;
   }
   if(digest->qop.qop_str.s && digest->qop.qop_str.len){
      flags2|=HAS_QoP_F;
      where[i++]=(unsigned char)(digest->qop.qop_str.s-hdrstart);
      where[i++]=(unsigned char)digest->qop.qop_str.len;
   }
   if(digest->nc.s && digest->nc.len){
      flags2|=HAS_NC_F;
      where[i++]=(unsigned char)(digest->nc.s-hdrstart);
      where[i++]=(unsigned char)digest->nc.len;
   }
   where[0]=flags1;
   where[1]=flags2;
   return i;
}


int print_encoded_digest(int fd,char *hdr,int hdrlen,unsigned char* payload,int paylen,char *prefix)
{
   int i=2;/* flags + flags1 */
   unsigned char flags1,flags2;

   flags1=payload[0];
   flags2=payload[1];
   dprintf(fd,"%s",prefix);
   for(i=0;i<paylen;i++)
      dprintf(fd,"%s%d%s",i==0?"ENCODED DIGEST=[":":",payload[i],i==paylen-1?"]\n":"");
   i=2;
   if(flags1 & HAS_NAME_F){
      dprintf(fd,"%sDIGEST NAME=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags1& HAS_REALM_F){
      dprintf(fd,"%sDIGEST REALM=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags1& HAS_NONCE_F){
      dprintf(fd,"%sDIGEST NONCE=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags1& HAS_URI_F){
      if(print_encoded_uri(fd,&payload[i+1],payload[i],hdr,hdrlen,strcat(prefix,"  "))<0){
	 prefix[strlen(prefix)-2]=0;
	 dprintf(fd,"Error parsing encoded URI\n");
	 return -1;
      }
      i+=payload[i]+1;
   }
   if(flags1& HAS_RESPONSE_F){
      dprintf(fd,"%sDIGEST RESPONSE=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags1& HAS_ALG_F){
      dprintf(fd,"%sDIGEST ALGORITHM=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags1& HAS_CNONCE_F){
      dprintf(fd,"%sDIGEST CNONCE=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags1& HAS_OPAQUE_F){
      dprintf(fd,"%sDIGEST OPAQUE=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags2& HAS_QoP_F){
      dprintf(fd,"%sDIGEST QualityOfProtection=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   if(flags2& HAS_NC_F){
      dprintf(fd,"%sDIGEST NonceCount=[%.*s]\n",prefix,payload[i+1],&hdr[payload[i]]);
      i+=2;
   }
   return 0;
}


/**
 *
 */
int dump_digest_test(char *hdr,int hdrlen,unsigned char* payload,int paylen,int fd,char segregationLevel)
{
   int i=2;/* 2*flags */
   unsigned char flags1=0,flags2=0;

   flags1=payload[0];
   flags2=payload[1];
   if(!(segregationLevel & ONLY_URIS))
      return dump_standard_hdr_test(hdr,hdrlen,payload,paylen,fd);
   i=2;
   if(flags1 & HAS_NAME_F)
      i+=2;
   if(flags1 & HAS_REALM_F)
      i+=2;
   if(flags1 & HAS_NONCE_F)
      i+=2;
   if(flags1 & HAS_URI_F){
      if(!(segregationLevel & JUNIT) && (segregationLevel & ONLY_URIS))
	 return dump_standard_hdr_test(hdr,hdrlen,&payload[i+1],payload[i],fd);
      if((segregationLevel & JUNIT) && (segregationLevel & ONLY_URIS))
	 return print_uri_junit_tests(hdr,hdrlen,&payload[i+1],payload[i],fd,1,"");
   }
   return 0;
}