aa419c5
From: Liviu Chircu <liviu@opensips.org>
aa419c5
Date: Fri, 12 Feb 2016 17:12:57 +0200
aa419c5
Subject: [PATCH] Improve qvalue parsing
aa419c5
aa419c5
* accept qvalues with no decimal digits (e.g. "1." and "0.")
aa419c5
  (this removes E_Q_DEC_MISSING from error.h)
aa419c5
* improve bad qvalue error reporting
aa419c5
aa419c5
(cherry picked from commit ed60363d9d66bf230872c535e3fc0cf7f117b2c7)
aa419c5
aa419c5
Conflicts:
aa419c5
	cfg.y
aa419c5
aa419c5
diff --git a/cfg.y b/cfg.y
aa419c5
index b969ce0..8b67517 100644
aa419c5
--- a/cfg.y
aa419c5
+++ b/cfg.y
aa419c5
@@ -132,7 +132,7 @@ extern int yylex();
aa419c5
 static void yyerror(char* s);
aa419c5
 static void yyerrorf(char* fmt, ...);
aa419c5
 static char* tmp;
aa419c5
-static int i_tmp;
aa419c5
+static int i_tmp, rc;
aa419c5
 static void* cmd_tmp;
aa419c5
 static struct socket_id* lst_tmp;
aa419c5
 static int rt;  /* Type of route block for find_export */
aa419c5
@@ -2751,13 +2751,18 @@ cmd:	 FORWARD LPAREN STRING RPAREN	{ mk_action2( $$, FORWARD_T,
aa419c5
 		| STRIP error { $$=0; yyerror("missing '(' or ')' ?"); }
aa419c5
 		| STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, "
aa419c5
 														"number expected"); }
aa419c5
-		| APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN { 
aa419c5
-				{   qvalue_t q;
aa419c5
-				if (str2q(&q, $5, strlen($5)) < 0) {
aa419c5
-					yyerror("bad argument, q value expected");
aa419c5
-				}
aa419c5
+		| APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN {
aa419c5
+			{
aa419c5
+				qvalue_t q;
aa419c5
+
aa419c5
+				rc = str2q(&q, $5, strlen($5));
aa419c5
+				if (rc < 0)
aa419c5
+					yyerrorf("bad qvalue (%.*s): %s",
aa419c5
+							 strlen($5), $5, qverr2str(rc));
aa419c5
+
aa419c5
 				mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, $3,
aa419c5
-						(void *)(long)q); } 
aa419c5
+						(void *)(long)q);
aa419c5
+			}
aa419c5
 		}
aa419c5
 		| APPEND_BRANCH LPAREN STRING RPAREN { mk_action2( $$, APPEND_BRANCH_T,
aa419c5
 						STR_ST, NUMBER_ST, $3, (void *)Q_UNSPECIFIED) ; }
aa419c5
diff --git a/error.c b/error.c
aa419c5
index 9042be3..715863b 100644
aa419c5
--- a/error.c
aa419c5
+++ b/error.c
aa419c5
@@ -105,10 +105,6 @@ int err2reason_phrase(
aa419c5
 			error_txt="q parameter too big";
aa419c5
 			*sip_error=-E_BAD_REQ;
aa419c5
 			break;
aa419c5
-		case E_Q_DEC_MISSING:
aa419c5
-			error_txt="Decimal part missing in q";
aa419c5
-			*sip_error=-E_BAD_REQ;
aa419c5
-			break;
aa419c5
 		case E_NO_DESTINATION:
aa419c5
 			error_txt="No destination available";
aa419c5
 			*sip_error=-E_BAD_SERVER;
aa419c5
diff --git a/error.h b/error.h
aa419c5
index 87854ca..8e6f6a9 100644
aa419c5
--- a/error.h
aa419c5
+++ b/error.h
aa419c5
@@ -46,8 +46,7 @@
aa419c5
 #define E_Q_INV_CHAR        -15		/*!< Invalid character in q */
aa419c5
 #define E_Q_EMPTY           -16		/*!< Empty q */
aa419c5
 #define E_Q_TOO_BIG         -17		/*!< q too big (> 1) */
aa419c5
-#define E_Q_DEC_MISSING     -18		/*!< Decimal part missing */
aa419c5
-#define E_NO_DESTINATION    -19		/*!< No available destination */
aa419c5
+#define E_NO_DESTINATION    -18		/*!< No available destination */
aa419c5
 
aa419c5
 /* opensips specific error codes */
aa419c5
 #define E_IP_BLOCKED      -473		/*!< destination filtered */
aa419c5
diff --git a/modules/registrar/sip_msg.c b/modules/registrar/sip_msg.c
aa419c5
index 28ad6bb..e5d895d 100644
aa419c5
--- a/modules/registrar/sip_msg.c
aa419c5
+++ b/modules/registrar/sip_msg.c
aa419c5
@@ -281,12 +281,16 @@ void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e, struct save
aa419c5
  */
aa419c5
 int calc_contact_q(param_t* _q, qvalue_t* _r)
aa419c5
 {
aa419c5
+	int rc;
aa419c5
+
aa419c5
 	if (!_q || (_q->body.len == 0)) {
aa419c5
 		*_r = default_q;
aa419c5
 	} else {
aa419c5
-		if (str2q(_r, _q->body.s, _q->body.len) < 0) {
aa419c5
+		rc = str2q(_r, _q->body.s, _q->body.len);
aa419c5
+		if (rc < 0) {
aa419c5
 			rerrno = R_INV_Q; /* Invalid q parameter */
aa419c5
-			LM_ERR("invalid q parameter\n");
aa419c5
+			LM_ERR("invalid qvalue (%.*s): %s\n",
aa419c5
+					_q->body.len, _q->body.s, qverr2str(rc));
aa419c5
 			return -1;
aa419c5
 		}
aa419c5
 	}
aa419c5
diff --git a/modules/uac_redirect/rd_funcs.c b/modules/uac_redirect/rd_funcs.c
aa419c5
index ef6a923..329f2cb 100644
aa419c5
--- a/modules/uac_redirect/rd_funcs.c
aa419c5
+++ b/modules/uac_redirect/rd_funcs.c
aa419c5
@@ -106,7 +106,7 @@ static void sort_contacts(contact_t *ct_list, str *ct_array,
aa419c5
 {
aa419c5
 	param_t *q_para;
aa419c5
 	qvalue_t q;
aa419c5
-	int i,j;
aa419c5
+	int i, j, rc;
aa419c5
 	char backup;
aa419c5
 
aa419c5
 	for( ; ct_list ; ct_list = ct_list->next ) {
aa419c5
@@ -123,8 +123,10 @@ static void sort_contacts(contact_t *ct_list, str *ct_array,
aa419c5
 		if (q_para==0 || q_para->body.len==0) {
aa419c5
 			q = DEFAULT_Q_VALUE;
aa419c5
 		} else {
aa419c5
-			if (str2q( &q, q_para->body.s, q_para->body.len)!=0) {
aa419c5
-				LM_ERR("invalid q param\n");
aa419c5
+			rc = str2q( &q, q_para->body.s, q_para->body.len);
aa419c5
+			if (rc != 0) {
aa419c5
+				LM_ERR("invalid qvalue (%.*s): %s\n",
aa419c5
+						q_para->body.len, q_para->body.s, qverr2str(rc));
aa419c5
 				/* skip this contact */
aa419c5
 				continue;
aa419c5
 			}
aa419c5
diff --git a/qvalue.c b/qvalue.c
aa419c5
index 5cd2cd9..34029f3 100644
aa419c5
--- a/qvalue.c
aa419c5
+++ b/qvalue.c
aa419c5
@@ -75,6 +75,7 @@ int str2q(qvalue_t* q, char* s, int len)
aa419c5
 				break;
aa419c5
 
aa419c5
 			case '.':
aa419c5
+				*q = 0;
aa419c5
 				state = ST_0_PT;
aa419c5
 				break;
aa419c5
 
aa419c5
@@ -167,15 +168,8 @@ int str2q(qvalue_t* q, char* s, int len)
aa419c5
 		}
aa419c5
 	}
aa419c5
 
aa419c5
-	switch(state) {
aa419c5
-	case ST_START:
aa419c5
+	if (state == ST_START)
aa419c5
 		return E_Q_EMPTY;
aa419c5
 
aa419c5
-	case ST_0_PT:
aa419c5
-	case ST_1_PT:
aa419c5
-		return E_Q_DEC_MISSING;
aa419c5
-
aa419c5
-	default:
aa419c5
-		return 0;
aa419c5
-	}
aa419c5
+	return 0;
aa419c5
 }
aa419c5
diff --git a/qvalue.h b/qvalue.h
aa419c5
index 96aa81f..717844c 100644
aa419c5
--- a/qvalue.h
aa419c5
+++ b/qvalue.h
aa419c5
@@ -76,6 +76,10 @@ typedef int qvalue_t;
aa419c5
 #define Q_PREFIX "0."
aa419c5
 #define Q_PREFIX_LEN (sizeof(Q_PREFIX) - 1)
aa419c5
 
aa419c5
+#define qverr2str(rc) \
aa419c5
+	(rc == E_Q_INV_CHAR ? "bad characters" : \
aa419c5
+	 rc == E_Q_EMPTY ? "empty value" : \
aa419c5
+	 rc == E_Q_TOO_BIG ? "max value is 1.0" : "bad qvalue")
aa419c5
 
aa419c5
 
aa419c5
 /*! \brief