"Fossies" - the Fresh Open Source Software Archive

Member "gammu-1.42.0/libgammu/phone/at/at-sms.c" (3 Oct 2020, 81032 Bytes) of package /linux/privat/gammu-1.42.0.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "at-sms.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.41.0_vs_1.42.0.

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), &current, &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), &current, &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), &current, &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  */