OLD | NEW |
1 // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sms_message.h" | 5 #include "sms_message.h" |
6 | 6 |
7 #include <glog/logging.h> | 7 #include <glog/logging.h> |
8 | 8 |
9 #include "utilities.h" | 9 #include "utilities.h" |
10 | 10 |
11 static const uint8_t INTL_E164_NUMBER_FORMAT = 0x91; | 11 static const uint8_t kIntlE164NumberFormat = 0x91; |
12 static const uint8_t SMSC_TIMESTAMP_LEN = 7; | 12 static const uint8_t kAlphaFormat = 0xd0; |
13 static const size_t MIN_PDU_LEN = 7 + SMSC_TIMESTAMP_LEN; | 13 static const uint8_t kDataCodingSchemeGsm7 = 0; |
| 14 static const uint8_t kDataCodingSchemeUcs2 = 8; |
| 15 static const uint8_t kSmscTimestampLen = 7; |
| 16 static const size_t kMinPduLen = 7 + kSmscTimestampLen; |
14 | 17 |
15 static char NibbleToChar(uint8_t nibble) { | 18 static char NibbleToChar(uint8_t nibble) { |
16 switch (nibble) { | 19 switch (nibble) { |
17 case 0: return '0'; | 20 case 0: return '0'; |
18 case 1: return '1'; | 21 case 1: return '1'; |
19 case 2: return '2'; | 22 case 2: return '2'; |
20 case 3: return '3'; | 23 case 3: return '3'; |
21 case 4: return '4'; | 24 case 4: return '4'; |
22 case 5: return '5'; | 25 case 5: return '5'; |
23 case 6: return '6'; | 26 case 6: return '6'; |
(...skipping 27 matching lines...) Expand all Loading... |
51 if (second != '\0') | 54 if (second != '\0') |
52 bcd += second; | 55 bcd += second; |
53 } | 56 } |
54 return bcd; | 57 return bcd; |
55 } | 58 } |
56 | 59 |
57 SmsMessage* SmsMessage::CreateMessage(const uint8_t* pdu, size_t pdu_len) { | 60 SmsMessage* SmsMessage::CreateMessage(const uint8_t* pdu, size_t pdu_len) { |
58 utilities::DumpHex(pdu, pdu_len); | 61 utilities::DumpHex(pdu, pdu_len); |
59 | 62 |
60 // Make sure the PDU is of a valid size | 63 // Make sure the PDU is of a valid size |
61 if (pdu_len < MIN_PDU_LEN) { | 64 if (pdu_len < kMinPduLen) { |
62 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " << MIN_PDU_LEN; | 65 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " << kMinPduLen; |
63 return NULL; | 66 return NULL; |
64 } | 67 } |
65 | 68 |
66 // Format of message: | 69 // Format of message: |
67 // | 70 // |
68 // 1 octet - length of SMSC information in octets, including type field | 71 // 1 octet - length of SMSC information in octets, including type field |
69 // 1 octet - type of address of SMSC (value 0x91 is international E.164) | 72 // 1 octet - type of address of SMSC (value 0x91 is international E.164) |
70 // variable - SMSC address | 73 // variable - SMSC address |
71 // 1 octet - first octet of SMS-DELIVER (value = 0x04) | 74 // 1 octet - first octet of SMS-DELIVER (value = 0x04) |
72 // 1 octet - length of sender address in decimal digits (semi-octets) | 75 // 1 octet - length of sender address in decimal digits (semi-octets) |
73 // 1 octet - type of sender address (value 0x91 is international E.164) | 76 // 1 octet - type of sender address (value 0x91 is international E.164) |
74 // variable - sender address | 77 // variable - sender address |
75 // 1 octet - protocol identifier (value = 0) | 78 // 1 octet - protocol identifier (value = 0) |
76 // 1 octet - data coding scheme (value = 0) | 79 // 1 octet - data coding scheme (value 0 is GSM7, 8 is UCS2) |
77 // 7 octets - SMSC timestamp | 80 // 7 octets - SMSC timestamp |
78 // 1 octet - user data length in septets | 81 // 1 octet - user data length in septets |
79 // variable - user data (body of message) | 82 // variable - user data (body of message) |
80 | 83 |
81 // Do a bunch of validity tests first so we can bail out early | 84 // Do a bunch of validity tests first so we can bail out early |
82 // if we're not able to handle the PDU. We first check the validity | 85 // if we're not able to handle the PDU. We first check the validity |
83 // of all length fields and make sure the PDU length is consistent | 86 // of all length fields and make sure the PDU length is consistent |
84 // with those values. | 87 // with those values. |
85 | 88 |
86 uint8_t smsc_addr_num_octets = pdu[0]; | 89 uint8_t smsc_addr_num_octets = pdu[0]; |
87 uint8_t variable_length_items = smsc_addr_num_octets; | 90 uint8_t variable_length_items = smsc_addr_num_octets; |
88 | 91 |
89 if (pdu_len < variable_length_items + MIN_PDU_LEN) { | 92 if (pdu_len < variable_length_items + kMinPduLen) { |
90 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " | 93 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " |
91 << variable_length_items + MIN_PDU_LEN; | 94 << variable_length_items + kMinPduLen; |
92 return NULL; | 95 return NULL; |
93 } | 96 } |
94 | 97 |
95 // where in the PDU the actual SMS protocol message begins | 98 // where in the PDU the actual SMS protocol message begins |
96 uint8_t msg_start_offset = 1 + smsc_addr_num_octets; | 99 uint8_t msg_start_offset = 1 + smsc_addr_num_octets; |
97 uint8_t sender_addr_num_digits = pdu[msg_start_offset + 1]; | 100 uint8_t sender_addr_num_digits = pdu[msg_start_offset + 1]; |
98 // round the sender address length up to an even number of semi-octets, | 101 // round the sender address length up to an even number of semi-octets, |
99 // and thus an integral number of octets | 102 // and thus an integral number of octets |
100 uint8_t sender_addr_num_octets = (sender_addr_num_digits + 1) >> 1; | 103 uint8_t sender_addr_num_octets = (sender_addr_num_digits + 1) >> 1; |
101 variable_length_items += sender_addr_num_octets; | 104 variable_length_items += sender_addr_num_octets; |
102 if (pdu_len < variable_length_items + MIN_PDU_LEN) { | 105 if (pdu_len < variable_length_items + kMinPduLen) { |
103 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " | 106 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " |
104 << variable_length_items + MIN_PDU_LEN; | 107 << variable_length_items + kMinPduLen; |
105 return NULL; | 108 return NULL; |
106 } | 109 } |
107 | 110 |
108 uint8_t tp_pid_offset = msg_start_offset + 3 + sender_addr_num_octets; | 111 uint8_t tp_pid_offset = msg_start_offset + 3 + sender_addr_num_octets; |
109 uint8_t user_data_offset = tp_pid_offset + 2 + SMSC_TIMESTAMP_LEN; | 112 uint8_t user_data_offset = tp_pid_offset + 2 + kSmscTimestampLen; |
110 uint8_t user_data_num_septets = pdu[user_data_offset]; | 113 uint8_t user_data_num_septets = pdu[user_data_offset]; |
111 variable_length_items += (7 * (user_data_num_septets + 1 )) / 8; | 114 variable_length_items += (7 * (user_data_num_septets + 1 )) / 8; |
112 | 115 |
113 if (pdu_len < variable_length_items + MIN_PDU_LEN) { | 116 if (pdu_len < variable_length_items + kMinPduLen) { |
114 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " | 117 LOG(INFO) << "PDU too short: " << pdu_len << " vs. " |
115 << variable_length_items + MIN_PDU_LEN; | 118 << variable_length_items + kMinPduLen; |
116 return NULL; | 119 return NULL; |
117 } | 120 } |
118 | 121 |
119 // wow do some validity checks on the values of several fields in the PDU | 122 // now do some validity checks on the values of several fields in the PDU |
120 | 123 |
121 // smsc number format must be international, E.164 | 124 // smsc number format must be international, E.164 |
122 if (pdu[1] != INTL_E164_NUMBER_FORMAT) { | 125 if (pdu[1] != kIntlE164NumberFormat) { |
123 LOG(INFO) << "Invalid SMSC address format: " << std::hex << (int)pdu[1] | 126 LOG(INFO) << "Invalid SMSC address format: " << std::hex << (int)pdu[1] |
124 << " vs. " << std::hex << INTL_E164_NUMBER_FORMAT; | 127 << " vs. " << std::hex << kIntlE164NumberFormat; |
125 return NULL; | 128 return NULL; |
126 } | 129 } |
127 // we only handle SMS-DELIVER messages, with more-messages-to-send false | 130 // we only handle SMS-DELIVER messages, with more-messages-to-send false |
128 if ((pdu[msg_start_offset] & 0x07) != 0x04) { | 131 if ((pdu[msg_start_offset] & 0x07) != 0x04) { |
129 LOG(INFO) << "Unhandled message type: " << std::hex | 132 LOG(INFO) << "Unhandled message type: " << std::hex |
130 << (int)pdu[msg_start_offset] << " vs. 0x04"; | 133 << (int)pdu[msg_start_offset] << " vs. 0x04"; |
131 return NULL; | 134 return NULL; |
132 } | 135 } |
| 136 // we only handle E164 and ALPHA sender address formats |
| 137 uint8_t sender_addr_type = pdu[msg_start_offset + 2]; |
| 138 if (sender_addr_type != kIntlE164NumberFormat && |
| 139 sender_addr_type != kAlphaFormat) { |
| 140 LOG(INFO) << "Unhandled sender address format: " << std::hex << |
| 141 sender_addr_type; |
| 142 return NULL; |
| 143 } |
133 // we only handle the basic protocol identifier | 144 // we only handle the basic protocol identifier |
134 if (pdu[tp_pid_offset] != 0) { | 145 if (pdu[tp_pid_offset] != 0) { |
135 LOG(INFO) << "Unhandled protocol identifier: " << std::hex | 146 LOG(INFO) << "Unhandled protocol identifier: " << std::hex |
136 << (int)pdu[tp_pid_offset] << " vs. 0x00"; | 147 << (int)pdu[tp_pid_offset] << " vs. 0x00"; |
137 return NULL; | 148 return NULL; |
138 } | 149 } |
139 // for data coding scheme, we only handle the default alphabet, i.e. GSM7 | 150 // for data coding scheme, we only handle the default alphabet, i.e. GSM7 |
140 if (pdu[tp_pid_offset+1] != 0) { | 151 uint8_t dcs = pdu[tp_pid_offset+1] & 0x0ec; |
| 152 if (dcs != kDataCodingSchemeGsm7 && dcs != kDataCodingSchemeUcs2) { |
141 LOG(INFO) << "Unhandled data coding scheme: " << std::hex | 153 LOG(INFO) << "Unhandled data coding scheme: " << std::hex |
142 << (int)pdu[tp_pid_offset+1] << " vs. 0x00"; | 154 << (int)pdu[tp_pid_offset+1]; |
143 return NULL; | 155 return NULL; |
144 } | 156 } |
145 | 157 |
146 std::string smsc_addr = SemiOctetsToBcdString(&pdu[2], | 158 std::string smsc_addr = SemiOctetsToBcdString(&pdu[2], |
147 smsc_addr_num_octets-1); | 159 smsc_addr_num_octets-1); |
148 std::string sender_addr = SemiOctetsToBcdString(&pdu[msg_start_offset+3], | 160 std::string sender_addr; |
149 sender_addr_num_octets); | 161 if (sender_addr_type == kIntlE164NumberFormat) |
| 162 sender_addr = SemiOctetsToBcdString(&pdu[msg_start_offset+3], |
| 163 sender_addr_num_octets); |
| 164 else { |
| 165 uint8_t *bytes = new uint8_t[sender_addr_num_octets+1]; |
| 166 bytes[0] = (sender_addr_num_digits * 4) / 7; |
| 167 memcpy(&bytes[1], &pdu[msg_start_offset+3], sender_addr_num_octets); |
| 168 sender_addr = utilities::Gsm7ToUtf8String(bytes); |
| 169 delete [] bytes; |
| 170 } |
| 171 |
150 std::string sc_timestamp = SemiOctetsToBcdString(&pdu[tp_pid_offset+2], | 172 std::string sc_timestamp = SemiOctetsToBcdString(&pdu[tp_pid_offset+2], |
151 SMSC_TIMESTAMP_LEN-1); | 173 kSmscTimestampLen-1); |
152 std::string msg_text = utilities::Gsm7ToUtf8String(&pdu[user_data_offset]); | 174 std::string msg_text; |
| 175 if (dcs == kDataCodingSchemeGsm7) |
| 176 msg_text = utilities::Gsm7ToUtf8String(&pdu[user_data_offset]); |
| 177 else |
| 178 msg_text = utilities::Ucs2ToUtf8String(&pdu[user_data_offset]); |
153 | 179 |
154 // The last two semi-octets of the timestamp indicate an offset from | 180 // The last two semi-octets of the timestamp indicate an offset from |
155 // GMT, and are handled differently than the first 12 semi-octets. | 181 // GMT, and are handled differently than the first 12 semi-octets. |
156 uint8_t toff_octet = pdu[tp_pid_offset+1+SMSC_TIMESTAMP_LEN]; | 182 uint8_t toff_octet = pdu[tp_pid_offset+1+kSmscTimestampLen]; |
157 sc_timestamp += (toff_octet & 0x8) ? '-' : '+'; | 183 sc_timestamp += (toff_octet & 0x8) ? '-' : '+'; |
158 uint8_t offset_in_hours = | 184 uint8_t offset_in_hours = |
159 ((toff_octet & 0x7) << 4 | (toff_octet & 0xf0) >> 4) / 4; | 185 ((toff_octet & 0x7) << 4 | (toff_octet & 0xf0) >> 4) / 4; |
160 sc_timestamp += (char)((offset_in_hours / 10) + '0'); | 186 sc_timestamp += (char)((offset_in_hours / 10) + '0'); |
161 sc_timestamp += (char)((offset_in_hours % 10) + '0'); | 187 sc_timestamp += (char)((offset_in_hours % 10) + '0'); |
162 | 188 |
163 return new SmsMessage(smsc_addr, sender_addr, sc_timestamp, msg_text); | 189 return new SmsMessage(smsc_addr, sender_addr, sc_timestamp, msg_text); |
164 } | 190 } |
OLD | NEW |