A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 /* (c) 2002-2008 by Marcin Wiacek and Michal Cihar */ 2 3 /** 4 * @file atgen.c 5 * @author Michal Čihař 6 * @author Marcin Wiacek 7 */ 8 /** 9 * @ingroup Phone 10 * @{ 11 */ 12 /** 13 * @addtogroup ATPhone 14 * @{ 15 */ 16 17 #include "gammu-error.h" 18 #define _GNU_SOURCE 19 #include <gammu-config.h> 20 21 #ifdef GSM_ENABLE_ATGEN 22 23 #include <string.h> 24 #include <time.h> 25 #include <ctype.h> 26 #include <stdarg.h> 27 #include <assert.h> 28 29 #include "../../gsmcomon.h" 30 #include "../../gsmphones.h" 31 #include "../../misc/coding/coding.h" 32 #include "../../service/gsmpbk.h" 33 #include "../pfunc.h" 34 35 #include "atgen.h" 36 #include "atfunc.h" 37 38 #include "../../../libgammu/misc/string.h" 39 40 GSM_Error ATGEN_SetSMSC(GSM_StateMachine *s, GSM_SMSC *smsc) 41 { 42 GSM_Error error; 43 /* 44 * String value. 45 * It indicates the SMSC number. 46 * The numberr is composed '*', '#', '0'-'9' 47 * The number contains 20 characters at most. 48 */ 49 unsigned char smscCmdReq[GSM_MAX_NUMBER_LENGTH + 12]={'\0'}; 50 51 if (smsc->Location != 1) { 52 return ERR_INVALIDLOCATION; 53 } 54 smprintf(s, "Setting SMSC\n"); 55 sprintf(smscCmdReq, "AT+CSCA=\"%s\"\r",DecodeUnicodeString(smsc->Number)); 56 error = ATGEN_WaitForAutoLen(s, smscCmdReq, 0x00, 40, ID_SetSMSC); 57 return error; 58 } 59 60 GSM_Error ATGEN_ReplyGetSMSMemories(GSM_Protocol_Message *msg, GSM_StateMachine *s) 61 { 62 char *pos_start = NULL, *pos_end = NULL, *pos_tmp = NULL; 63 const char *Line; 64 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 65 66 switch (Priv->ReplyState) { 67 case AT_Reply_OK: 68 /* Reply here is: 69 * (memories for reading)[, (memories for writing)[, (memories for storing received messages)]] 70 * each memory is in quotes, 71 * Example: ("SM"), ("SM"), ("SM") 72 * 73 * TODO: Reply can be also "SM", "SM", "SM" 74 * 75 * We need to get from this supported memories. For this case 76 * we assume, that just appearence of memory makes it 77 * available for everything. Then we need to find out whether 78 * phone supports writing to memory. This is done by searching 79 * for "), (", which will appear between lists. 80 * 81 * @todo: Add support for BM (broadcast messages). 82 */ 83 Priv->PhoneSaveSMS = AT_NOTAVAILABLE; 84 Priv->SIMSaveSMS = AT_NOTAVAILABLE; 85 Priv->SRSaveSMS = AT_NOTAVAILABLE; 86 87 Line = GetLineString(msg->Buffer, &Priv->Lines, 2); 88 /* Skip empty line in response */ 89 if (strcmp(Line, "") == 0) { 90 Line = GetLineString(msg->Buffer, &Priv->Lines, 3); 91 } 92 if (strcmp(Line, "+CPMS: ") == 0 && Priv->Manufacturer == AT_Samsung) { 93 smprintf(s, "Assuming broken Samsung response, both memories available!\n"); 94 Priv->PhoneSMSMemory = AT_AVAILABLE; 95 Priv->SIMSMSMemory = AT_AVAILABLE; 96 Priv->PhoneSaveSMS = AT_AVAILABLE; 97 Priv->SIMSaveSMS = AT_AVAILABLE; 98 goto completed; 99 } 100 101 if (strchr(msg->Buffer, '(') == NULL) { 102 smprintf(s, "Assuming broken iWOW style response, no lists!\n"); 103 pos_start = strstr(msg->Buffer, "\", \""); 104 105 if (pos_start == NULL) { 106 pos_start = strstr(msg->Buffer, "\",\""); 107 } 108 } else { 109 pos_start = strstr(msg->Buffer, "), ("); 110 111 if (pos_start == NULL) { 112 pos_start = strstr(msg->Buffer, "),("); 113 } 114 } 115 if (pos_start != NULL) { 116 /* Detect which memories we can use for saving */ 117 pos_end = strchrnul(pos_start + 1, ')'); 118 pos_tmp = strstr(pos_start, "\"SM\""); 119 120 if (pos_tmp != NULL && pos_tmp < pos_end) { 121 Priv->SIMSaveSMS = AT_AVAILABLE; 122 } 123 pos_tmp = strstr(pos_start, "\"ME\""); 124 125 if (pos_tmp != NULL && pos_tmp < pos_end) { 126 Priv->PhoneSaveSMS = AT_AVAILABLE; 127 } 128 129 pos_tmp = strstr(pos_start, "\"SR\""); 130 131 if (pos_tmp != NULL && pos_tmp < pos_end) { 132 Priv->SRSaveSMS = AT_AVAILABLE; 133 } 134 } 135 if (strstr(msg->Buffer, "\"SM\"") != NULL) { 136 Priv->SIMSMSMemory = AT_AVAILABLE; 137 } else { 138 Priv->SIMSMSMemory = AT_NOTAVAILABLE; 139 } 140 if (strstr(msg->Buffer, "\"SR\"") != NULL) { 141 Priv->SRSMSMemory = AT_AVAILABLE; 142 } else { 143 Priv->SRSMSMemory = AT_NOTAVAILABLE; 144 } 145 if (strstr(msg->Buffer, "\"ME\"") != NULL) { 146 Priv->PhoneSMSMemory = AT_AVAILABLE; 147 } else { 148 Priv->PhoneSMSMemory = AT_NOTAVAILABLE; 149 150 /* Check for Motorola style folders */ 151 if (strstr(msg->Buffer, "\"MT\"") != NULL && strstr(msg->Buffer, "\"OM\"") != NULL) { 152 Priv->PhoneSMSMemory = AT_AVAILABLE; 153 Priv->PhoneSaveSMS = AT_AVAILABLE; 154 Priv->MotorolaSMS = TRUE; 155 } 156 157 } 158 completed: 159 smprintf(s, "Available SMS memories received: read: ME : %s, SM : %s, SR : %s save: ME : %s, SM : %s, SR : %s, Motorola = %s\n", 160 Priv->PhoneSMSMemory == AT_AVAILABLE ? "ok" : "N/A", 161 Priv->SIMSMSMemory == AT_AVAILABLE ? "ok" : "N/A", 162 Priv->SRSMSMemory == AT_AVAILABLE ? "ok" : "N/A", 163 Priv->PhoneSaveSMS == AT_AVAILABLE ? "ok" : "N/A", 164 Priv->SIMSaveSMS == AT_AVAILABLE ? "ok" : "N/A", 165 Priv->SRSaveSMS == AT_AVAILABLE ? "ok" : "N/A", 166 Priv->MotorolaSMS ? "yes" : "no" 167 ); 168 169 return ERR_NONE; 170 case AT_Reply_Error: 171 return ERR_NOTSUPPORTED; 172 case AT_Reply_CMSError: 173 return ATGEN_HandleCMSError(s); 174 case AT_Reply_CMEError: 175 return ATGEN_HandleCMEError(s); 176 default: 177 return ERR_UNKNOWNRESPONSE; 178 } 179 } 180 181 GSM_Error ATGEN_GetSMSMemories(GSM_StateMachine *s) 182 { 183 GSM_Error error; 184 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 185 186 smprintf(s, "Getting available SMS memories\n"); 187 error = ATGEN_WaitForAutoLen(s, "AT+CPMS=?\r", 0x00, 200, ID_GetSMSMemories); 188 189 if (error != ERR_NONE) { 190 return error; 191 } 192 193 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_SM)) { 194 smprintf(s, "Forcing support for SM storage!\n"); 195 Priv->SIMSaveSMS = AT_AVAILABLE; 196 Priv->SIMSMSMemory = AT_AVAILABLE; 197 } 198 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_ME)) { 199 smprintf(s, "Forcing support for ME storage!\n"); 200 Priv->PhoneSMSMemory = AT_AVAILABLE; 201 Priv->PhoneSaveSMS = AT_AVAILABLE; 202 } 203 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_SR)) { 204 smprintf(s, "Forcing support for SR storage!\n"); 205 Priv->SRSMSMemory = AT_AVAILABLE; 206 } 207 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_NO_SR)) { 208 smprintf(s, "Forcing to disable SR storage!\n"); 209 Priv->SRSMSMemory = AT_NOTAVAILABLE; 210 } 211 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_NO_ME)) { 212 smprintf(s, "Forcing to disable ME storage!\n"); 213 Priv->PhoneSMSMemory = AT_NOTAVAILABLE; 214 Priv->PhoneSaveSMS = AT_NOTAVAILABLE; 215 } 216 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_NO_SM)) { 217 smprintf(s, "Forcing to disable SM storage!\n"); 218 Priv->SIMSMSMemory = AT_NOTAVAILABLE; 219 Priv->SIMSaveSMS = AT_NOTAVAILABLE; 220 } 221 222 // count standard folders 223 Priv->NumFolders = 0; 224 if(ATGEN_IsMemoryAvailable(Priv, MEM_SM)) 225 Priv->NumFolders++; 226 227 if(ATGEN_IsMemoryAvailable(Priv, MEM_ME)) 228 Priv->NumFolders++; 229 230 return ERR_NONE; 231 } 232 233 GSM_Error ATGEN_SetSMSMemory(GSM_StateMachine *s, gboolean SIM, gboolean for_write, gboolean outbox) 234 { 235 GSM_Error error; 236 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 237 238 /* 239 * Store message to memory. 240 */ 241 unsigned char cpmsCmdReq[] = "AT+CPMS=\"XX\",\"XX\"\r"; 242 size_t cpmsCmdReqLength = strlen(cpmsCmdReq); 243 244 /* If phone encodes also values in command, we need normal charset */ 245 if (Priv->EncodedCommands) { 246 error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL); 247 248 if (error != ERR_NONE) { 249 return error; 250 } 251 } 252 if ((SIM && Priv->SIMSMSMemory == 0) || (!SIM && Priv->PhoneSMSMemory == 0)) { 253 /* We silently ignore error here, because when this fails, we can try to setmemory anyway */ 254 ATGEN_GetSMSMemories(s); 255 } 256 257 /* If phone can not save SMS, don't try to set memory for saving */ 258 if (for_write) { 259 if (SIM && Priv->SIMSaveSMS == AT_NOTAVAILABLE) { 260 smprintf(s, "Saving SMS not supported!\n"); 261 return ERR_NOTSUPPORTED; 262 } 263 if (!SIM && Priv->PhoneSaveSMS == AT_NOTAVAILABLE) { 264 smprintf(s, "Saving SMS not supported!\n"); 265 return ERR_NOTSUPPORTED; 266 } 267 } else { 268 /* No need to set memory for writing */ 269 cpmsCmdReq[12] = '\r'; 270 cpmsCmdReqLength = 13; 271 } 272 if (SIM) { 273 if (Priv->SMSMemory == MEM_SM && (Priv->SMSMemoryWrite || !for_write)) { 274 return ERR_NONE; 275 } 276 if (Priv->SIMSMSMemory == AT_NOTAVAILABLE) { 277 return ERR_NOTSUPPORTED; 278 } 279 cpmsCmdReq[9] = 'S'; cpmsCmdReq[10] = 'M'; 280 cpmsCmdReq[14] = 'S'; cpmsCmdReq[15] = 'M'; 281 282 smprintf(s, "Setting SMS memory type to SM\n"); 283 error = ATGEN_WaitFor(s, cpmsCmdReq, cpmsCmdReqLength, 0x00, 20, ID_SetMemoryType); 284 285 if (Priv->SIMSMSMemory == 0 && error != ERR_NONE) { 286 Priv->SIMSMSMemory = AT_AVAILABLE; 287 } 288 if (error == ERR_NOTSUPPORTED) { 289 smprintf(s, "Can't access SIM card?\n"); 290 return ERR_SECURITYERROR; 291 } 292 if (error != ERR_NONE) { 293 return error; 294 } 295 Priv->SMSMemory = MEM_SM; 296 Priv->SMSMemoryWrite = for_write; 297 } else { 298 if (Priv->SMSMemory == MEM_ME && (Priv->SMSMemoryWrite || !for_write)) { 299 return ERR_NONE; 300 } 301 if (Priv->PhoneSMSMemory == AT_NOTAVAILABLE) { 302 return ERR_NOTSUPPORTED; 303 } 304 if (Priv->MotorolaSMS) { 305 cpmsCmdReq[9] = 'M'; cpmsCmdReq[10] = 'T'; 306 307 if (outbox) { 308 cpmsCmdReq[14] = 'O'; cpmsCmdReq[15] = 'M'; 309 } else { 310 cpmsCmdReq[14] = 'I'; cpmsCmdReq[15] = 'M'; 311 } 312 } else { 313 cpmsCmdReq[9] = 'M'; cpmsCmdReq[10] = 'E'; 314 cpmsCmdReq[14] = 'M'; cpmsCmdReq[15] = 'E'; 315 } 316 smprintf(s, "Setting SMS memory type to ME\n"); 317 error = ATGEN_WaitFor(s, cpmsCmdReq, cpmsCmdReqLength, 0x00, 200, ID_SetMemoryType); 318 319 if (Priv->PhoneSMSMemory == 0 && error == ERR_NONE) { 320 Priv->PhoneSMSMemory = AT_AVAILABLE; 321 } 322 if (error != ERR_NONE) { 323 return error; 324 } 325 Priv->SMSMemory = MEM_ME; 326 Priv->SMSMemoryWrite = for_write; 327 } 328 return error; 329 } 330 331 GSM_Error ATGEN_SetSMSMode(GSM_StateMachine *s, int mode) 332 { 333 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 334 GSM_Error error = ERR_NONE; 335 if (mode == SMS_AT_PDU) 336 { 337 error = ATGEN_WaitForAutoLen(s, "AT+CMGF=0\r", 0x00, 9, ID_GetSMSMode); 338 if (error == ERR_NONE) { 339 Priv->SMSMode = SMS_AT_PDU; 340 } 341 return error; 342 } else { 343 error = ATGEN_WaitForAutoLen(s, "AT+CMGF=1\r", 0x00, 9, ID_GetSMSMode); 344 if (error == ERR_NONE) { 345 Priv->SMSMode = SMS_AT_TXT; 346 error = ATGEN_WaitForAutoLen(s, "AT+CSDH=1\r", 0x00, 3, ID_GetSMSMode); 347 348 if (error == ERR_NONE) { 349 Priv->SMSTextDetails = TRUE; 350 } else { 351 error = ERR_NONE; 352 } 353 } 354 return error; 355 } 356 357 } 358 359 GSM_Error ATGEN_GetSMSMode(GSM_StateMachine *s) 360 { 361 GSM_Error error = ERR_NONE; 362 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 363 364 if (Priv->SMSMode != 0) { 365 return ERR_NONE; 366 } 367 368 /* Prefer PDU mode for most phones */ 369 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_USE_SMSTEXTMODE)) { 370 smprintf(s, "Forcibily enabled SMS text mode\n"); 371 } else { 372 smprintf(s, "Trying SMS PDU mode\n"); 373 if (ATGEN_SetSMSMode(s, SMS_AT_PDU) == ERR_NONE) { 374 return ERR_NONE; 375 } 376 } 377 smprintf(s, "Trying SMS text mode\n"); 378 ATGEN_SetSMSMode(s, SMS_AT_TXT); 379 return error; 380 } 381 382 GSM_Error ATGEN_SetRequestedSMSMemory(GSM_StateMachine *s, GSM_MemoryType memoryType, gboolean writeable, 383 GSM_Phone_RequestID requestId) 384 { 385 GSM_Error error; 386 unsigned char command[20]; 387 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 388 389 if (!memoryType || memoryType == MEM_INVALID) { 390 smprintf_level(s, D_ERROR, "SMS memory type not set or invalid.\n"); 391 return ERR_INVALID_OPERATION; 392 } 393 394 if (!ATGEN_IsMemoryAvailable(Priv, memoryType) || 395 (writeable && !ATGEN_IsMemoryWriteable(Priv, memoryType))) 396 { 397 smprintf_level(s, D_ERROR, "Requested memory not available for %s: %s (%d)\n", 398 writeable ? "writing" : "reading", 399 GSM_MemoryTypeToString(memoryType), memoryType); 400 return ERR_MEMORY_NOT_AVAILABLE; 401 } 402 403 if (Priv->SMSMemory == memoryType && Priv->SMSMemoryWrite == writeable) { 404 smprintf(s, "Requested memory type already set: %s\n", 405 GSM_MemoryTypeToString(memoryType)); 406 return ERR_NONE; 407 } 408 409 snprintf(command, 20, "AT+CPMS=\"%s\"\r", GSM_MemoryTypeToString(memoryType)); 410 if (writeable) { 411 // if it's writeable we assume it's also readable 412 snprintf(command + 12, 8, ",\"%s\"\r", GSM_MemoryTypeToString(memoryType)); 413 } 414 415 /* If phone encodes also values in command, we need normal charset */ 416 if (Priv->EncodedCommands) { 417 error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL); 418 419 if (error != ERR_NONE) { 420 return error; 421 } 422 } 423 424 smprintf(s, "Setting SMS memory to %s\n", command + 8); 425 error = ATGEN_WaitFor(s, command, strlen(command), 0x00, 20, requestId); 426 427 if(error == ERR_NONE) { 428 Priv->SMSMemory = memoryType; 429 Priv->SMSMemoryWrite = writeable; 430 } 431 return error; 432 } 433 434 GSM_Error ATGEN_GetSMSLocation(GSM_StateMachine *s, GSM_SMSMessage *sms, unsigned char *folderid, int *location, gboolean for_write) 435 { 436 GSM_Error error; 437 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 438 int ifolderid = 0, maxfolder = 0; 439 440 if (Priv->PhoneSMSMemory == 0 || Priv->SIMSMSMemory == 0 || Priv->SRSMSMemory == 0) { 441 error = ATGEN_GetSMSMemories(s); 442 if(error != ERR_NONE) 443 return error; 444 } 445 446 if (Priv->SIMSMSMemory != AT_AVAILABLE && Priv->PhoneSMSMemory != AT_AVAILABLE) { 447 smprintf(s, "No SMS memory at all!\n"); 448 return ERR_NOTSUPPORTED; 449 } 450 if (Priv->SIMSMSMemory == AT_AVAILABLE && Priv->PhoneSMSMemory == AT_AVAILABLE) { 451 /* Both available */ 452 maxfolder = 4; 453 } else { 454 /* One available */ 455 maxfolder = 2; 456 } 457 458 /* simulate flat SMS memory */ 459 if (sms->Folder == 0x00) { 460 ifolderid = sms->Location / GSM_PHONE_MAXSMSINFOLDER; 461 462 if (ifolderid + 1 > maxfolder) { 463 smprintf(s, "Too high location for flat folder: %d (folder=%d, maxfolder=%d)\n", 464 sms->Location, 465 ifolderid + 1, 466 maxfolder); 467 return ERR_NOTSUPPORTED; 468 } 469 *folderid = ifolderid + 1; 470 *location = sms->Location - ifolderid * GSM_PHONE_MAXSMSINFOLDER; 471 } else { 472 if (sms->Folder > 2 * maxfolder) { 473 smprintf(s, "Too high folder: folder=%d, maxfolder=%d\n", 474 sms->Folder, 475 maxfolder); 476 return ERR_NOTSUPPORTED; 477 } 478 *folderid = sms->Folder <= 2 ? 1 : 2; 479 *location = sms->Location; 480 } 481 482 /* Some phones start locations from 0, handle them here */ 483 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_LOCATION_0)) { 484 (*location)--; 485 } 486 smprintf(s, "SMS folder %i & location %i -> ATGEN folder %i & location %i\n", 487 sms->Folder, sms->Location, *folderid, *location); 488 489 // if needed memory type already set, use it 490 if(sms->Memory && sms->Memory != MEM_INVALID) { 491 return ATGEN_SetRequestedSMSMemory(s, sms->Memory, for_write, ID_SetMemoryType); 492 } 493 494 /* Set the needed memory type */ 495 if (Priv->SIMSMSMemory == AT_AVAILABLE && 496 *folderid == 1) { 497 sms->Memory = MEM_SM; 498 return ATGEN_SetSMSMemory(s, TRUE, for_write, (sms->Folder % 2) == 0); 499 } else { 500 sms->Memory = MEM_ME; 501 return ATGEN_SetSMSMemory(s, FALSE, for_write, (sms->Folder % 2) == 0); 502 } 503 } 504 505 /** 506 * Converts location from AT internal to Gammu API. We need to ensure 507 * locations in API are sequential over all folders. 508 */ 509 void ATGEN_SetSMSLocation(GSM_StateMachine *s, GSM_SMSMessage *sms, unsigned char folderid, int location) 510 { 511 sms->Folder = 0; /* Flat memory */ 512 sms->Location = (folderid - 1) * GSM_PHONE_MAXSMSINFOLDER + location; 513 514 /* Some phones start locations from 0, handle them here */ 515 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_LOCATION_0)) { 516 sms->Location++; 517 } 518 smprintf(s, "ATGEN folder %i & location %i -> SMS folder %i & location %i\n", 519 folderid, location, sms->Folder, sms->Location); 520 } 521 522 GSM_Error ATGEN_DecodePDUMessage(GSM_StateMachine *s, const char *PDU, const int state) 523 { 524 GSM_Error error; 525 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 526 GSM_SMSMessage *sms = &s->Phone.Data.GetSMSMessage->SMS[0]; 527 unsigned char *buffer; 528 size_t parse_len = 0, length = 0; 529 530 length = strlen(PDU); 531 532 /* Special dummy message used by Siemens MC35i to fill up memory when using MT storage */ 533 if (strcmp(PDU, "00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") == 0) { 534 return ERR_CORRUPTED; 535 } else if (strcmp(PDU, "00") == 0) { 536 return ERR_EMPTY; 537 } 538 539 /* Allocate memory for binary data */ 540 buffer = (unsigned char*)malloc((length / 2) + 1); 541 if (buffer == NULL) { 542 return ERR_MOREMEMORY; 543 } 544 545 /* 546 * Strip possible ,0 at the end of reply. 547 * It actually should not be there, but it simply happens with some phones. 548 */ 549 while (length >= 2 && PDU[length - 1] == '0' && PDU[length - 2] == ',') { 550 length -= 2; 551 } 552 553 /* Decode hex encoded binary data */ 554 if (!DecodeHexBin(buffer, PDU, length)) { 555 smprintf(s, "Failed to decode hex string!\n"); 556 free(buffer); 557 return ERR_CORRUPTED; 558 } 559 560 /* We decoded hex -> binary */ 561 length /= 2; 562 563 /* Set message state */ 564 switch (state) { 565 case 0: 566 sms->State = SMS_UnRead; 567 break; 568 case 1: 569 sms->State = SMS_Read; 570 break; 571 case 2: 572 sms->State = SMS_UnSent; 573 break; 574 default: 575 sms->State = SMS_Sent; 576 break; 577 } 578 579 /* Decode PDU */ 580 error = GSM_DecodePDUFrame(&(s->di), sms, buffer, length, &parse_len, TRUE); 581 582 if (error != ERR_NONE) { 583 free(buffer); 584 return error; 585 } 586 if (parse_len != length) { 587 smprintf(s, "Did not parse all PDU data (%u, %u)!\n", (unsigned int)parse_len, (unsigned int)length); 588 589 if (buffer[parse_len] == 0xff) { 590 smprintf(s, "Assuming broken phone which pads SMS data with FF\n"); 591 } else if (buffer[parse_len] == 0x89) { 592 /* Not sure what the data here means, see tests/at-sms/39.dump */ 593 smprintf(s, "Assuming we can ignore anything starting with 0x89\n"); 594 } else if(sms->PDU == SMS_Status_Report) { 595 smprintf(s, "Assuming we can ignore extra data after successfully parsing status report\n"); 596 } 597 else { 598 free(buffer); 599 return ERR_UNKNOWN; 600 } 601 } 602 free(buffer); 603 604 /* Set folder */ 605 switch (sms->PDU) { 606 case SMS_Deliver: 607 /* Fix possibly wrong state */ 608 if (sms->State == SMS_Sent) { 609 sms->State = SMS_Read; 610 } 611 /* @bug Broken when MEM_SM is not available */ 612 if (Priv->SMSMemory == MEM_SM) { 613 sms->Folder = 1; /*INBOX SIM*/ 614 } else { 615 sms->Folder = 3; /*INBOX ME*/ 616 } 617 sms->InboxFolder = TRUE; 618 break; 619 case SMS_Submit: 620 /* @bug Broken when MEM_SM is not available */ 621 if (Priv->SMSMemory == MEM_SM) { 622 sms->Folder = 2; /*OUTBOX SIM*/ 623 smprintf(s, "Outbox SIM\n"); 624 } else { 625 sms->Folder = 4; /*OUTBOX ME*/ 626 } 627 sms->InboxFolder = FALSE; 628 break; 629 case SMS_Status_Report: 630 sms->PDU = SMS_Status_Report; 631 sms->Folder = 1; /*INBOX SIM*/ 632 sms->InboxFolder = TRUE; 633 break; 634 } 635 return ERR_NONE; 636 } 637 638 GSM_Error ATGEN_ReadSMSText(GSM_Protocol_Message *msg, GSM_StateMachine *s, GSM_SMSMessage *sms) 639 { 640 int i; 641 const char *line; 642 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 643 size_t length = 0; 644 GSM_Error error; 645 646 /* Go trough all lines till OK */ 647 for (i = 3; strcmp(line = GetLineString(msg->Buffer, &Priv->Lines, i), "OK") != 0; i++) { 648 if (i > 3) { 649 /* Include new line */ 650 sms->Text[(2 * sms->Length) + 0] = 0; 651 sms->Text[(2 * sms->Length) + 1] = '\n'; 652 sms->Text[(2 * sms->Length) + 2] = 0; 653 sms->Text[(2 * sms->Length) + 3] = 0; 654 sms->Length++; 655 } 656 length = GetLineLength(msg->Buffer, &Priv->Lines, i); 657 error = ATGEN_DecodeText(s, line, length, 658 sms->Text + (2 * sms->Length), 659 sizeof(sms->Text) - (2 * sms->Length), 660 TRUE, FALSE); 661 if (error != ERR_NONE) { 662 return error; 663 } 664 sms->Length += length; 665 } 666 return ERR_NONE; 667 } 668 669 GSM_Error ATGEN_ReplyGetSMSMessage(GSM_Protocol_Message *msg, GSM_StateMachine *s) 670 { 671 GSM_Error error = ERR_UNKNOWN; 672 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 673 GSM_SMSMessage *sms = &s->Phone.Data.GetSMSMessage->SMS[0]; 674 unsigned char buffer[3000] = {'\0'}, firstbyte = 0, TPDCS = 0, TPUDL = 0, TPStatus = 0, TPPID = 0; 675 int current = 0, i = 0; 676 int state = 0; 677 unsigned char *ptr; 678 char *comma; 679 char *expected_comma; 680 681 switch (Priv->ReplyState) { 682 case AT_Reply_OK: 683 if (Priv->Lines.numbers[4] == 0x00) return ERR_EMPTY; 684 s->Phone.Data.GetSMSMessage->Number = 1; 685 s->Phone.Data.GetSMSMessage->SMS[0].Name[0] = 0; 686 s->Phone.Data.GetSMSMessage->SMS[0].Name[1] = 0; 687 688 switch (Priv->SMSMode) { 689 case SMS_AT_PDU: 690 CopyLineString(buffer, msg->Buffer, &Priv->Lines, 2); 691 692 /* Parse reply */ 693 error = ATGEN_ParseReply(s, buffer, "+CMGR: @i, @0", &state); 694 if (error == ERR_UNKNOWNRESPONSE) { 695 error = ATGEN_ParseReply(s, buffer, "+CMGR: ,@0"); 696 } 697 if (error == ERR_UNKNOWNRESPONSE) { 698 /* Some phones like ES75 lack state information, which we ignore anywa */ 699 error = ATGEN_ParseReply(s, buffer, "+CMGR: @i", &state); 700 } 701 if (error == ERR_UNKNOWNRESPONSE) { 702 /* Some phones like QUALCOMM lack state information */ 703 error = ATGEN_ParseReply(s, buffer, "+CMGR: ,,@i", &state); 704 } 705 if (error != ERR_NONE) { 706 return error; 707 } 708 709 /* Siemens MC35 (only ?) */ 710 if (strcmp(buffer, "+CMGR: 0,,0") == 0) { 711 return ERR_EMPTY; 712 } 713 714 error = ATGEN_DecodePDUMessage(s, GetLineString(msg->Buffer,&Priv->Lines,3), state); 715 return error; 716 case SMS_AT_TXT: 717 GSM_SetDefaultReceivedSMSData(sms); 718 719 /* 720 * This is just a hack until proper parsing of text mode is done. 721 * It uses old style of manual parsing, to skip entries parsed above. 722 */ 723 current = 0; 724 725 /* Skip to first : */ 726 while (msg->Buffer[current] != ':') { 727 current++; 728 } 729 current++; 730 /* Skip any spaces */ 731 while (msg->Buffer[current] == ' ') { 732 current++; 733 } 734 735 /* Grab first parameter */ 736 current += ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 737 738 /* Remove leading " */ 739 for (ptr = buffer; *ptr == '"'; ptr++); 740 if (ptr != buffer) { 741 memmove (buffer, ptr, strlen (ptr) + 1); 742 } 743 744 /* Go to the end of string */ 745 for (ptr = buffer; *ptr; ptr++); 746 /* Remove trailing " */ 747 ptr--; 748 while (ptr >= buffer && *ptr == '"') { 749 ptr--; 750 } 751 ptr++; 752 *ptr = 0; 753 754 smprintf(s, "Message type: %s\n", buffer); 755 756 /* Check message type */ 757 if (!strcmp(buffer,"0") || !strcmp(buffer,"REC UNREAD")) { 758 smprintf(s, "SMS type - deliver\n"); 759 sms->State = SMS_UnRead; 760 sms->PDU = SMS_Deliver; 761 762 if (Priv->SMSMemory == MEM_SM) { 763 sms->Folder = 1; /*INBOX SIM*/ 764 } else { 765 sms->Folder = 3; /*INBOX ME*/ 766 } 767 sms->InboxFolder = TRUE; 768 } else if (!strcmp(buffer,"1") || !strcmp(buffer,"REC READ")) { 769 smprintf(s, "SMS type - deliver\n"); 770 sms->State = SMS_Read; 771 sms->PDU = SMS_Deliver; 772 773 if (Priv->SMSMemory == MEM_SM) { 774 sms->Folder = 1; /*INBOX SIM*/ 775 } else { 776 sms->Folder = 3; /*INBOX ME*/ 777 } 778 sms->InboxFolder = TRUE; 779 } else if (!strcmp(buffer,"2") || !strcmp(buffer,"STO UNSENT")) { 780 smprintf(s, "SMS type - submit\n"); 781 sms->State = SMS_UnSent; 782 sms->PDU = SMS_Submit; 783 784 if (Priv->SMSMemory == MEM_SM) { 785 sms->Folder = 2; /*OUTBOX SIM*/ 786 } else { 787 sms->Folder = 4; /*OUTBOX ME*/ 788 } 789 sms->InboxFolder = FALSE; 790 } else if (!strcmp(buffer,"3") || !strcmp(buffer,"STO SENT")) { 791 smprintf(s, "SMS type - submit\n"); 792 sms->State = SMS_Sent; 793 sms->PDU = SMS_Submit; 794 795 if (Priv->SMSMemory == MEM_SM) { 796 sms->Folder = 2; /*OUTBOX SIM*/ 797 } else { 798 sms->Folder = 4; /*OUTBOX ME*/ 799 } 800 sms->InboxFolder = FALSE; 801 } else { 802 smprintf(s, "Uknown message state: %s\n", buffer); 803 return ERR_UNKNOWN; 804 } 805 806 /* Do we have detailed format? */ 807 if (Priv->SMSTextDetails == FALSE) { 808 sms->Class = 1; 809 sms->Coding = SMS_Coding_Default_No_Compression; 810 sms->UDH.Type = UDH_NoUDH; 811 sms->Length = 0; 812 sms->SMSC.Number[0]=0; 813 sms->SMSC.Number[1]=0; 814 sms->ReplyViaSameSMSC = FALSE; 815 816 return ATGEN_ReadSMSText(msg, s, sms); 817 } 818 819 current += ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 820 /* It's delivery report according to Nokia AT standards */ 821 if ((sms->Folder == 1 || sms->Folder == 3) && buffer[0]!=0 && buffer[0]!='"') { 822 /* ??? */ 823 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 824 825 /* Sender number */ 826 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 827 828 /* FIXME: support for all formats */ 829 EncodeUnicode(sms->Number,buffer+1,strlen(buffer)-2); 830 smprintf(s, "Sender \"%s\"\n",DecodeUnicodeString(sms->Number)); 831 832 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 833 firstbyte = atoi(buffer); 834 835 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer+i); 836 smprintf(s, "\"%s\"\n",buffer); 837 error = ATGEN_DecodeDateTime(s, &sms->DateTime, buffer); 838 839 if (error != ERR_NONE) { 840 return error; 841 } 842 /* Date of SMSC response */ 843 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 844 i = strlen(buffer); 845 buffer[i] = ','; 846 i++; 847 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer+i); 848 smprintf(s, "\"%s\"\n",buffer); 849 error = ATGEN_DecodeDateTime(s, &sms->SMSCTime, buffer); 850 851 if (error != ERR_NONE) { 852 return error; 853 } 854 /* TPStatus */ 855 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 856 TPStatus = atoi(buffer); 857 buffer[PHONE_SMSDeliver.firstbyte] = firstbyte; 858 buffer[PHONE_SMSStatusReport.TPStatus] = TPStatus; 859 error = GSM_DecodeSMSFrameStatusReportData(&(s->di), sms, buffer, PHONE_SMSStatusReport); 860 861 if (error != ERR_NONE) { 862 return error; 863 } 864 /* NO SMSC number */ 865 sms->SMSC.Number[0]=0; 866 sms->SMSC.Number[1]=0; 867 sms->PDU = SMS_Status_Report; 868 sms->ReplyViaSameSMSC = FALSE; 869 } else { 870 /* FIXME: support for all formats */ 871 EncodeUnicode(sms->Number,buffer+1,strlen(buffer)-2); 872 873 if (strlen(buffer)!=0) { 874 EncodeUnicode(sms->Number,buffer+1,strlen(buffer)-2); 875 } 876 smprintf(s, "Sender \"%s\"\n",DecodeUnicodeString(sms->Number)); 877 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 878 879 /* Sending datetime */ 880 881 if (sms->Folder == 1 || sms->Folder == 3) { 882 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 883 884 smprintf(s, "\"%s\"\n",buffer); 885 886 if (*buffer) { 887 error = ATGEN_DecodeDateTime(s, &sms->DateTime, buffer); 888 if (error != ERR_NONE) { 889 return error; 890 } 891 } else { 892 /* FIXME: What is the proper undefined GSM_DateTime ? */ 893 memset(&sms->DateTime, 0, sizeof(sms->DateTime)); 894 } 895 error = ATGEN_DecodeDateTime(s, &sms->DateTime, buffer); 896 897 if (error != ERR_NONE) { 898 return error; 899 } 900 } 901 902 /* address type */ 903 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 904 905 /* First byte */ 906 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 907 firstbyte = atoi(buffer); 908 sms->ReplyViaSameSMSC = FALSE; 909 smprintf (s, "buffer firstbyte:%s\n", buffer); 910 911 /* TP PID */ 912 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 913 sms->ReplaceMessage = 0; 914 915 TPPID = atoi(buffer); 916 917 /* TP DCS */ 918 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 919 TPDCS = atoi(buffer); 920 smprintf(s, "TPDCS: %02x\n", TPDCS); 921 /* SMSC number */ 922 /* FIXME: support for all formats */ 923 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 924 925 if (buffer[0] != '"' && buffer[0]) { 926 /*TP VP */ 927 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 928 } 929 930 EncodeUnicode(sms->SMSC.Number,buffer+1,strlen(buffer)-2); 931 932 /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */ 933 if ((firstbyte & 128)==128) { 934 sms->ReplyViaSameSMSC = TRUE; 935 } 936 937 if (TPPID > 0x40 && TPPID < 0x48) { 938 sms->ReplaceMessage = TPPID - 0x40; 939 } 940 smprintf(s, "TPPID: %02x %i\n", TPPID, TPPID); 941 942 /* Format of SMSC number */ 943 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 944 945 /* TPUDL */ 946 current+=ATGEN_ExtractOneParameter(msg->Buffer+current, buffer); 947 TPUDL = atoi(buffer); 948 current++; 949 sms->Coding = GSM_GetMessageCoding(&(s->di), TPDCS); 950 sms->Class = -1; 951 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */ 952 if ((TPDCS & 0xD0) == 0x10 || (TPDCS & 0xF0) == 0xF0) { 953 sms->Class = TPDCS & 3; 954 } 955 956 smprintf(s, "SMS class: %i\n",sms->Class); 957 958 switch (sms->Coding) { 959 case SMS_Coding_Default_No_Compression: 960 /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */ 961 /* If not SMS with UDH, it's coded normal */ 962 /* If UDH available, treat it as Unicode or 8 bit */ 963 if ((firstbyte & 0x40)!=0x40) { 964 sms->UDH.Type = UDH_NoUDH; 965 error = ATGEN_ReadSMSText(msg, s, sms); 966 if (sms->Length == TPUDL + 4) { 967 char *tail; 968 tail = sms->Text + 2 * (UnicodeLength (sms->Text) - 4); 969 if (tail[0] == 0 && tail[1] == ',' && tail[4] == 0 && tail[5] == ',') { 970 tail[1] = 0; 971 sms->Length = TPUDL; 972 } 973 } 974 if (sms->Length != TPUDL) { 975 smprintf(s, "WARNING: Indicated message length (%d) does not match real (%d)\n", TPUDL, sms->Length); 976 } 977 break; 978 } 979 FALLTHROUGH 980 case SMS_Coding_Unicode_No_Compression: 981 case SMS_Coding_8bit: 982 if ((firstbyte & 0x40)==0x40 && GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_UTF8_ENCODED)) { 983 comma = strchr (msg->Buffer+current, ','); 984 985 if (sms->Coding == SMS_Coding_Default_No_Compression) { 986 expected_comma = (char *)msg->Buffer+current + ((7 * TPUDL + 7) / 8) * 2; 987 } else { 988 expected_comma = (char *)msg->Buffer+current + TPUDL * 2; 989 } 990 if (comma == expected_comma || !comma) { 991 comma = expected_comma; 992 } else { 993 smprintf (s, "UDL fix: %d,", TPUDL); 994 if (sms->Coding == SMS_Coding_Default_No_Compression) { 995 TPUDL = ((comma - ((char *)msg->Buffer+current)) * 4) / 7; 996 } else { 997 TPUDL = (comma - ((char *)msg->Buffer+current)) / 2; 998 } 999 smprintf (s, "%d\n", TPUDL); 1000 } 1001 DecodeHexBin(buffer+PHONE_SMSDeliver.Text, msg->Buffer+current, comma - (char *) (msg->Buffer+current)); 1002 buffer[PHONE_SMSDeliver.firstbyte] = firstbyte; 1003 buffer[PHONE_SMSDeliver.TPDCS] = TPDCS; 1004 buffer[PHONE_SMSDeliver.TPUDL] = TPUDL; 1005 return GSM_DecodeSMSFrameText(&(s->di), sms, buffer, PHONE_SMSDeliver); 1006 } 1007 1008 if (sms->Coding == SMS_Coding_Unicode_No_Compression && GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMS_UTF8_ENCODED)) { 1009 DecodeUTF8(buffer+PHONE_SMSDeliver.Text, msg->Buffer+current, TPUDL); 1010 TPUDL = 2 * UnicodeLength (buffer+PHONE_SMSDeliver.Text); 1011 } else { 1012 DecodeHexBin(buffer+PHONE_SMSDeliver.Text, msg->Buffer+current, TPUDL*2); 1013 } 1014 buffer[PHONE_SMSDeliver.firstbyte] = firstbyte; 1015 buffer[PHONE_SMSDeliver.TPDCS] = TPDCS; 1016 buffer[PHONE_SMSDeliver.TPUDL] = TPUDL; 1017 return GSM_DecodeSMSFrameText(&(s->di), sms, buffer, PHONE_SMSDeliver); 1018 default: 1019 break; 1020 } 1021 } 1022 return ERR_NONE; 1023 default: 1024 smprintf(s, "Internal error - SMS mode not set!\n"); 1025 return ERR_BUG; 1026 } 1027 break; 1028 case AT_Reply_CMSError: 1029 if (Priv->ErrorCode == 320 || Priv->ErrorCode == 500) { 1030 return ERR_EMPTY; 1031 } else { 1032 return ATGEN_HandleCMSError(s); 1033 } 1034 case AT_Reply_CMEError: 1035 return ATGEN_HandleCMEError(s); 1036 case AT_Reply_Error: 1037 /* A2D returns Error with empty location */ 1038 return ERR_EMPTY; 1039 default: 1040 break; 1041 } 1042 return ERR_UNKNOWNRESPONSE; 1043 } 1044 1045 GSM_Error ATGEN_GetSMS(GSM_StateMachine *s, GSM_MultiSMSMessage *sms) 1046 { 1047 GSM_Error error; 1048 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1049 unsigned char req[20] = {'\0'}, folderid = 0; 1050 int location = 0, getfolder = 0, add = 0, length = 0; 1051 GSM_AT_SMS_Modes oldmode; 1052 1053 /* Set mode of SMS */ 1054 error = ATGEN_GetSMSMode(s); 1055 if (error != ERR_NONE) { 1056 return error; 1057 } 1058 1059 oldmode = Priv->SMSMode; 1060 1061 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_READ_SMSTEXTMODE)) { 1062 ATGEN_SetSMSMode(s, SMS_AT_TXT); 1063 } 1064 1065 /* Clear SMS structure of any possible junk */ 1066 GSM_SetDefaultReceivedSMSData(&sms->SMS[0]); 1067 error = ATGEN_GetSMSLocation(s, &sms->SMS[0], &folderid, &location, FALSE); 1068 1069 if (error != ERR_NONE) { 1070 goto fail; 1071 } 1072 if (Priv->SMSMemory == MEM_ME && GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMSME900)) { 1073 add = 899; 1074 } 1075 length = sprintf(req, "AT+CMGR=%i\r", location + add); 1076 1077 /* There is possibility that date will be encoded in text mode */ 1078 if (Priv->SMSMode == SMS_AT_TXT) { 1079 error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL); 1080 1081 if (error != ERR_NONE) { 1082 goto fail; 1083 } 1084 } 1085 s->Phone.Data.GetSMSMessage = sms; 1086 smprintf(s, "Getting SMS\n"); 1087 error = ATGEN_WaitFor(s, req, length, 0x00, 50, ID_GetSMSMessage); 1088 1089 if (error == ERR_NONE || error == ERR_CORRUPTED) { 1090 getfolder = sms->SMS[0].Folder; 1091 ATGEN_SetSMSLocation(s, &sms->SMS[0], folderid, location); 1092 sms->SMS[0].Folder = getfolder; 1093 if(sms->SMS[0].Memory != MEM_SR) { 1094 sms->SMS[0].Memory = MEM_SM; 1095 if (getfolder > 2) sms->SMS[0].Memory = MEM_ME; 1096 } 1097 } 1098 fail: 1099 if (oldmode != Priv->SMSMode) { 1100 ATGEN_SetSMSMode(s, oldmode); 1101 } 1102 1103 return error; 1104 1105 } 1106 1107 GSM_Error ATGEN_ReplyGetMessageList(GSM_Protocol_Message *msg, GSM_StateMachine *s) 1108 { 1109 GSM_Error error; 1110 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1111 GSM_SMSMessage sms; 1112 int line = 1, cur = 0, allocsize = 0; 1113 char *tmp = NULL; 1114 const char *str; 1115 1116 switch (Priv->ReplyState) { 1117 case AT_Reply_OK: 1118 break; 1119 case AT_Reply_Error: 1120 return ERR_NOTSUPPORTED; 1121 case AT_Reply_CMSError: 1122 if (Priv->ErrorCode == 320 || Priv->ErrorCode == 500) { 1123 return ERR_EMPTY; 1124 } else { 1125 return ATGEN_HandleCMSError(s); 1126 } 1127 case AT_Reply_CMEError: 1128 return ATGEN_HandleCMEError(s); 1129 default: 1130 return ERR_UNKNOWNRESPONSE; 1131 } 1132 smprintf(s, "SMS listing received\n"); 1133 Priv->SMSCount = 0; 1134 Priv->SMSCache = NULL; 1135 1136 /* Walk through lines with +CMGL: */ 1137 /* First line is our command so we can skip it */ 1138 for (line = 2; strcmp("OK", str = GetLineString(msg->Buffer, &Priv->Lines, line)) != 0; line++) { 1139 /* 1140 * Find +CMGL, it should be on beginning, but it does not have to (see 1141 * corruption mentioned at the end of loop. 1142 */ 1143 str = strstr(str, "+CMGL:"); 1144 1145 if (str == NULL) { 1146 /* 1147 * Sometimes an SMS message will contain a line break. In SMS text 1148 * mode we skip to the next line and try again to find +CMGL. 1149 * FIXME: Can we do the same for SMS PDU mode? 1150 */ 1151 if (Priv->SMSMode == SMS_AT_PDU) { 1152 smprintf(s, "Can not find +CMGL:!\n"); 1153 return ERR_UNKNOWN; 1154 } 1155 continue; 1156 } 1157 1158 /* Parse reply */ 1159 error = ATGEN_ParseReply(s, str, "+CMGL: @i, @0", &cur); 1160 1161 if (error != ERR_NONE) { 1162 return error; 1163 } 1164 Priv->SMSCount++; 1165 1166 /* Reallocate buffer if needed */ 1167 if (allocsize <= Priv->SMSCount) { 1168 allocsize += 20; 1169 Priv->SMSCache = (GSM_AT_SMS_Cache *)realloc(Priv->SMSCache, allocsize * sizeof(GSM_AT_SMS_Cache)); 1170 1171 if (Priv->SMSCache == NULL) { 1172 return ERR_MOREMEMORY; 1173 } 1174 } 1175 1176 /* Should we use index instead of location? Samsung P900 needs this hack. */ 1177 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_BROKEN_CMGL)) { 1178 ATGEN_SetSMSLocation(s, &sms, Priv->SMSReadFolder, Priv->SMSCount); 1179 } else { 1180 ATGEN_SetSMSLocation(s, &sms, Priv->SMSReadFolder, cur); 1181 } 1182 Priv->SMSCache[Priv->SMSCount - 1].Location = sms.Location; 1183 Priv->SMSCache[Priv->SMSCount - 1].State = -1; 1184 1185 /* Go to PDU/Text data */ 1186 line++; 1187 1188 /* Fill in cache of PDU data */ 1189 if (Priv->SMSMode == SMS_AT_PDU) { 1190 error = ATGEN_ParseReply(s, str, "+CMGL: @i, @i, @0", 1191 &cur, 1192 &Priv->SMSCache[Priv->SMSCount - 1].State); 1193 1194 if (error != ERR_NONE) { 1195 smprintf(s, "Failed to parse reply, not using cache!\n"); 1196 Priv->SMSCache[Priv->SMSCount - 1].State = -1; 1197 } 1198 /* Get next line (PDU data) */ 1199 str = GetLineString(msg->Buffer, &Priv->Lines, line); 1200 1201 if (strlen(str) >= GSM_AT_MAXPDULEN) { 1202 smprintf(s, "PDU (%s) too long for cache, skipping!\n", str); 1203 Priv->SMSCache[Priv->SMSCount - 1].State = -1; 1204 } else { 1205 strcpy(Priv->SMSCache[Priv->SMSCount - 1].PDU, str); 1206 1207 /* Some phones corrupt output and do not put new line before +CMGL occassionally */ 1208 tmp = strstr(Priv->SMSCache[Priv->SMSCount - 1].PDU, "+CMGL:"); 1209 1210 if (tmp != NULL) { 1211 smprintf(s, "WARNING: Line should contain PDU data, but contains +CMGL, stripping it!\n"); 1212 *tmp = 0; 1213 1214 /* Go line back, because we have to process this line again */ 1215 line--; 1216 } 1217 } 1218 } 1219 1220 } 1221 smprintf(s, "Read %d SMS locations\n", Priv->SMSCount); 1222 return ERR_NONE; 1223 } 1224 1225 GSM_Error ATGEN_GetSMSList(GSM_StateMachine *s, gboolean first) 1226 { 1227 GSM_Error error; 1228 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1229 int used = 0; 1230 1231 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_DISABLE_CMGL)) { 1232 return ERR_NOTSUPPORTED; 1233 } 1234 1235 /* Set mode of SMS */ 1236 error = ATGEN_GetSMSMode(s); 1237 1238 if (error != ERR_NONE) { 1239 return error; 1240 } 1241 1242 /* Get number of messages */ 1243 error = ATGEN_GetSMSStatus(s,&Priv->LastSMSStatus); 1244 1245 if (error != ERR_NONE) { 1246 return error; 1247 } 1248 if (first) { 1249 Priv->SMSReadFolder = 1; 1250 1251 if (Priv->SIMSMSMemory == AT_AVAILABLE) { 1252 error = ATGEN_SetSMSMemory(s, TRUE, FALSE, FALSE); 1253 1254 if (error != ERR_NONE) { 1255 return error; 1256 } 1257 used = Priv->LastSMSStatus.SIMUsed; 1258 } else if (Priv->PhoneSMSMemory == AT_AVAILABLE) { 1259 error = ATGEN_SetSMSMemory(s, FALSE, FALSE, FALSE); 1260 1261 if (error != ERR_NONE) { 1262 return error; 1263 } 1264 used = Priv->LastSMSStatus.PhoneUsed; 1265 } else { 1266 return ERR_NOTSUPPORTED; 1267 } 1268 } else { 1269 Priv->SMSReadFolder = 2; 1270 1271 if (Priv->PhoneSMSMemory == AT_AVAILABLE) { 1272 error = ATGEN_SetSMSMemory(s, FALSE, FALSE, FALSE); 1273 1274 if (error != ERR_NONE) { 1275 return error; 1276 } 1277 used = Priv->LastSMSStatus.PhoneUsed; 1278 } else { 1279 return ERR_NOTSUPPORTED; 1280 } 1281 } 1282 Priv->LastSMSRead = 0; 1283 Priv->SMSCount = 0; 1284 1285 if (Priv->SMSCache != NULL) { 1286 free(Priv->SMSCache); 1287 Priv->SMSCache = NULL; 1288 } 1289 smprintf(s, "Getting SMS locations\n"); 1290 1291 if (Priv->SMSMode == SMS_AT_TXT) { 1292 error = ATGEN_WaitForAutoLen(s, "AT+CMGL=\"ALL\"\r", 0x00, 500, ID_GetSMSMessage); 1293 } else { 1294 error = ATGEN_WaitForAutoLen(s, "AT+CMGL=4\r", 0x00, 500, ID_GetSMSMessage); 1295 } 1296 if (error == ERR_NOTSUPPORTED) { 1297 error = ATGEN_WaitForAutoLen(s, "AT+CMGL\r", 0x00, 500, ID_GetSMSMessage); 1298 } 1299 /* 1300 * We did not read anything, but it is correct, indicate that 1301 * cache should be used (even if it is empty). 1302 */ 1303 if (error == ERR_NONE && Priv->SMSCache == NULL) { 1304 Priv->SMSCache = (GSM_AT_SMS_Cache *)realloc(Priv->SMSCache, sizeof(GSM_AT_SMS_Cache)); 1305 } 1306 if (used != Priv->SMSCount && (error == ERR_NONE || error == ERR_EMPTY)) { 1307 smprintf(s, "WARNING: Used messages according to CPMS %d, but CMGL returned %d. Expect problems!\n", used, Priv->SMSCount); 1308 if (! GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_USE_SMSTEXTMODE)) { 1309 smprintf(s, "HINT: Your might want to use F_USE_SMSTEXTMODE flag\n"); 1310 } 1311 return ERR_NONE; 1312 } 1313 return error; 1314 } 1315 1316 GSM_Error ATGEN_GetNextSMS(GSM_StateMachine *s, GSM_MultiSMSMessage *sms, gboolean start) 1317 { 1318 GSM_Error error; 1319 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1320 int usedsms = 0, i = 0, found = -1, tmpfound = -1; 1321 1322 if (Priv->PhoneSMSMemory == 0) { 1323 error = ATGEN_SetSMSMemory(s, FALSE, FALSE, FALSE); 1324 1325 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 1326 return error; 1327 } 1328 } 1329 if (Priv->SIMSMSMemory == 0) { 1330 error = ATGEN_SetSMSMemory(s, TRUE, FALSE, FALSE); 1331 1332 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 1333 return error; 1334 } 1335 } 1336 if (Priv->SIMSMSMemory == AT_NOTAVAILABLE && Priv->PhoneSMSMemory == AT_NOTAVAILABLE) return ERR_NOTSUPPORTED; 1337 1338 /* On start we need to init everything */ 1339 if (start) { 1340 /* Start from beginning */ 1341 sms->SMS[0].Location = 0; 1342 Priv->LastSMSRead = 0; 1343 1344 /* Get list of messages */ 1345 error = ATGEN_GetSMSList(s, TRUE); 1346 } else { 1347 error = ERR_NONE; 1348 } 1349 1350 /* Use listed locations if we have them */ 1351 if (error == ERR_NONE && Priv->SMSCache != NULL) { 1352 if (start) { 1353 found = 0; 1354 } else { 1355 for (i = 0; i < Priv->SMSCount; i++) { 1356 if (Priv->SMSCache[i].Location == sms->SMS[0].Location) { 1357 found = i + 1; 1358 break; 1359 } 1360 if ((Priv->SMSCache[i].Location < sms->SMS[0].Location) 1361 && ((tmpfound == -1) || 1362 (sms->SMS[0].Location - Priv->SMSCache[i].Location < 1363 sms->SMS[0].Location - Priv->SMSCache[tmpfound - 1].Location)) 1364 ) { 1365 tmpfound = i + 1; 1366 } 1367 } 1368 } 1369 1370 if (found == -1) { 1371 smprintf(s, "Invalid location passed to %s!\n", __FUNCTION__); 1372 1373 if (tmpfound == -1) { 1374 return ERR_INVALIDLOCATION; 1375 } else { 1376 smprintf(s, "Attempting to skip to next location!\n"); 1377 found = tmpfound; 1378 } 1379 } 1380 smprintf(s, "Cache status: Found: %d, count: %d\n", found, Priv->SMSCount); 1381 1382 if (found >= Priv->SMSCount) { 1383 /* Have we read all folders? */ 1384 if (Priv->SMSReadFolder == Priv->NumFolders) { 1385 return ERR_EMPTY; 1386 } 1387 1388 /* Get list of messages */ 1389 error = ATGEN_GetSMSList(s, FALSE); 1390 1391 /* Not supported folder? We're done then. */ 1392 if (error == ERR_NOTSUPPORTED) { 1393 return ERR_EMPTY; 1394 } 1395 if (error != ERR_NONE) { 1396 return error; 1397 } 1398 1399 /* Did we read anything? */ 1400 if (Priv->SMSCache != NULL && Priv->SMSCount == 0) { 1401 return ERR_EMPTY; 1402 } 1403 1404 /* Start again */ 1405 found = 0; 1406 } 1407 1408 /* We might get no messages in listing above */ 1409 if (Priv->SMSCache != NULL) { 1410 sms->SMS[0].Folder = 0; 1411 sms->Number = 1; 1412 sms->SMS[0].Memory = Priv->SMSMemory; 1413 sms->SMS[0].Location = Priv->SMSCache[found].Location; 1414 1415 if (Priv->SMSCache[found].State != -1) { 1416 /* Get message from cache */ 1417 GSM_SetDefaultReceivedSMSData(&sms->SMS[0]); 1418 s->Phone.Data.GetSMSMessage = sms; 1419 smprintf(s, "Getting message from cache\n"); 1420 smprintf(s, "%s\n", Priv->SMSCache[found].PDU); 1421 error = ATGEN_DecodePDUMessage(s, 1422 Priv->SMSCache[found].PDU, 1423 Priv->SMSCache[found].State); 1424 1425 /* Is the entry corrupted? */ 1426 if (error != ERR_CORRUPTED) { 1427 return error; 1428 } 1429 /* Mark it as invalid */ 1430 Priv->SMSCache[found].State = -1; 1431 /* And fall back to normal reading */ 1432 } 1433 1434 /* Finally read the message */ 1435 smprintf(s, "Reading next message on location %d\n", sms->SMS[0].Location); 1436 return ATGEN_GetSMS(s, sms); 1437 } 1438 } 1439 1440 /* Ensure LastSMSStatus is up to date */ 1441 error = ATGEN_GetSMSStatus(s, &Priv->LastSMSStatus); 1442 1443 if (error != ERR_NONE) { 1444 return error; 1445 } 1446 1447 /* Use brute force if listing does not work */ 1448 while (TRUE) { 1449 sms->SMS[0].Location++; 1450 1451 if (sms->SMS[0].Location < GSM_PHONE_MAXSMSINFOLDER) { 1452 if (Priv->SIMSMSMemory == AT_AVAILABLE) { 1453 usedsms = Priv->LastSMSStatus.SIMUsed; 1454 } else { 1455 usedsms = Priv->LastSMSStatus.PhoneUsed; 1456 } 1457 1458 if (Priv->LastSMSRead >= usedsms) { 1459 if (Priv->PhoneSMSMemory == AT_NOTAVAILABLE || Priv->LastSMSStatus.PhoneUsed == 0) { 1460 smprintf(s, "No more messages to read\n"); 1461 return ERR_EMPTY; 1462 } 1463 Priv->LastSMSRead = 0; 1464 1465 /* Start on next folder */ 1466 sms->SMS[0].Location = GSM_PHONE_MAXSMSINFOLDER + 1; 1467 } 1468 } else { 1469 if (Priv->PhoneSMSMemory == AT_NOTAVAILABLE) return ERR_EMPTY; 1470 if (Priv->LastSMSRead >= Priv->LastSMSStatus.PhoneUsed) return ERR_EMPTY; 1471 } 1472 sms->SMS[0].Folder = 0; 1473 error = ATGEN_GetSMS(s, sms); 1474 1475 if (error == ERR_NONE) { 1476 Priv->LastSMSRead++; 1477 break; 1478 } 1479 if (error != ERR_EMPTY && error != ERR_INVALIDLOCATION) return error; 1480 } 1481 return error; 1482 } 1483 1484 GSM_Error ATGEN_ReplyGetSMSStatus(GSM_Protocol_Message *msg, GSM_StateMachine *s) 1485 { 1486 GSM_Error error; 1487 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1488 GSM_SMSMemoryStatus *SMSStatus = s->Phone.Data.SMSStatus; 1489 unsigned char buffer[50] = {'\0'}; 1490 int used = 0, size = 0; 1491 1492 switch (Priv->ReplyState) { 1493 case AT_Reply_OK: 1494 smprintf(s, "SMS status received\n"); 1495 1496 /* Check for +CPMS: 0,30,0,30,8,330, this is according to ETSI */ 1497 error = ATGEN_ParseReply(s, 1498 GetLineString(msg->Buffer, &Priv->Lines, 2), 1499 "+CPMS: @i, @i, @0", 1500 &used, &size); 1501 if (error != ERR_NONE) { 1502 /* 1503 * Samsung formats this different way, sample response: 1504 * 1 "AT+CPMS="SM","SM"" 1505 * 2 "+CPMS:"SM",3,30,"SM",3,30,"SM",3,30" 1506 * 3 "OK" 1507 */ 1508 error = ATGEN_ParseReply(s, 1509 GetLineString(msg->Buffer, &Priv->Lines, 2), 1510 "+CPMS: @s, @i, @i, @0", 1511 &buffer, sizeof(buffer), &used, &size); 1512 } 1513 if (error != ERR_NONE) { 1514 /* For phones with single memory */ 1515 error = ATGEN_ParseReply(s, 1516 GetLineString(msg->Buffer, &Priv->Lines, 2), 1517 "+CPMS: @i, @i", 1518 &used, &size); 1519 } 1520 if (error == ERR_NONE) { 1521 smprintf(s, "Used : %i\n", used); 1522 smprintf(s, "Size : %i\n", size); 1523 if ((strstr(msg->Buffer, "CPMS=\"ME") != NULL) || 1524 (Priv->MotorolaSMS && strstr(msg->Buffer, "CPMS=\"MT") != NULL)) { 1525 SMSStatus->PhoneUsed = used; 1526 SMSStatus->PhoneSize = size; 1527 } else { 1528 SMSStatus->SIMUsed = used; 1529 SMSStatus->SIMSize = size; 1530 } 1531 } 1532 return error; 1533 case AT_Reply_Error: 1534 if (strstr(msg->Buffer,"SM")!=NULL) { 1535 smprintf(s, "Can't access SIM card\n"); 1536 return ERR_SECURITYERROR; 1537 } 1538 return ERR_NOTSUPPORTED; 1539 case AT_Reply_CMSError: 1540 return ATGEN_HandleCMSError(s); 1541 case AT_Reply_CMEError: 1542 return ATGEN_HandleCMEError(s); 1543 default: 1544 break; 1545 } 1546 return ERR_UNKNOWNRESPONSE; 1547 } 1548 1549 GSM_Error ATGEN_GetSMSStatus(GSM_StateMachine *s, GSM_SMSMemoryStatus *status) 1550 { 1551 GSM_Error error; 1552 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1553 1554 /* No templates at all */ 1555 status->TemplatesUsed = 0; 1556 status->SIMUsed = 0; 1557 status->SIMUnRead = 0; 1558 status->SIMSize = 0; 1559 s->Phone.Data.SMSStatus = status; 1560 1561 if ((Priv->SIMSMSMemory == 0) || (Priv->PhoneSMSMemory == 0)) { 1562 /* We silently ignore error here, because when this fails, we can try to setmemory anyway */ 1563 ATGEN_GetSMSMemories(s); 1564 } 1565 if (Priv->PhoneSMSMemory == 0) { 1566 error = ATGEN_SetSMSMemory(s, FALSE, FALSE, FALSE); 1567 1568 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 1569 return error; 1570 } 1571 } 1572 if (Priv->SIMSMSMemory == 0) { 1573 error = ATGEN_SetSMSMemory(s, TRUE, FALSE, FALSE); 1574 1575 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 1576 return error; 1577 } 1578 } 1579 if (Priv->SIMSMSMemory == AT_AVAILABLE) { 1580 smprintf(s, "Getting SIM SMS status\n"); 1581 1582 if (Priv->SIMSaveSMS == AT_AVAILABLE) { 1583 error = ATGEN_WaitForAutoLen(s, "AT+CPMS=\"SM\",\"SM\"\r", 0x00, 200, ID_GetSMSStatus); 1584 Priv->SMSMemoryWrite = TRUE; 1585 } else { 1586 error = ATGEN_WaitForAutoLen(s, "AT+CPMS=\"SM\"\r", 0x00, 200, ID_GetSMSStatus); 1587 Priv->SMSMemoryWrite = FALSE; 1588 } 1589 if (error != ERR_NONE) { 1590 return error; 1591 } 1592 Priv->SMSMemory = MEM_SM; 1593 } 1594 status->PhoneUsed = 0; 1595 status->PhoneUnRead = 0; 1596 status->PhoneSize = 0; 1597 1598 if (Priv->PhoneSMSMemory == AT_AVAILABLE) { 1599 smprintf(s, "Getting phone SMS status\n"); 1600 1601 if (Priv->PhoneSaveSMS == AT_AVAILABLE) { 1602 if (Priv->MotorolaSMS) { 1603 error = ATGEN_WaitForAutoLen(s, "AT+CPMS=\"MT\"\r", 0x00, 200, ID_GetSMSStatus); 1604 Priv->SMSMemoryWrite = FALSE; 1605 } else { 1606 error = ATGEN_WaitForAutoLen(s, "AT+CPMS=\"ME\",\"ME\"\r", 0x00, 200, ID_GetSMSStatus); 1607 Priv->SMSMemoryWrite = TRUE; 1608 } 1609 } else { 1610 error = ATGEN_WaitForAutoLen(s, "AT+CPMS=\"ME\"\r", 0x00, 200, ID_GetSMSStatus); 1611 Priv->SMSMemoryWrite = FALSE; 1612 } 1613 if (error != ERR_NONE) { 1614 return error; 1615 } 1616 Priv->SMSMemory = MEM_ME; 1617 } 1618 return ERR_NONE; 1619 } 1620 1621 GSM_Error ATGEN_ReplyAddSMSMessage(GSM_Protocol_Message *msg, GSM_StateMachine *s) 1622 { 1623 GSM_Error error; 1624 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1625 size_t i = 0; 1626 1627 switch (Priv->ReplyState) { 1628 case AT_Reply_SMSEdit: 1629 if (s->Protocol.Data.AT.EditMode) { 1630 s->Protocol.Data.AT.EditMode = FALSE; 1631 return ERR_NONE; 1632 } 1633 smprintf(s, "Received unexpected SMS edit prompt!\n"); 1634 return ERR_UNKNOWN; 1635 case AT_Reply_OK: 1636 smprintf(s, "SMS saved OK\n"); 1637 1638 /* Number of lines */ 1639 i = 0; 1640 1641 while (Priv->Lines.numbers[i*2+1] != 0) { 1642 i++; 1643 } 1644 error = ATGEN_ParseReply(s, 1645 GetLineString(msg->Buffer, &Priv->Lines, i - 1), 1646 "+CMGW: @i", 1647 &s->Phone.Data.SaveSMSMessage->Location); 1648 1649 if (error != ERR_NONE) { 1650 return error; 1651 } 1652 smprintf(s, "Saved at AT location %i\n", 1653 s->Phone.Data.SaveSMSMessage->Location); 1654 /* Adjust location */ 1655 ATGEN_SetSMSLocation( 1656 s, 1657 s->Phone.Data.SaveSMSMessage, 1658 /* We care only about SIM/Phone */ 1659 s->Phone.Data.SaveSMSMessage->Folder <= 2 ? 1 : 2, 1660 s->Phone.Data.SaveSMSMessage->Location 1661 ); 1662 return ERR_NONE; 1663 case AT_Reply_Error: 1664 smprintf(s, "Error\n"); 1665 return ERR_NOTSUPPORTED; 1666 case AT_Reply_CMSError: 1667 /* This error occurs in case that phone couldn't save SMS */ 1668 return ATGEN_HandleCMSError(s); 1669 case AT_Reply_CMEError: 1670 return ATGEN_HandleCMEError(s); 1671 default: 1672 break; 1673 } 1674 return ERR_UNKNOWNRESPONSE; 1675 } 1676 1677 GSM_Error ATGEN_MakeSMSFrame(GSM_StateMachine *s, GSM_SMSMessage *message, unsigned char *hexreq, size_t hexlength, int *current, size_t *length2) 1678 { 1679 GSM_Error error; 1680 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1681 GSM_SMSC SMSC; 1682 unsigned char req[1000] = {'\0'}, buffer[1000] = {'\0'}; 1683 int i = 0, length = 0; 1684 size_t len; 1685 1686 /* Set mode of SMS */ 1687 error = ATGEN_GetSMSMode(s); 1688 1689 if (error != ERR_NONE) { 1690 return error; 1691 } 1692 length = 0; 1693 *current = 0; 1694 1695 switch (Priv->SMSMode) { 1696 case SMS_AT_PDU: 1697 if (message->PDU == SMS_Deliver) { 1698 smprintf(s, "SMS Deliver\n"); 1699 error = PHONE_EncodeSMSFrame(s,message,buffer,PHONE_SMSDeliver,&length,TRUE); 1700 1701 if (error != ERR_NONE) { 1702 return error; 1703 } 1704 length = length - PHONE_SMSDeliver.Text; 1705 1706 for (i = 0;i < buffer[PHONE_SMSDeliver.SMSCNumber]+1;i++) { 1707 req[(*current)++]=buffer[PHONE_SMSDeliver.SMSCNumber+i]; 1708 } 1709 req[(*current)++]=buffer[PHONE_SMSDeliver.firstbyte]; 1710 1711 for (i = 0;i<((buffer[PHONE_SMSDeliver.Number]+1)/2+1)+1;i++) { 1712 req[(*current)++]=buffer[PHONE_SMSDeliver.Number+i]; 1713 } 1714 req[(*current)++]=buffer[PHONE_SMSDeliver.TPPID]; 1715 req[(*current)++]=buffer[PHONE_SMSDeliver.TPDCS]; 1716 1717 for(i = 0;i < 7;i++) { 1718 req[(*current)++]=buffer[PHONE_SMSDeliver.DateTime+i]; 1719 } 1720 req[(*current)++]=buffer[PHONE_SMSDeliver.TPUDL]; 1721 1722 for(i = 0;i < length;i++) { 1723 req[(*current)++]=buffer[PHONE_SMSDeliver.Text+i]; 1724 } 1725 EncodeHexBin(hexreq, req, *current); 1726 *length2 = *current * 2; 1727 *current = *current - (req[PHONE_SMSDeliver.SMSCNumber]+1); 1728 } else { 1729 smprintf(s, "SMS Submit\n"); 1730 error = PHONE_EncodeSMSFrame(s,message,buffer,PHONE_SMSSubmit,&length,TRUE); 1731 1732 if (error != ERR_NONE) { 1733 return error; 1734 } 1735 length = length - PHONE_SMSSubmit.Text; 1736 1737 for (i = 0;i < buffer[PHONE_SMSSubmit.SMSCNumber]+1;i++) { 1738 req[(*current)++]=buffer[PHONE_SMSSubmit.SMSCNumber+i]; 1739 } 1740 req[(*current)++]=buffer[PHONE_SMSSubmit.firstbyte]; 1741 req[(*current)++]=buffer[PHONE_SMSSubmit.TPMR]; 1742 1743 for (i = 0;i<((buffer[PHONE_SMSSubmit.Number]+1)/2+1)+1;i++) { 1744 req[(*current)++]=buffer[PHONE_SMSSubmit.Number+i]; 1745 } 1746 req[(*current)++]=buffer[PHONE_SMSSubmit.TPPID]; 1747 req[(*current)++]=buffer[PHONE_SMSSubmit.TPDCS]; 1748 req[(*current)++]=buffer[PHONE_SMSSubmit.TPVP]; 1749 req[(*current)++]=buffer[PHONE_SMSSubmit.TPUDL]; 1750 1751 for(i = 0;i < length;i++) { 1752 req[(*current)++]=buffer[PHONE_SMSSubmit.Text+i]; 1753 } 1754 req[(*current)+1]='\0'; 1755 EncodeHexBin(hexreq, req, *current); 1756 *length2 = *current * 2; 1757 *current = *current - (req[PHONE_SMSSubmit.SMSCNumber]+1); 1758 } 1759 break; 1760 case SMS_AT_TXT: 1761 if (Priv->Manufacturer != AT_Nokia) { 1762 if (message->Coding != SMS_Coding_Default_No_Compression) { 1763 return ERR_NOTSUPPORTED; 1764 } 1765 } 1766 error = PHONE_EncodeSMSFrame(s,message,req,PHONE_SMSDeliver,&i,TRUE); 1767 1768 if (error != ERR_NONE) { 1769 return error; 1770 } 1771 CopyUnicodeString(SMSC.Number,message->SMSC.Number); 1772 SMSC.Location = 1; 1773 error = ATGEN_SetSMSC(s,&SMSC); 1774 1775 if (error != ERR_NONE) { 1776 return error; 1777 } 1778 len = sprintf(buffer, "AT+CSMP=%i,%i,%i,%i\r", 1779 req[PHONE_SMSDeliver.firstbyte], 1780 req[PHONE_SMSDeliver.TPVP], 1781 req[PHONE_SMSDeliver.TPPID], 1782 req[PHONE_SMSDeliver.TPDCS]); 1783 error = ATGEN_WaitFor(s, buffer, len, 0x00, 40, ID_SetSMSParameters); 1784 1785 if (error == ERR_NOTSUPPORTED) { 1786 /* Nokia Communicator 9000i doesn't support <vp> parameter */ 1787 len = sprintf(buffer, "AT+CSMP=%i,,%i,%i\r", 1788 req[PHONE_SMSDeliver.firstbyte], 1789 req[PHONE_SMSDeliver.TPPID], 1790 req[PHONE_SMSDeliver.TPDCS]); 1791 error = ATGEN_WaitFor(s, buffer, len, 0x00, 40, ID_SetSMSParameters); 1792 } 1793 if (error != ERR_NONE) { 1794 smprintf(s, "WARNING: Failed to set message parameters, continuing without them!\n"); 1795 } 1796 switch (message->Coding) { 1797 case SMS_Coding_Default_No_Compression: 1798 /* If not SMS with UDH, it's as normal text */ 1799 if (message->UDH.Type == UDH_NoUDH) { 1800 error = ATGEN_EncodeText( 1801 s, message->Text, UnicodeLength(message->Text), hexreq, hexlength, length2 1802 ); 1803 if (error != ERR_NONE) { 1804 return error; 1805 } 1806 break; 1807 } 1808 FALLTHROUGH 1809 case SMS_Coding_Unicode_No_Compression: 1810 case SMS_Coding_8bit: 1811 error = PHONE_EncodeSMSFrame(s,message,buffer,PHONE_SMSDeliver,current,TRUE); 1812 1813 if (error != ERR_NONE) { 1814 return error; 1815 } 1816 EncodeHexBin (hexreq, buffer+PHONE_SMSDeliver.Text, buffer[PHONE_SMSDeliver.TPUDL]); 1817 *length2 = buffer[PHONE_SMSDeliver.TPUDL] * 2; 1818 break; 1819 default: 1820 break; 1821 } 1822 break; 1823 } 1824 return ERR_NONE; 1825 } 1826 1827 GSM_Error ATGEN_AddSMS(GSM_StateMachine *s, GSM_SMSMessage *sms) 1828 { 1829 GSM_Error error, error2; 1830 GSM_Phone_Data *Phone = &s->Phone.Data; 1831 unsigned char buffer[1000] = {'\0'}, hexreq[1000] = {'\0'},folderid = 0; 1832 const char *statetxt; 1833 int state = 0, Replies = 0, reply = 0, current = 0, location = 0; 1834 size_t length = 0; 1835 size_t len; 1836 1837 /* This phone supports only sent/unsent messages on SIM */ 1838 if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SMSONLYSENT)) { 1839 if (sms->Folder != 2) { 1840 smprintf(s, "This phone supports only folder = 2!\n"); 1841 return ERR_NOTSUPPORTED; 1842 } 1843 } 1844 1845 /* Check the lower bound (this is static, we do not support flat memory here */ 1846 if (sms->Folder <= 0) { 1847 smprintf(s, "Flat memory not supported for adding!\n"); 1848 return ERR_WRONGFOLDER; 1849 } 1850 1851 /* We don't actually need this, but let's initialise it. */ 1852 sms->Location = 0; 1853 1854 /* Set correct memory type */ 1855 error = ATGEN_GetSMSLocation(s, sms, &folderid, &location, TRUE); 1856 1857 if (error != ERR_NONE) { 1858 return error; 1859 } 1860 1861 /* Set message type based on folder */ 1862 if ((sms->Folder % 2) == 1) { 1863 /* Inbox folder */ 1864 sms->PDU = SMS_Deliver; 1865 } else { 1866 /* Outbox folder */ 1867 sms->PDU = SMS_Submit; 1868 1869 if (sms->Memory == MEM_ME && 1870 GSM_IsPhoneFeatureAvailable(Phone->ModelInfo, F_SUBMIT_SIM_ONLY)) { 1871 smprintf(s, "This phone probably does not support saving submit messages to ME location!\n"); 1872 smprintf(s, "But trying anyway...\n"); 1873 } 1874 } 1875 1876 /* Format SMS frame */ 1877 error = ATGEN_MakeSMSFrame(s, sms, hexreq, sizeof(hexreq), ¤t, &length); 1878 1879 if (error != ERR_NONE) { 1880 return error; 1881 } 1882 1883 switch (Phone->Priv.ATGEN.SMSMode) { 1884 case SMS_AT_PDU: 1885 if (sms->PDU == SMS_Deliver) { 1886 state = 0; 1887 if (sms->State == SMS_Read || sms->State == SMS_Sent) state = 1; 1888 } else { 1889 state = 2; 1890 if (sms->State == SMS_Read || sms->State == SMS_Sent) state = 3; 1891 } 1892 /* Siemens M20 */ 1893 if (GSM_IsPhoneFeatureAvailable(Phone->ModelInfo, F_M20SMS)) { 1894 /* No (good and 100% working) support for alphanumeric numbers */ 1895 if (sms->Number[1]!='+' && (sms->Number[1]<'0' || sms->Number[1]>'9')) { 1896 EncodeUnicode(sms->Number,"123",3); 1897 error = ATGEN_MakeSMSFrame(s, sms, hexreq, sizeof(hexreq), ¤t, &length); 1898 if (error != ERR_NONE) return error; 1899 } 1900 } 1901 len = sprintf(buffer, "AT+CMGW=%i,%i\r",current,state); 1902 break; 1903 case SMS_AT_TXT: 1904 if (sms->PDU == SMS_Deliver) { 1905 statetxt = "REC UNREAD"; 1906 if (sms->State == SMS_Read || sms->State == SMS_Sent) statetxt = "REC READ"; 1907 } else { 1908 statetxt = "STO UNSENT"; 1909 if (sms->State == SMS_Read || sms->State == SMS_Sent) statetxt = "STO SENT"; 1910 } 1911 /* Siemens M20 */ 1912 if (GSM_IsPhoneFeatureAvailable(Phone->ModelInfo, F_M20SMS)) { 1913 /* No (good and 100% working) support for alphanumeric numbers */ 1914 /* FIXME: Try to autodetect support for <stat> (statetxt) parameter although: 1915 * Siemens M20 supports +CMGW <stat> specification but on my model it just 1916 * reports ERROR (and <stat> is not respected). 1917 * Fortunately it will write "+CMGW: <index>\n" before and the message gets written 1918 */ 1919 if (sms->Number[1]!='+' && (sms->Number[1]<'0' || sms->Number[1]>'9')) { 1920 len = sprintf(buffer, "AT+CMGW=\"123\",,\"%s\"\r",statetxt); 1921 } else { 1922 len = sprintf(buffer, "AT+CMGW=\"%s\",,\"%s\"\r",DecodeUnicodeString(sms->Number),statetxt); 1923 } 1924 } else { 1925 len = sprintf(buffer, "AT+CMGW=\"%s\",,\"%s\"\r",DecodeUnicodeString(sms->Number),statetxt); 1926 } 1927 break; 1928 default: 1929 smprintf(s, "Internal error - SMS mode not set!\n"); 1930 return ERR_BUG; 1931 } 1932 Phone->SaveSMSMessage = sms; 1933 1934 for (reply = 0;reply < s->ReplyNum;reply++) { 1935 if (reply != 0) { 1936 smprintf_level(s, D_ERROR, "[Retrying %i]\n", reply+1); 1937 } 1938 s->Protocol.Data.AT.EditMode = TRUE; 1939 Replies = s->ReplyNum; 1940 s->ReplyNum = 1; 1941 smprintf(s,"Waiting for modem prompt\n"); 1942 error = ATGEN_WaitFor(s, buffer, len, 0x00, 20, ID_SaveSMSMessage); 1943 s->ReplyNum = Replies; 1944 1945 if (error == ERR_NONE) { 1946 Phone->DispatchError = ERR_TIMEOUT; 1947 Phone->RequestID = ID_SaveSMSMessage; 1948 usleep(100000); 1949 smprintf(s, "Saving SMS\n"); 1950 error = s->Protocol.Functions->WriteMessage(s, hexreq, length, 0x00); 1951 1952 if (error != ERR_NONE) { 1953 return error; 1954 } 1955 usleep(500000); 1956 1957 /* CTRL+Z ends entering */ 1958 error = s->Protocol.Functions->WriteMessage(s, "\x1A", 1, 0x00); 1959 1960 if (error != ERR_NONE) { 1961 return error; 1962 } 1963 usleep(100000); 1964 error = GSM_WaitForOnce(s, NULL, 0x00, 0x00, 40); 1965 1966 if (error != ERR_TIMEOUT) { 1967 return error; 1968 } 1969 } else { 1970 smprintf(s, "Escaping SMS mode\n"); 1971 error2 = s->Protocol.Functions->WriteMessage(s, "\x1B\r", 2, 0x00); 1972 1973 if (error2 != ERR_NONE) { 1974 return error2; 1975 } 1976 return error; 1977 } 1978 } 1979 1980 return Phone->DispatchError; 1981 } 1982 1983 GSM_Error ATGEN_ReplySendSMS(GSM_Protocol_Message *msg, GSM_StateMachine *s) 1984 { 1985 GSM_Error error; 1986 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 1987 int i = 0,reference = 0; 1988 1989 switch (Priv->ReplyState) { 1990 case AT_Reply_SMSEdit: 1991 if (s->Protocol.Data.AT.EditMode) { 1992 s->Protocol.Data.AT.EditMode = FALSE; 1993 return ERR_NONE; 1994 } 1995 smprintf(s, "Received unexpected SMS edit prompt!\n"); 1996 return ERR_UNKNOWN; 1997 case AT_Reply_OK: 1998 smprintf(s, "SMS sent OK\n"); 1999 2000 /* Number of lines */ 2001 i = 0; 2002 while (Priv->Lines.numbers[i*2+1] != 0) { 2003 i++; 2004 } 2005 error = ATGEN_ParseReply(s, 2006 GetLineString(msg->Buffer, &Priv->Lines, i - 1), 2007 "+CMGS: @i", 2008 &reference); 2009 2010 if (error != ERR_NONE) { 2011 reference = -1; 2012 } 2013 2014 if(s->User.SendSMSStatus != NULL) { 2015 s->User.SendSMSStatus(s, 0, reference, s->User.SendSMSStatusUserData); 2016 } 2017 return ERR_NONE; 2018 2019 case AT_Reply_CMSError: 2020 smprintf(s, "Error %i\n",Priv->ErrorCode); 2021 2022 if (s->User.SendSMSStatus != NULL) { 2023 s->User.SendSMSStatus(s, Priv->ErrorCode, -1, s->User.SendSMSStatusUserData); 2024 } 2025 return ATGEN_HandleCMSError(s); 2026 case AT_Reply_CMEError: 2027 smprintf(s, "Error %i\n",Priv->ErrorCode); 2028 2029 if (s->User.SendSMSStatus != NULL) { 2030 s->User.SendSMSStatus(s, Priv->ErrorCode, -1, s->User.SendSMSStatusUserData); 2031 } 2032 return ATGEN_HandleCMEError(s); 2033 case AT_Reply_Error: 2034 if (s->User.SendSMSStatus != NULL) { 2035 s->User.SendSMSStatus(s, -1, -1, s->User.SendSMSStatusUserData); 2036 } 2037 return ERR_UNKNOWN; 2038 default: 2039 if (s->User.SendSMSStatus != NULL) { 2040 s->User.SendSMSStatus(s, -1, -1, s->User.SendSMSStatusUserData); 2041 } 2042 return ERR_UNKNOWNRESPONSE; 2043 } 2044 } 2045 2046 GSM_Error ATGEN_SendSMS(GSM_StateMachine *s, GSM_SMSMessage *sms) 2047 { 2048 GSM_Error error, error2; 2049 GSM_Phone_Data *Phone = &s->Phone.Data; 2050 unsigned char buffer[1000] = {'\0'}, hexreq[1000] = {'\0'}; 2051 int current = 0, Replies = 0, retries = 0; 2052 size_t length = 0; 2053 size_t len; 2054 2055 if (sms->PDU == SMS_Deliver) { 2056 sms->PDU = SMS_Submit; 2057 } 2058 error = ATGEN_MakeSMSFrame(s, sms, hexreq, sizeof(hexreq), ¤t, &length); 2059 2060 if (error != ERR_NONE) { 2061 return error; 2062 } 2063 2064 if (sms->SMSC.Number[0] == 0x00 && sms->SMSC.Number[1] == 0x00) { 2065 smprintf(s,"No SMSC in SMS to send\n"); 2066 return ERR_EMPTYSMSC; 2067 } 2068 2069 switch (Phone->Priv.ATGEN.SMSMode) { 2070 case SMS_AT_PDU: 2071 len = sprintf(buffer, "AT+CMGS=%i\r",current); 2072 break; 2073 case SMS_AT_TXT: 2074 len = sprintf(buffer, "AT+CMGS=\"%s\"\r",DecodeUnicodeString(sms->Number)); 2075 break; 2076 default: 2077 smprintf(s, "Internal error - SMS mode not set!\n"); 2078 return ERR_BUG; 2079 } 2080 2081 /* We will be SMS edit mode */ 2082 s->Protocol.Data.AT.EditMode = TRUE; 2083 /* 2084 * We handle retries on our own, because we need to escape after 2085 * failure to avoid sending message with AT commands. 2086 */ 2087 Replies = s->ReplyNum; 2088 s->ReplyNum = 1; 2089 2090 while (retries < s->ReplyNum) { 2091 smprintf(s,"Waiting for modem prompt\n"); 2092 error = ATGEN_WaitFor(s, buffer, len, 0x00, 30, ID_IncomingFrame); 2093 2094 /* Restore original value */ 2095 s->ReplyNum = Replies; 2096 2097 if (error == ERR_NONE) { 2098 usleep(100000); 2099 smprintf(s, "Sending SMS\n"); 2100 error = s->Protocol.Functions->WriteMessage(s, hexreq, length, 0x00); 2101 2102 if (error != ERR_NONE) { 2103 return error; 2104 } 2105 usleep(500000); 2106 /* CTRL+Z ends entering */ 2107 error = s->Protocol.Functions->WriteMessage(s, "\x1A", 1, 0x00); 2108 usleep(100000); 2109 return error; 2110 } 2111 smprintf(s, "Escaping SMS mode\n"); 2112 error2 = s->Protocol.Functions->WriteMessage(s, "\x1B\r", 2, 0x00); 2113 if (error2 != ERR_NONE) { 2114 return error2; 2115 } 2116 retries++; 2117 } 2118 return error; 2119 } 2120 2121 GSM_Error ATGEN_SendSavedSMS(GSM_StateMachine *s, int Folder, int Location) 2122 { 2123 GSM_Error error; 2124 GSM_MultiSMSMessage msms; 2125 unsigned char req[100] = {'\0'}, smsfolder = 0; 2126 int location = 0; 2127 size_t len; 2128 2129 msms.Number = 0; 2130 msms.SMS[0].Folder = Folder; 2131 msms.SMS[0].Location = Location; 2132 msms.SMS[0].Memory = 0; 2133 2134 /* By reading SMS we check if it is really inbox/outbox */ 2135 error = ATGEN_GetSMS(s, &msms); 2136 2137 if (error != ERR_NONE) { 2138 return error; 2139 } 2140 2141 /* Can not send from other folder that outbox */ 2142 if (msms.SMS[0].Folder != 2 && msms.SMS[0].Folder != 4) { 2143 return ERR_NOTSUPPORTED; 2144 } 2145 2146 /* Set back original position as it was probably adjusted when 2147 * reading message from phone (eg. folder was filled in). */ 2148 msms.SMS[0].Folder = Folder; 2149 msms.SMS[0].Location = Location; 2150 msms.SMS[0].Memory = 0; 2151 2152 /* Adjust location to real ones */ 2153 error = ATGEN_GetSMSLocation(s, &msms.SMS[0], &smsfolder, &location, FALSE); 2154 2155 if (error != ERR_NONE) { 2156 return error; 2157 } 2158 len = sprintf(req, "AT+CMSS=%i\r",location); 2159 error = s->Protocol.Functions->WriteMessage(s, req, len, 0x00); 2160 usleep(strlen(req)*1000); 2161 return error; 2162 } 2163 2164 GSM_Error ATGEN_ReplyGetSMSC(GSM_Protocol_Message *msg, GSM_StateMachine *s) 2165 { 2166 GSM_Error error; 2167 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2168 GSM_SMSC *SMSC = s->Phone.Data.SMSC; 2169 int number_type = 0; 2170 2171 switch (Priv->ReplyState) { 2172 case AT_Reply_OK: 2173 smprintf(s, "SMSC info received\n"); 2174 2175 /* Parse reply */ 2176 error = ATGEN_ParseReply(s, 2177 GetLineString(msg->Buffer, &Priv->Lines, 2), 2178 "+CSCA: @p, @i", 2179 SMSC->Number, sizeof(SMSC->Number), 2180 &number_type); 2181 if (error != ERR_NONE) { 2182 error = ATGEN_ParseReply(s, 2183 GetLineString(msg->Buffer, &Priv->Lines, 2), 2184 "+CSCA: @p, @0", 2185 SMSC->Number, sizeof(SMSC->Number)); 2186 } 2187 if (error != ERR_NONE) { 2188 error = ATGEN_ParseReply(s, 2189 GetLineString(msg->Buffer, &Priv->Lines, 2), 2190 "+CSCA: @p", 2191 SMSC->Number, sizeof(SMSC->Number)); 2192 number_type = NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN; 2193 } 2194 if (error != ERR_NONE) { 2195 return error; 2196 } 2197 if (UnicodeLength(SMSC->Number) == 0) return ERR_EMPTY; 2198 2199 /* International number */ 2200 GSM_TweakInternationalNumber(SMSC->Number, number_type); 2201 2202 /* Some things we can not find out */ 2203 SMSC->Format = SMS_FORMAT_Text; 2204 SMSC->Validity.Format = SMS_Validity_RelativeFormat; 2205 SMSC->Validity.Relative = SMS_VALID_Max_Time; 2206 SMSC->Name[0] = 0; 2207 SMSC->Name[1] = 0; 2208 SMSC->DefaultNumber[0] = 0; 2209 SMSC->DefaultNumber[1] = 0; 2210 return ERR_NONE; 2211 case AT_Reply_CMSError: 2212 return ATGEN_HandleCMSError(s); 2213 case AT_Reply_CMEError: 2214 return ATGEN_HandleCMEError(s); 2215 case AT_Reply_Error: 2216 return ERR_NOTSUPPORTED; 2217 default: 2218 break; 2219 } 2220 return ERR_UNKNOWNRESPONSE; 2221 } 2222 2223 GSM_Error ATGEN_GetSMSC(GSM_StateMachine *s, GSM_SMSC *smsc) 2224 { 2225 GSM_Error error; 2226 2227 /* Only one location supported */ 2228 if (smsc->Location != 1) { 2229 return ERR_INVALIDLOCATION; 2230 } 2231 2232 /* We prefer normal charset */ 2233 error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL); 2234 2235 if (error != ERR_NONE) { 2236 return error; 2237 } 2238 2239 /* Issue command */ 2240 s->Phone.Data.SMSC = smsc; 2241 smprintf(s, "Getting SMSC\n"); 2242 error = ATGEN_WaitForAutoLen(s, "AT+CSCA?\r", 0x00, 40, ID_GetSMSC); 2243 return error; 2244 } 2245 2246 GSM_Error ATGEN_ReplyDeleteSMSMessage(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s) 2247 { 2248 switch (s->Phone.Data.Priv.ATGEN.ReplyState) { 2249 case AT_Reply_OK: 2250 smprintf(s, "SMS deleted OK\n"); 2251 return ERR_NONE; 2252 case AT_Reply_Error: 2253 smprintf(s, "Invalid location\n"); 2254 return ERR_INVALIDLOCATION; 2255 case AT_Reply_CMSError: 2256 return ATGEN_HandleCMSError(s); 2257 case AT_Reply_CMEError: 2258 return ATGEN_HandleCMEError(s); 2259 default: 2260 break; 2261 } 2262 return ERR_UNKNOWNRESPONSE; 2263 } 2264 2265 GSM_Error ATGEN_DeleteSMS(GSM_StateMachine *s, GSM_SMSMessage *sms) 2266 { 2267 GSM_Error error; 2268 GSM_MultiSMSMessage msms; 2269 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2270 unsigned char req[20] = {'\0'}, folderid = 0; 2271 int location = 0, length = 0; 2272 2273 msms.Number = 0; 2274 msms.SMS[0] = *sms; 2275 2276 /* By reading SMS we check if it is really inbox/outbox */ 2277 error = ATGEN_GetSMS(s, &msms); 2278 2279 if (error != ERR_NONE && error != ERR_CORRUPTED) { 2280 return error; 2281 } 2282 2283 error = ATGEN_GetSMSLocation(s, sms, &folderid, &location, 2284 ATGEN_IsMemoryWriteable(Priv, sms->Memory)); 2285 2286 if (error != ERR_NONE) { 2287 return error; 2288 } 2289 smprintf(s, "Deleting SMS\n"); 2290 length = sprintf(req, "AT+CMGD=%i\r",location); 2291 error = ATGEN_WaitFor(s, req, length, 0x00, 5, ID_DeleteSMSMessage); 2292 return error; 2293 } 2294 2295 GSM_Error ATGEN_GetSMSFolders(GSM_StateMachine *s, GSM_SMSFolders *folders) 2296 { 2297 GSM_Error error; 2298 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2299 int used = 0; 2300 2301 if (Priv->PhoneSMSMemory == 0) { 2302 error = ATGEN_SetSMSMemory(s, FALSE, FALSE, FALSE); 2303 2304 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 2305 return error; 2306 } 2307 } 2308 if (Priv->SIMSMSMemory == 0) { 2309 error = ATGEN_SetSMSMemory(s, TRUE, FALSE, FALSE); 2310 2311 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 2312 return error; 2313 } 2314 } 2315 folders->Number = 0; 2316 2317 if (Priv->PhoneSMSMemory == AT_NOTAVAILABLE && Priv->SIMSMSMemory == AT_NOTAVAILABLE) { 2318 return ERR_NONE; 2319 } 2320 PHONE_GetSMSFolders(s,folders); 2321 2322 if (Priv->SIMSMSMemory == AT_AVAILABLE) { 2323 used = 2; 2324 } 2325 if (Priv->PhoneSMSMemory == AT_AVAILABLE) { 2326 if (used != 0) { 2327 CopyUnicodeString(folders->Folder[used ].Name,folders->Folder[0].Name); 2328 CopyUnicodeString(folders->Folder[used + 1].Name,folders->Folder[1].Name); 2329 folders->Folder[used ].InboxFolder = folders->Folder[0].InboxFolder; 2330 folders->Folder[used + 1].InboxFolder = folders->Folder[1].InboxFolder; 2331 folders->Folder[used ].OutboxFolder = folders->Folder[0].OutboxFolder; 2332 folders->Folder[used + 1].OutboxFolder = folders->Folder[1].OutboxFolder; 2333 } 2334 folders->Folder[used ].Memory = MEM_ME; 2335 folders->Folder[used + 1].Memory = MEM_ME; 2336 folders->Number += 2; 2337 used += 2; 2338 } 2339 return ERR_NONE; 2340 } 2341 2342 GSM_Error ATGEN_IncomingSMSCInfo(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s UNUSED) 2343 { 2344 return ERR_NONE; 2345 } 2346 2347 GSM_Error ATGEN_SetFastSMSSending(GSM_StateMachine *s, gboolean enable) 2348 { 2349 GSM_Error error; 2350 2351 if (enable) { 2352 smprintf(s, "Enabling fast SMS sending\n"); 2353 error = ATGEN_WaitForAutoLen(s, "AT+CMMS=2\r", 0x00, 40, ID_SetFastSMSSending); 2354 } else { 2355 smprintf(s, "Disabling fast SMS sending\n"); 2356 error = ATGEN_WaitForAutoLen(s, "AT+CMMS=0\r", 0x00, 40, ID_SetFastSMSSending); 2357 } 2358 return error; 2359 } 2360 2361 GSM_Error ATGEN_IncomingSMSInfo(GSM_Protocol_Message *msg, GSM_StateMachine *s) 2362 { 2363 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2364 char *buffer = msg->Buffer; 2365 GSM_SMSMessage sms; 2366 GSM_Error error; 2367 2368 char mem_tag[3]; // eg: "SM\0" 2369 const size_t cmd_len = 6; 2370 2371 if(!s->User.IncomingSMS || !s->Phone.Data.EnableIncomingSMS) 2372 return ERR_NONE; 2373 2374 memset(&sms, 0, sizeof(sms)); 2375 sms.State = 0; 2376 sms.InboxFolder = TRUE; 2377 sms.PDU = 0; 2378 2379 if(strncmp(buffer, "+CMTI:", cmd_len) == 0) { 2380 smprintf(s, "Incoming SMS information\n"); 2381 } 2382 else if(strncmp(buffer, "+CDSI:", cmd_len) == 0) { 2383 smprintf(s, "Incoming SMS status report information\n"); 2384 sms.PDU = SMS_Status_Report; 2385 } 2386 else { 2387 smprintf(s, "Unrecognised response\n"); 2388 return ERR_UNKNOWNRESPONSE; 2389 } 2390 2391 error = ATGEN_ParseReply(s, buffer + cmd_len, " @r, @i", 2392 &mem_tag, sizeof(mem_tag), 2393 &sms.Location); 2394 if (error != ERR_NONE) 2395 return error; 2396 2397 sms.Memory = GSM_StringToMemoryType(mem_tag); 2398 if (!ATGEN_IsMemoryAvailable(Priv, sms.Memory)) { 2399 smprintf(s, "Incoming SMS information ignored as %s memory is disabled\n", mem_tag); 2400 return ERR_NONE; 2401 } 2402 2403 switch(sms.Memory) { 2404 case MEM_ME: 2405 case MEM_MT: 2406 sms.Folder = Priv->SIMSMSMemory == AT_AVAILABLE ? 3 : 1; 2407 break; 2408 case MEM_SM: 2409 case MEM_SR: 2410 sms.Folder = 1; 2411 break; 2412 default: 2413 smprintf(s, "Unsupported memory type\n"); 2414 return ERR_NOTSUPPORTED; 2415 } 2416 2417 s->User.IncomingSMS(s, &sms, s->User.IncomingSMSUserData); 2418 2419 return ERR_NONE; 2420 } 2421 2422 GSM_Error ATGEN_IncomingSMSDeliver(GSM_Protocol_Message *msg, GSM_StateMachine *s) 2423 { 2424 GSM_Phone_Data *Data = &s->Phone.Data; 2425 GSM_SMSMessage sms; 2426 unsigned char buffer[300] = {'\0'}, smsframe[800] = {'\0'}; 2427 int current = 0, length, i = 0; 2428 2429 smprintf(s, "Incoming SMS received (Deliver)\n"); 2430 2431 if (Data->EnableIncomingSMS && s->User.IncomingSMS != NULL) { 2432 sms.State = SMS_UnRead; 2433 sms.InboxFolder = TRUE; 2434 sms.PDU = SMS_Deliver; 2435 2436 /* T310 with larger SMS goes crazy and mix this incoming 2437 * frame with normal answers. PDU is always last frame 2438 * We find its' number and parse it */ 2439 while (Data->Priv.ATGEN.Lines.numbers[i*2+1] != 0) { 2440 /* FIXME: handle special chars correctly */ 2441 i++; 2442 } 2443 DecodeHexBin (buffer, 2444 GetLineString(msg->Buffer,&Data->Priv.ATGEN.Lines,i), 2445 GetLineLength(msg->Buffer,&Data->Priv.ATGEN.Lines,i)); 2446 2447 /* We use locations from SMS layouts like in ../phone2.c(h) */ 2448 for(i = 0;i < buffer[0]+1;i++) { 2449 smsframe[i]=buffer[current++]; 2450 } 2451 smsframe[12]=buffer[current++]; 2452 length=((buffer[current])+1)/2+1; 2453 2454 for(i = 0;i < length+1;i++) { 2455 smsframe[PHONE_SMSDeliver.Number+i]=buffer[current++]; 2456 } 2457 smsframe[PHONE_SMSDeliver.TPPID] = buffer[current++]; 2458 smsframe[PHONE_SMSDeliver.TPDCS] = buffer[current++]; 2459 2460 for(i = 0;i < 7;i++) { 2461 smsframe[PHONE_SMSDeliver.DateTime+i]=buffer[current++]; 2462 } 2463 smsframe[PHONE_SMSDeliver.TPUDL] = buffer[current++]; 2464 2465 for(i = 0;i < smsframe[PHONE_SMSDeliver.TPUDL];i++) { 2466 smsframe[i+PHONE_SMSDeliver.Text]=buffer[current++]; 2467 } 2468 GSM_DecodeSMSFrame(&(s->di), &sms,smsframe,PHONE_SMSDeliver); 2469 s->User.IncomingSMS(s, &sms, s->User.IncomingSMSUserData); 2470 } 2471 return ERR_NONE; 2472 } 2473 2474 GSM_Error ATGEN_IncomingSMSReport(GSM_Protocol_Message *msg, GSM_StateMachine *s) 2475 { 2476 GSM_Error error; 2477 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2478 GSM_SMSMessage sms; 2479 char buffer[300] = {'\0'}; 2480 int pduSize = 0; 2481 size_t parseSize = 0; 2482 2483 assert(strncasecmp("+CDS:", msg->Buffer, 5) == 0); 2484 2485 if (!s->Phone.Data.EnableIncomingSMS || s->User.IncomingSMS == NULL) 2486 return ERR_NONE; 2487 2488 smprintf(s, "Incoming SMS received (Report)\n"); 2489 2490 memset(&sms, 0, sizeof(sms)); 2491 sms.State = SMS_UnRead; 2492 sms.InboxFolder = TRUE; 2493 sms.Folder = 1; 2494 pduSize = GetLineLength(msg->Buffer, &Priv->Lines, 2); 2495 assert(pduSize >= 0); 2496 2497 if(!DecodeHexBin(buffer, GetLineString(msg->Buffer, &Priv->Lines, 2), (size_t)pduSize)) { 2498 smprintf(s, "Failed to decode hex string!\n"); 2499 return ERR_CORRUPTED; 2500 } 2501 2502 error = GSM_DecodePDUFrame(&(s->di), &sms, buffer, (size_t)pduSize, &parseSize, TRUE); 2503 2504 if(error == ERR_NONE) 2505 s->User.IncomingSMS(s, &sms, s->User.IncomingSMSUserData); 2506 2507 return error; 2508 } 2509 2510 gboolean InRange(int *range, int i) { 2511 while (*range != -1) { 2512 if (*range == i) { 2513 return TRUE; 2514 } 2515 range++; 2516 } 2517 return FALSE; 2518 } 2519 2520 int *GetRange(GSM_StateMachine *s, const char *buffer) 2521 { 2522 int *result = NULL; 2523 size_t allocated = 0, pos = 0; 2524 const char *chr = buffer; 2525 char *endptr = NULL; 2526 gboolean in_range = FALSE; 2527 int current, diff, i; 2528 2529 smprintf(s, "Parsing range: %s\n", chr); 2530 2531 if (*chr != '(') { 2532 return NULL; 2533 } 2534 chr++; 2535 2536 while (*chr != ')' && *chr != 0) { 2537 /* Read current number */ 2538 current = strtol(chr, &endptr, 10); 2539 2540 /* Detect how much numbers we have to store */ 2541 if (in_range) { 2542 diff = current - result[pos - 1]; 2543 } else { 2544 diff = 1; 2545 } 2546 2547 /* Did we parse anything? */ 2548 if (endptr == chr) { 2549 smprintf(s, "Failed to find number in range!\n"); 2550 free(result); 2551 return NULL; 2552 } 2553 /* Allocate more memory if needed */ 2554 if (allocated < pos + diff + 1) { 2555 result = (int *)realloc(result, sizeof(int) * (pos + diff + 10)); 2556 if (result == NULL) { 2557 smprintf(s, "Not enough memory to parse range!\n"); 2558 return NULL; 2559 } 2560 allocated = pos + 10 + diff; 2561 } 2562 2563 /* Store number is memory */ 2564 if (!in_range) { 2565 result[pos++] = current; 2566 } else { 2567 for (i = result[pos - 1] + 1; i <= current; i++) { 2568 result[pos++] = i; 2569 } 2570 in_range = FALSE; 2571 } 2572 /* Skip to next char after number */ 2573 chr = endptr; 2574 2575 /* Check for character after number */ 2576 if (*chr == '-') { 2577 in_range = TRUE; 2578 chr++; 2579 } else if (*chr == ',') { 2580 chr++; 2581 } else if (*chr == ')') { 2582 result[pos++] = -1; 2583 break; 2584 } else if (*chr != ',') { 2585 smprintf(s, "Bad character in range: %c\n", *chr); 2586 free(result); 2587 return NULL; 2588 } 2589 } 2590 if (result == NULL) { 2591 return NULL; 2592 } 2593 smprintf(s, "Returning range: "); 2594 2595 for (i = 0; result[i] != -1; i++) { 2596 smprintf(s, "%d, ", result[i]); 2597 } 2598 smprintf(s, "-1\n"); 2599 return result; 2600 } 2601 2602 GSM_Error ATGEN_ReplyGetCNMIMode(GSM_Protocol_Message *msg, GSM_StateMachine *s) 2603 { 2604 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2605 const char *buffer; 2606 int *range = NULL; 2607 int param = -1; 2608 2609 switch (Priv->ReplyState) { 2610 case AT_Reply_OK: 2611 break; 2612 case AT_Reply_Error: 2613 return ERR_NOTSUPPORTED; 2614 case AT_Reply_CMSError: 2615 return ATGEN_HandleCMSError(s); 2616 case AT_Reply_CMEError: 2617 return ATGEN_HandleCMEError(s); 2618 default: 2619 return ERR_UNKNOWNRESPONSE; 2620 } 2621 2622 /* Sample responses we get here: 2623 AT+CNMI=? 2624 +CNMI: (0-2),(0,1,3),(0),(0,1),(0,1) 2625 2626 Or: 2627 +CNMI:(0-3),(0-3),(0-3),(0,1),(0,1) 2628 2629 Or: 2630 +CNMI: (2),(0-1,3),(0,2),(0-1),(0)" 2631 */ 2632 Priv->CNMIMode = 0; 2633 Priv->CNMIProcedure = 0; 2634 Priv->CNMIDeliverProcedure = 0; 2635 #ifdef GSM_ENABLE_CELLBROADCAST 2636 Priv->CNMIBroadcastProcedure = 0; 2637 #endif 2638 Priv->CNMIClearUnsolicitedResultCodes = 0; 2639 2640 buffer = GetLineString(msg->Buffer, &Priv->Lines, 2); 2641 2642 if (buffer == NULL) { 2643 return ERR_UNKNOWNRESPONSE; 2644 } 2645 while (isspace((int)*buffer)) { 2646 buffer++; 2647 } 2648 if (strncmp(buffer, "+CNMI:", 6) != 0) { 2649 return ERR_UNKNOWNRESPONSE; 2650 } 2651 buffer += 7; 2652 buffer = strchr(buffer, '('); 2653 2654 if (buffer == NULL) { 2655 return ERR_UNKNOWNRESPONSE; 2656 } 2657 range = GetRange(s, buffer); 2658 2659 if (range == NULL) { 2660 return ERR_UNKNOWNRESPONSE; 2661 } 2662 param = s->CurrentConfig->CNMIParams[0]; 2663 if (param >= 0 && InRange(range, param)) { 2664 Priv->CNMIMode = param; 2665 } 2666 else if (InRange(range, 2)) { 2667 Priv->CNMIMode = 2; /* 2 = buffer messages and send them when link is free */ 2668 } 2669 else if (InRange(range, 3)) { 2670 Priv->CNMIMode = 3; /* 3 = send messages directly */ 2671 } 2672 else { 2673 free(range); 2674 range = NULL; 2675 return ERR_NONE; /* we don't want: 1 = ignore new messages, 0 = store messages and no indication */ 2676 } 2677 free(range); 2678 range = NULL; 2679 2680 buffer++; 2681 buffer = strchr(buffer, '('); 2682 2683 if (buffer == NULL) { 2684 return ERR_UNKNOWNRESPONSE; 2685 } 2686 range = GetRange(s, buffer); 2687 if (range == NULL) { 2688 return ERR_UNKNOWNRESPONSE; 2689 } 2690 2691 param = s->CurrentConfig->CNMIParams[1]; 2692 if (param >= 0 && InRange(range, param)) { 2693 Priv->CNMIProcedure = param; 2694 } 2695 else if (InRange(range, 1)) { 2696 Priv->CNMIProcedure = 1; /* 1 = store message and send where it is stored */ 2697 } 2698 else if (InRange(range, 2)) { 2699 Priv->CNMIProcedure = 2; /* 2 = route message to TE */ 2700 } 2701 else if (InRange(range, 3)) { 2702 Priv->CNMIProcedure = 3; /* 3 = 1 + route class 3 to TE */ 2703 } 2704 /* we don't want: 0 = just store to memory */ 2705 free(range); 2706 range = NULL; 2707 2708 buffer++; 2709 buffer = strchr(buffer, '('); 2710 #ifdef GSM_ENABLE_CELLBROADCAST 2711 if (buffer == NULL) { 2712 return ERR_UNKNOWNRESPONSE; 2713 } 2714 range = GetRange(s, buffer); 2715 2716 if (range == NULL) { 2717 return ERR_UNKNOWNRESPONSE; 2718 } 2719 2720 param = s->CurrentConfig->CNMIParams[2]; 2721 if (param >= 0 && InRange(range, param)) { 2722 Priv->CNMIBroadcastProcedure = param; 2723 } 2724 else if (InRange(range, 2)) { 2725 Priv->CNMIBroadcastProcedure = 2; /* 2 = route message to TE */ 2726 } 2727 else if (InRange(range, 1)) { 2728 Priv->CNMIBroadcastProcedure = 1; /* 1 = store message and send where it is stored */ 2729 } 2730 else if (InRange(range, 3)) { 2731 Priv->CNMIBroadcastProcedure = 3; /* 3 = 1 + route class 3 to TE */ 2732 } 2733 /* we don't want: 0 = just store to memory */ 2734 free(range); 2735 range = NULL; 2736 #endif 2737 2738 buffer++; 2739 buffer = strchr(buffer, '('); 2740 2741 if (buffer == NULL) { 2742 return ERR_UNKNOWNRESPONSE; 2743 } 2744 range = GetRange(s, buffer); 2745 2746 if (range == NULL) { 2747 return ERR_UNKNOWNRESPONSE; 2748 } 2749 2750 param = s->CurrentConfig->CNMIParams[3]; 2751 if (param >= 0 && InRange(range, param)) { 2752 Priv->CNMIDeliverProcedure = param; 2753 } 2754 else if (InRange(range, 2)) { 2755 Priv->CNMIDeliverProcedure = 2; /* 2 = store message and send where it is stored */ 2756 } 2757 else if (InRange(range, 1)) { 2758 Priv->CNMIDeliverProcedure = 1; /* 1 = route message to TE */ 2759 } 2760 /* we don't want: 0 = no routing */ 2761 free(range); 2762 range = NULL; 2763 2764 buffer++; 2765 buffer = strchr(buffer, '('); 2766 2767 if (buffer == NULL) { 2768 return ERR_NONE; 2769 } 2770 range = GetRange(s, buffer); 2771 2772 if (range == NULL) { 2773 return ERR_UNKNOWNRESPONSE; 2774 } 2775 2776 param = s->CurrentConfig->CNMIParams[4]; 2777 if (param >= 0 && InRange(range, param)) { 2778 Priv->CNMIClearUnsolicitedResultCodes = param; 2779 } 2780 free(range); 2781 range = NULL; 2782 2783 return ERR_NONE; 2784 } 2785 2786 GSM_Error ATGEN_GetCNMIMode(GSM_StateMachine *s) 2787 { 2788 GSM_Error error; 2789 2790 error = ATGEN_WaitForAutoLen(s, "AT+CNMI=?\r", 0x00, 80, ID_GetCNMIMode); 2791 return error; 2792 } 2793 2794 GSM_Error ATGEN_SetCNMI(GSM_StateMachine *s) 2795 { 2796 char buffer[100]; 2797 int length = 0; 2798 GSM_Error error; 2799 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2800 2801 if (Priv->CNMIMode == -1) { 2802 error = ATGEN_GetCNMIMode(s); 2803 2804 if (error != ERR_NONE) { 2805 return error; 2806 } 2807 } 2808 2809 if (Priv->CNMIMode == 0) { 2810 return ERR_NOTSUPPORTED; 2811 } 2812 if (Priv->CNMIBroadcastProcedure == 0) { 2813 return ERR_NOTSUPPORTED; 2814 } 2815 2816 length = sprintf( 2817 buffer, 2818 "AT+CNMI=%d,%d,%d,%d,%d\r", 2819 Priv->CNMIMode, 2820 s->Phone.Data.EnableIncomingSMS ? Priv->CNMIProcedure : 0, 2821 #ifdef GSM_ENABLE_CELLBROADCAST 2822 s->Phone.Data.EnableIncomingCB ? Priv->CNMIBroadcastProcedure : 0, 2823 #else 2824 0, 2825 #endif 2826 Priv->CNMIDeliverProcedure, 2827 Priv->CNMIClearUnsolicitedResultCodes 2828 ); 2829 2830 return ATGEN_WaitFor(s, buffer, length, 0x00, 80, ID_SetIncomingSMS); 2831 } 2832 2833 2834 GSM_Error ATGEN_SetIncomingCB(GSM_StateMachine *s, gboolean enable) 2835 { 2836 #ifdef GSM_ENABLE_CELLBROADCAST 2837 if (s->Phone.Data.EnableIncomingCB != enable) { 2838 s->Phone.Data.EnableIncomingCB = enable; 2839 return ATGEN_SetCNMI(s); 2840 } 2841 return ERR_NONE; 2842 #else 2843 return ERR_SOURCENOTAVAILABLE; 2844 #endif 2845 } 2846 2847 2848 GSM_Error ATGEN_SetIncomingSMS(GSM_StateMachine *s, gboolean enable) 2849 { 2850 GSM_Error error = ERR_NONE; 2851 GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN; 2852 2853 /* We will need this when incoming message, but we can not invoke AT commands there: */ 2854 if (Priv->PhoneSMSMemory == 0) { 2855 error = ATGEN_SetSMSMemory(s, FALSE, FALSE, FALSE); 2856 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 2857 return error; 2858 } 2859 } 2860 if (Priv->SIMSMSMemory == 0) { 2861 error = ATGEN_SetSMSMemory(s, TRUE, FALSE, FALSE); 2862 2863 if (error != ERR_NONE && error != ERR_NOTSUPPORTED) { 2864 return error; 2865 } 2866 } 2867 if (s->Phone.Data.EnableIncomingSMS != enable) { 2868 s->Phone.Data.EnableIncomingSMS = enable; 2869 return ATGEN_SetCNMI(s); 2870 } 2871 return ERR_NONE; 2872 } 2873 2874 #ifdef GSM_ENABLE_CELLBROADCAST 2875 2876 GSM_Error ATGEN_ReplyIncomingCB(GSM_Protocol_Message *msg, GSM_StateMachine *s) 2877 { 2878 #if 0 2879 GSM_CBMessage CB; 2880 char Buffer[300] = {'\0'},Buffer2[300] = {'\0'}; 2881 int i = 0; 2882 size_t j = 0; 2883 2884 DecodeHexBin (Buffer,msg->Buffer+6,msg.Length-6); 2885 DumpMessage(&di ,Buffer,msg->Length-6); 2886 2887 CB.Channel = Buffer[4]; 2888 2889 for (j = 0;j < msg->Length;j++) { 2890 smprintf(s, "j=" SIZE_T_FORMAT "\n",j); 2891 i = GSM_UnpackEightBitsToSeven(0, msg->Buffer[6], msg.Buffer[6], msg.Buffer+j, Buffer2); 2892 i = msg->Buffer[6] - 1; 2893 2894 while (i != 0) { 2895 if (Buffer[i] == 13) i = i - 1; else break; 2896 } 2897 DecodeDefault(CB.Text, Buffer2, msg->Buffer[6], TRUE, NULL); 2898 smprintf(s, "Channel %i, text \"%s\"\n",CB.Channel,DecodeUnicodeString(CB.Text)); 2899 } 2900 if (s->Phone.Data.EnableIncomingCB && s->User.IncomingCB != NULL) { 2901 s->User.IncomingCB(s,CB); 2902 } 2903 return ERR_NONE; 2904 #else 2905 smprintf(s, "CB received\n"); 2906 return ERR_NONE; 2907 #endif 2908 } 2909 2910 #endif 2911 2912 2913 #endif 2914 /*@}*/ 2915 /*@}*/ 2916 2917 /* How should editor hadle tabs in this file? Add editor commands here. 2918 * vim: noexpandtab sw=8 ts=8 sts=8: 2919 */