{********************************************** PDU Conversion Unit v1.0 (c)2004 Mpu Gondrong (wawan @ gatra . com) Release to Public Domain, feel free to use and modify Some of this code based on PduConv: http://freshmeat.net/projects/pduconv/ See brief description of PDU: http://www.dreamfabric.com/sms/ ***********************************************} unit PduConv; interface type TSmsFio = (fioMT2, fioMT1, fioMM_RD, fioVP2, fioVP1, fioSR, fioUDHI, fioRP); TSmsFios = set of TSmsFio; TSmsMTI = (mtiDeliver, mtiDeliverRep, mtiStatusRep, mtiCommand, mtiSubmit, mtiSubmitRep); TSmsPdu = record Center, // sms center number Phone, // mobile station number Text: string; // sms content Fio: TSmsFios; // first octect MTI: TSmsMTI; // message type indicator Ref, // reference number Stat, // status report PID, // protocol identifier DCS: Byte; // data coding scheme SCTS: TDateTime; // service center time stamp end; function DecodePdu(ScToMs: Boolean; const Txt: string): TSmsPdu; function EncodePdu(const Str, Phone: string): string; implementation uses SysUtils; { Define Non-Printable Characters as a question mark } const NPC7 = 63; NPC8 = Ord('?'); (*************************************************************************** This lookup table converts from ISO-8859-1 8-bit ASCII to the 7 bit "default alphabet" as defined in ETSI GSM 03.38 ISO-characters that don't have any correspondning character in the 7-bit alphabet is replaced with the NPC7-character. If there's a close match between the ISO-char and a 7-bit character (for example the letter i with a circumflex and the plain i-character) a substitution is done. These "close-matches" are marked in the lookup table by having its value negated. There are some character (for example the curly brace "{") that must be converted into a 2 byte 7-bit sequence. These characters are marked in the table by having 256 added to its value. ***************************************************************************) lookup_ascii8to7: array [0..255] of SmallInt = ( NPC7, { 0 null [NUL] } NPC7, { 1 start of heading [SOH] } NPC7, { 2 start of text [STX] } NPC7, { 3 end of text [ETX] } NPC7, { 4 end of transmission [EOT] } NPC7, { 5 enquiry [ENQ] } NPC7, { 6 acknowledge [ACK] } NPC7, { 7 bell [BEL] } NPC7, { 8 backspace [BS] } NPC7, { 9 horizontal tab [HT] } 10, { 10 line feed [LF] } NPC7, { 11 vertical tab [VT] } 10+256, { 12 form feed [FF] } 13, { 13 carriage return [CR] } NPC7, { 14 shift out [SO] } NPC7, { 15 shift in [SI] } NPC7, { 16 data link escape [DLE] } NPC7, { 17 device control 1 [DC1] } NPC7, { 18 device control 2 [DC2] } NPC7, { 19 device control 3 [DC3] } NPC7, { 20 device control 4 [DC4] } NPC7, { 21 negative acknowledge [NAK] } NPC7, { 22 synchronous idle [SYN] } NPC7, { 23 end of trans. block [ETB] } NPC7, { 24 cancel [CAN] } NPC7, { 25 end of medium [EM] } NPC7, { 26 substitute [SUB] } NPC7, { 27 escape [ESC] } NPC7, { 28 file separator [FS] } NPC7, { 29 group separator [GS] } NPC7, { 30 record separator [RS] } NPC7, { 31 unit separator [US] } 32, { 32 space } 33, { 33 ! exclamation mark } 34, { 34 " double quotation mark } 35, { 35 # number sign } 2, { 36 $ dollar sign } 37, { 37 % percent sign } 38, { 38 & ampersand } 39, { 39 ' apostrophe } 40, { 40 ( left parenthesis } 41, { 41 ) right parenthesis } 42, { 42 * asterisk } 43, { 43 + plus sign } 44, { 44 , comma } 45, { 45 - hyphen } 46, { 46 . period } 47, { 47 / slash, } 48, { 48 0 digit 0 } 49, { 49 1 digit 1 } 50, { 50 2 digit 2 } 51, { 51 3 digit 3 } 52, { 52 4 digit 4 } 53, { 53 5 digit 5 } 54, { 54 6 digit 6 } 55, { 55 7 digit 7 } 56, { 56 8 digit 8 } 57, { 57 9 digit 9 } 58, { 58 : colon } 59, { 59 ; semicolon } 60, { 60 < less-than sign } 61, { 61 = equal sign } 62, { 62 > greater-than sign } 63, { 63 ? question mark } 0, { 64 @ commercial at sign } 65, { 65 A uppercase A } 66, { 66 B uppercase B } 67, { 67 C uppercase C } 68, { 68 D uppercase D } 69, { 69 E uppercase E } 70, { 70 F uppercase F } 71, { 71 G uppercase G } 72, { 72 H uppercase H } 73, { 73 I uppercase I } 74, { 74 J uppercase J } 75, { 75 K uppercase K } 76, { 76 L uppercase L } 77, { 77 M uppercase M } 78, { 78 N uppercase N } 79, { 79 O uppercase O } 80, { 80 P uppercase P } 81, { 81 Q uppercase Q } 82, { 82 R uppercase R } 83, { 83 S uppercase S } 84, { 84 T uppercase T } 85, { 85 U uppercase U } 86, { 86 V uppercase V } 87, { 87 W uppercase W } 88, { 88 X uppercase X } 89, { 89 Y uppercase Y } 90, { 90 Z uppercase Z } 60+256, { 91 [ left square bracket } 47+256, { 92 \ backslash } 62+256, { 93 ] right square bracket } 20+256, { 94 ^ circumflex accent } 17, { 95 _ underscore } -39, { 96 ` back apostrophe } 97, { 97 a lowercase a } 98, { 98 b lowercase b } 99, { 99 c lowercase c } 100, { 100 d lowercase d } 101, { 101 e lowercase e } 102, { 102 f lowercase f } 103, { 103 g lowercase g } 104, { 104 h lowercase h } 105, { 105 i lowercase i } 106, { 106 j lowercase j } 107, { 107 k lowercase k } 108, { 108 l lowercase l } 109, { 109 m lowercase m } 110, { 110 n lowercase n } 111, { 111 o lowercase o } 112, { 112 p lowercase p } 113, { 113 q lowercase q } 114, { 114 r lowercase r } 115, { 115 s lowercase s } 116, { 116 t lowercase t } 117, { 117 u lowercase u } 118, { 118 v lowercase v } 119, { 119 w lowercase w } 120, { 120 x lowercase x } 121, { 121 y lowercase y } 122, { 122 z lowercase z } 40+256, (* 123 { left brace *) 64+256, { 124 | vertical bar } 41+256, (* 125 } right brace *) 61+256, { 126 ~ tilde accent } NPC7, { 127 delete [DEL] } NPC7, { 128 } NPC7, { 129 } -39, { 130 low left rising single quote } -102, { 131 lowercase italic f } -34, { 132 low left rising double quote } NPC7, { 133 low horizontal ellipsis } NPC7, { 134 dagger mark } NPC7, { 135 double dagger mark } NPC7, { 136 letter modifying circumflex } NPC7, { 137 per thousand (mille) sign } -83, { 138 uppercase S caron or hacek } -39, { 139 left single angle quote mark } -214, { 140 uppercase OE ligature } NPC7, { 141 } NPC7, { 142 } NPC7, { 143 } NPC7, { 144 } -39, { 145 left single quotation mark } -39, { 146 right single quote mark } -34, { 147 left double quotation mark } -34, { 148 right double quote mark } -42, { 149 round filled bullet } -45, { 150 en dash } -45, { 151 em dash } -39, { 152 small spacing tilde accent } NPC7, { 153 trademark sign } -115, { 154 lowercase s caron or hacek } -39, { 155 right single angle quote mark } -111, { 156 lowercase oe ligature } NPC7, { 157 } NPC7, { 158 } -89, { 159 uppercase Y dieresis or umlaut } -32, { 160   non-breaking space } 64, { 161 ¡ inverted exclamation mark } -99, { 162 ¢ cent sign } 1, { 163 £ pound sterling sign } 36, { 164 ¤ general currency sign } 3, { 165 ¥ yen sign } -33, { 166 ¦ broken vertical bar } 95, { 167 § section sign } -34, { 168 ¨ spacing dieresis or umlaut } NPC7, { 169 © copyright sign } NPC7, { 170 ª feminine ordinal indicator } -60, { 171 « left (double) angle quote } NPC7, { 172 ¬ logical not sign } -45, { 173 ­ soft hyphen } NPC7, { 174 ® registered trademark sign } NPC7, { 175 ¯ spacing macron (long) accent } NPC7, { 176 ° degree sign } NPC7, { 177 ± plus-or-minus sign } -50, { 178 ² superscript 2 } -51, { 179 ³ superscript 3 } -39, { 180 ´ spacing acute accent } -117, { 181 µ micro sign } NPC7, { 182 ¶ paragraph sign, pilcrow sign } NPC7, { 183 · middle dot, centered dot } NPC7, { 184 ¸ spacing cedilla } -49, { 185 ¹ superscript 1 } NPC7, { 186 º masculine ordinal indicator } -62, { 187 » right (double) angle quote (guillemet) } NPC7, { 188 ¼ fraction 1/4 } NPC7, { 189 ½ fraction 1/2 } NPC7, { 190 ¾ fraction 3/4 } 96, { 191 ¿ inverted question mark } -65, { 192 À uppercase A grave } -65, { 193 Á uppercase A acute } -65, { 194 Â uppercase A circumflex } -65, { 195 Ã uppercase A tilde } 91, { 196 Ä uppercase A dieresis or umlaut } 14, { 197 Å uppercase A ring } 28, { 198 Æ uppercase AE ligature } 9, { 199 Ç uppercase C cedilla } -31, { 200 È uppercase E grave } 31, { 201 É uppercase E acute } -31, { 202 Ê uppercase E circumflex } -31, { 203 Ë uppercase E dieresis or umlaut } -73, { 204 Ì uppercase I grave } -73, { 205 Í uppercase I acute } -73, { 206 Î uppercase I circumflex } -73, { 207 Ï uppercase I dieresis or umlaut } -68, { 208 Ð uppercase ETH } 93, { 209 Ñ uppercase N tilde } -79, { 210 Ò uppercase O grave } -79, { 211 Ó uppercase O acute } -79, { 212 Ô uppercase O circumflex } -79, { 213 Õ uppercase O tilde } 92, { 214 Ö uppercase O dieresis or umlaut } -42, { 215 × multiplication sign } 11, { 216 Ø uppercase O slash } -85, { 217 Ù uppercase U grave } -85, { 218 Ú uppercase U acute } -85, { 219 Û uppercase U circumflex } 94, { 220 Ü uppercase U dieresis or umlaut } -89, { 221 Ý uppercase Y acute } NPC7, { 222 Þ uppercase THORN } 30, { 223 ß lowercase sharp s, sz ligature } 127, { 224 à lowercase a grave } -97, { 225 á lowercase a acute } -97, { 226 â lowercase a circumflex } -97, { 227 ã lowercase a tilde } 123, { 228 ä lowercase a dieresis or umlaut } 15, { 229 å lowercase a ring } 29, { 230 æ lowercase ae ligature } -9, { 231 ç lowercase c cedilla } 4, { 232 è lowercase e grave } 5, { 233 é lowercase e acute } -101, { 234 ê lowercase e circumflex } -101, { 235 ë lowercase e dieresis or umlaut } 7, { 236 ì lowercase i grave } 7, { 237 í lowercase i acute } -105, { 238 î lowercase i circumflex } -105, { 239 ï lowercase i dieresis or umlaut } NPC7, { 240 ð lowercase eth } 125, { 241 ñ lowercase n tilde } 8, { 242 ò lowercase o grave } -111, { 243 ó lowercase o acute } -111, { 244 ô lowercase o circumflex } -111, { 245 õ lowercase o tilde } 124, { 246 ö lowercase o dieresis or umlaut } -47, { 247 ÷ division sign } 12, { 248 ø lowercase o slash } 6, { 249 ù lowercase u grave } -117, { 250 ú lowercase u acute } -117, { 251 û lowercase u circumflex } 126, { 252 ü lowercase u dieresis or umlaut } -121, { 253 ý lowercase y acute } NPC7, { 254 þ lowercase thorn } -121 { 255 ÿ lowercase y dieresis or umlaut } ); {*************************************************************************** This lookup table converts from the 7 bit "default alphabet" as defined in ETSI GSM 03.38 to a standard ISO-8859-1 8-bit ASCII. Some characters in the 7-bit alphabet does not exist in the ISO character set, they are replaced by the NPC8-character. If the character is decimal 27 (ESC) the following character have a special meaning and must be handled separately. ***************************************************************************} lookup_ascii7to8: array[0..127] of Byte = ( 64, { 0 @ COMMERCIAL AT } 163, { 1 £ POUND SIGN } 36, { 2 $ DOLLAR SIGN } 165, { 3 ¥ YEN SIGN } 232, { 4 è LATIN SMALL LETTER E WITH GRAVE } 233, { 5 é LATIN SMALL LETTER E WITH ACUTE } 249, { 6 ù LATIN SMALL LETTER U WITH GRAVE } 236, { 7 ì LATIN SMALL LETTER I WITH GRAVE } 242, { 8 ò LATIN SMALL LETTER O WITH GRAVE } 199, { 9 Ç LATIN CAPITAL LETTER C WITH CEDILLA } 10, { 10 LINE FEED } 216, { 11 Ø LATIN CAPITAL LETTER O WITH STROKE } 248, { 12 ø LATIN SMALL LETTER O WITH STROKE } 13, { 13 CARRIAGE RETURN } 197, { 14 Å LATIN CAPITAL LETTER A WITH RING ABOVE } 229, { 15 å LATIN SMALL LETTER A WITH RING ABOVE } NPC8, { 16 GREEK CAPITAL LETTER DELTA } 95, { 17 _ LOW LINE } NPC8, { 18 GREEK CAPITAL LETTER PHI } NPC8, { 19 GREEK CAPITAL LETTER GAMMA } NPC8, { 20 GREEK CAPITAL LETTER LAMBDA } NPC8, { 21 GREEK CAPITAL LETTER OMEGA } NPC8, { 22 GREEK CAPITAL LETTER PI } NPC8, { 23 GREEK CAPITAL LETTER PSI } NPC8, { 24 GREEK CAPITAL LETTER SIGMA } NPC8, { 25 GREEK CAPITAL LETTER THETA } NPC8, { 26 GREEK CAPITAL LETTER XI } 27, { 27 ESCAPE TO EXTENSION TABLE } 198, { 28 Æ LATIN CAPITAL LETTER AE } 230, { 29 æ LATIN SMALL LETTER AE } 223, { 30 ß LATIN SMALL LETTER SHARP S (German) } 201, { 31 É LATIN CAPITAL LETTER E WITH ACUTE } 32, { 32 SPACE } 33, { 33 ! EXCLAMATION MARK } 34, { 34 " QUOTATION MARK } 35, { 35 # NUMBER SIGN } 164, { 36 ¤ CURRENCY SIGN } 37, { 37 % PERCENT SIGN } 38, { 38 & AMPERSAND } 39, { 39 ' APOSTROPHE } 40, { 40 ( LEFT PARENTHESIS } 41, { 41 ) RIGHT PARENTHESIS } 42, { 42 * ASTERISK } 43, { 43 + PLUS SIGN } 44, { 44 , COMMA } 45, { 45 - HYPHEN-MINUS } 46, { 46 . FULL STOP } 47, { 47 / SOLIDUS (SLASH) } 48, { 48 0 DIGIT ZERO } 49, { 49 1 DIGIT ONE } 50, { 50 2 DIGIT TWO } 51, { 51 3 DIGIT THREE } 52, { 52 4 DIGIT FOUR } 53, { 53 5 DIGIT FIVE } 54, { 54 6 DIGIT SIX } 55, { 55 7 DIGIT SEVEN } 56, { 56 8 DIGIT EIGHT } 57, { 57 9 DIGIT NINE } 58, { 58 : COLON } 59, { 59 ; SEMICOLON } 60, { 60 < LESS-THAN SIGN } 61, { 61 = EQUALS SIGN } 62, { 62 > GREATER-THAN SIGN } 63, { 63 ? QUESTION MARK } 161, { 64 ¡ INVERTED EXCLAMATION MARK } 65, { 65 A LATIN CAPITAL LETTER A } 66, { 66 B LATIN CAPITAL LETTER B } 67, { 67 C LATIN CAPITAL LETTER C } 68, { 68 D LATIN CAPITAL LETTER D } 69, { 69 E LATIN CAPITAL LETTER E } 70, { 70 F LATIN CAPITAL LETTER F } 71, { 71 G LATIN CAPITAL LETTER G } 72, { 72 H LATIN CAPITAL LETTER H } 73, { 73 I LATIN CAPITAL LETTER I } 74, { 74 J LATIN CAPITAL LETTER J } 75, { 75 K LATIN CAPITAL LETTER K } 76, { 76 L LATIN CAPITAL LETTER L } 77, { 77 M LATIN CAPITAL LETTER M } 78, { 78 N LATIN CAPITAL LETTER N } 79, { 79 O LATIN CAPITAL LETTER O } 80, { 80 P LATIN CAPITAL LETTER P } 81, { 81 Q LATIN CAPITAL LETTER Q } 82, { 82 R LATIN CAPITAL LETTER R } 83, { 83 S LATIN CAPITAL LETTER S } 84, { 84 T LATIN CAPITAL LETTER T } 85, { 85 U LATIN CAPITAL LETTER U } 86, { 86 V LATIN CAPITAL LETTER V } 87, { 87 W LATIN CAPITAL LETTER W } 88, { 88 X LATIN CAPITAL LETTER X } 89, { 89 Y LATIN CAPITAL LETTER Y } 90, { 90 Z LATIN CAPITAL LETTER Z } 196, { 91 Ä LATIN CAPITAL LETTER A WITH DIAERESIS } 214, { 92 Ö LATIN CAPITAL LETTER O WITH DIAERESIS } 209, { 93 Ñ LATIN CAPITAL LETTER N WITH TILDE } 220, { 94 Ü LATIN CAPITAL LETTER U WITH DIAERESIS } 167, { 95 § SECTION SIGN } 191, { 96 ¿ INVERTED QUESTION MARK } 97, { 97 a LATIN SMALL LETTER A } 98, { 98 b LATIN SMALL LETTER B } 99, { 99 c LATIN SMALL LETTER C } 100, { 100 d LATIN SMALL LETTER D } 101, { 101 e LATIN SMALL LETTER E } 102, { 102 f LATIN SMALL LETTER F } 103, { 103 g LATIN SMALL LETTER G } 104, { 104 h LATIN SMALL LETTER H } 105, { 105 i LATIN SMALL LETTER I } 106, { 106 j LATIN SMALL LETTER J } 107, { 107 k LATIN SMALL LETTER K } 108, { 108 l LATIN SMALL LETTER L } 109, { 109 m LATIN SMALL LETTER M } 110, { 110 n LATIN SMALL LETTER N } 111, { 111 o LATIN SMALL LETTER O } 112, { 112 p LATIN SMALL LETTER P } 113, { 113 q LATIN SMALL LETTER Q } 114, { 114 r LATIN SMALL LETTER R } 115, { 115 s LATIN SMALL LETTER S } 116, { 116 t LATIN SMALL LETTER T } 117, { 117 u LATIN SMALL LETTER U } 118, { 118 v LATIN SMALL LETTER V } 119, { 119 w LATIN SMALL LETTER W } 120, { 120 x LATIN SMALL LETTER X } 121, { 121 y LATIN SMALL LETTER Y } 122, { 122 z LATIN SMALL LETTER Z } 228, { 123 ä LATIN SMALL LETTER A WITH DIAERESIS } 246, { 124 ö LATIN SMALL LETTER O WITH DIAERESIS } 241, { 125 ñ LATIN SMALL LETTER N WITH TILDE } 252, { 126 ü LATIN SMALL LETTER U WITH DIAERESIS } 224 { 127 à LATIN SMALL LETTER A WITH GRAVE } ); (* The double bytes below must be handled separately after the table lookup. 12 27 10 FORM FEED 94 27 20 ^ CIRCUMFLEX ACCENT 123 27 40 { LEFT CURLY BRACKET 125 27 41 } RIGHT CURLY BRACKET 92 27 47 \ REVERSE SOLIDUS (BACKSLASH) 91 27 60 [ LEFT SQUARE BRACKET 126 27 61 ~ TILDE 93 27 62 ] RIGHT SQUARE BRACKET 124 27 64 | VERTICAL BAR *) function HexToInt(W: PWord): Byte; var B, C: Byte; begin B := Lo(W^) - Ord('0'); if B > 9 then Dec(B, 7); C := Hi(W^) - Ord('0'); if C > 9 then Dec(C, 7); Result := B shl 4 + C; end; function IntToHex(B: Byte): Word; var C: Byte; begin C := (B and $F); B := B shr 4; if C > 9 then Inc(C, 7); if B > 9 then Inc(B, 7); Result := (C + Ord('0')) shl 8 or (B + Ord('0')); end; function PduToDec(P: PWord; Len: Cardinal): string; var W: PWord; begin SetLength(Result, Len); W := @Result[1]; while Len > 0 do begin W^ := Swap(P^); Inc(P); Inc(W); Dec(Len, 2); end; if (PChar(W)-1)^ = 'F' then SetLength(Result, Length(Result)-1); end; function DecToPdu(P: PWord; Len: Cardinal): string; var W: PByte; begin // decimal semi octet -- if Odd(Len) then Inc(Len); SetLength(Result, Len div 2); W := @Result[1]; while Len > 0 do begin W^ := (Hi(P^) shr 4 + Ord('0')) or (Lo(P^) + Ord('0')); Inc(P); Inc(W); Dec(Len, 2); end; end; function PduToBin(P: PWord; Len: Cardinal): string; var W: PByte; I: Integer; begin // make hex_pdu to bin_pdu -- SetLength(Result, Len div 2); W := @Result[1]; for I := 1 to Len div 2 do begin W^ := HexToInt(P); Inc(P); Inc(W); end; end; { * Use a lookup table to convert from the 7-bit default alphabet * used by SMS to an ISO-8859-1 ASCII string. * a7bit An array of the 7-bit 'string' to convert } function Convert_7bit_To_Ascii(const a7bit: string): string; var R: Integer; W: PChar; C: Char; begin SetLength(Result, Length(a7bit) * 2); R := 0; W := @Result[1]; while R < Length(a7bit) do begin Inc(R); C := Chr(lookup_ascii7to8[Ord(a7bit[R])]); if C = #27 then begin Inc(R); case Ord(a7bit[R]) of 10: C := #12; 20: C := '^'; 40: C := '{'; 41: C := '}'; 47: C := '\'; 60: C := '['; 61: C := '~'; 62: C := ']'; 64: C := '|'; else C := Chr(NPC8); end; end; W^ := C; Inc(W); end; SetLength(Result, W - @Result[1]); end; { * Use a lookup table to convert from an ISO-8859-1 string to * the 7-bit default alphabet used by SMS. * ascii The string to convert } function convert_ascii_to_7bit(const Ascii: string): string; var I: Integer; W: PChar; M: SmallInt; begin SetLength(Result, Length(Ascii) * 2); W := @Result[1]; for I := 1 to Length(Ascii) do begin M := Abs(lookup_ascii8to7[Ord(ascii[I])]); if M > 256 then begin W^ := #27; Inc(W); Dec(M, 256); end; W^ := Chr(M); Inc(W); end; SetLength(Result, W - @Result[1]); end; { * Convert a PDU-coded string to ISO-8859-1 ASCII * *pdu The pdu-array to convert to cleartext } function PduToAscii(W: PWord; Len: Cardinal): string; var R: Integer; C: PChar; S: string; begin SetLength(Result, Len); S := PduToBin(W, Len); C := @Result[1]; for R := 0 to Length(S)-1 do begin if R mod 7 = 0 then C^ := Chr(Ord(S[R+1]) and $7F) else if R mod 7 = 6 then begin C^ := Chr(((Ord(S[R+1]) shl 6) or (Ord(S[R]) shr 2)) and $7F); Inc(C); C^ := Chr(Ord(S[R+1]) shr 1 and $7F); end else C^ := Chr(((Ord(S[R+1]) shl (R mod 7)) or (Ord(S[R]) shr (7+1 - (R mod 7)))) and $7F); Inc(C); end; SetLength(Result, C - @Result[1]); Result := Convert_7bit_To_Ascii(Result); end; { * Convert an ISO-8859-1 ASCII string to an array of PDU-coded bytes * *ascii The ISO-cleartext to convert } function AsciiToPdu(const Ascii: string; P: PWord): Integer; var I: Cardinal; R: PChar; S: string; begin S := Convert_Ascii_To_7bit(Ascii); R := @S[1]; Result := Length(S); for I := 0 to Result-1 do begin P^ := IntToHex((Ord(R^) shr (I mod 7) and $7F) or (Ord((R+1)^) shl (7-(I mod 7)) and $FF)); if (I mod 7) = 6 then Inc(R); Inc(R); Inc(P); end; Result := (Result - Result div 8) * 2; end; function PduToDateTime(W: PWord): TDateTime; type TScts = packed record Y, M, D, H, N, S: Byte; T: ShortInt; end; PScts = ^TScts; var S: string; Cen: Word; I: Integer; B: PByte; begin S := PduToBin(W, 14); B := @S[1]; for I := 1 to 7 do begin B^ := (B^ and $0F) * 10 + B^ shr 4; Inc(B); end; with PScts(@S[1])^ do begin if Y > 95 then Cen := 1900 else Cen := 2000; Result := EncodeDate(Cen + Y, M, D) + EncodeTime(H, N, S, 0) - (T / 96) + (7 / 24); // GMT+7 end; end; { Decode PDU text to TSmsPdu structure ScToMs: True if PDU from SMS Center, False if from Mobile Station Txt: PDU text Result: TSMSPdu record } function DecodePdu(ScToMs: Boolean; const Txt: string): TSmsPdu; type TPDUAddr = packed record Len, Toa, Str: Word; end; PPDUAddr = ^TPDUAddr; var I, J: Integer; begin I := 1; with PPDUAddr(@Txt[I])^ do begin J := HexToInt(@Len)* 2; if J > 0 then Result.Center := PduToDec(@Str, J-2); Inc(I, J + 2); end; // type of PDU (from first octet) -- Result.Fio := TSmsFios(HexToInt(@Txt[I])); Inc(I, 2); case Byte(Result.Fio) and 3 of 0:if ScToMs then Result.MTI := mtiDeliver else begin Result.MTI := mtiDeliverRep; Result.Ref := HexToInt(@Txt[I]); Exit; end; 1: if ScToMs then begin Result.MTI := mtiSubmitRep; Result.Ref := HexToInt(@Txt[I]); Exit; end else begin Result.MTI := mtiSubmit; Exit; end; 2:if ScToMs then begin Result.MTI := mtiStatusRep; Result.Ref := HexToInt(@Txt[I]); Inc(I, 2); end else begin Result.MTI := mtiCommand; Exit; end; end; // only sms_deliver & status_report is processed -- with PPDUAddr(@Txt[I])^ do begin J := HexToInt(@Len); if Odd(J) then Inc(J); Result.Phone := PduToDec(@Str, J); Inc(I, J + 4); end; if Result.MTI = mtiDeliver then begin Result.PID := HexToInt(@Txt[I]); Result.DCS := HexToInt(@Txt[I+2]); Inc(I, 4); end; Result.SCTS := PduToDateTime(@Txt[I]); Inc(I, 14); if Result.MTI = mtiDeliver then begin J := HexToInt(@Txt[I]); Result.Text := PduToAscii(@Txt[I+2], Length(Txt) - I); if Length(Result.Text) > J then SetLength(Result.Text, J); end else begin Result.Text := ''; Result.Stat := HexToInt(@Txt[I+14]); // 0..2 = success end; end; { Encode ascii text to PDU text (to submit via mobile station) Str: Plain text to encode Phone: Mobile station number Result: PDU text } function EncodePdu(const Str, Phone: string): string; type TPduSubmit = packed record Smsc_Len, Fo, Ref, Rcv_Len, Rcv_Toa: Word; end; PPduSubmit = ^TPduSubmit; var W: PWord; B: TSmsFios; I: Integer; begin SetLength(Result, Length(Str)*2 + Length(Phone)*2 + 9*2); B := [fioSR, fioVP1, fioMM_RD, fioMT2]; with PPduSubmit(@Result[1])^ do begin Smsc_Len := $3030; // 00: without service center Fo := IntToHex(Byte(B)); Ref := $3030; // 00: ref from phone Rcv_Len := IntToHex(Length(Phone)); Rcv_Toa := $3139; // 91: internat. format end; W := @Result[SizeOf(TPduSubmit)+1]; for I := 1 to Length(Phone) shr 1 do begin W^ := Swap(PWord(@Phone[(I-1)*2+1])^); Inc(W); end; if Odd(Length(Phone)) then begin W^ := Swap($4600 + Ord(Phone[Length(Phone)])); Inc(W); end; W^ := $3030; // PID: 00 Inc(W); W^ := $3030; // DCS: 00 Inc(W); W^ := $4141; // VP: AA (4 days) Inc(W); W^ := IntToHex(Length(Str)); Inc(W); SetLength(Result, PChar(W) - @Result[1] + AsciiToPdu(Str, W)); end; end.