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