delphi - When I encode/decode SMS PDU (GSM 7 Bit) user data, do I need prepend the UDH first? -


while can encode , decode user data part of sms message when udh not present, i'm having trouble doing when udh is present (in case, concatenated sms).

when decode or encode user data, need prepend udh text before doing so?

this article provides encoding routine sample compensates udh padding bits (which still don't understand) doesn't give example of data being passed routine don't have clear use case (and not find decoding sample on site): http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/.

so far, have been able results if prepend udh user data before decoding it, suspect coincidence.

as example (using values https://en.wikipedia.org/wiki/concatenated_sms):

udh := '050003000302'; encoded_user_data_part := 'd06536fb0dbabfe56c32'; // padding, evidently decodeduserdata := decode7bit(udh + encoded_user_data_part); writeln(decodeduserdata); 

output: "ß@ø¿Æ @hello world"

encodeduserdata := encode7bit(decodeduserdata); decodeduserdata := decode7bit(encodedencodeduserdata); writeln(decodeduserdata); 

same output: "ß@ø¿Æ @hello world"

without prepending udh garbage:

decodeduserdata := decode7bit(encoded_user_data_part); writeln(decodeduserdata); 

output: "pkyy§an§eyi"

what correct way of handling this?

am supposed include udh text when encoding user data?

am supposed strip off garbage characters after decoding, or (as suspect) off base assumption?

while decoding algorithm here seems work without udh doesn't seem take udh information account: looking gsm 7bit encode/decode algorithm.

i eternally grateful if set me straight on correct way proceed. clear examples/code samples appreciated. ;-)

i provide small sample application includes algorithms if feels solve riddle.

edit 1:

i'm using delphi xe2 update 4 hotfix 1

edit 2:

thanks @whosrdaddy, able encoding/decoding routines work.

as side note, curious why user data needed on 7-bit boundary when udh wasn't encoded it, last sentence in paragraph etsi specification quoted @whosrdaddy answered that:

if 7 bit data used , tp-ud-header not finish on septet boundary fill bits inserted after last information element data octet there integral number of septets entire tp-ud header. this ensure sm starts on octet boundary earlier phase mobile capable of displaying sm although tp-ud header in tp-ud field may not understood

my code based in part on examples following resources:

looking gsm 7bit encode/decode algorithm

https://en.wikipedia.org/wiki/concatenated_sms

http://mobiletidings.com/2009/02/18/combining-sms-messages/

http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/

http://mobileforensics.files.wordpress.com/2007/06/understanding_sms.pdf

http://www.dreamfabric.com/sms/

http://www.mediaburst.co.uk/blog/concatenated-sms/

here's code else who's had trouble sms encoding/decoding. i'm sure can simplified/optimized (and comments welcome), i've tested several different permutations , udh header lengths success. hope helps.

unit smsutils;  interface  uses windows, classes, math;  function encode7bit(const atext: string; audhlen: byte;   out atextlen: byte): string;  function decode7bit(const apdudata: string; audhlen: integer): string;  implementation  var   g7bittoasciitable: array [0 .. 127] of byte;   gasciito7bittable: array [0 .. 255] of byte;  procedure initializetables; var   asciivalue: integer;   i: integer; begin   // create 7-bit ascii table   g7bittoasciitable[0] := 64; // @   g7bittoasciitable[1] := 163;   g7bittoasciitable[2] := 36;   g7bittoasciitable[3] := 165;   g7bittoasciitable[4] := 232;   g7bittoasciitable[5] := 223;   g7bittoasciitable[6] := 249;   g7bittoasciitable[7] := 236;   g7bittoasciitable[8] := 242;   g7bittoasciitable[9] := 199;   g7bittoasciitable[10] := 10;   g7bittoasciitable[11] := 216;   g7bittoasciitable[12] := 248;   g7bittoasciitable[13] := 13;   g7bittoasciitable[14] := 197;   g7bittoasciitable[15] := 229;   g7bittoasciitable[16] := 0;   g7bittoasciitable[17] := 95;   g7bittoasciitable[18] := 0;   g7bittoasciitable[19] := 0;   g7bittoasciitable[20] := 0;   g7bittoasciitable[21] := 0;   g7bittoasciitable[22] := 0;   g7bittoasciitable[23] := 0;   g7bittoasciitable[24] := 0;   g7bittoasciitable[25] := 0;   g7bittoasciitable[26] := 0;   g7bittoasciitable[27] := 0;   g7bittoasciitable[28] := 198;   g7bittoasciitable[29] := 230;   g7bittoasciitable[30] := 223;   g7bittoasciitable[31] := 201;   g7bittoasciitable[32] := 32;   g7bittoasciitable[33] := 33;   g7bittoasciitable[34] := 34;   g7bittoasciitable[35] := 35;   g7bittoasciitable[36] := 164;   g7bittoasciitable[37] := 37;   g7bittoasciitable[38] := 38;   g7bittoasciitable[39] := 39;   g7bittoasciitable[40] := 40;   g7bittoasciitable[41] := 41;   g7bittoasciitable[42] := 42;   g7bittoasciitable[43] := 43;   g7bittoasciitable[44] := 44;   g7bittoasciitable[45] := 45;   g7bittoasciitable[46] := 46;   g7bittoasciitable[47] := 47;   g7bittoasciitable[48] := 48;   g7bittoasciitable[49] := 49;   g7bittoasciitable[50] := 50;   g7bittoasciitable[51] := 51;   g7bittoasciitable[52] := 52;   g7bittoasciitable[53] := 53;   g7bittoasciitable[54] := 54;   g7bittoasciitable[55] := 55;   g7bittoasciitable[56] := 56;   g7bittoasciitable[57] := 57;   g7bittoasciitable[58] := 58;   g7bittoasciitable[59] := 59;   g7bittoasciitable[60] := 60;   g7bittoasciitable[61] := 61;   g7bittoasciitable[62] := 62;   g7bittoasciitable[63] := 63;   g7bittoasciitable[64] := 161;   g7bittoasciitable[65] := 65;   g7bittoasciitable[66] := 66;   g7bittoasciitable[67] := 67;   g7bittoasciitable[68] := 68;   g7bittoasciitable[69] := 69;   g7bittoasciitable[70] := 70;   g7bittoasciitable[71] := 71;   g7bittoasciitable[72] := 72;   g7bittoasciitable[73] := 73;   g7bittoasciitable[74] := 74;   g7bittoasciitable[75] := 75;   g7bittoasciitable[76] := 76;   g7bittoasciitable[77] := 77;   g7bittoasciitable[78] := 78;   g7bittoasciitable[79] := 79;   g7bittoasciitable[80] := 80;   g7bittoasciitable[81] := 81;   g7bittoasciitable[82] := 82;   g7bittoasciitable[83] := 83;   g7bittoasciitable[84] := 84;   g7bittoasciitable[85] := 85;   g7bittoasciitable[86] := 86;   g7bittoasciitable[87] := 87;   g7bittoasciitable[88] := 88;   g7bittoasciitable[89] := 89;   g7bittoasciitable[90] := 90;   g7bittoasciitable[91] := 196;   g7bittoasciitable[92] := 204;   g7bittoasciitable[93] := 209;   g7bittoasciitable[94] := 220;   g7bittoasciitable[95] := 167;   g7bittoasciitable[96] := 191;   g7bittoasciitable[97] := 97;   g7bittoasciitable[98] := 98;   g7bittoasciitable[99] := 99;   g7bittoasciitable[100] := 100;   g7bittoasciitable[101] := 101;   g7bittoasciitable[102] := 102;   g7bittoasciitable[103] := 103;   g7bittoasciitable[104] := 104;   g7bittoasciitable[105] := 105;   g7bittoasciitable[106] := 106;   g7bittoasciitable[107] := 107;   g7bittoasciitable[108] := 108;   g7bittoasciitable[109] := 109;   g7bittoasciitable[110] := 110;   g7bittoasciitable[111] := 111;   g7bittoasciitable[112] := 112;   g7bittoasciitable[113] := 113;   g7bittoasciitable[114] := 114;   g7bittoasciitable[115] := 115;   g7bittoasciitable[116] := 116;   g7bittoasciitable[117] := 117;   g7bittoasciitable[118] := 118;   g7bittoasciitable[119] := 119;   g7bittoasciitable[120] := 120;   g7bittoasciitable[121] := 121;   g7bittoasciitable[122] := 122;   g7bittoasciitable[123] := 228;   g7bittoasciitable[124] := 246;   g7bittoasciitable[125] := 241;   g7bittoasciitable[126] := 252;   g7bittoasciitable[127] := 224;    // create ascii 7-bit table   zeromemory(@gasciito7bittable, sizeof(gasciito7bittable));   := 0 high(g7bittoasciitable)   begin     asciivalue := g7bittoasciitable[i];     gasciito7bittable[asciivalue] := i;   end; end;  function convertasciito7bit(const atext: string; audhlen: byte): ansistring; const   esc = #27;   escaped_ascii_codes = [#94, #123, #125, #92, #91, #126, #93, #124, #164]; var   septet: byte;   ch: ansichar;   i: integer; begin   := 1 length(atext)   begin     ch := ansichar(atext[i]);     if not(ch in escaped_ascii_codes)       septet := gasciito7bittable[byte(ch)]     else     begin       result := result + esc;       case (ch) of         #12: septet := 10;         #94: septet := 20;         #123: septet := 40;         #125: septet := 41;         #92: septet := 47;         #91: septet := 60;         #126: septet := 61;         #93: septet := 62;         #124: septet := 64;         #164: septet := 101;       else septet := 0;       end;     end;     result := result + ansichar(septet);   end; end;  function convert7bittoascii(const atext: ansistring): string; const   esc = #27; var   textlen: integer;   ch: char;   i: integer; begin   result := '';   textlen := length(atext);   := 1;   while (i <= textlen)   begin     ch := char(atext[i]);     if (ch <> esc)       result := result + char(g7bittoasciitable[ord(ch)])     else     begin       inc(i); // skip esc       if (i <= textlen)       begin         ch := char(atext[i]);         case (ch) of           #10: ch := #12;           #20: ch := #94;           #40: ch := #123;           #41: ch := #125;           #47: ch := #92;           #60: ch := #91;           #61: ch := #126;           #62: ch := #93;           #64: ch := #124;           #101: ch := #164;         end;         result := result + ch;       end;     end;     inc(i);   end; end;  function strtohex(const atext: ansistring): ansistring; overload; var   textlen: integer; begin   // set text buffer size   textlen := length(atext);   // set length of result double string length   setlength(result, textlen * 2);   // convert string hex   bintohex(pansichar(atext), pansichar(result), textlen); end;  function strtohex(const atext: string): string; overload; begin   result := string(strtohex(ansistring(atext))); end;  function hextostr(const atext: ansistring): ansistring; overload; var   resultlen: integer; begin   // set length of result half text length   resultlen := length(atext) div 2;   setlength(result, resultlen);   // convert hex string   if (hextobin(pansichar(atext), pansichar(result), resultlen) <> resultlen)     result := 'error converting hex string: ' + atext; end;  function hextostr(const atext: string): string; overload; begin   result := string(hextostr(ansistring(atext))); end;  function encode7bit(const atext: string; audhlen: byte;   out atextlen: byte): string; // atext: ascii text // audhlen: length of udh including udh len byte (e.g. '050003cc0101' = 6 bytes) // atextlen: returns length of text encoded.  can different // length(atext) due escape characters // returns text encoded pdu hex string var   text7bit: ansistring;   pdu: ansistring;   pduidx: integer;   pdulen: byte;   paddingbits: byte;   bitstomove: byte;   septet: byte;   octet: byte;   prevoctet: byte;   shiftedoctet: byte;   i: integer; begin   result := '';   text7bit := convertasciito7bit(atext, audhlen);   atextlen := length(text7bit);   bitstomove := 0;   // determine how many padding bits needed based on udh   if (audhlen > 0)     paddingbits := 7 - ((audhlen * 8) mod 7)   else     paddingbits := 0;   // calculate number of bytes needed store 7-bit text   // along padding bits required   pdulen := ceil(((atextlen * 7) + paddingbits) / 8);   // reserve space pdu bytes   pdu := ansistring(stringofchar(#0, pdulen));   pduidx := 1;   := 1 atextlen   begin     if (bitstomove = 7)       bitstomove := 0     else     begin       // convert current character septet (7-bits) , make room       // bits next 1       septet := (byte(text7bit[i]) shr bitstomove);       if (i = atextlen)         octet := septet       else       begin         // convert next character septet , copy bits         // octet (pdu byte)         octet := septet or           byte((byte(text7bit[i + 1]) shl byte(7 - bitstomove)));       end;       byte(pdu[pduidx]) := octet;       inc(pduidx);       inc(bitstomove);     end;   end;   // following code pads pdu on *right* shifting *left*   // <paddingbits>. using same bit storage convention   // 7-bit compression routine above, taking significant   // <paddingbits> each pdu byte , moving them least significant   // bits of next pdu byte. if there no room in last pdu byte   // high bits of previous byte removed, bits   // placed additional byte reserved purpose.   // note: <pdulen> has been set account reserved byte if   // required.   if (paddingbits > 0)   begin     setlength(result, (pdulen * 2));     prevoctet := 0;     pduidx := 1 pdulen     begin       octet := byte(pdu[pduidx]);       if (pduidx = 1)         shiftedoctet := byte(octet shl paddingbits)       else         shiftedoctet := byte(octet shl paddingbits) or           byte(prevoctet shr (8 - paddingbits));       byte(pdu[pduidx]) := shiftedoctet;       prevoctet := octet;     end;   end;   result := string(strtohex(pdu)); end;  function decode7bit(const apdudata: string; audhlen: integer): string; // apdudata: hex string representation of pdu data // audhlen: length of udh including udh len (e.g. '050003cc0101' = 6 bytes) // returns decoded ascii text var   pdu: ansistring;   numseptets: byte;   septets: ansistring;   pduidx: integer;   pdulen: integer;   by: byte;   currby: byte;   left: byte;   mask: byte;   nextby: byte;   octet: byte;   nextoctet: byte;   paddingbits: byte;   shiftedoctet: byte;   i: integer; begin   result := '';   paddingbits := 0;   // convert hex string bytes   pdu := ansistring(hextostr(apdudata));   pdulen := length(pdu);   // following code removes padding @ end of pdu shifting   // *right* <paddingbits>. taking least significant   // <paddingbits> following pdu byte , moving them   // significant current pdu byte.   if (audhlen > 0)   begin     paddingbits := 7 - ((audhlen * 8) mod 7);     pduidx := 1 pdulen     begin       octet := byte(pdu[pduidx]);       if (pduidx = pdulen)         shiftedoctet := byte(octet shr paddingbits)       else       begin         nextoctet := byte(pdu[pduidx + 1]);         shiftedoctet := byte(octet shr paddingbits) or           byte(nextoctet shl (8 - paddingbits));       end;       byte(pdu[pduidx]) := shiftedoctet;     end;   end;   // decode   // number of septets in pdu after excluding padding bits   numseptets := ((pdulen * 8) - paddingbits) div 7;   septets := ansistring(stringofchar(#0, numseptets));   left := 7;   mask := $7f;   nextby := 0;   pduidx := 1;   := 1 numseptets   begin     if mask = 0     begin       septets[i] := ansichar(nextby);       left := 7;       mask := $7f;       nextby := 0;     end     else     begin       if (pduidx > pdulen)         break;       := byte(pdu[pduidx]);       inc(pduidx);       currby := ((by , mask) shl (7 - left)) or nextby;       nextby := (by , (not mask)) shr left;       septets[i] := ansichar(currby);       mask := mask shr 1;       left := left - 1;     end;   end; //   // remove last character if unused   // kind of hack, frankly don't know how else compensate   // it.   if (septets[numseptets] = #0)     setlength(septets, numseptets - 1);   // convert 7-bit alphabet ascii   result := convert7bittoascii(septets); end;  initialization   initializetables; end. 

no don't include udh part when encoding, if read gsm phase 2 specification on page 57, mention fact : "if 7 bit data used , tp-ud-header not finish on septet boundary fill bits inserted after last information element data octet there integral number of septets entire tp-ud header". when include udh part not case, need calculate offset (= number of fill bits)

calculating offset, code assumes udhpart ansistring:

len := length(udhpart) shr 1; offset := 7 - ((len * 8) mod 7);  // fill bits 

now when encoding 7bit data, proceed normal @ end, shift data offset bits left, code has encoded data in variable result (ansistring):

 // fill bits  if offset > 0   begin    v := result;    len := length(v);    bytesremain := ceil(((len * 7)+offset) / 8);           result := stringofchar(#0, bytesremain);    inpos := 1 bytesremain     begin      if inpos = 1       byte(result[inpos]) := byte(v[inpos]) shl offset      else       byte(result[inpos]) := (byte(v[inpos]) shl offset) or (byte(v[inpos-1]) shr (8 - offset));     end;   end; 

decoding same thing really, first shift 7 bit data offset bits right before decoding...

i hope set onto right track...


Comments

Popular posts from this blog

Glib GIOChannel -

python - Obscure curses error message when creating a sub window -