1
2
3
4
5 package net.sf.atmodem4j.core.gsm;
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 import java.util.Calendar;
36 import java.util.Date;
37 import java.util.TimeZone;
38 import net.sf.atmodem4j.core.gsm.Pdu.Encoding;
39
40
41
42
43
44 public class PduDecoder {
45
46 private static ThreadLocal<PduDecoder> pduFactory =
47 new ThreadLocal<PduDecoder>();
48 private String octets;
49 private int currentPos;
50
51 private void decodeDataCodingScheme(Pdu pdu) {
52 short octet = decodeOctet();
53 if ((octet & 0xC0) == 0x00) {
54 pdu.setCompressed((octet & 0x20) == 0x20);
55 switch (octet & 0x0C) {
56 case 0x00:
57 pdu.setEncoding(Encoding.TEXT_7BIT);
58 break;
59 case 0x04:
60 pdu.setEncoding(Encoding.DATA_8BIT);
61 break;
62 case 0x08:
63 pdu.setEncoding(Encoding.TEXT_16BIT);
64 }
65
66 } else {
67 throw new RuntimeException("Data coding group not supported");
68 }
69 }
70
71 private PduDeliver decodePduDeliver(String smscAddress) {
72 PduDeliver pdu = new PduDeliver();
73 pdu.setSmscAddress(smscAddress);
74
75 pdu.setOrginatorAddress(decodeAddress());
76
77 currentPos++;
78
79 decodeDataCodingScheme(pdu);
80 pdu.setSmscTimestamp(decodeTimeStamp());
81 return pdu;
82 }
83
84 private PduSubmit decodePduSubmit(String smscAddress) {
85 PduSubmit pdu = new PduSubmit();
86 pdu.setSmscAddress(smscAddress);
87
88 currentPos++;
89
90 pdu.setDestinationAddress(decodeAddress());
91
92 currentPos++;
93
94 decodeDataCodingScheme(pdu);
95 return pdu;
96 }
97
98 private int decodeSemiOctet() {
99 int offset = 2 * currentPos++;
100 return Character.digit(octets.charAt(offset), 10) + Character.digit(
101 octets.charAt(offset + 1), 10) * 10;
102 }
103
104 private Date decodeTimeStamp() {
105 int year = 2001 + decodeSemiOctet();
106 int month = decodeSemiOctet();
107 int day = decodeSemiOctet();
108 int hour = decodeSemiOctet();
109 int minute = decodeSemiOctet();
110 int second = decodeSemiOctet();
111 int timezoneOffset15 = (byte) decodeSemiOctet();
112 Calendar cal = Calendar.getInstance();
113 cal.setTimeZone(TimeZone.getTimeZone("GMT" + (timezoneOffset15 >= 0 ? "+" + timezoneOffset15 / 4.0
114 : timezoneOffset15 / 4.0)));
115 cal.set(year - 1, month - 1, day, hour - timezoneOffset15 / 4, minute +
116 (timezoneOffset15 % 4) * 15, second);
117 return cal.getTime();
118 }
119
120 private byte[] decodeUserDataOctets() {
121 byte[] result = new byte[(octets.length() / 2) - currentPos];
122 for (int i = 0; i < result.length; i++) {
123 result[i] = (byte)decodeOctet();
124 }
125 return result;
126 }
127
128 public static PduDecoder getPduDecoder() {
129 if (pduFactory.get() == null) {
130 pduFactory.set(new PduDecoder());
131 }
132 return pduFactory.get();
133 }
134
135
136
137
138
139
140 public Pdu decodeFromOctets(String octets) {
141 Pdu result = null;
142 this.octets = octets;
143
144 currentPos = 0;
145
146 String smscAddress = decodeSmscAddress();
147
148 int octet = decodeOctet();
149
150 boolean isUserDataHeader = (octet & 0x40) == 0x40;
151 switch (octet & 0x03) {
152 case 0x00:
153 result = decodePduDeliver(smscAddress);
154 break;
155 case 0x01:
156 result = decodePduSubmit(smscAddress);
157
158
159 currentPos++;
160 break;
161 }
162
163 final int userDataLength = decodeOctet();
164 final int userDataStart = currentPos;
165 int userDataHeaderLength = 0;
166 if (isUserDataHeader) {
167
168 userDataHeaderLength = decodeOctet();
169 userDataHeaderLength ++;
170 int refNumber = decodeOctet();
171 int userDataHeaderALength = decodeOctet();
172 result.setMsgId(decodeOctet());
173 result.setPartCount(decodeOctet());
174 result.setPartIndex(decodeOctet());
175 }
176 result.setUserData(decodeUserDataOctets(), userDataLength, userDataHeaderLength);
177 if (currentPos != octets.length() / 2) {
178 throw new RuntimeException("length mismatch");
179 }
180 return result;
181 }
182
183 private String decodeSmscAddress() {
184 StringBuilder sb = new StringBuilder();
185 int length = decodeOctet();
186 if (length == 0) {
187 return "";
188 }
189 int addressType = decodeOctet();
190 if (addressType == 0x91) {
191 sb.append('+');
192 }
193 final int end = (currentPos + length - 1) * 2;
194 for (int strPos = currentPos * 2; strPos < end; strPos += 2) {
195 sb.append(octets.charAt(strPos + 1));
196 sb.append(octets.charAt(strPos));
197 currentPos++;
198 }
199 return sb.toString();
200 }
201
202 private String decodeAddress() {
203 StringBuilder sb = new StringBuilder();
204 int length = decodeOctet();
205 if (length == 0) {
206 currentPos++;
207 return "";
208 }
209 int addressType = decodeOctet();
210 if (addressType == 0x91) {
211 sb.append('+');
212 }
213 for (int strPos = currentPos * 2; strPos < (currentPos + length / 2) * 2;
214 strPos += 2) {
215 sb.append(octets.charAt(strPos + 1));
216 sb.append(octets.charAt(strPos));
217 }
218 if (length % 2 != 0) {
219
220 sb.append(octets.charAt(currentPos * 2 + length));
221 }
222 currentPos += ((length % 2 == 0) ? length / 2 : length / 2 + 1);
223 return sb.toString();
224 }
225
226 private short decodeOctet() {
227 int offset = 2 * currentPos++;
228 return (short) (Character.digit(octets.charAt(offset), 16) << 4 |
229 Character.digit(octets.charAt(offset + 1), 16));
230 }
231 }