PageRenderTime 45ms CodeModel.GetById 15ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/pdu.c

http://datacard.googlecode.com/
C | 806 lines | 496 code | 65 blank | 245 comment | 80 complexity | 2c292146530be56a633c257a4ec6f264 MD5 | raw file
  1/*
  2   Copyright (C) 2010 bg <bg_one@mail.ru>
  3*/
  4#ifdef HAVE_CONFIG_H
  5#include <config.h>
  6#endif /* HAVE_CONFIG_H */
  7
  8#include <errno.h>			/* EINVAL ENOMEM E2BIG */
  9
 10#include "pdu.h"
 11#include "helpers.h"			/* dial_digit_code() */
 12#include "char_conv.h"			/* utf8_to_hexstr_ucs2() */
 13
 14/* SMS-SUBMIT format
 15	SCA		1..12 octet(s)		Service Center Address information element
 16	  octets
 17	        1		Length of Address (minimal 0)
 18	        2		Type of Address
 19	    3  12		Address
 20
 21	PDU-type	1 octet			Protocol Data Unit Type
 22	  bits
 23	      1 0 MTI	Message Type Indicator Parameter describing the message type 00 means SMS-DELIVER 01 means SMS-SUBMIT
 24				0 0	SMS-DELIVER (SMSC ==> MS)
 25						or
 26					SMS-DELIVER REPORT (MS ==> SMSC, is generated automatically by the MOBILE, after receiving a SMS-DELIVER)
 27
 28				0 1	SMS-SUBMIT (MS ==> SMSC)
 29						or
 30					SMS-SUBMIT REPORT (SMSC ==> MS)
 31
 32				1 0	SMS-STATUS REPORT (SMSC ==> MS)
 33						or
 34					SMS-COMMAND (MS ==> SMSC)
 35				1 1	Reserved
 36
 37		2 RD	Reject Duplicate
 38				0    Instruct the SMSC to accept an SMS-SUBMIT for an short message still
 39					held in the SMSC which has the same MR and
 40				1    Instruct the SMSC to reject an SMS-SUBMIT for an short message still
 41					held in the SMSC which has the same MR and DA as a previosly
 42					submitted short message from the same OA.
 43
 44	      4 3 VPF	Validity Period Format	Parameter indicating whether or not the VP field is present
 45	      			0 0	VP field is not present
 46	      			0 1	Reserved
 47	      			1 0	VP field present an integer represented (relative)
 48	      			1 1	VP field present an semi-octet represented (absolute)
 49
 50		5 SRR	Status Report Request Parameter indicating if the MS has requested a status report
 51				0	A status report is not requested
 52				1	A status report is requested
 53
 54		6 UDHI	User Data Header Indicator Parameter indicating that the UD field contains a header
 55				0	The UD field contains only the short message
 56				1	The beginning of the UD field contains a header in addition of the short message
 57		7 RP	Reply Path Parameter indicating that Reply Path exists
 58				0	Reply Path parameter is not set in this PDU
 59				1	Reply Path parameter is set in this PDU
 60
 61	MR		1 octet		Message Reference
 62						The MR field gives an integer (0..255) representation of a reference number of the SMSSUBMIT submitted to the SMSC by the MS.
 63						! notice: at the MOBILE the MR is generated automatically, -anyway you have to generate it a possible entry is for example ”00H” !
 64	DA		2-12 octets	Destination Address
 65	  octets
 66		1	Length of Address (of BCD digits!)
 67		2	Type of Address
 68	     3 12	Address
 69	PID		1 octet		Protocol Identifier
 70						The PID is the information element by which the Transport Layer either refers to the higher
 71						layer protocol being used, or indicates interworking with a certain type of telematic device.
 72						here are some examples of PID codings:
 73		00H: The PDU has to be treat as a short message
 74		41H: Replace Short Message Type1
 75		  ....
 76		47H: Replace Short Message Type7
 77					Another description:
 78		
 79		Bit7 bit6 (bit 7 = 0, bit 6 = 0)
 80		l 0 0 Assign bits 0..5, the values are defined as follows.
 81		l 1 0 Assign bits 0..5, the values are defined as follows.
 82		l 0 1 Retain
 83		l 1 1 Assign bits 0..5 for special use of SC
 84		Bit5 values:
 85		l 0: No interworking, but SME-to-SME protocol
 86		l 1: Telematic interworking (in this situation , value of bits4...0 is
 87		valid)
 88		Interface Description for HUAWEI EV-DO Data Card AT Commands
 89		All rights reserved Page 73 , Total 140
 90		Bit4...Bit0: telematic devices type identifier. If the value is 1 0 0 1 0, it
 91		indicates email. Other values are not supported currently.
 92		
 93		
 94	DCS		1 octet			Data Coding Scheme
 95		
 96	VP		0,1,7 octet(s)		Validity Period
 97	UDL		1 octet			User Data Length
 98	UD		0-140 octets		User Data
 99*/
100
101/* SMS-DELIVER format
102	SCA		1..12 octet(s)		Service Center Address information element
103	  octets
104	        1		Length of Address (minimal 0)
105	        2		Type of Address
106	    3  12		Address
107
108	PDU-type	1 octet			Protocol Data Unit Type
109	  bits
110	      1 0 MTI	Message Type Indicator Parameter describing the message type 00 means SMS-DELIVER 01 means SMS-SUBMIT
111				0 0	SMS-DELIVER (SMSC ==> MS)
112						or
113					SMS-DELIVER REPORT (MS ==> SMSC, is generated automatically by the MOBILE, after receiving a SMS-DELIVER)
114
115				0 1	SMS-SUBMIT (MS ==> SMSC)
116						or
117					SMS-SUBMIT REPORT (SMSC ==> MS)
118
119				1 0	SMS-STATUS REPORT (SMSC ==> MS)
120						or
121					SMS-COMMAND (MS ==> SMSC)
122				1 1	Reserved
123
124		2 MMS	More Messages to Send	Parameter indicating whether or not there are more messages to send
125				0 More messages are waiting for the MS in the SMSC
126				1 No more messages are waiting for the MS in the SMSC
127	      4 3 Reserved
128
129		5 SRI	Status Report Indication	Parameter indicating if the SME has requested a status report
130				0 A status report will not be returned to the SME
131				1 A status report will be returned to the SME
132				
133		6 UDHI	User Data Header Indicator Parameter indicating that the UD field contains a header
134				0	The UD field contains only the short message
135				1	The beginning of the UD field contains a header in addition of the short message
136		7 RP	Reply Path Parameter indicating that Reply Path exists
137				0	Reply Path parameter is not set in this PDU
138				1	Reply Path parameter is set in this PDU
139
140	OA		2-12 octets	Originator Address
141	  octets
142		1	Length of Address (of BCD digits!)
143		2	Type of Address
144	     3 12	Address
145	PID		1 octet		Protocol Identifier
146						The PID is the information element by which the Transport Layer either refers to the higher
147						layer protocol being used, or indicates interworking with a certain type of telematic device.
148						here are some examples of PID codings:
149		00H: The PDU has to be treat as a short message
150		41H: Replace Short Message Type1
151		  ....
152		47H: Replace Short Message Type7
153					Another description:
154		
155		Bit7 bit6 (bit 7 = 0, bit 6 = 0)
156		l 0 0 Assign bits 0..5, the values are defined as follows.
157		l 1 0 Assign bits 0..5, the values are defined as follows.
158		l 0 1 Retain
159		l 1 1 Assign bits 0..5 for special use of SC
160		Bit5 values:
161		l 0: No interworking, but SME-to-SME protocol
162		l 1: Telematic interworking (in this situation , value of bits4...0 is
163		valid)
164		Interface Description for HUAWEI EV-DO Data Card AT Commands
165		All rights reserved Page 73 , Total 140
166		Bit4...Bit0: telematic devices type identifier. If the value is 1 0 0 1 0, it
167		indicates email. Other values are not supported currently.
168		
169		
170	DCS		1 octet			Data Coding Scheme
171
172	SCTS		7 octets		Service Center Time Stamp
173	UDL		1 octet			User Data Length
174	UD		0-140 octets		User Data, may be prepended by User Data Header see UDHI flag
175	    octets
176	    	1 opt UDHL	Total number of Octets in UDH
177	    	? IEIa
178	    	? IEIDLa
179	    	? IEIDa
180	    	? IEIb
181		  ...
182*/
183
184#define NUMBER_TYPE_INTERNATIONAL		0x91
185
186/* Message Type Indicator Parameter */
187#define PDUTYPE_MTI_SHIFT			0
188#define PDUTYPE_MTI_SMS_DELIVER			(0x00 << PDUTYPE_MTI_SHIFT)
189#define PDUTYPE_MTI_SMS_DELIVER_REPORT		(0x00 << PDUTYPE_MTI_SHIFT)
190#define PDUTYPE_MTI_SMS_SUBMIT			(0x01 << PDUTYPE_MTI_SHIFT)
191#define PDUTYPE_MTI_SMS_SUBMIT_REPORT		(0x01 << PDUTYPE_MTI_SHIFT)
192#define PDUTYPE_MTI_SMS_STATUS_REPORT		(0x02 << PDUTYPE_MTI_SHIFT)
193#define PDUTYPE_MTI_SMS_COMMAND			(0x02 << PDUTYPE_MTI_SHIFT)
194#define PDUTYPE_MTI_RESERVED			(0x03 << PDUTYPE_MTI_SHIFT)
195
196#define PDUTYPE_MTI_MASK			(0x03 << PDUTYPE_MTI_SHIFT)
197#define PDUTYPE_MTI(pdutype)			((pdutype) & PDUTYPE_MTI_MASK)
198
199/* Reject Duplicate */
200#define PDUTYPE_RD_SHIFT			2
201#define PDUTYPE_RD_ACCEPT			(0x00 << PDUTYPE_RD_SHIFT)
202#define PDUTYPE_RD_REJECT			(0x01 << PDUTYPE_RD_SHIFT)
203
204/* Validity Period Format */
205#define PDUTYPE_VPF_SHIFT			3
206#define PDUTYPE_VPF_NOT_PRESENT			(0x00 << PDUTYPE_VPF_SHIFT)
207#define PDUTYPE_VPF_RESERVED			(0x01 << PDUTYPE_VPF_SHIFT)
208#define PDUTYPE_VPF_RELATIVE			(0x02 << PDUTYPE_VPF_SHIFT)
209#define PDUTYPE_VPF_ABSOLUTE			(0x03 << PDUTYPE_VPF_SHIFT)
210
211/* Status Report Request */
212#define PDUTYPE_SRR_SHIFT			5
213#define PDUTYPE_SRR_NOT_REQUESTED		(0x00 << PDUTYPE_SRR_SHIFT)
214#define PDUTYPE_SRR_REQUESTED			(0x01 << PDUTYPE_SRR_SHIFT)
215
216/* User Data Header Indicator */
217#define PDUTYPE_UDHI_SHIFT			6
218#define PDUTYPE_UDHI_NO_HEADER			(0x00 << PDUTYPE_UDHI_SHIFT)
219#define PDUTYPE_UDHI_HAS_HEADER			(0x01 << PDUTYPE_UDHI_SHIFT)
220#define PDUTYPE_UDHI_MASK			(0x01 << PDUTYPE_UDHI_SHIFT)
221#define PDUTYPE_UDHI(pdutype)			((pdutype) & PDUTYPE_UDHI_MASK)
222
223/* eply Path Parameter */
224#define PDUTYPE_RP_SHIFT			7
225#define PDUTYPE_RP_IS_NOT_SET			(0x00 << PDUTYPE_RP_SHIFT)
226#define PDUTYPE_RP_IS_SET			(0x01 << PDUTYPE_RP_SHIFT)
227
228#define PDU_MESSAGE_REFERENCE			0x00		/* assigned by MS */
229
230#define PDU_PID_SMS				0x00		/* bit5 No interworking, but SME-to-SME protocol = SMS */
231#define PDU_PID_EMAIL				0x32		/* bit5 Telematic interworking, bits 4..0 0x 12  = email */
232
233/* DCS */
234/*   bits 1..0 Class */
235#define PDU_DCS_CLASS_SHIFT			0
236#define PDU_DCS_CLASS0				(0x00 << PDU_DCS_CLASS_SHIFT)	/* Class 0, provides display and responds SC */
237#define PDU_DCS_CLASS1				(0x01 << PDU_DCS_CLASS_SHIFT)	/* Class 1, saves to MS (NV); or saves to the UIM card when MS is full */
238#define PDU_DCS_CLASS2				(0x02 << PDU_DCS_CLASS_SHIFT)	/* Class 2, dedicated for the UIM card The storage status is reported to SC after storage If the UIM card is full, failure and reason are reported to SC */
239#define PDU_DCS_CLASS3				(0x03 << PDU_DCS_CLASS_SHIFT)	/* Class 3, stored to TE, MS receives messages and does not sent to TE, but responds to SC */
240#define PDU_DCS_CLASS_MASK			(0x03 << PDU_DCS_CLASS_SHIFT)
241#define PDU_DCS_CLASS(dcs)			((dcs) & PDU_DCS_CLASS_MASK)
242
243/*   bits 3..2 Alpabet */
244#define PDU_DCS_ALPABET_SHIFT			2
245#define PDU_DCS_ALPABET_7BIT			(0x00 << PDU_DCS_ALPABET_SHIFT)
246#define PDU_DCS_ALPABET_8BIT			(0x01 << PDU_DCS_ALPABET_SHIFT)
247#define PDU_DCS_ALPABET_UCS2			(0x02 << PDU_DCS_ALPABET_SHIFT)
248#define PDU_DCS_ALPABET_MASK			(0x03 << PDU_DCS_ALPABET_SHIFT)
249#define PDU_DCS_ALPABET(dcs)			((dcs) & PDU_DCS_ALPABET_MASK)
250
251/*   bit 4 */
252#define PDU_DCS_BITS10_CTRL_SHIFT		4
253#define PDU_DCS_BITS10_RETAIN			(0x00 << PDU_DCS_BIT10_CTRL_SHIFT)
254#define PDU_DCS_BITS10_INUSE			(0x01 << PDU_DCS_BIT10_CTRL_SHIFT)
255#define PDU_DCS_BITS10_CTRL_MASK		(0x01 << PDU_DCS_BIT10_CTRL_SHIFT)
256#define PDU_DCS_BITS10_CTRL(dcs)		((dcs) & PDU_DCS_BITS10_CTRL_MASK)
257
258/*   bit 5 */
259#define PDU_DCS_COMPRESSION_SHIFT		5
260#define PDU_DCS_NOT_COMPESSED			(0x00 << PDU_DCS_COMPRESSION_SHIFT)
261#define PDU_DCS_COMPESSED			(0x01 << PDU_DCS_COMPRESSION_SHIFT)
262#define PDU_DCS_COMPRESSION_MASK		(0x01 << PDU_DCS_COMPRESSION_SHIFT)
263#define PDU_DCS_COMPRESSION(dcs)		((dcs) & PDU_DCS_COMPRESSION_MASK)
264
265/*   bit 7..6 */
266#define PDU_DCS_76_SHIFT			6
267#define PDU_DCS_76_00				(0x00 << PDU_DCS_76_SHIFT)
268#define PDU_DCS_76_MASK				(0x03 << PDU_DCS_76_SHIFT)
269#define PDU_DCS_76(dcs)				((dcs) & PDU_DCS_76_MASK)
270
271#define ROUND_UP2(x)		(((x) + 1) & (0xFFFFFFFF << 1))
272#define LENGTH2OCTETS(x)	(((x) + 1)/2)
273
274#/* get digit code, 0 if invalid  */
275EXPORT_DEF char pdu_digit2code(char digit)
276{
277	switch(digit)
278	{
279		case '0':
280		case '1':
281		case '2':
282		case '3':
283		case '4':
284		case '5':
285		case '6':
286		case '7':
287		case '8':
288		case '9':
289			break;
290		case '*':
291			digit = 'A';
292			break;
293		case '#':
294			digit = 'B';
295			break;
296		case 'a':
297		case 'A':
298			digit = 'C';
299			break;
300		case 'b':
301		case 'B':
302			digit = 'D';
303			break;
304		case 'c':
305		case 'C':
306			digit = 'E';
307			break;
308		default:
309			return 0;
310	}
311	return digit;
312}
313
314#/* */
315static char pdu_code2digit(char code)
316{
317	switch(code)
318	{
319		case '0':
320		case '1':
321		case '2':
322		case '3':
323		case '4':
324		case '5':
325		case '6':
326		case '7':
327		case '8':
328		case '9':
329			break;
330		case 'a':
331		case 'A':
332			code = '*';
333			break;
334		case 'b':
335		case 'B':
336			code = '#';
337			break;
338		case 'c':
339		case 'C':
340			code = 'A';
341			break;
342		case 'd':
343		case 'D':
344			code = 'B';
345			break;
346		case 'e':
347		case 'E':
348			code = 'C';
349			break;
350		case 'F':
351			return 0;
352		default:
353			return -1;
354	}
355	return code;
356}
357
358#/* convert minutes to relative VP value */
359static int pdu_relative_validity(unsigned minutes)
360{
361#define DIV_UP(x,y)	(((x)+(y)-1)/(y))
362/*
363	0 ... 143  (vp + 1) * 5 minutes				   5  ...   720		m = (vp + 1) * 5		m / 5 - 1 = vp
364	144...167  12 hours + (vp - 143) * 30 minutes		 750  ...  1440		m = 720 + (vp - 143) * 30	(m - 720) / 30 + 143 = m / 30 + 119
365	168...196  (vp - 166) * 1 day				2880  ... 43200		m = (vp - 166) * 1440		(m / 1440) + 166
366	197...255  (vp - 192) * 1 week			       50400  ...635040		m = (vp - 192) * 10080		(m / 10080) + 192
367*/
368	int validity;
369	if(minutes <= 720)
370		validity = DIV_UP(minutes, 5) - 1;
371	else if(minutes <= 1440)
372		validity = DIV_UP(minutes, 30) + 119;
373	else if(minutes <= 43200)
374		validity = DIV_UP(minutes, 1440) + 166;
375	else if(minutes <= 635040)
376		validity = DIV_UP(minutes, 10080) + 192;
377	else
378		validity = 0xFF;
379	return validity;
380#undef DIV_UP
381}
382
383#/* convert 2 hex digits of PDU to byte, return < 0 on error */
384static int pdu_parse_byte(char ** digits2hex, size_t * length)
385{
386	int res = -1;
387	int res2;
388	
389	if(*length >= 2)
390	{
391		res = parse_hexdigit(*digits2hex[0]);
392		if(res >= 0)
393		{
394			(*digits2hex)++;
395			(*length)--;
396			res2 = parse_hexdigit(*digits2hex[0]);
397			if(res2 >= 0)
398			{
399				(*digits2hex)++;
400				(*length)--;
401				return (res << 4) | res2;
402			}
403		}
404	}
405	return res;
406}
407
408/*!
409 * \brief Store number in PDU 
410 * \param buffer -- pointer to place where number will be stored, CALLER MUST be provide length + 2 bytes of buffer
411 * \param number -- phone number w/o leading '+'
412 * \param length -- length of number
413 * \return number of bytes written to buffer
414 */
415static int pdu_store_number(char* buffer, const char* number, unsigned length)
416{
417	int i;
418	for(i = 0; length > 1; length -=2, i +=2)
419	{
420		buffer[i] = pdu_digit2code(number[i + 1]);
421		buffer[i + 1] = pdu_digit2code(number[i]);
422	}
423	
424	if(length)
425	{
426		buffer[i] = 'F';
427		buffer[i+1] = pdu_digit2code(number[i]);
428		i += 2;
429	}
430	return i;
431}
432
433#/* reverse of pdu_store_number() */
434static int pdu_parse_number(char ** pdu, size_t * pdu_length, unsigned digits, int * toa, char * number, size_t num_len)
435{
436	const char * begin;
437
438	if(num_len < digits + 1)
439		return -ENOMEM;
440
441	begin = *pdu;
442	*toa = pdu_parse_byte(pdu, pdu_length);
443	if(*toa >= 0)
444	{
445		unsigned syms = ROUND_UP2(digits);
446		if(syms <= *pdu_length)
447		{
448			char digit;
449			if(*toa == NUMBER_TYPE_INTERNATIONAL)
450				*number++ = '+';
451			for(; syms > 0; syms -= 2, *pdu += 2, *pdu_length -= 2)
452			{
453				digit = pdu_code2digit(pdu[0][1]);
454				if(digit <= 0)
455					return -1;
456				*number++ = digit;
457
458				digit = pdu_code2digit(pdu[0][0]);
459				if(digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0)))
460					return -1;
461
462				*number++ = digit;
463			}
464			if((digits & 0x1) == 0)
465				*number = 0;
466			return *pdu - begin;
467		}
468	}
469
470	return -EINVAL;
471}
472
473
474#/* return bytes (not octets!) of pdu occupied by SCA or <0 on errors */
475static int pdu_parse_sca(char ** pdu, size_t * length)
476{
477	/* get length of SCA field */
478	int sca_len = pdu_parse_byte(pdu, length);
479
480	if(sca_len >= 0)
481	{
482		sca_len *= 2;
483		if((size_t)sca_len <= *length)
484		{
485			*pdu += sca_len;
486			*length -= sca_len;
487
488			/* TODO: Parse SCA Address */
489			
490			return sca_len + 2;
491		}
492	}
493	return -EINVAL;
494}
495
496#/* TODO: implement */
497static int pdu_parse_timestamp(char ** pdu, size_t * length)
498{
499	if(*length >= 14)
500	{
501		*pdu += 14;
502		*length -= 14;
503		return 14;
504	}
505	return -EINVAL;
506}
507
508#/* TODO: remove */
509static int check_encoding(const char* msg, unsigned length)
510{
511	str_encoding_t possible_enc = get_encoding(RECODE_ENCODE, msg, length);
512	if(possible_enc == STR_ENCODING_7BIT_HEX)
513		return PDU_DCS_ALPABET_7BIT;
514	return PDU_DCS_ALPABET_UCS2;
515}
516
517/*!
518 * \brief Build PDU text for SMS
519 * \param buffer -- pointer to place where PDU will be stored
520 * \param length -- length of buffer
521 * \param csca -- number of SMS center may be with leading '+' in International format
522 * \param dst -- destination number for SMS may be with leading '+' in International format
523 * \param msg -- SMS message in utf-8
524 * \param valid_minutes -- Validity period
525 * \param srr -- Status Report Request
526 * \param sca_len -- pointer where length of SCA header (in bytes) will be stored
527 * \return number of bytes written to buffer w/o trailing 0x1A or 0, -ENOMEM if buffer too short, -EINVAL on iconv recode errors, -E2BIG if message too long
528 */
529EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* csca, const char* dst, const char* msg, unsigned valid_minutes, int srr, int* sca_len)
530{
531	char tmp;
532	int len = 0;
533	int data_len;
534
535	int csca_toa = NUMBER_TYPE_INTERNATIONAL;
536	int dst_toa = NUMBER_TYPE_INTERNATIONAL;
537	int pdutype= PDUTYPE_MTI_SMS_SUBMIT | PDUTYPE_RD_ACCEPT | PDUTYPE_VPF_RELATIVE | PDUTYPE_SRR_NOT_REQUESTED | PDUTYPE_UDHI_NO_HEADER | PDUTYPE_RP_IS_NOT_SET;
538	int dcs;
539	
540	unsigned dst_len;
541	unsigned csa_len;
542	unsigned msg_len;
543
544	/* detect msg encoding and use 7Bit or UCS-2, not use 8Bit */
545	msg_len = strlen(msg);
546	dcs = check_encoding(msg, msg_len);
547
548	/* cannot exceed 140 octets for no compressed or cannot exceed 160 septets for compressed */
549	if(((PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2) && msg_len > 70) || (PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT && msg_len > 140) || msg_len > 160)
550	{
551		return -E2BIG;
552	}
553
554	if(csca[0] == '+')
555		csca++;
556
557	if(dst[0] == '+')
558		dst++;
559
560	/* count length of strings */
561	csa_len = strlen(csca);
562	dst_len = strlen(dst);
563
564	/* check buffer has enougth space */
565	if(length < ((csa_len == 0 ? 2 : 4 + ROUND_UP2(csa_len)) + 8 + ROUND_UP2(dst_len) + 8 + msg_len * 4 + 4))
566		return -ENOMEM;
567
568	/* SCA Length */
569	/* Type-of-address of the SMSC */
570	/* Address of SMSC */
571	if(csa_len)
572	{
573		len += snprintf(buffer + len, length - len, "%02X%02X", 1 + LENGTH2OCTETS(csa_len), csca_toa);
574		len += pdu_store_number(buffer + len, csca, csa_len);
575		*sca_len = len;
576	}
577	else
578	{
579		buffer[len++] = '0';
580		buffer[len++] = '0';
581		*sca_len = 2;
582	}
583
584	if(srr)
585		pdutype |= PDUTYPE_SRR_REQUESTED;
586
587	/* PDU-type */
588	/* TP-Message-Reference. The "00" value here lets the phone set the message reference number itself */
589	/* Address-Length */
590	/* Type-of-address of the sender number */
591	len += snprintf(buffer + len, length - len, "%02X%02X%02X%02X", pdutype, PDU_MESSAGE_REFERENCE, dst_len, dst_toa);
592
593	/*  Destination address */
594	len += pdu_store_number(buffer + len, dst, dst_len);
595
596	/* TODO: also check message limit in 178 octet of TPDU (w/o SCA) */
597	/* forward TP-User-Data */
598	data_len = str_recode(RECODE_ENCODE, dcs == PDU_DCS_ALPABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT_HEX, msg, msg_len, buffer + len + 8, length - len - 11);
599	if(data_len < 0)
600	{
601		return -EINVAL;
602	}
603
604	/* calc UDL */
605	if(dcs == PDU_DCS_ALPABET_UCS2)
606		msg_len = data_len / 2;
607
608	/* TP-PID. Protocol identifier  */
609	/* TP-DCS. Data coding scheme */
610	/* TP-Validity-Period */
611	/* TP-User-Data-Length */
612	tmp = buffer[len + 8];
613	len += snprintf(buffer + len, length - len, "%02X%02X%02X%02X", PDU_PID_SMS, dcs, pdu_relative_validity(valid_minutes), msg_len);
614	buffer[len] = tmp;
615
616	len += data_len;
617	return len;
618}
619
620
621#/* */
622static str_encoding_t pdu_dcs_alpabet2encoding(int alpabet)
623{
624	str_encoding_t rv = STR_ENCODING_UNKNOWN;
625
626	alpabet >>= PDU_DCS_ALPABET_SHIFT;
627	switch(alpabet)
628	{
629		case (PDU_DCS_ALPABET_7BIT >> PDU_DCS_ALPABET_SHIFT):
630			rv = STR_ENCODING_7BIT_HEX;
631			break;
632		case (PDU_DCS_ALPABET_8BIT >> PDU_DCS_ALPABET_SHIFT):
633			rv = STR_ENCODING_8BIT_HEX;
634			break;
635		case (PDU_DCS_ALPABET_UCS2 >> PDU_DCS_ALPABET_SHIFT):
636			rv = STR_ENCODING_UCS2_HEX;
637			break;
638	}
639
640	return rv;
641}
642
643/*!
644 * \brief Parse PDU
645 * \param pdu -- SCA + TPDU
646 * \param tpdu_length -- length of TPDU in octets
647 * \return 0 on success
648 */
649/* TODO: split long function */
650EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc)
651{
652	const char * err = NULL;
653	size_t pdu_length = strlen(*pdu);
654	
655	/* decode SCA */
656	int field_len = pdu_parse_sca(pdu, &pdu_length);
657	if(field_len > 0)
658	{
659	    if(tpdu_length * 2 == pdu_length)
660	    {
661		int pdu_type = pdu_parse_byte(pdu, &pdu_length);
662		if(pdu_type >= 0)
663		{
664			/* TODO: also handle PDUTYPE_MTI_SMS_SUBMIT_REPORT and PDUTYPE_MTI_SMS_STATUS_REPORT */
665			if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_DELIVER)
666			{
667				int oa_digits = pdu_parse_byte(pdu, &pdu_length);
668				if(oa_digits > 0)
669				{
670					int oa_toa;
671					field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len);
672					if(field_len > 0)
673					{
674						int pid = pdu_parse_byte(pdu, &pdu_length);
675						*oa_enc = STR_ENCODING_7BIT;
676						if(pid >= 0)
677						{
678						   /* TODO: support other types of messages */
679						   if(pid == PDU_PID_SMS)
680						   {
681							int dcs = pdu_parse_byte(pdu, &pdu_length);
682							if(dcs >= 0)
683							{
684							    // TODO: support compression
685							    if( PDU_DCS_76(dcs) == PDU_DCS_76_00
686							    		&&
687							    	PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPESSED
688							    		&&
689							    		(
690							    		PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT
691							    			||
692							    		PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT
693							    			||
694							    		PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2
695							    		)
696							    	)
697							    {
698								int ts = pdu_parse_timestamp(pdu, &pdu_length);
699								*msg_enc = pdu_dcs_alpabet2encoding(PDU_DCS_ALPABET(dcs));
700								if(ts >= 0)
701								{
702									int udl = pdu_parse_byte(pdu, &pdu_length);
703									if(udl >= 0)
704									{
705										/* calculate number of octets in UD */
706										if(PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT)
707											udl = ((udl + 1) * 7) >> 3;
708										if((size_t)udl * 2 == pdu_length)
709										{
710											if(PDUTYPE_UDHI(pdu_type) == PDUTYPE_UDHI_HAS_HEADER)
711											{
712												/* TODO: implement header parse */
713												int udhl = pdu_parse_byte(pdu, &pdu_length);
714												if(udhl >= 0)
715												{
716													/* NOTE: UDHL count octets no need calculation */
717													if(pdu_length >= (size_t)(udhl * 2))
718													{
719														/* skip UDH */
720														*pdu += udhl * 2;
721														pdu_length -= udhl * 2;
722													}
723													else
724													{
725														err = "Invalid UDH";
726													}
727												}
728												else
729												{
730													err = "Can't parse UDHL";
731												}
732											}
733											/* save message */
734											*msg = *pdu;
735										}
736										else
737										{
738											*pdu -= 2;
739											err = "UDL not match with UD length";
740										}
741									}
742									else
743									{
744										err = "Can't parse UDL";
745									}
746								}
747								else
748								{
749									err = "Can't parse Timestamp";
750								}
751							    }
752							    else
753							    {
754								*pdu -= 2;
755								err = "Unsupported DCS value";
756							    }
757							}
758							else
759							{
760								err = "Can't parse DSC";
761							}
762						    }
763						    else
764						    {
765						    	err = "Unhandled PID value, only SMS supported";
766						    }
767						}
768						else
769						{
770							err = "Can't parse PID";
771						}
772					}
773					else
774					{
775						err = "Can't parse OA";
776					}
777				}
778				else
779				{
780					err = "Can't parse length of OA";
781				}
782			}
783			else
784			{
785				*pdu -= 2;
786				err = "Unhandled PDU Type MTI only SMS-DELIVER supported";
787			}
788		}
789		else
790		{
791			err = "Can't parse PDU Type";
792		}
793	    }
794	    else
795	    {
796		err = "TPDU length not matched with actual length";
797	    }
798	}
799	else
800	{
801		err = "Can't parse SCA";
802	}
803
804	return err;
805}
806