[SMS] implement GSM340_TP_VPF_ABSOLUTE

- Added function "gsm340_scts" to decode the service center time stamp
  into a UTC/GMT timestamp
- in function gsm340_validity_period: can now decode validity period
  format absolute.
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 6e59eaf..f941d90 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -216,10 +216,14 @@
 	return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
 }
 
+static time_t gsm340_scts(u_int8_t *scts);
+
 static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
 {
 	u_int8_t vp;
 	unsigned long minutes;
+	time_t expires;
+	time_t now;
 
 	switch (sms_vpf) {
 	case GSM340_TP_VPF_RELATIVE:
@@ -236,8 +240,12 @@
 		break;
 	case GSM340_TP_VPF_ABSOLUTE:
 		/* Chapter 9.2.3.12.2 */
-		/* FIXME: like service center time stamp */
-		DEBUGP(DSMS, "VPI absolute not implemented yet\n");
+		expires = gsm340_scts(sms_vp);
+		now = mktime(gmtime(NULL));
+		if (expires <= now)
+			minutes = 0;
+		else
+			minutes = (expires-now)/60;
 		break;
 	case GSM340_TP_VPF_ENHANCED:
 		/* Chapter 9.2.3.12.3 */
@@ -317,6 +325,7 @@
 	return len_in_bytes;
 }
 
+/* Turn int into semi-octet representation: 98 => 0x89 */
 static u_int8_t bcdify(u_int8_t value)
 {
 	u_int8_t ret;
@@ -327,6 +336,21 @@
 	return ret;
 }
 
+/* Turn semi-octet representation into int: 0x89 => 98 */
+static u_int8_t unbcdify(u_int8_t value)
+{
+	u_int8_t ret;
+
+	if ((value & 0x0F) > 9 || (value >> 4) > 9)
+		DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
+
+	ret = (value&0x0F)*10;
+	if (ret > 90)
+		ret += value>>4;
+
+	return ret;
+}
+
 /* Generate 03.40 TP-SCTS */
 static void gsm340_gen_scts(u_int8_t *scts, time_t time)
 {
@@ -338,7 +362,30 @@
 	*scts++ = bcdify(tm->tm_hour);
 	*scts++ = bcdify(tm->tm_min);
 	*scts++ = bcdify(tm->tm_sec);
-	*scts++ = 0;	/* FIXME: timezone */
+	*scts++ = bcdify(tm->tm_gmtoff/(60*15));
+}
+
+/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
+static time_t gsm340_scts(u_int8_t *scts)
+{
+	struct tm tm;
+
+	u_int8_t yr = unbcdify(*scts++);
+
+	if (yr <= 80)
+		tm.tm_year = 100 + yr;
+	else
+		tm.tm_year = yr;
+	tm.tm_mon  = unbcdify(*scts++) - 1;
+	tm.tm_mday = unbcdify(*scts++);
+	tm.tm_hour = unbcdify(*scts++);
+	tm.tm_min  = unbcdify(*scts++);
+	tm.tm_sec  = unbcdify(*scts++);
+	/* according to gsm 03.40 time zone is
+	   "expressed in quarters of an hour" */
+	tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
+
+	return mktime(&tm);
 }
 
 /* generate a msgb containing a TPDU derived from struct gsm_sms,