Fix ussd message corruption
[samsung-ril.git] / ss.c
1 /**
2  * This file is part of samsung-ril.
3  *
4  * Copyright (C) 2012 Paul Kocialkowski <contact@oaulk.fr>
5  *
6  * samsung-ril is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * samsung-ril is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with samsung-ril.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #define LOG_TAG "RIL-SS"
22 #include <utils/Log.h>
23
24 #include "samsung-ril.h"
25 #include "util.h"
26
27 void ipc_ss_ussd_complete(struct ipc_message_info *info)
28 {
29         struct ipc_gen_phone_res *phone_res = (struct ipc_gen_phone_res *) info->data;
30         int rc;
31
32         rc = ipc_gen_phone_res_check(phone_res);
33         if(rc < 0) {
34                 LOGE("There was an error, aborting USSD request");
35
36                 RIL_onRequestComplete(reqGetToken(info->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
37                 ril_state.ussd_state = 0;
38
39                 return;
40         }
41
42         RIL_onRequestComplete(reqGetToken(info->aseq), RIL_E_SUCCESS, NULL, 0);
43 }
44
45 void ril_request_send_ussd(RIL_Token t, void *data, size_t datalen)
46 {
47         char *data_enc = NULL;
48         int data_enc_len = 0;
49
50         char *message =NULL;
51         struct ipc_ss_ussd *ussd = NULL;
52
53         int message_size = 0xc0;
54
55         switch(ril_state.ussd_state) {
56                 case 0:
57                 case IPC_SS_USSD_NO_ACTION_REQUIRE:
58                 case IPC_SS_USSD_TERMINATED_BY_NET:
59                 case IPC_SS_USSD_OTHER_CLIENT:
60                 case IPC_SS_USSD_NOT_SUPPORT:
61                 case IPC_SS_USSD_TIME_OUT:
62                         LOGD("USSD Tx encoding is GSM7");
63
64                         data_enc_len = ascii2gsm7(data, &data_enc, datalen);
65                         // message_size = data_enc_len + sizeof(struct ipc_ss_ussd);
66
67                         if(data_enc_len > message_size) {
68                                 LOGE("USSD message size is too long, aborting");
69                                 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
70
71                                 free(data_enc);
72
73                                 return;
74                         }
75
76                         message = malloc(message_size);
77                         memset(message, 0, message_size);
78
79                         ussd = (struct ipc_ss_ussd *) message;
80                         ussd->state = IPC_SS_USSD_NO_ACTION_REQUIRE;
81                         ussd->dcs = 0x0f; // GSM7 in that case
82                         ussd->length = data_enc_len;
83
84                         memcpy((void *) (message + sizeof(struct ipc_ss_ussd)), data_enc, data_enc_len);
85
86                         free(data_enc);
87
88                         break;
89                 case IPC_SS_USSD_ACTION_REQUIRE:
90                 default:
91                         LOGD("USSD Tx encoding is ASCII");
92
93                         data_enc_len = asprintf(&data_enc, "%s", data);
94                         // message_size = data_enc_len + sizeof(struct ipc_ss_ussd);
95
96                         if(data_enc_len > message_size) {
97                                 LOGE("USSD message size is too long, aborting");
98                                 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
99
100                                 free(data_enc);
101
102                                 return;
103                         }
104
105                         message = malloc(message_size);
106                         memset(message, 0, message_size);
107
108                         ussd = (struct ipc_ss_ussd *) message;
109                         ussd->state = IPC_SS_USSD_ACTION_REQUIRE;
110                         ussd->dcs = 0x0f; // ASCII in that case
111                         ussd->length = data_enc_len;
112
113                         memcpy((void *) (message + sizeof(struct ipc_ss_ussd)), data_enc, data_enc_len);
114
115                         free(data_enc);
116
117                         break;
118         }
119
120         if(message == NULL) {
121                 LOGE("USSD message is empty, aborting");
122
123                 RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
124                 return;
125         }
126
127         ipc_gen_phone_res_expect_to_func(reqGetId(t), IPC_SS_USSD,
128                 ipc_ss_ussd_complete);
129
130         ipc_fmt_send(IPC_SS_USSD, IPC_TYPE_EXEC, (void *) message, message_size, reqGetId(t));
131 }
132
133 void ril_request_cancel_ussd(RIL_Token t, void *data, size_t datalen)
134 {
135         struct ipc_ss_ussd ussd;
136
137         memset(&ussd, 0, sizeof(ussd));
138
139         ussd.state = IPC_SS_USSD_TERMINATED_BY_NET;
140         ril_state.ussd_state = IPC_SS_USSD_TERMINATED_BY_NET;
141         
142         ipc_gen_phone_res_expect_to_complete(reqGetId(t), IPC_SS_USSD);
143
144         ipc_fmt_send(IPC_SS_USSD, IPC_TYPE_EXEC, (void *) &ussd, sizeof(ussd), reqGetId(t));
145 }
146
147 void ipc2ril_ussd_state(struct ipc_ss_ussd *ussd, char *message[2])
148 {
149         switch(ussd->state) {
150                 case IPC_SS_USSD_NO_ACTION_REQUIRE:
151                         asprintf(&message[0], "%d", 0);
152                         break;
153                 case IPC_SS_USSD_ACTION_REQUIRE:
154                         asprintf(&message[0], "%d", 1);
155                         break;
156                 case IPC_SS_USSD_TERMINATED_BY_NET:
157                         asprintf(&message[0], "%d", 2);
158                         break;
159                 case IPC_SS_USSD_OTHER_CLIENT:
160                         asprintf(&message[0], "%d", 3);
161                         break;
162                 case IPC_SS_USSD_NOT_SUPPORT:
163                         asprintf(&message[0], "%d", 4);
164                         break;
165                 case IPC_SS_USSD_TIME_OUT:
166                         asprintf(&message[0], "%d", 5);
167                         break;
168         }
169 }
170
171 void ipc_ss_ussd(struct ipc_message_info *info)
172 {
173         char *data_dec = NULL;
174         int data_dec_len = 0;
175         SmsCodingScheme codingScheme;
176
177         char *message[2];
178
179         struct ipc_ss_ussd *ussd = NULL;
180         unsigned char state;
181
182         memset(message, 0, sizeof(message));
183
184         ussd = (struct ipc_ss_ussd *) info->data;
185
186         ipc2ril_ussd_state(ussd, message);
187
188         ril_state.ussd_state = ussd->state;
189
190         if(ussd->length > 0 && info->length > 0 && info->data != NULL) {
191                 codingScheme = sms_get_coding_scheme(ussd->dcs);
192                 switch(codingScheme) {
193                         case SMS_CODING_SCHEME_GSM7:
194                                 LOGD("USSD Rx encoding is GSM7");
195
196                                 data_dec_len = gsm72ascii(info->data
197                                         + sizeof(struct ipc_ss_ussd), &data_dec, info->length - sizeof(struct ipc_ss_ussd));
198                                 asprintf(&message[1], "%s", data_dec);
199                                 message[1][data_dec_len] = '\0';
200
201                                 break;
202                         case SMS_CODING_SCHEME_UCS2:
203                                 LOGD("USSD Rx encoding %x is UCS2", ussd->dcs);
204
205                                 data_dec_len = info->length - sizeof(struct ipc_ss_ussd);
206                                 message[1] = malloc(data_dec_len * 4 + 1);
207
208                                 int i, result = 0;
209                                 char *ucs2 = (char*)info->data + sizeof(struct ipc_ss_ussd);
210                                 for (i = 0; i < data_dec_len; i += 2) {
211                                         int c = (ucs2[i] << 8) | ucs2[1 + i];
212                                         result += utf8_write(message[1], result, c);
213                                 }
214                                 message[1][result] = '\0';
215                                 break;
216                         default:
217                                 LOGD("USSD Rx encoding %x is unknown, assuming ASCII",
218                                         ussd->dcs);
219
220                                 data_dec_len = info->length - sizeof(struct ipc_ss_ussd);
221                                 asprintf(&message[1], "%s", info->data + sizeof(struct ipc_ss_ussd));
222                                 message[1][data_dec_len] = '\0';
223                                 break;
224                 }
225         }
226
227         RIL_onUnsolicitedResponse(RIL_UNSOL_ON_USSD, message, sizeof(message));
228 }