"Fossies" - the Fresh Open Source Software Archive

Member "gammu-1.42.0/libgammu/phone/at/atgen.c" (3 Oct 2020, 192105 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 "atgen.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.

    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 #define _GNU_SOURCE
   18 #include <gammu-config.h>
   19 
   20 #ifdef GSM_ENABLE_ATGEN
   21 
   22 #include <string.h>
   23 #include <time.h>
   24 #include <ctype.h>
   25 #include <stdarg.h>
   26 
   27 #include "../../gsmcomon.h"
   28 #include "../../gsmphones.h"
   29 #include "../../misc/coding/coding.h"
   30 #include "../../service/gsmmisc.h"
   31 #include "../../service/gsmpbk.h"
   32 #include "../pfunc.h"
   33 
   34 #include "atgen.h"
   35 #include "atfunc.h"
   36 
   37 #include "samsung.h"
   38 #include "siemens.h"
   39 #include "motorola.h"
   40 #include "sonyericsson.h"
   41 
   42 #include "../../../libgammu/misc/string.h"
   43 
   44 #ifdef GSM_ENABLE_ALCATEL
   45 GSM_Error ALCATEL_ProtocolVersionReply (GSM_Protocol_Message *, GSM_StateMachine *);
   46 #endif
   47 
   48 #ifdef GSM_ENABLE_ATOBEX
   49 #include "../atobex/atobexfunc.h"
   50 #endif
   51 
   52 
   53 typedef struct {
   54     const GSM_AT_Charset    charset;
   55     const char      *text;
   56     const gboolean      unicode;
   57     const gboolean      ira;
   58     const gboolean      GSM;
   59 } GSM_AT_Charset_Info;
   60 
   61 /**
   62  * List of charsets and text identifying them in phone responses, order
   63  * defines their preferences, so if first is found it is used.
   64  */
   65 static GSM_AT_Charset_Info AT_Charsets[] = {
   66     {AT_CHARSET_HEX,    "HEX",      FALSE,  FALSE,  FALSE},
   67     {AT_CHARSET_GSM,    "GSM",      FALSE,  FALSE,  TRUE},
   68     {AT_CHARSET_PCCP437,    "PCCP437",  FALSE,  FALSE,  FALSE},
   69     {AT_CHARSET_UTF_8,  "UTF-8",    TRUE,   FALSE,  FALSE},
   70     {AT_CHARSET_UTF8,   "UTF8",     TRUE,   FALSE,  FALSE},
   71     {AT_CHARSET_UCS_2,  "UCS-2",    TRUE,   FALSE,  FALSE},
   72     {AT_CHARSET_UCS2,   "UCS2",     TRUE,   FALSE,  FALSE},
   73     {AT_CHARSET_IRA,    "IRA",      FALSE,  TRUE,   TRUE},
   74     {AT_CHARSET_ASCII,  "ASCII",    FALSE,  TRUE,   TRUE},
   75 #ifdef ICONV_FOUND
   76     {AT_CHARSET_ISO88591,   "8859-1",   FALSE,  FALSE,  FALSE},
   77     {AT_CHARSET_ISO88592,   "8859-2",   FALSE,  FALSE,  FALSE},
   78     {AT_CHARSET_ISO88593,   "8859-3",   FALSE,  FALSE,  FALSE},
   79     {AT_CHARSET_ISO88594,   "8859-4",   FALSE,  FALSE,  FALSE},
   80     {AT_CHARSET_ISO88595,   "8859-5",   FALSE,  FALSE,  FALSE},
   81     {AT_CHARSET_ISO88596,   "8859-6",   FALSE,  FALSE,  FALSE},
   82 #endif
   83     {0,         NULL,       FALSE,  FALSE,  FALSE}
   84 };
   85 
   86 typedef struct {
   87     int     Number;
   88     char    Text[60];
   89 } ATErrorCode;
   90 
   91 static ATErrorCode CMSErrorCodes[] = {
   92     /*
   93      * Error codes not specified here were either undefined or reserved in my
   94      * copy of specifications, if you have newer one, please fill in the gaps.
   95      */
   96     /* 0...127 from GSM 04.11 Annex E-2 */
   97     {1,    "Unassigned (unallocated) number"},
   98     {8,    "Operator determined barring"},
   99     {10,   "Call barred"},
  100     {21,   "Short message transfer rejected"},
  101     {27,   "Destination out of service"},
  102     {28,   "Unidentified subscriber"},
  103     {29,   "Facility rejected"},
  104     {30,   "Unknown subscriber"},
  105     {38,   "Network out of order"},
  106     {41,   "Temporary failure"},
  107     {42,   "Congestion"},
  108     {47,   "Resources unavailable, unspecified"},
  109     {50,   "Requested facility not subscribed"},
  110     {69,   "Requested facility not implemented"},
  111     {81,   "Invalid short message transfer reference value"},
  112     {95,   "Invalid message, unspecified"},
  113     {96,   "Invalid mandatory information"},
  114     {97,   "Message type non-existent or not implemented"},
  115     {98,   "Message not compatible with short message protocol state"},
  116     {99,   "Information element non-existent or not implemented"},
  117     {111,  "Protocol error, unspecified"},
  118     {127,  "Interworking, unspecified"},
  119     /* 128...255 from GSM 03.40 subclause 9.2.3.22 */
  120     {0x80, "Telematic interworking not supported"},
  121     {0x81, "Short message Type 0 not supported"},
  122     {0x82, "Cannot replace short message"},
  123     {0x8F, "Unspecified TP-PID error"},
  124     {0x90, "Data coding scheme (alphabet) not supported"},
  125     {0x91, "Message class not supported"},
  126     {0x9F, "Unspecified TP-DCS error"},
  127     {0xA0, "Command cannot be actioned"},
  128     {0xA1, "Command unsupported"},
  129     {0xAF, "Unspecified TP-Command error"},
  130     {0xB0, "TPDU not supported"},
  131     {0xC0, "SC busy"},
  132     {0xC1, "No SC subscription"},
  133     {0xC2, "SC system failure"},
  134     {0xC3, "Invalid SME address"},
  135     {0xC4, "Destination SME barred"},
  136     {0xC5, "SM Rejected-Duplicate SM"},
  137     {0xC6, "TP-VPF not supported"},
  138     {0xC7, "TP-VP not supported"},
  139     {0xD0, "SIM SMS storage full"},
  140     {0xD1, "No SMS storage capability in SIM"},
  141     {0xD2, "Error in MS"},
  142     {0xD3, "Memory Capacity Exceede"},
  143     {0xD4, "SIM Application Toolkit Busy"},
  144     {0xFF, "Unspecified error cause"},
  145     /* From Siemens documentation, does not have to be valid for all vendors */
  146     {256, "Operation temporary not allowed"},
  147     {257, "call barred"},
  148     {258, "phone busy"},
  149     {259, "user abort"},
  150     {260, "invalid dial string"},
  151     {261, "ss not executed"},
  152     {262, "SIM blocked"},
  153     {263, "Invalid Block"},
  154     /* 300...511 from GSM 07.05 subclause 3.2.5 */
  155     {300,  "ME failure"},
  156     {301,  "SMS service of ME reserved"},
  157     {302,  "operation not allowed"},
  158     {303,  "operation not supported"},
  159     {304,  "invalid PDU mode parameter"},
  160     {305,  "invalid text mode parameter"},
  161     {310,  "SIM not inserted"},
  162     {311,  "SIM PIN required"},
  163     {312,  "PH-SIM PIN required"},
  164     {313,  "SIM failure"},
  165     {314,  "SIM busy"},
  166     {315,  "SIM wrong"},
  167     {316,  "SIM PUK required"},
  168     {317,  "SIM PIN2 required"},
  169     {318,  "SIM PUK2 required"},
  170     {320,  "memory failure"},
  171     {321,  "invalid memory index"},
  172     {322,  "memory full"},
  173     {330,  "SMSC address unknown"},
  174     {331,  "no network service"},
  175     {332,  "network timeout"},
  176     {340,  "no CNMA acknowledgement expected"},
  177     {500,  "unknown error"},
  178     /* > 512 are manufacturer specific according to GSM 07.05 subclause 3.2.5 */
  179     {516,  "Motorola - too high location?"},
  180     /* Siemens */
  181     {512, "User abort"},
  182     {513, "unable to store"},
  183     {514, "invalid status"},
  184     {515, "invalid character in address string"},
  185     {516, "invalid length"},
  186     {517, "invalid character in pdu"},
  187     {519, "invalid length or character"},
  188     {520, "invalid character in text"},
  189     {521, "timer expired"},
  190     {522, "Operation temporary not allowed"},
  191     {532, "SIM not ready"},
  192     {534, "Cell Broadcast error unknown"},
  193     {535, "PS busy"},
  194     {538, "invalid parameter"},
  195     {549, "incorrect PDU length"},
  196     {550, "invalid message type indication (MTI)"},
  197     {551, "invalid (non-hex) chars in address"},
  198     {553, "incorrect PDU length (UDL)"},
  199     {554, "incorrect SCA length"},
  200     {578, "GPRS - unspecified activation rejection"},
  201     {588, "GPRS - feature not supported"},
  202     {594, "GPRS - invalid address length"},
  203     {595, "GPRS - invalid character in address string"},
  204     {596, "GPRS - invalid cid value"},
  205     {607, "GPRS - missing or unknown APN"},
  206     {615, "network failure"},
  207     {616, "network is down"},
  208     {625, "GPRS - pdp type not supported"},
  209     {630, "GPRS - profile (cid) not defined"},
  210     {632, "GPRS - QOS not accepted"},
  211     {633, "GPRS - QOS validation fail"},
  212     {639, "service type not yet available"},
  213     {640, "operation of service temporary not allowed"},
  214     {643, "GPRS - unknown PDP address or type"},
  215     {644, "GPRS - unknown PDP context"},
  216     {646, "GPRS - QOS invalid parameter"},
  217     {764, "missing input value"},
  218     {765, "invalid input value"},
  219     {767, "operation failed"},
  220     {769, "unable to get control of required module"},
  221     {770, "SIM invalid - network reject"},
  222     {771, "call setup in progress"},
  223     {772, "SIM powered down"},
  224     {-1,   ""}
  225 };
  226 
  227 static ATErrorCode CMEErrorCodes[] = {
  228     /* CME Error codes from GSM 07.07 section 9.2 */
  229     {0,   "phone failure"},
  230     {1,   "no connection to phone"},
  231     {2,   "phone-adaptor link reserved"},
  232     {3,   "operation not allowed"},
  233     {4,   "operation not supported"},
  234     {5,   "PH-SIM PIN required"},
  235     {10,  "SIM not inserted"},
  236     {11,  "SIM PIN required"},
  237     {12,  "SIM PUK required"},
  238     {13,  "SIM failure"},
  239     {14,  "SIM busy"},
  240     {15,  "SIM wrong"},
  241     {16,  "incorrect password"},
  242     {17,  "SIM PIN2 required"},
  243     {18,  "SIM PUK2 required"},
  244     {20,  "memory full"},
  245     {21,  "invalid index"},
  246     {22,  "not found"},
  247     {23,  "memory failure"},
  248     {24,  "text string too long"},
  249     {25,  "invalid characters in text string"},
  250     {26,  "dial string too long"},
  251     {27,  "invalid characters in dial string"},
  252     {30,  "no network service"},
  253     {31,  "network timeout"},
  254     /* 3GPP TS 27.007 /2/ */
  255     {32,  "Network not allowed - emergency calls only."},
  256     {40,  "Network personalization PIN required."},
  257     {41,  "Network personalization PUK required."},
  258     {42,  "Network subset personalization PIN required."},
  259     {43,  "Network subset personalization PUK required."},
  260     {44,  "Service provider personalization PIN required."},
  261     {45,  "Service provider personalization PUK required."},
  262     {46,  "Corporate personalization PIN required."},
  263     {47,  "Corporate personalization PUK required."},
  264     {100, "unknown"},
  265     /* GPRS-related errors - (#X = GSM 04.08 cause codes) */
  266     {103,  "Illegal MS (#3)."},
  267     {106,  "Illegal ME (#6)."},
  268     {107,  "GPRS services not allowed (#7)."},
  269     {111,  "Public Land Mobile Network (PLMN) not allowed (#11)."},
  270     {112,  "Location area not allowed (#12)."},
  271     {113,  "Roaming not allowed in this location area (#13)."},
  272     /* Errors related to a failure in Activating a Context and
  273        Other GPRS errors */
  274     {132,  "Service option not supported (#32)."},
  275     {133,  "Requested service option not subscribed (#33)."},
  276     {134,  "Service option temporarily out of order (#34)."},
  277     {148,  "Unspecified GPRS error."},
  278     {149,  "PDP authentication failure."},
  279     {150,  "Invalid mobile class."},
  280     {-1,   ""}
  281 };
  282 
  283 static char samsung_location_error[] = "[Samsung] Empty location";
  284 
  285 GSM_Error ATGEN_BeforeDeferredEventHook(GSM_StateMachine *s)
  286 {
  287   /* we can do this because deferred events must only be run when no other
  288    * request is executing */
  289   s->Protocol.Data.AT.Msg.Length = 0;
  290 
  291   return ERR_NONE;
  292 }
  293 
  294 gboolean ATGEN_IsMemoryAvailable(const GSM_Phone_ATGENData *data, GSM_MemoryType type)
  295 {
  296     return
  297             (type == MEM_ME && data->PhoneSMSMemory == AT_AVAILABLE) ||
  298             (type == MEM_SM && data->SIMSMSMemory == AT_AVAILABLE) ||
  299             (type == MEM_MT && (data->PhoneSMSMemory == AT_AVAILABLE || data->SIMSMSMemory == AT_AVAILABLE)) ||
  300             (type == MEM_SR && data->SRSMSMemory == AT_AVAILABLE);
  301 }
  302 
  303 gboolean ATGEN_IsMemoryWriteable(const GSM_Phone_ATGENData *data, GSM_MemoryType type)
  304 {
  305     // we assume that if memory is writeable, then it's also enabled
  306     return
  307             (type == MEM_ME && data->PhoneSaveSMS == AT_AVAILABLE) ||
  308             (type == MEM_SM && data->SIMSaveSMS == AT_AVAILABLE) ||
  309             (type == MEM_MT && (data->PhoneSaveSMS == AT_AVAILABLE || data->SIMSaveSMS == AT_AVAILABLE)) ||
  310             (type == MEM_SR && data->SRSaveSMS == AT_AVAILABLE);
  311 }
  312 
  313 GSM_Error ATGEN_HandleCMEError(GSM_StateMachine *s)
  314 {
  315     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
  316 
  317     if (Priv->ErrorCode == 0) {
  318         smprintf(s, "CME Error occured, but it's type not detected\n");
  319     } else if (Priv->ErrorText == NULL) {
  320         smprintf(s, "CME Error %i, no description available\n", Priv->ErrorCode);
  321     } else {
  322         smprintf(s, "CME Error %i: \"%s\"\n", Priv->ErrorCode, Priv->ErrorText);
  323     }
  324     /* For error codes descriptions see table a bit above */
  325     switch (Priv->ErrorCode) {
  326         case -1:
  327             return ERR_EMPTY;
  328         case 4:
  329         case 601: /* This seems to be returned by SE P1i when writing to pbk */
  330             return ERR_NOTSUPPORTED;
  331         case 3:
  332         case 5:
  333         case 11:
  334         case 12:
  335         case 16:
  336         case 17:
  337         case 18:
  338         case 40:
  339         case 41:
  340         case 42:
  341         case 43:
  342         case 44:
  343         case 45:
  344         case 46:
  345         case 47:
  346             return ERR_SECURITYERROR;
  347         case 10:
  348         case 13:
  349         case 14:
  350         case 15:
  351             return ERR_NOSIM;
  352         case 20:
  353             return ERR_FULL;
  354         case 21:
  355             return ERR_INVALIDLOCATION;
  356         case 22:
  357             return ERR_EMPTY;
  358         case 23:
  359             return ERR_MEMORY;
  360         case 24:
  361         case 25:
  362         case 26:
  363         case 27:
  364             return ERR_INVALIDDATA;
  365         case 30:
  366         case 31:
  367         case 32:
  368             return ERR_NETWORK_ERROR;
  369         case 515:
  370             return ERR_BUSY;
  371         default:
  372             return ERR_UNKNOWN;
  373     }
  374 }
  375 
  376 GSM_Error ATGEN_HandleCMSError(GSM_StateMachine *s)
  377 {
  378     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
  379 
  380     if (Priv->ErrorCode == 0) {
  381         smprintf(s, "CMS Error occured, but it's type not detected\n");
  382     } else if (Priv->ErrorText == NULL) {
  383         smprintf(s, "CMS Error %i, no description available\n", Priv->ErrorCode);
  384     } else {
  385         smprintf(s, "CMS Error %i: \"%s\"\n", Priv->ErrorCode, Priv->ErrorText);
  386     }
  387     /* For error codes descriptions see table a bit above */
  388     switch (Priv->ErrorCode) {
  389         case 0xD3:
  390             return ERR_FULL;
  391         case 0:
  392         case 300:
  393         case 320:
  394             return ERR_PHONE_INTERNAL;
  395         case 38:
  396         case 41:
  397         case 42:
  398         case 47:
  399         case 111:
  400         case 331:
  401         case 332:
  402         case 615:
  403         case 616:
  404             return ERR_NETWORK_ERROR;
  405         case 304:
  406             return ERR_NOTSUPPORTED;
  407         case 305:
  408         case 514:
  409         case 515:
  410         case 517:
  411         case 519:
  412         case 520:
  413         case 538:
  414         case 549:
  415         case 550:
  416         case 551:
  417         case 553:
  418         case 554:
  419             return ERR_BUG;
  420         case 302:
  421         case 311:
  422         case 312:
  423         case 316:
  424         case 317:
  425         case 318:
  426             return ERR_SECURITYERROR;
  427         case 313:
  428         case 314:
  429         case 315:
  430             return ERR_NOSIM;
  431         case 322:
  432             return ERR_FULL;
  433         case 321:
  434         case 516:
  435             return ERR_INVALIDLOCATION;
  436         case 535:
  437             return ERR_BUSY;
  438         default:
  439             return ERR_UNKNOWN;
  440     }
  441 }
  442 
  443 /**
  444  * Wrapper around \ref GSM_WaitFor, which automatically sets
  445  * correct Motorola mode. It accepts same parameters as
  446  * \ref GSM_WaitFor.
  447  */
  448 GSM_Error ATGEN_WaitFor(GSM_StateMachine *s, const char * cmd, size_t len,
  449             int type, int timeout, GSM_Phone_RequestID request)
  450 {
  451     GSM_Error error;
  452         error = MOTOROLA_SetMode(s, cmd);
  453         if (error != ERR_NONE) {
  454         return error;
  455     }
  456         error = GSM_WaitFor(s, cmd, len, type, timeout, request);
  457     return error;
  458 }
  459 
  460 /**
  461  * Checks whether string contains some non hex chars.
  462  *
  463  * \param text String to check.
  464  *
  465  * \return True when text does not contain non hex chars.
  466  */
  467 gboolean ATGEN_HasOnlyHexChars(const char *text, const size_t length)
  468 {
  469     size_t i = 0;
  470 
  471     for (i = 0; i < length; i++) {
  472         if (!isxdigit((int)(unsigned char)text[i])) {
  473             return FALSE;
  474         }
  475     }
  476     return TRUE;
  477 }
  478 
  479 /**
  480  * Checks whether string contains only digits.
  481  *
  482  * \param text String to check.
  483  *
  484  * \return True when text does not contain non digits chars.
  485  */
  486 gboolean ATGEN_HasOnlyDigits(const char *text, const size_t length)
  487 {
  488     size_t i = 0;
  489 
  490     for (i = 0; i < length; i++) {
  491         if (!isdigit((int)(unsigned char)text[i])) {
  492             return FALSE;
  493         }
  494     }
  495     return TRUE;
  496 }
  497 
  498 /**
  499  * Detects whether given text can be UCS2.
  500  *
  501  * \param s State machine structure.
  502  * \param len Length of string.
  503  * \param text Text.
  504  * \return True when text can be UCS2.
  505  */
  506 gboolean ATGEN_IsUCS2(const char *text, const size_t length)
  507 {
  508     return (length > 3) &&
  509         (length % 4 == 0) &&
  510         ATGEN_HasOnlyHexChars(text, length);
  511 }
  512 
  513 /**
  514  * Detects whether given text can be HEX.
  515  *
  516  * \param s State machine structure.
  517  * \param len Length of string.
  518  * \param text Text.
  519  * \return True when text can be HEX.
  520  */
  521 gboolean ATGEN_IsHex(const char *text, const size_t length)
  522 {
  523     return (length > 4) &&
  524         (length % 2 == 0) &&
  525         ATGEN_HasOnlyHexChars(text, length);
  526 }
  527 
  528 /**
  529  * Detects whether given text can be phone number.
  530  *
  531  * \param s State machine structure.
  532  * \param len Length of string.
  533  * \param text Text.
  534  * \return True when text can be HEX.
  535  */
  536 gboolean ATGEN_IsNumber(const char *text, const size_t length)
  537 {
  538     return ATGEN_HasOnlyDigits(text, length);
  539 }
  540 
  541 /**
  542  * Encodes text to current phone charset.
  543  *
  544  * \param s State machine structure.
  545  * \param input Input string.
  546  * \param inlength Length of string to convert.
  547  * \param output Storage for converted text.
  548  * \param outlength Size of output storage.
  549  *
  550  * \return Error code.
  551  */
  552 GSM_Error ATGEN_EncodeText(GSM_StateMachine *s,
  553         const unsigned char *input,
  554         const size_t inlength,
  555         unsigned char *output,
  556         const size_t outlength,
  557         size_t *resultlength
  558         )
  559 {
  560     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
  561     unsigned char *uname = NULL;
  562     size_t len = inlength;
  563 
  564     /* As input is unicode, we should not need that much memory, but it is safe */
  565     uname = (unsigned char *)malloc(2 * (inlength + 1));
  566 
  567     if (uname == NULL) {
  568         return ERR_MOREMEMORY;
  569     }
  570     switch (Priv->Charset) {
  571         case AT_CHARSET_HEX:
  572             EncodeDefault(uname, input, &len, TRUE, NULL);
  573             EncodeHexBin(output, uname, len);
  574             len = strlen(output);
  575             break;
  576         case AT_CHARSET_GSM:
  577             smprintf(s, "str: %s\n", DecodeUnicodeString(input));
  578             EncodeDefault(output, input, &len, TRUE, NULL);
  579             break;
  580         case AT_CHARSET_UCS2:
  581         case AT_CHARSET_UCS_2:
  582             EncodeHexUnicode(output, input, UnicodeLength(input));
  583             len = strlen(output);
  584             break;
  585         case AT_CHARSET_IRA:
  586         case AT_CHARSET_ASCII:
  587             free(uname);
  588             uname = NULL;
  589             return ERR_NOTSUPPORTED;
  590         case AT_CHARSET_UTF8:
  591         case AT_CHARSET_UTF_8:
  592             EncodeUTF8(output, input);
  593             len = strlen(output);
  594             break;
  595 #ifdef ICONV_FOUND
  596         case AT_CHARSET_PCCP437:
  597             IconvEncode("CP437", input, 2 * len, output, outlength);
  598             len = strlen(output);
  599             break;
  600         case AT_CHARSET_ISO88591:
  601             IconvEncode("ISO-8859-1", input, 2 * len, output, outlength);
  602             len = strlen(output);
  603             break;
  604         case AT_CHARSET_ISO88592:
  605             IconvEncode("ISO-8859-2", input, 2 * len, output, outlength);
  606             len = strlen(output);
  607             break;
  608         case AT_CHARSET_ISO88593:
  609             IconvEncode("ISO-8859-3", input, 2 * len, output, outlength);
  610             len = strlen(output);
  611             break;
  612         case AT_CHARSET_ISO88594:
  613             IconvEncode("ISO-8859-4", input, 2 * len, output, outlength);
  614             len = strlen(output);
  615             break;
  616         case AT_CHARSET_ISO88595:
  617             IconvEncode("ISO-8859-5", input, 2 * len, output, outlength);
  618             len = strlen(output);
  619             break;
  620         case AT_CHARSET_ISO88596:
  621             IconvEncode("ISO-8859-6", input, 2 * len, output, outlength);
  622             len = strlen(output);
  623             break;
  624 #else
  625         case AT_CHARSET_PCCP437:
  626             /* FIXME: correctly encode to PCCP437 */
  627             smprintf(s, "str: %s\n", DecodeUnicodeString(input));
  628             EncodeDefault(output, input, &len, TRUE, NULL);
  629             break;
  630 #endif
  631         default:
  632             smprintf(s, "Unsupported charset! (%d)\n", Priv->Charset);
  633             free(uname);
  634             uname = NULL;
  635             return ERR_SOURCENOTAVAILABLE;
  636         }
  637     *resultlength = len;
  638     free(uname);
  639     uname = NULL;
  640     return ERR_NONE;
  641 }
  642 
  643 
  644 /**
  645  * Decodes text from phone encoding to internal representation.
  646  *
  647  * \param s State machine structure.
  648  * \param input Input string.
  649  * \param length Length of string to convert.
  650  * \param output Storage for converted text.
  651  * \param outlength Size of output storage.
  652  * \param guess Allow guessing whether input is really encoded.
  653  * \param phone Whether input is phone number, used only when guessing.
  654  *
  655  * \return Error code.
  656  */
  657 GSM_Error ATGEN_DecodeText(GSM_StateMachine *s,
  658         const unsigned char *input,
  659         const size_t length,
  660         unsigned char *output,
  661         const size_t outlength,
  662         const gboolean guess,
  663         const gboolean phone)
  664 {
  665     unsigned char *buffer;
  666     GSM_AT_Charset charset;
  667     GSM_Phone_ATGENData     *Priv   = &s->Phone.Data.Priv.ATGEN;
  668     gboolean is_hex, is_ucs, is_number;
  669 
  670     /* Default to charset from state machine */
  671     charset = s->Phone.Data.Priv.ATGEN.Charset;
  672 
  673     /* Canonical names */
  674     if (charset == AT_CHARSET_UCS_2) {
  675         charset = AT_CHARSET_UCS2;
  676     } else if (charset == AT_CHARSET_UTF_8) {
  677         charset = AT_CHARSET_UTF8;
  678     }
  679 
  680     /* Basic type checks */
  681     is_hex = ATGEN_IsHex(input, length);
  682     is_ucs = ATGEN_IsUCS2(input, length);
  683 
  684     /* Can we do guesses? */
  685     if (guess) {
  686         is_number = ATGEN_IsNumber(input, length);
  687         /* Are there HEX only chars? */
  688         if  (charset == AT_CHARSET_HEX
  689             && ! is_hex) {
  690             charset = AT_CHARSET_GSM;
  691         }
  692         /* Should be HEX encoded, but is a phone number */
  693         if  (charset == AT_CHARSET_HEX && is_number) {
  694             /* Check whether it would not be number hex decoded as well */
  695             buffer = (unsigned char *)malloc(length);
  696 
  697             if (buffer == NULL) {
  698                 return ERR_MOREMEMORY;
  699             }
  700             DecodeHexBin(buffer, input, length);
  701             if (!ATGEN_IsNumber(buffer, strlen(buffer))) {
  702                 charset = AT_CHARSET_GSM;
  703             }
  704             free(buffer);
  705         }
  706         /*
  707          * Motorola sometimes replies in UCS2 while there is HEX chosen.
  708          * If string starts with two zeroes, it is definitely not HEX.
  709          */
  710         if  (charset == AT_CHARSET_HEX
  711             && is_ucs
  712             && input[0] == '0'
  713             && input[1] == '0'
  714             && input[4] == '0'
  715             && input[5] == '0'
  716             ) {
  717             charset = AT_CHARSET_UCS2;
  718         }
  719         /*
  720          * For phone numbers, we can assume all unicode chars
  721          * will be < 256, so they will fit one byte.
  722          */
  723         if  (charset == AT_CHARSET_UCS2
  724             && (! is_ucs ||
  725                 (phone &&
  726                 (input[0] != '0' ||
  727                  input[1] != '0' ||
  728                  input[4] != '0' ||
  729                  input[5] != '0'
  730                 )))) {
  731             charset = AT_CHARSET_GSM;
  732         }
  733         /*
  734          * Phone number can also contain email, catch it by @.
  735          */
  736         if  (charset == AT_CHARSET_GSM
  737             && phone
  738             && (! is_ucs)
  739             && strchr(input, '@') != NULL) {
  740             charset = AT_CHARSET_UTF8;
  741         }
  742         /*
  743          * Phone number are unusally not that long
  744          */
  745         if  (charset == AT_CHARSET_GSM
  746             && phone
  747             && length >= 16
  748             && is_ucs) {
  749             charset = AT_CHARSET_UCS2;
  750         }
  751         /*
  752          * Motorola sometimes criples HEX reply while UCS-2 is chosen.
  753          * It seems to be identified by trailing zero.
  754          */
  755         if  (charset == AT_CHARSET_UCS2
  756             && is_hex
  757             && Priv->Manufacturer == AT_Motorola
  758             && input[length - 1] == '0'
  759             && input[length - 2] == '0'
  760             ) {
  761             charset = AT_CHARSET_HEX;
  762         }
  763     } else {
  764         /* No guessing, but still do some sanity checks */
  765         if (charset == AT_CHARSET_UCS2 && !is_ucs) {
  766             charset = AT_CHARSET_GSM;
  767         }
  768         if (charset == AT_CHARSET_HEX && !is_hex) {
  769             charset = AT_CHARSET_GSM;
  770         }
  771     }
  772 
  773     /* Check for broken phones */
  774     if (charset == AT_CHARSET_GSM &&
  775         GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_FORCE_UTF8)) {
  776         charset = AT_CHARSET_UTF8;
  777     }
  778 
  779     /* Finally do conversion */
  780     switch (charset) {
  781         case AT_CHARSET_HEX:
  782             /* Length must be enough, because we have two chars for byte */
  783             buffer = (unsigned char *)malloc(length);
  784 
  785             if (buffer == NULL) {
  786                 return ERR_MOREMEMORY;
  787             }
  788             DecodeHexBin(buffer, input, length);
  789             if (2 * strlen(buffer) >= outlength) {
  790                 free(buffer);
  791                 return ERR_MOREMEMORY;
  792             }
  793             DecodeDefault(output, buffer, strlen(buffer), TRUE, NULL);
  794             free(buffer);
  795             buffer = NULL;
  796             break;
  797         case AT_CHARSET_GSM:
  798             if (2 * length >= outlength) return ERR_MOREMEMORY;
  799             DecodeDefault(output, input, length, TRUE, NULL);
  800             break;
  801         case AT_CHARSET_UCS2:
  802         case AT_CHARSET_UCS_2:
  803             if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SAMSUNG_UTF8)) {
  804                 unsigned char *buf;
  805                 unsigned i;
  806                 int state = 0;
  807 
  808                 if (length >= outlength) {
  809                     return ERR_MOREMEMORY;
  810                 }
  811                 buf = malloc (length / 2 + 5);
  812                 if (!buf) {
  813                     return ERR_MOREMEMORY;
  814                 }
  815                 DecodeHexUnicode(buf, input, length);
  816                 for (i = 0; 2 * i + 1 < length / 2; i++) {
  817                     buf[i] = (buf[2 * i] == 0x20 && buf[2 * i + 1] == 0xac) ? 0xe5 : buf[2 * i + 1];
  818                     if (!(buf[i] & 0x80)) {
  819                         if (state && buf[i] == 0x40) {
  820                             buf[i] = 0x80;
  821                             state--;
  822                         } else if (state && buf[i] == 0x5b) {
  823                             buf[i] = 0xbc;
  824                             state--;
  825                         } else if (state && buf[i] == 0x5c) {
  826                             buf[i] = 0xaf;
  827                             state--;
  828                         } else if (state && buf[i] == 0x5d) {
  829                             buf[i] = 0xbe;
  830                             state--;
  831                         } else if (state && buf[i] == 0x5e) {
  832                             buf[i] = 0x94;
  833                             state--;
  834                         } else if (state && buf[i] == 0x7b) {
  835                             buf[i] = 0xa8;
  836                             state--;
  837                         } else if (state && buf[i] == 0x7d) {
  838                             buf[i] = 0xa9;
  839                             state--;
  840                         } else if (state && buf[i] == 0x7e) {
  841                             buf[i] = 0xbd;
  842                             state--;
  843                         } else {
  844                             state = 0;
  845                         }
  846                     } else if ((buf[i] & 0xc0) == 0x80) {
  847                         if (state) {
  848                             state--;
  849                         }
  850                     } else if ((buf[i] & 0xe0) == 0xc0) {
  851                         state = 1;
  852                     } else if ((buf[i] & 0xf0) == 0xe0) {
  853                         state = 2;
  854                     } else {
  855                         state = 3;
  856                     }
  857 
  858                 }
  859                 buf[i] = 0;
  860 
  861                 DecodeUTF8(output, buf, length / 2);
  862                 free (buf);
  863             } else {
  864                 if (length / 2 >= outlength) {
  865                     return ERR_MOREMEMORY;
  866                 }
  867                 DecodeHexUnicode(output, input, length);
  868             }
  869             break;
  870         case AT_CHARSET_IRA: /* IRA is ASCII only, so it's safe to treat is as UTF-8 */
  871         case AT_CHARSET_ASCII:
  872         case AT_CHARSET_UTF8:
  873         case AT_CHARSET_UTF_8:
  874             if (2 * length >= outlength) return ERR_MOREMEMORY;
  875             DecodeUTF8(output, input, length);
  876             break;
  877 #ifdef ICONV_FOUND
  878         case AT_CHARSET_PCCP437:
  879             IconvDecode("CP437", input, length, output, outlength);
  880             break;
  881         case AT_CHARSET_ISO88591:
  882             IconvDecode("ISO-8859-1", input, length, output, outlength);
  883             break;
  884         case AT_CHARSET_ISO88592:
  885             IconvDecode("ISO-8859-2", input, length, output, outlength);
  886             break;
  887         case AT_CHARSET_ISO88593:
  888             IconvDecode("ISO-8859-3", input, length, output, outlength);
  889             break;
  890         case AT_CHARSET_ISO88594:
  891             IconvDecode("ISO-8859-4", input, length, output, outlength);
  892             break;
  893         case AT_CHARSET_ISO88595:
  894             IconvDecode("ISO-8859-5", input, length, output, outlength);
  895             break;
  896         case AT_CHARSET_ISO88596:
  897             IconvDecode("ISO-8859-6", input, length, output, outlength);
  898             break;
  899 #else
  900         case AT_CHARSET_PCCP437:
  901             /* FIXME: correctly decode PCCP437 */
  902             if (2 * length >= outlength) return ERR_MOREMEMORY;
  903             DecodeDefault(output, input, length, FALSE, NULL);
  904             break;
  905 #endif
  906         default:
  907             smprintf(s, "Unsupported charset! (%d)\n", charset);
  908             return ERR_SOURCENOTAVAILABLE;
  909     }
  910 
  911     return ERR_NONE;
  912 }
  913 
  914 int ATGEN_ExtractOneParameter(unsigned char *input, unsigned char *output)
  915 {
  916     int position = 0;
  917     gboolean    inside_quotes = FALSE;
  918 
  919     while ((*input!=',' || inside_quotes) && *input != 0x0d && *input != 0x00) {
  920         if (*input == '"') inside_quotes = ! inside_quotes;
  921         *output = *input;
  922         input   ++;
  923         output  ++;
  924         position++;
  925     }
  926     *output = 0;
  927     position++;
  928     return position;
  929 }
  930 
  931 /**
  932  * Grabs single string parameter from AT command reply. Removing possible quotes.
  933  *
  934  * \param s State machine structure.
  935  * \param input Input string to parse.
  936  * \param output Pointer to pointer to char, buffer will be allocated.
  937  *
  938  * \return Length of parsed string.
  939  */
  940 size_t ATGEN_GrabString(GSM_StateMachine *s, const unsigned char *input, unsigned char **output)
  941 {
  942     size_t size = 4, position = 0;
  943     gboolean inside_quotes = FALSE;
  944 
  945     /* Allocate initial buffer in case string is empty */
  946     *output = (unsigned char *)malloc(size);
  947 
  948     if (*output == NULL) {
  949         smprintf(s, "Ran out of memory!\n");
  950         return 0;
  951     }
  952     while (inside_quotes ||
  953             (  *input != ','
  954             && *input != ')'
  955             && *input != 0x0d
  956             && *input != 0x0a
  957             && *input != 0x00)) {
  958         /* Check for quotes */
  959         if (*input == '"') {
  960             inside_quotes = ! inside_quotes;
  961         }
  962 
  963         /* We also allocate space for traling zero */
  964         if (position + 2 > size) {
  965             size += 10;
  966             *output = (unsigned char *)realloc(*output, size);
  967             if (*output == NULL) {
  968                 smprintf(s, "Ran out of memory!\n");
  969                 return 0;
  970             }
  971         }
  972 
  973         /* Copy to output */
  974         (*output)[position] = *input;
  975         position++;
  976         input   ++;
  977     }
  978 
  979     (*output)[position] = 0;
  980 
  981     /* Strip quotes */
  982     if ((*output)[0] == '"' && (*output)[position - 1]) {
  983         memmove(*output, (*output) + 1, position - 2);
  984         (*output)[position - 2] = 0;
  985     }
  986 
  987     smprintf(s, "Grabbed string from reply: \"%s\" (parsed %ld bytes)\n", *output, (long)position);
  988     return position;
  989 }
  990 
  991 /**
  992  * This function parses datetime strings in the format:
  993  * [YY[YY]/MM/DD,]hh:mm[:ss[+TZ]] , [] enclosed parts are optional
  994  * (or the same hex/unicode encoded).
  995  *
  996  * It also handles when date and time are swapped.
  997  *
  998  * And tries to detect if the date is not in format MM-DD-YYYY.
  999  *
 1000  * @todo Too many static buffers are used here.
 1001  */
 1002 GSM_Error ATGEN_DecodeDateTime(GSM_StateMachine *s, GSM_DateTime *dt, unsigned char *_input)
 1003 {
 1004     unsigned char       buffer[100]={'\0'};
 1005     unsigned char       *pos = NULL;
 1006     unsigned char       buffer_unicode[200]={'\0'};
 1007     unsigned char       input[100]={'\0'};
 1008     char separator = '\0', *separator_pos, *comma_pos, *date_start, *time_start;
 1009     int year;
 1010     GSM_Error error;
 1011     size_t len;
 1012 
 1013     strncpy(input, _input, 100);
 1014     input[99] = '\0';
 1015     pos = input;
 1016 
 1017     /* Strip possible leading comma */
 1018     if (*pos == ',') pos++;
 1019     if (input[0] == 0) return ERR_EMPTY;
 1020     if (input[strlen(pos) - 1] == ',') input[strlen(pos) - 1] = 0;
 1021     if (input[0] == 0) return ERR_EMPTY;
 1022 
 1023     /* Strip possible quotes */
 1024     if (*pos == '"') pos++;
 1025     if (input[0] == 0) return ERR_EMPTY;
 1026     if (input[strlen(pos) - 1] == '"') input[strlen(pos) - 1] = 0;
 1027     if (input[0] == 0) return ERR_EMPTY;
 1028 
 1029     /* Convert to normal charset */
 1030     error = ATGEN_DecodeText(s,
 1031             pos, strlen(pos),
 1032             buffer_unicode, sizeof(buffer_unicode),
 1033             TRUE, FALSE);
 1034     if (error != ERR_NONE) return error;
 1035     DecodeUnicode(buffer_unicode, buffer);
 1036 
 1037     pos = buffer;
 1038 
 1039     /* Strip possible quotes again */
 1040     if (*pos == '"') {
 1041         pos++;
 1042     }
 1043     len = strlen(pos);
 1044     if (len == 0) {
 1045         return ERR_EMPTY;
 1046     }
 1047     if (buffer[len - 1] == '"') {
 1048         buffer[len - 1] = 0;
 1049     }
 1050 
 1051     /* Check whether date is separated by / or - */
 1052     if ((separator_pos = strchr(pos, '/')) != NULL) {
 1053         separator = '/';
 1054     } else if ((separator_pos = strchr(pos, '-')) != NULL) {
 1055         separator = '-';
 1056     }
 1057 
 1058     /* Find out where we have comma */
 1059     comma_pos = strchr(pos, ',');
 1060 
 1061     /* Skip comma and possible whitespace */
 1062     if (comma_pos != NULL) {
 1063         while (isspace(*(comma_pos + 1)) && *(comma_pos + 1) != '\0') {
 1064             comma_pos++;
 1065         }
 1066     }
 1067 
 1068     /* Find out locations of date parts */
 1069     if (comma_pos != NULL && separator_pos > comma_pos) {
 1070         date_start = comma_pos + 1;
 1071         time_start = pos;
 1072     } else if (separator_pos != NULL) {
 1073         date_start = pos;
 1074         time_start = comma_pos + 1;
 1075     } else {
 1076         date_start = NULL;
 1077         time_start = pos;
 1078     }
 1079 
 1080     /* Do we have date? */
 1081     if (date_start != NULL) {
 1082         dt->Year = atoi(date_start);
 1083         pos = strchr(date_start, separator);
 1084         if (pos == NULL) return ERR_UNKNOWN;
 1085         pos++;
 1086         dt->Month = atoi(pos);
 1087         pos = strchr(pos, separator);
 1088         if (pos == NULL) return ERR_UNKNOWN;
 1089         pos++;
 1090         dt->Day = atoi(pos);
 1091 
 1092         /* Are day, month and year swapped? */
 1093         if (dt->Day > 31) {
 1094             year = dt->Day;
 1095             dt->Day = dt->Month;
 1096             dt->Month = dt->Year;
 1097             dt->Year = year;
 1098         }
 1099 
 1100         /* Do we need to handle Y2K (Samsung usually does this)? */
 1101         if (dt->Year > 80 && dt->Year < 1000) {
 1102             dt->Year += 1900;
 1103         } else if (dt->Year < 100) {
 1104             dt->Year += 2000;
 1105         }
 1106 
 1107     } else {
 1108         /* if date was not found, it is still necessary to initialize
 1109            the variables, maybe Today() would be better in some replies */
 1110         dt->Year = 0;
 1111         dt->Month = 0;
 1112         dt->Day = 0;
 1113     }
 1114 
 1115     /* Parse time */
 1116     dt->Hour = atoi(time_start);
 1117     pos = strchr(time_start, ':');
 1118     if (pos == NULL) return ERR_UNKNOWN;
 1119     pos++;
 1120     dt->Minute = atoi(pos);
 1121     pos = strchr(pos, ':');
 1122     if (pos != NULL) {
 1123         /* seconds present */
 1124         pos++;
 1125         dt->Second = atoi(pos);
 1126     } else {
 1127         dt->Second = 0;
 1128     }
 1129 
 1130     pos = strchr(time_start, '+');
 1131     if (pos == NULL)
 1132       pos = strchr(time_start, '-');
 1133 
 1134     if (pos != NULL) {
 1135         /* timezone present */
 1136         dt->Timezone = (*pos == '+' ? 1 : -1) * atoi(pos+1) * 3600 / 4;
 1137     } else {
 1138         dt->Timezone = 0;
 1139     }
 1140     smprintf(s, "Parsed date: %d-%d-%d %d:%d:%d, TZ %d\n",
 1141         dt->Year,
 1142         dt->Month,
 1143         dt->Day,
 1144         dt->Hour,
 1145         dt->Minute,
 1146         dt->Second,
 1147         dt->Timezone);
 1148     return ERR_NONE;
 1149 }
 1150 
 1151 GSM_Error ATGEN_ParseReply(GSM_StateMachine *s, const unsigned char *input, const char *format, ...)
 1152 {
 1153     const char *fmt = format;
 1154     const char *input_pos = input;
 1155     char *endptr = NULL, *out_s = NULL, *search_pos = NULL;
 1156     GSM_DateTime *out_dt;
 1157     unsigned char *out_us = NULL,*buffer = NULL,*buffer2=NULL;
 1158     size_t length = 0,storage_size = 0;
 1159     int *out_i = NULL;
 1160     long int *out_l = NULL;
 1161     va_list ap;
 1162     GSM_Error error = ERR_NONE;
 1163 
 1164     smprintf(s, "Parsing %s with %s\n", input, format);
 1165 
 1166     va_start(ap, format);
 1167     while (*fmt) {
 1168         switch(*fmt++) {
 1169             case '@':
 1170                 if (*fmt == 0) {
 1171                     smprintf(s, "Invalid format string: %s\n", format);
 1172                     error = ERR_BUG;
 1173                     goto end;
 1174                 }
 1175                 switch(*fmt++) {
 1176                     case 'i':
 1177                         out_i = va_arg(ap, int *);
 1178                         *out_i = strtol(input_pos, &endptr, 10);
 1179                         if (endptr == input_pos) {
 1180                             error = ERR_UNKNOWNRESPONSE;
 1181                             goto end;
 1182                         }
 1183                         smprintf(s, "Parsed int %d\n", *out_i);
 1184                         input_pos = endptr;
 1185                         break;
 1186                     case 'n':
 1187                         out_i = va_arg(ap, int *);
 1188                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1189                         *out_i = strtol(buffer, &endptr, 10);
 1190                         if (endptr == (char *)buffer) {
 1191                             free(buffer);
 1192                             error = ERR_UNKNOWNRESPONSE;
 1193                             goto end;
 1194                         }
 1195                         free(buffer);
 1196                         smprintf(s, "Parsed int %d\n", *out_i);
 1197                         input_pos += length;
 1198                         break;
 1199                     case 'I':
 1200                         out_i = va_arg(ap, int *);
 1201                         *out_i = strtol(input_pos, &endptr, 10);
 1202                         if (endptr == input_pos) {
 1203                             smprintf(s, "Number empty\n");
 1204                             *out_i = 0;
 1205                         } else {
 1206                             smprintf(s, "Parsed int %d\n", *out_i);
 1207                             input_pos = endptr;
 1208                         }
 1209                         break;
 1210                     case 'l':
 1211                         out_l = va_arg(ap, long int *);
 1212                         *out_l = strtol(input_pos, &endptr, 10);
 1213                         if (endptr == input_pos) {
 1214                             error = ERR_UNKNOWNRESPONSE;
 1215                             goto end;
 1216                         }
 1217                         smprintf(s, "Parsed long int %ld\n", *out_l);
 1218                         input_pos = endptr;
 1219                         break;
 1220                     case 'p':
 1221                         out_s = va_arg(ap, char *);
 1222                         storage_size = va_arg(ap, size_t);
 1223                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1224                         smprintf(s, "Parsed phone string \"%s\"\n", buffer);
 1225                         error = ATGEN_DecodeText(s,
 1226                                 buffer, strlen(buffer),
 1227                                 out_s, storage_size,
 1228                                 TRUE, TRUE);
 1229                         if (error == ERR_NONE) {
 1230                             smprintf(s, "Phone string decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1231                         }
 1232                         free(buffer);
 1233                         buffer = NULL;
 1234 
 1235                         if (error != ERR_NONE) {
 1236                             goto end;
 1237                         }
 1238                         input_pos += length;
 1239                         break;
 1240                     case 's':
 1241                         out_s = va_arg(ap, char *);
 1242                         storage_size = va_arg(ap, size_t);
 1243                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1244                         smprintf(s, "Parsed generic string \"%s\"\n", buffer);
 1245                         error = ATGEN_DecodeText(s,
 1246                                 buffer, strlen(buffer),
 1247                                 out_s, storage_size,
 1248                                 TRUE, FALSE);
 1249                         if (error == ERR_NONE) {
 1250                             smprintf(s, "Generic string decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1251                         }
 1252                         free(buffer);
 1253                         buffer = NULL;
 1254 
 1255                         if (error != ERR_NONE) {
 1256                             goto end;
 1257                         }
 1258                         input_pos += length;
 1259                         break;
 1260                     case 't':
 1261                         out_s = va_arg(ap, char *);
 1262                         storage_size = va_arg(ap, size_t);
 1263                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1264                         smprintf(s, "Parsed string with length \"%s\"\n", buffer);
 1265                         if (!isdigit((int)buffer[0])) {
 1266                             free(buffer);
 1267                             buffer = NULL;
 1268                             error = ERR_UNKNOWNRESPONSE;
 1269                             goto end;
 1270                         }
 1271                         search_pos = strchr(buffer, ',');
 1272                         if (search_pos == NULL) {
 1273                             free(buffer);
 1274                             buffer = NULL;
 1275                             error = ERR_UNKNOWNRESPONSE;
 1276                             goto end;
 1277                         }
 1278                         search_pos++;
 1279                         error = ATGEN_DecodeText(s,
 1280                                 search_pos, strlen(search_pos),
 1281                                 out_s, storage_size,
 1282                                 TRUE, FALSE);
 1283                         if (error == ERR_NONE) {
 1284                             smprintf(s, "String with length decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1285                         }
 1286                         free(buffer);
 1287                         buffer = NULL;
 1288 
 1289                         if (error != ERR_NONE) {
 1290                             goto end;
 1291                         }
 1292                         input_pos += length;
 1293                         break;
 1294                     case 'u':
 1295                         out_s = va_arg(ap, char *);
 1296                         storage_size = va_arg(ap, size_t);
 1297                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1298                         smprintf(s, "Parsed utf-8 string  \"%s\"\n", buffer);
 1299                         DecodeUTF8(out_s, buffer, strlen(buffer));
 1300                         smprintf(s, "utf-8 string with length decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1301                         free(buffer);
 1302                         buffer = NULL;
 1303                         input_pos += length;
 1304                         break;
 1305                     case 'T':
 1306                         out_s = va_arg(ap, char *);
 1307                         storage_size = va_arg(ap, size_t);
 1308                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1309                         smprintf(s, "Parsed utf-8 string with length \"%s\"\n", buffer);
 1310                         if (!isdigit((int)buffer[0])) {
 1311                             free(buffer);
 1312                             buffer = NULL;
 1313                             error = ERR_UNKNOWNRESPONSE;
 1314                             goto end;
 1315                         }
 1316                         search_pos = strchr(buffer, ',');
 1317                         if (search_pos == NULL) {
 1318                             free(buffer);
 1319                             buffer = NULL;
 1320                             error = ERR_UNKNOWNRESPONSE;
 1321                             goto end;
 1322                         }
 1323                         search_pos++;
 1324                         DecodeUTF8(out_s, search_pos, strlen(search_pos));
 1325                         smprintf(s, "utf-8 string with length decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1326                         free(buffer);
 1327                         buffer = NULL;
 1328                         input_pos += length;
 1329                         break;
 1330                     case 'e':
 1331                         out_s = va_arg(ap, char *);
 1332                         storage_size = va_arg(ap, size_t);
 1333                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1334                         smprintf(s, "Parsed generic string \"%s\"\n", buffer);
 1335                         error = ATGEN_DecodeText(s,
 1336                                 buffer, strlen(buffer),
 1337                                 out_s, storage_size,
 1338                                 FALSE, FALSE);
 1339                         if (error == ERR_NONE) {
 1340                             smprintf(s, "Generic string decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1341                         }
 1342                         free(buffer);
 1343                         buffer = NULL;
 1344 
 1345                         if (error != ERR_NONE) {
 1346                             goto end;
 1347                         }
 1348                         input_pos += length;
 1349                         break;
 1350                     case 'S':
 1351                         out_s = va_arg(ap, char *);
 1352                         storage_size = va_arg(ap, size_t);
 1353                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1354                         if (buffer[0] == 0x02 && buffer[strlen(buffer) - 1] == 0x03) {
 1355                             memmove(buffer, buffer + 1, strlen(buffer) - 2);
 1356                             buffer[strlen(buffer) - 2] = 0;
 1357                         }
 1358                         smprintf(s, "Parsed Samsung string \"%s\"\n", buffer);
 1359                         DecodeUTF8(out_s, buffer, strlen(buffer));
 1360                         smprintf(s, "Samsung string decoded as \"%s\"\n", DecodeUnicodeString(out_s));
 1361                         free(buffer);
 1362                         buffer = NULL;
 1363                         input_pos += length;
 1364                         break;
 1365                     case 'r':
 1366                         out_us = va_arg(ap, unsigned char *);
 1367                         storage_size = va_arg(ap, size_t);
 1368                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1369                         smprintf(s, "Parsed raw string \"%s\"\n", buffer);
 1370                         if (strlen(buffer) > storage_size) {
 1371                             free(buffer);
 1372                             buffer = NULL;
 1373                             error = ERR_MOREMEMORY;
 1374                             goto end;
 1375                         }
 1376                         strcpy(out_us, buffer);
 1377                         free(buffer);
 1378                         buffer = NULL;
 1379                         input_pos += length;
 1380                         break;
 1381                     case 'd':
 1382                         out_dt = va_arg(ap, GSM_DateTime *);
 1383                         length = ATGEN_GrabString(s, input_pos, &buffer);
 1384                         /* Fix up reply from broken phones which split
 1385                          * date to two strings */
 1386                         if (length > 0 &&  *(input_pos + length) == ',' &&
 1387                                 strchr(buffer, ',') == NULL
 1388                                 ) {
 1389                             length++;
 1390                             length += ATGEN_GrabString(s, input_pos + length, &buffer2);
 1391                             buffer = (unsigned char *)realloc(buffer, length + 2);
 1392                             strcat(buffer, ",");
 1393                             strcat(buffer, buffer2);
 1394                             free(buffer2);
 1395                             buffer2=NULL;
 1396                         }
 1397                         /* Ignore missing date */
 1398                         if (strlen(buffer) != 0) {
 1399                             smprintf(s, "Parsed string for date \"%s\"\n", buffer);
 1400                             error = ATGEN_DecodeDateTime(s, out_dt, buffer);
 1401                             free(buffer);
 1402                             buffer = NULL;
 1403 
 1404                             if (error != ERR_NONE) {
 1405                                 goto end;
 1406                             }
 1407                             input_pos += length;
 1408                         } else {
 1409                             free(buffer);
 1410                             buffer = NULL;
 1411                         }
 1412                         break;
 1413                     case '@':
 1414                         if (*input_pos++ != '@') {
 1415                             error = ERR_UNKNOWNRESPONSE;
 1416                             goto end;
 1417                         }
 1418                         break;
 1419                     case '0':
 1420                         /* Just skip the rest */
 1421                         goto end;
 1422                     default:
 1423                         smprintf(s, "Invalid format string (@%c): %s\n", *(fmt - 1), format);
 1424                         error = ERR_BUG;
 1425                         goto end;
 1426                 }
 1427                 break;
 1428             case ' ':
 1429                 while (isspace((int)*input_pos)) input_pos++;
 1430                 break;
 1431             default:
 1432                 if (*input_pos++ != *(fmt - 1)) {
 1433                     error = ERR_UNKNOWNRESPONSE;
 1434                     goto end;
 1435                 }
 1436                 break;
 1437         }
 1438     }
 1439 
 1440     /* Ignore trailing spaces */
 1441     while (isspace((int)*input_pos)) input_pos++;
 1442 
 1443     if (*input_pos != 0) {
 1444         smprintf(s, "String do not end same!\n");
 1445         error = ERR_UNKNOWNRESPONSE;
 1446         goto end;
 1447     }
 1448 end:
 1449     va_end(ap);
 1450     return error;
 1451 }
 1452 
 1453 int ATGEN_PrintReplyLines(GSM_StateMachine *s)
 1454 {
 1455     int i = 0;
 1456     GSM_Phone_ATGENData     *Priv   = &s->Phone.Data.Priv.ATGEN;
 1457     GSM_Protocol_Message    *msg    = s->Phone.Data.RequestMsg;
 1458 
 1459     /* Find number of lines */
 1460     while (Priv->Lines.numbers[i*2+1] != 0) {
 1461         /* FIXME: handle special chars correctly */
 1462         smprintf(s, "%i \"%s\"\n",i+1,GetLineString(msg->Buffer,&Priv->Lines,i+1));
 1463         i++;
 1464     }
 1465     return i;
 1466 }
 1467 
 1468 GSM_Error ATGEN_DispatchMessage(GSM_StateMachine *s)
 1469 {
 1470     GSM_Phone_ATGENData     *Priv   = &s->Phone.Data.Priv.ATGEN;
 1471     GSM_Protocol_Message    *msg    = s->Phone.Data.RequestMsg;
 1472     int             i = 0,j = 0,k = 0;
 1473     const char      *err, *line;
 1474     ATErrorCode     *ErrorCodes = NULL;
 1475     char *line1, *line2;
 1476 
 1477     SplitLines(msg->Buffer, msg->Length, &Priv->Lines, "\x0D\x0A", 2, "\"", 1, TRUE);
 1478 
 1479     /* Find number of lines */
 1480     i = ATGEN_PrintReplyLines(s);
 1481 
 1482     /* Check for duplicated command in response (bug#1069) */
 1483     if (i >= 2) {
 1484         /* Get first two lines */
 1485         line1 = strdup(GetLineString(msg->Buffer, &Priv->Lines, 1));
 1486         line2 = strdup(GetLineString(msg->Buffer, &Priv->Lines, 2));
 1487         if (line1 == NULL || line2 == NULL) {
 1488             free(line1);
 1489             free(line2);
 1490             return ERR_MOREMEMORY;
 1491         }
 1492         /* Is it AT command? */
 1493         if (strncmp(line1, "AT", 2) == 0) {
 1494             /* Are two lines same */
 1495             if (strcmp(line1, line2) == 0) {
 1496                 smprintf(s, "Removing first reply, because it is duplicated\n");
 1497                 /* Remove first line */
 1498                 memmove(Priv->Lines.numbers, Priv->Lines.numbers + 2, (Priv->Lines.allocated - 2) * sizeof(int));
 1499                 i--;
 1500                 ATGEN_PrintReplyLines(s);
 1501             }
 1502         }
 1503         /* Free allocated memory */
 1504         free(line1);
 1505         free(line2);
 1506     }
 1507 
 1508     Priv->ReplyState    = AT_Reply_Unknown;
 1509     Priv->ErrorText         = NULL;
 1510     Priv->ErrorCode         = 0;
 1511 
 1512     line = GetLineString(msg->Buffer,&Priv->Lines,i);
 1513 
 1514     smprintf(s, "Checking line: %s\n", line);
 1515 
 1516     if (!strcmp(line,"OK")) {
 1517         Priv->ReplyState = AT_Reply_OK;
 1518     }
 1519     if (!strncmp(line,"+CPIN:", 6) && s->Protocol.Data.AT.CPINNoOK) {
 1520         Priv->ReplyState = AT_Reply_OK;
 1521     }
 1522     if (!strcmp(line,"> ")) {
 1523         Priv->ReplyState = AT_Reply_SMSEdit;
 1524     }
 1525     if (!strcmp(line,"CONNECT")) {
 1526         Priv->ReplyState = AT_Reply_Connect;
 1527     }
 1528     if (!strcmp(line,"ERROR")) {
 1529         Priv->ReplyState = AT_Reply_Error;
 1530     }
 1531     if (!strcmp(line,"NO CARRIER")) {
 1532         Priv->ReplyState = AT_Reply_Error;
 1533     }
 1534 
 1535     if (!strncmp(line,"+CME ERROR:",11)) {
 1536         Priv->ReplyState = AT_Reply_CMEError;
 1537         ErrorCodes = CMEErrorCodes;
 1538     }
 1539     if (!strncmp(line,"+CMS ERROR:",11)) {
 1540         Priv->ReplyState = AT_Reply_CMSError;
 1541         ErrorCodes = CMSErrorCodes;
 1542     }
 1543 
 1544     /* Huawei E220 returns COMMAND NOT SUPPORT on AT+MODE=2 */
 1545     if (!strncmp(line, "COMMAND NOT SUPPORT", 19)) {
 1546         Priv->ReplyState = AT_Reply_Error;
 1547     }
 1548 
 1549     /* Motorola A1200 */
 1550     if (!strncmp(line, "MODEM ERROR:", 12)) {
 1551         Priv->ReplyState = AT_Reply_Error;
 1552     }
 1553 
 1554     /* FIXME: Samsung phones can answer +CME ERROR:-1 meaning empty location */
 1555     if (Priv->ReplyState == AT_Reply_CMEError && Priv->Manufacturer == AT_Samsung) {
 1556         err = line + 11;
 1557         Priv->ErrorCode = atoi(err);
 1558 
 1559         if (Priv->ErrorCode == -1) {
 1560             Priv->ErrorText = samsung_location_error;
 1561             return GSM_DispatchMessage(s);
 1562         }
 1563     }
 1564 
 1565     if (Priv->ReplyState == AT_Reply_CMEError || Priv->ReplyState == AT_Reply_CMSError) {
 1566         if (ErrorCodes == NULL) {
 1567             return ERR_BUG;
 1568         }
 1569             j = 0;
 1570         /* One char behind +CM[SE] ERROR */
 1571         err = line + 11;
 1572         while (err[j] && !isalnum((int)err[j])) j++;
 1573 
 1574         if (isdigit((int)err[j])) {
 1575             Priv->ErrorCode = atoi(&(err[j]));
 1576             for (k = 0; ErrorCodes[k].Number != -1; k++) {
 1577                 if (ErrorCodes[k].Number == Priv->ErrorCode) {
 1578                     Priv->ErrorText = ErrorCodes[k].Text;
 1579                     break;
 1580                 }
 1581             }
 1582         } else if (isalpha((int)err[j])) {
 1583             for (k = 0; ErrorCodes[k].Number != -1; k++) {
 1584                 if (!strncmp(err + j, ErrorCodes[k].Text, strlen(ErrorCodes[k].Text))) {
 1585                     Priv->ErrorCode = ErrorCodes[k].Number;
 1586                     Priv->ErrorText = ErrorCodes[k].Text;
 1587                     break;
 1588                 }
 1589             }
 1590         }
 1591     }
 1592     smprintf(s, "AT reply state: %d\n", Priv->ReplyState);
 1593     return GSM_DispatchMessage(s);
 1594 }
 1595 
 1596 GSM_Error ATGEN_GenericReplyIgnore(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s UNUSED)
 1597 {
 1598     return ERR_NONE;
 1599 }
 1600 
 1601 GSM_Error ATGEN_GenericReply(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 1602 {
 1603     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 1604         case AT_Reply_OK:
 1605         case AT_Reply_Connect:
 1606             return ERR_NONE;
 1607         case AT_Reply_Error:
 1608             return ERR_UNKNOWN;
 1609         case AT_Reply_CMSError:
 1610             return ATGEN_HandleCMSError(s);
 1611         case AT_Reply_CMEError:
 1612             return ATGEN_HandleCMEError(s);
 1613         default:
 1614             break;
 1615     }
 1616     return ERR_UNKNOWNRESPONSE;
 1617 }
 1618 
 1619 GSM_Error ATGEN_SQWEReply(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 1620 {
 1621     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 1622 
 1623     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 1624         case AT_Reply_OK:
 1625             /* Parse reply code */
 1626             return ATGEN_ParseReply(s,
 1627                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 1628                     "^SQWE: @i",
 1629                     &Priv->SQWEMode);
 1630         case AT_Reply_Connect:
 1631             return ERR_NONE;
 1632         case AT_Reply_Error:
 1633             return ERR_NOTSUPPORTED;
 1634         case AT_Reply_CMSError:
 1635             return ATGEN_HandleCMSError(s);
 1636         case AT_Reply_CMEError:
 1637             return ATGEN_HandleCMEError(s);
 1638         default:
 1639             break;
 1640     }
 1641     return ERR_UNKNOWNRESPONSE;
 1642 }
 1643 
 1644 GSM_Error ATGEN_ReplyGetUSSD(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 1645 {
 1646     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 1647     GSM_USSDMessage ussd;
 1648     GSM_Error error = ERR_NONE;
 1649     unsigned char *pos = NULL;
 1650     int code = 0;
 1651     int dcs = 0;
 1652     GSM_Coding_Type coding;
 1653     char hex_encoded[2 * (GSM_MAX_USSD_LENGTH + 1)] = {0};
 1654     char packed[GSM_MAX_USSD_LENGTH + 1] = {0};
 1655     char decoded[GSM_MAX_USSD_LENGTH + 1] = {0};
 1656 
 1657     /*
 1658      * Reply format:
 1659      * +CUSD: 2,"...",15
 1660      */
 1661     smprintf(s, "Incoming USSD received\n");
 1662 
 1663     if (s->Phone.Data.EnableIncomingUSSD) {
 1664         /* Find start of reply */
 1665         pos = strstr(msg->Buffer, "+CUSD:");
 1666         if (pos == NULL) {
 1667             if (s->Phone.Data.RequestID == ID_GetUSSD) {
 1668                 /*
 1669                  * We usually get reply right after AT+CUSD=, but
 1670                  * if this is not the case, we should wait.
 1671                  */
 1672                 return ERR_NONE;
 1673             }
 1674             return ERR_UNKNOWNRESPONSE;
 1675         }
 1676 
 1677         /* Parse reply code */
 1678         error = ATGEN_ParseReply(s, pos,
 1679                 "+CUSD: @i @0",
 1680                 &code);
 1681 
 1682         if (error != ERR_NONE) return error;
 1683 
 1684         /* Try to parse text here, we ignore error code intentionally */
 1685         ussd.Text[0] = 0;
 1686         ussd.Text[1] = 0;
 1687 
 1688         /* Decode status */
 1689         smprintf(s, "Status: %d\n", code);
 1690         switch(code) {
 1691             case 0:
 1692                 ussd.Status = USSD_NoActionNeeded;
 1693                 break;
 1694             case 1:
 1695                 ussd.Status = USSD_ActionNeeded;
 1696                 break;
 1697             case 2:
 1698                 ussd.Status = USSD_Terminated;
 1699                 break;
 1700             case 3:
 1701                 ussd.Status = USSD_AnotherClient;
 1702                 break;
 1703             case 4:
 1704                 ussd.Status = USSD_NotSupported;
 1705                 error = ERR_NETWORK_ERROR;
 1706                 goto done;
 1707             case 5:
 1708                 ussd.Status = USSD_Timeout;
 1709                 error = ERR_TIMEOUT;
 1710                 goto done;
 1711             default:
 1712                 ussd.Status = USSD_Unknown;
 1713         }
 1714 
 1715         // if it looks like we only received a USSD code we're done.
 1716       if(strchr(msg->Buffer + 8, ',') == NULL)
 1717         goto done;
 1718 
 1719         error = ATGEN_ParseReply(s, pos,
 1720                     "+CUSD: @i, @r, @i @0",
 1721                     &code,
 1722                     hex_encoded, sizeof(hex_encoded),
 1723                     &dcs);
 1724 
 1725         if (error == ERR_NONE || GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_ENCODED_USSD)) {
 1726             if (error != ERR_NONE) {
 1727                 dcs = 0;
 1728                 error = ATGEN_ParseReply(s, pos,
 1729                         "+CUSD: @i, @r @0",
 1730                         &code,
 1731                         hex_encoded, sizeof(hex_encoded));
 1732             }
 1733             if (error != ERR_NONE) {
 1734                 goto done;
 1735             }
 1736 
 1737             if ((dcs & 0xc0) == 0) {
 1738                 if ((dcs & 0x30) != 0x10) {
 1739                     /* GSM-7 */
 1740                     coding = SMS_Coding_Default_No_Compression;
 1741                 } else {
 1742                     if ((dcs & 0xf) == 0) {
 1743                         /* GSM-7 */
 1744                         coding = SMS_Coding_Default_No_Compression;
 1745                     } else if ((dcs & 0xf) == 1) {
 1746                         coding = SMS_Coding_Unicode_No_Compression;
 1747                     } else {
 1748                         smprintf(s, "WARNING: unknown DCS: 0x%02x\n", dcs);
 1749                         coding = SMS_Coding_Default_No_Compression;
 1750                     }
 1751                 }
 1752             } else {
 1753                 /* Fallback to SMS coding */
 1754                 coding = GSM_GetMessageCoding(&(s->di), dcs);
 1755             }
 1756 
 1757             smprintf(s, "USSD coding DCS = %d -> Coding = %d\n", dcs, coding);
 1758 
 1759             if (coding == SMS_Coding_Default_No_Compression) {
 1760                 if (Priv->Charset == AT_CHARSET_HEX || GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_ENCODED_USSD)) {
 1761                     DecodeHexBin(packed, hex_encoded, strlen(hex_encoded));
 1762                     GSM_UnpackEightBitsToSeven(0, strlen(hex_encoded), sizeof(decoded), packed, decoded);
 1763                     DecodeDefault(ussd.Text, decoded, strlen(decoded), TRUE, NULL);
 1764                 } else {
 1765                     error = ATGEN_DecodeText(s, hex_encoded, strlen(hex_encoded), ussd.Text, sizeof(ussd.Text) - 1, FALSE, FALSE);
 1766                     if (error != ERR_NONE) {
 1767                         return error;
 1768                     }
 1769                 }
 1770             } else if (coding == SMS_Coding_Unicode_No_Compression) {
 1771                 DecodeHexUnicode(ussd.Text, hex_encoded, strlen(hex_encoded));
 1772             } else if (coding == SMS_Coding_8bit) {
 1773                 DecodeHexBin(decoded, hex_encoded, strlen(hex_encoded));
 1774                 GSM_UnpackEightBitsToSeven(0, strlen(hex_encoded), sizeof(decoded), packed, decoded);
 1775                 DecodeDefault(ussd.Text, decoded, strlen(decoded), TRUE, NULL);
 1776                 smprintf(s, "WARNING: 8-bit encoding!\n");
 1777             } else {
 1778                 smprintf(s, "WARNING: unknown encoding!\n");
 1779             }
 1780         } else {
 1781             error = ATGEN_ParseReply(s, pos,
 1782                     "+CUSD: @i, @s @0",
 1783                     &code,
 1784                     ussd.Text, sizeof(ussd.Text));
 1785             if (error != ERR_NONE) {
 1786                 goto done;
 1787             }
 1788         }
 1789 
 1790 done:
 1791         /* Notify application */
 1792         if (s->User.IncomingUSSD != NULL) {
 1793             s->User.IncomingUSSD(s, &ussd, s->User.IncomingUSSDUserData);
 1794         }
 1795     }
 1796 
 1797     return error;
 1798 }
 1799 
 1800 GSM_Error ATGEN_SetIncomingUSSD(GSM_StateMachine *s, gboolean enable)
 1801 {
 1802     GSM_Error error;
 1803 
 1804     error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 1805 
 1806     if (error != ERR_NONE) {
 1807         return error;
 1808     }
 1809     if (enable) {
 1810         smprintf(s, "Enabling incoming USSD\n");
 1811         error = ATGEN_WaitForAutoLen(s, "AT+CUSD=1\r", 0x00, 10, ID_SetUSSD);
 1812     } else {
 1813         if (!GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NO_STOP_CUSD)) {
 1814             smprintf(s, "Terminating possible incoming USSD\n");
 1815             error = ATGEN_WaitForAutoLen(s, "AT+CUSD=2\r", 0x00, 10, ID_SetUSSD);
 1816         }
 1817         smprintf(s, "Disabling incoming USSD\n");
 1818         error = ATGEN_WaitForAutoLen(s, "AT+CUSD=0\r", 0x00, 10, ID_SetUSSD);
 1819     }
 1820     if (error == ERR_NONE) {
 1821         s->Phone.Data.EnableIncomingUSSD = enable;
 1822     }
 1823     if (error == ERR_UNKNOWN) {
 1824         return ERR_NOTSUPPORTED;
 1825     }
 1826     return error;
 1827 }
 1828 
 1829 GSM_Error ATGEN_ReplyGetModel(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 1830 {
 1831     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 1832     GSM_Phone_Data      *Data = &s->Phone.Data;
 1833     const char *pos, *pos2 = NULL;
 1834     const char *line;
 1835 
 1836     if (s->Phone.Data.Priv.ATGEN.ReplyState != AT_Reply_OK) {
 1837         return ERR_NOTSUPPORTED;
 1838     }
 1839     line = GetLineString(msg->Buffer, &Priv->Lines, 2);
 1840     pos = line;
 1841 
 1842     /* Samsungs gives all information at once */
 1843     if (strstr(line, "Manufacturer") != NULL) {
 1844         line = GetLineString(msg->Buffer, &Priv->Lines, 3);
 1845         if (strstr(line, "Model") == NULL) {
 1846             line = GetLineString(msg->Buffer, &Priv->Lines, 2);
 1847         }
 1848         pos = line;
 1849     }
 1850 
 1851     /*
 1852      * Motorola returns something like:
 1853      * "+CGMM: "GSM900","GSM1800","GSM1900","GSM850","MODEL=V3""
 1854      */
 1855     if ((pos2 = strstr(line, "\"MODEL=")) != NULL) {
 1856         pos = pos2 + 7; /* Skip above string */
 1857         pos2 = strchr(pos, '"'); /* Find end quote */
 1858     /* Sometimes phone adds this before manufacturer (Motorola) */
 1859     } else if (strncmp("+CGMM: \"", line, 8) == 0) {
 1860         pos += 8; /* Skip above string */
 1861         pos2 = strchr(pos, '"'); /* Find end quote */
 1862     /* Sometimes phone adds this before manufacturer (Sagem) */
 1863     } else if (strncmp("+CGMM: ", line, 7) == 0) {
 1864         pos += 7; /* Skip above string */
 1865     }
 1866     /* Samsung */
 1867     if (strncmp("Model: ", pos, 7) == 0) {
 1868         pos += 7; /* Skip above string */
 1869     }
 1870     /* Samsung */
 1871     if (strncmp("I: ", pos, 3) == 0) {
 1872         pos += 3; /* Skip above string */
 1873     }
 1874 
 1875     /* Skip white spaces */
 1876     while (isspace(*pos)) {
 1877         pos++;
 1878     }
 1879     if (pos2 == NULL) {
 1880         pos2 = pos + strlen(pos);
 1881     }
 1882     /* Go before last char */
 1883     pos2--;
 1884     while(isspace(*pos2) && pos2 > pos) {
 1885         pos2--;
 1886     }
 1887 
 1888     /* Now store string if it fits */
 1889     if (1 + pos2 - pos > GSM_MAX_MODEL_LENGTH) {
 1890         smprintf(s, "WARNING: Model name too long, increase GSM_MAX_MODEL_LENGTH to at least %ld (currently %d)\n",
 1891                 (long int)(1 + pos2 - pos),
 1892                 GSM_MAX_MODEL_LENGTH);
 1893     }
 1894 
 1895     strncpy(Data->Model, pos, MIN(1 + pos2 - pos, GSM_MAX_MODEL_LENGTH));
 1896     Data->Model[1 + pos2 - pos] = 0;
 1897 
 1898     Data->ModelInfo = GetModelData(s, NULL, Data->Model, NULL);
 1899 
 1900     if (Data->ModelInfo->number[0] == 0)
 1901         Data->ModelInfo = GetModelData(s, NULL, NULL, Data->Model);
 1902 
 1903     if (Data->ModelInfo->number[0] == 0)
 1904         Data->ModelInfo = GetModelData(s, Data->Model, NULL, NULL);
 1905 
 1906     if (Data->ModelInfo->number[0] == 0) {
 1907         smprintf(s, "Unknown model, but it should still work\n");
 1908     }
 1909     smprintf(s, "[Model name: `%s']\n", Data->Model);
 1910     smprintf(s, "[Model data: `%s']\n", Data->ModelInfo->number);
 1911     smprintf(s, "[Model data: `%s']\n", Data->ModelInfo->model);
 1912 
 1913     s->Protocol.Data.AT.FastWrite = !GSM_IsPhoneFeatureAvailable(Data->ModelInfo, F_SLOWWRITE);
 1914     s->Protocol.Data.AT.CPINNoOK = GSM_IsPhoneFeatureAvailable(Data->ModelInfo, F_CPIN_NO_OK);
 1915 
 1916     return ERR_NONE;
 1917 }
 1918 
 1919 GSM_Error ATGEN_GetModel(GSM_StateMachine *s)
 1920 {
 1921     GSM_Error error;
 1922 
 1923     if (s->Phone.Data.Model[0] != 0) return ERR_NONE;
 1924 
 1925     smprintf(s, "Getting model\n");
 1926     error = ATGEN_WaitForAutoLen(s, "AT+CGMM\r", 0x00, 10, ID_GetModel);
 1927 
 1928     if (error != ERR_NONE) {
 1929         error = ATGEN_WaitForAutoLen(s, "ATI4\r", 0x00, 10, ID_GetModel);
 1930     }
 1931     if (error == ERR_NONE) {
 1932         smprintf_level(s, D_TEXT, "[Connected model  - \"%s\"]\n",
 1933                 s->Phone.Data.Model);
 1934     }
 1935     return error;
 1936 }
 1937 
 1938 GSM_Error ATGEN_ReplyGetManufacturer(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 1939 {
 1940     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 1941 
 1942     typedef struct {
 1943         char name[20];
 1944         GSM_AT_Manufacturer id;
 1945     } vendors_t;
 1946     vendors_t vendors[] = {
 1947         {"Falcom", AT_Falcom},
 1948         {"Nokia", AT_Nokia},
 1949         {"Siemens", AT_Siemens},
 1950         {"Sharp", AT_Sharp},
 1951         {"Huawei", AT_Huawei},
 1952         {"Sony Ericsson", AT_Ericsson},
 1953         {"Ericsson", AT_Ericsson},
 1954         {"iPAQ", AT_HP},
 1955         {"Alcatel", AT_Alcatel},
 1956         {"Samsung", AT_Samsung},
 1957         {"Philips", AT_Philips},
 1958         {"Mitsubishi", AT_Mitsubishi},
 1959         {"Motorola", AT_Motorola},
 1960         {"Option", AT_Option},
 1961         {"Wavecom", AT_Wavecom},
 1962         {"Qualcomm", AT_Qualcomm},
 1963         {"Telit", AT_Telit},
 1964         {"ZTE", AT_ZTE},
 1965         {"\0", 0}
 1966     };
 1967     vendors_t *vendor;
 1968 
 1969 
 1970 
 1971     switch (Priv->ReplyState) {
 1972     case AT_Reply_OK:
 1973         smprintf(s, "Manufacturer info received\n");
 1974         Priv->Manufacturer = AT_Unknown;
 1975 
 1976         if (GetLineLength(msg->Buffer, &Priv->Lines, 2) <= GSM_MAX_MANUFACTURER_LENGTH) {
 1977             CopyLineString(s->Phone.Data.Manufacturer, msg->Buffer, &Priv->Lines, 2);
 1978         } else {
 1979             smprintf(s, "WARNING: Manufacturer name too long, increase GSM_MAX_MANUFACTURER_LENGTH to at least %d\n", GetLineLength(msg->Buffer, &Priv->Lines, 2));
 1980             s->Phone.Data.Manufacturer[0] = 0;
 1981         }
 1982         /* Sometimes phone adds this before manufacturer (Sagem) */
 1983         if (strncmp("+CGMI: ", s->Phone.Data.Manufacturer, 7) == 0) {
 1984             memmove(s->Phone.Data.Manufacturer, s->Phone.Data.Manufacturer + 7, strlen(s->Phone.Data.Manufacturer + 7) + 1);
 1985         }
 1986         /* Samsung */
 1987         if (strncmp("Manufacturer: ", s->Phone.Data.Manufacturer, 14) == 0) {
 1988             memmove(s->Phone.Data.Manufacturer, s->Phone.Data.Manufacturer + 14, strlen(s->Phone.Data.Manufacturer + 14) + 1);
 1989         }
 1990         if (strncmp("I: ", s->Phone.Data.Manufacturer, 3) == 0) {
 1991             memmove(s->Phone.Data.Manufacturer, s->Phone.Data.Manufacturer + 3, strlen(s->Phone.Data.Manufacturer + 3) + 1);
 1992         }
 1993 
 1994         /* Lookup in vendor table */
 1995         for (vendor = vendors; vendor->id != 0; vendor++) {
 1996             if (strcasestr(msg->Buffer, vendor->name)) {
 1997                 strcpy(s->Phone.Data.Manufacturer, vendor->name);
 1998                 Priv->Manufacturer = vendor->id;
 1999             }
 2000         }
 2001 
 2002         /* Vendor specific hacks*/
 2003         if (Priv->Manufacturer == AT_Falcom && strstr(msg->Buffer,"A2D")) {
 2004             strcpy(s->Phone.Data.Model,"A2D");
 2005             s->Phone.Data.ModelInfo = GetModelData(s, NULL, s->Phone.Data.Model, NULL);
 2006             smprintf(s, "Model A2D\n");
 2007         }
 2008         if (Priv->Manufacturer == AT_Nokia) {
 2009             smprintf(s, "HINT: Consider using Nokia specific protocol instead of generic AT.\n");
 2010         }
 2011 
 2012         /*
 2013          * IAXmodem can not currently reasonably work with Gammu,
 2014          * but we can try to fixup at least something.
 2015          */
 2016         if (strstr(msg->Buffer, "www.soft-switch.org")) {
 2017             /* It replies OK to anything, but this just clutters output */
 2018             Priv->Mode = FALSE;
 2019         }
 2020         smprintf(s, "[Manufacturer: %s]\n", s->Phone.Data.Manufacturer);
 2021         return ERR_NONE;
 2022     case AT_Reply_CMSError:
 2023         return ATGEN_HandleCMSError(s);
 2024     case AT_Reply_CMEError:
 2025         return ATGEN_HandleCMEError(s);
 2026     case AT_Reply_Error:
 2027         return ERR_NOTSUPPORTED;
 2028     default:
 2029         break;
 2030     }
 2031     return ERR_UNKNOWNRESPONSE;
 2032 }
 2033 
 2034 GSM_Error ATGEN_GetManufacturer(GSM_StateMachine *s)
 2035 {
 2036     GSM_Error error;
 2037     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2038 
 2039     if (Priv->Manufacturer != 0 && s->Phone.Data.Manufacturer[0] != 0) return ERR_NONE;
 2040 
 2041     strcpy(s->Phone.Data.Manufacturer, "Unknown");
 2042 
 2043     error = ATGEN_WaitForAutoLen(s, "AT+CGMI\r", 0x00, 40, ID_GetManufacturer);
 2044 
 2045     if (error != ERR_NONE) {
 2046         error = ATGEN_WaitForAutoLen(s, "ATI3\r", 0x00, 40, ID_GetManufacturer);
 2047     }
 2048     return ERR_NONE;
 2049 }
 2050 
 2051 GSM_Error ATGEN_ReplyGetFirmware(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2052 {
 2053 
 2054     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2055     int line = 0;
 2056 
 2057     strcpy(s->Phone.Data.Version, "Unknown");
 2058 
 2059     if (s->Phone.Data.Priv.ATGEN.ReplyState != AT_Reply_OK) {
 2060         return ERR_NOTSUPPORTED;
 2061     }
 2062 
 2063     s->Phone.Data.VerNum = 0;
 2064     if (Priv->ReplyState == AT_Reply_OK) {
 2065         line = 2;
 2066 
 2067         if (strstr(GetLineString(msg->Buffer, &Priv->Lines, line), "Manufacturer:") != NULL) {
 2068             line ++;
 2069         }
 2070         if (strstr(GetLineString(msg->Buffer, &Priv->Lines, line), "Model:") != NULL) {
 2071             line ++;
 2072         }
 2073         if (GetLineLength(msg->Buffer, &Priv->Lines, line) > GSM_MAX_VERSION_LENGTH - 1) {
 2074             smprintf(s, "Please increase GSM_MAX_VERSION_LENGTH!\n");
 2075             return ERR_MOREMEMORY;
 2076         }
 2077         CopyLineString(s->Phone.Data.Version, msg->Buffer, &Priv->Lines, line);
 2078         /* Sometimes phone adds this before version (Sagem) */
 2079         if (strncmp("+CGMR: ", s->Phone.Data.Version, 7) == 0) {
 2080             /* Need to use memmove as strcpy does not correctly handle overlapping regions */
 2081             memmove(s->Phone.Data.Version, s->Phone.Data.Version + 7, strlen(s->Phone.Data.Version + 7) + 1);
 2082         }
 2083         /* Sometimes phone adds this before version (Shrap) */
 2084         if (strncmp("Revision: ", s->Phone.Data.Version, 10) == 0) {
 2085             /* Need to use memmove as strcpy does not correctly handle overlapping regions */
 2086             memmove(s->Phone.Data.Version, s->Phone.Data.Version + 10, strlen(s->Phone.Data.Version + 10) + 1);
 2087         }
 2088         /* Samsung */
 2089         if (strncmp("I: ", s->Phone.Data.Version, 3) == 0) {
 2090             /* Need to use memmove as strcpy does not correctly handle overlapping regions */
 2091             memmove(s->Phone.Data.Version, s->Phone.Data.Version + 3, strlen(s->Phone.Data.Version + 3) + 1);
 2092         }
 2093         /* Add second line if it also contains version information */
 2094         if (strcmp(GetLineString(msg->Buffer, &Priv->Lines, 3), "OK") != 0) {
 2095             if (GetLineLength(msg->Buffer, &Priv->Lines, 3) + 1 + strlen(s->Phone.Data.Version) < GSM_MAX_VERSION_LENGTH - 1) {
 2096                 strcat(s->Phone.Data.Version, ",");
 2097                 CopyLineString(s->Phone.Data.Version + strlen(s->Phone.Data.Version), msg->Buffer, &Priv->Lines, 3);
 2098             }
 2099         }
 2100     }
 2101     smprintf(s, "Received firmware version: \"%s\"\n",s->Phone.Data.Version);
 2102     GSM_CreateFirmwareNumber(s);
 2103     return ERR_NONE;
 2104 }
 2105 
 2106 GSM_Error ATGEN_GetFirmware(GSM_StateMachine *s)
 2107 {
 2108     GSM_Error error;
 2109 
 2110     if (s->Phone.Data.Version[0] != 0) return ERR_NONE;
 2111 
 2112     smprintf(s, "Getting firmware versions\n");
 2113     error = ATGEN_WaitForAutoLen(s, "AT+CGMR\r", 0x00, 16, ID_GetFirmware);
 2114 
 2115     if (error != ERR_NONE) {
 2116         error = ATGEN_WaitForAutoLen(s, "ATI5\r", 0x00, 10, ID_GetFirmware);
 2117     }
 2118 
 2119     if (error == ERR_NONE) {
 2120         smprintf_level(s, D_TEXT, "[Firmware version - \"%s\"]\n",
 2121                 s->Phone.Data.Version);
 2122     }
 2123     return error;
 2124 }
 2125 
 2126 GSM_Error ATGEN_PostConnect(GSM_StateMachine *s)
 2127 {
 2128     GSM_Error error;
 2129 
 2130     if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_HUAWEI_INIT)) {
 2131         /* Disable Huawei specific unsolicited codes */
 2132         error = ATGEN_WaitForAutoLen(s, "AT^CURC=0\r", 0x00, 10, ID_SetIncomingCall);
 2133         if (error != ERR_NONE) {
 2134             return error;
 2135         }
 2136 
 2137         /* Power on the modem */
 2138         error = GSM_WaitForAutoLen(s, "AT+CFUN=1\r", 0, 40, ID_SetPower);
 2139         if (error != ERR_NONE) {
 2140             return error;
 2141         }
 2142 
 2143         /* Tell device that this is modem port */
 2144         error = ATGEN_WaitForAutoLen(s, "AT^PORTSEL=1\r", 0x00, 10, ID_SetIncomingCall);
 2145         if (error != ERR_NONE) {
 2146             return error;
 2147         }
 2148     }
 2149 
 2150     if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_ZTE_INIT)) {
 2151         /* Disable CDROM mode */
 2152         error = ATGEN_WaitForAutoLen(s, "AT+ZCDRUN=8\r", 0x00, 10, ID_Initialise);
 2153         if (error != ERR_NONE) {
 2154             return error;
 2155         }
 2156 
 2157         /* Stay online */
 2158         error = ATGEN_WaitForAutoLen(s, "AT+ZOPRT=5\r", 0x00, 10, ID_Initialise);
 2159         if (error != ERR_NONE) {
 2160             return error;
 2161         }
 2162     }
 2163 
 2164     return ERR_NONE;
 2165 }
 2166 
 2167 GSM_Error ATGEN_Initialise(GSM_StateMachine *s)
 2168 {
 2169     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2170     GSM_Error               error;
 2171         char                    buff[2]={0};
 2172 
 2173     InitLines(&Priv->Lines);
 2174 
 2175     Priv->SMSMode           = 0;
 2176     Priv->SQWEMode          = -1;
 2177     Priv->SMSTextDetails        = FALSE;
 2178     Priv->Manufacturer      = 0;
 2179     Priv->MotorolaSMS       = FALSE;
 2180     Priv->PhoneSMSMemory        = 0;
 2181     Priv->PhoneSaveSMS      = 0;
 2182     Priv->SIMSaveSMS        = 0;
 2183     Priv->SIMSMSMemory      = 0;
 2184     Priv->SMSMemory         = 0;
 2185     Priv->SMSMemoryWrite        = FALSE;
 2186     Priv->PBKMemory         = 0;
 2187     Priv->PBKSBNR           = 0;
 2188     Priv->PBK_SPBR          = 0;
 2189     Priv->PBK_MPBR          = 0;
 2190     Priv->SamsungCalendar       = 0;
 2191     Priv->Charset           = 0;
 2192     Priv->EncodedCommands       = FALSE;
 2193     Priv->NormalCharset     = 0;
 2194     Priv->IRACharset        = 0;
 2195     Priv->GSMCharset        = 0;
 2196     Priv->UnicodeCharset        = 0;
 2197     Priv->PBKMemories[0]        = 0;
 2198     Priv->FirstCalendarPos      = 0;
 2199     Priv->FirstFreeCalendarPos  = 0;
 2200     Priv->NextMemoryEntry       = 0;
 2201     Priv->FirstMemoryEntry      = -1;
 2202     Priv->MotorolaFirstMemoryEntry  = -1;
 2203     Priv->file.Used         = 0;
 2204     Priv->file.Buffer       = NULL;
 2205     Priv->Mode          = FALSE;
 2206     Priv->MemorySize        = 0;
 2207     Priv->MotorolaMemorySize    = 0;
 2208     Priv->MemoryUsed        = 0;
 2209     Priv->TextLength        = 0;
 2210     Priv->NumberLength      = 0;
 2211 
 2212     Priv->CNMIMode          = -1;
 2213     Priv->CNMIProcedure     = -1;
 2214     Priv->CNMIDeliverProcedure  = -1;
 2215 #ifdef GSM_ENABLE_CELLBROADCAST
 2216     Priv->CNMIBroadcastProcedure    = -1;
 2217 #endif
 2218     Priv->CNMIClearUnsolicitedResultCodes = -1;
 2219 
 2220     Priv->ErrorText         = NULL;
 2221 
 2222     Priv->SMSCount          = 0;
 2223     Priv->SMSCache          = NULL;
 2224     Priv->ReplyState        = 0;
 2225 
 2226     if (s->ConnectionType != GCT_IRDAAT && s->ConnectionType != GCT_BLUEAT) {
 2227         /* We try to escape AT+CMGS mode, at least Siemens M20
 2228          * then needs to get some rest
 2229          */
 2230         smprintf(s, "Escaping SMS mode\n");
 2231         error = s->Protocol.Functions->WriteMessage(s, "\x1B\r", 2, 0x00);
 2232         if (error != ERR_NONE) {
 2233             return error;
 2234         }
 2235 
 2236             /* Grab any possible garbage */
 2237             while (s->Device.Functions->ReadDevice(s, buff, sizeof(buff)) > 0) {
 2238             usleep(10000);
 2239         }
 2240     }
 2241 
 2242         /* When some phones (Alcatel BE5) is first time connected, it needs extra
 2243          * time to react, sending just AT wakes up the phone and it then can react
 2244          * to ATE1. We don't need to check whether this fails as it is just to
 2245          * wake up the phone and does nothing.
 2246          */
 2247         smprintf(s, "Sending simple AT command to wake up some devices\n");
 2248     error = GSM_WaitForAutoLen(s, "AT\r", 0x00, 20, ID_Initialise);
 2249 
 2250     /* We want to see our commands to allow easy detection of reply functions */
 2251     smprintf(s, "Enabling echo\n");
 2252     error = GSM_WaitForAutoLen(s, "ATE1\r", 0x00, 10, ID_EnableEcho);
 2253 
 2254     /* Some modems (Sony Ericsson GC 79, GC 85) need to enable functionality
 2255      * (with reset), otherwise they return ERROR on anything!
 2256      */
 2257     if (error == ERR_UNKNOWN) {
 2258         error = GSM_WaitForAutoLen(s, "AT+CFUN=1,1\r", 0x00, 10, ID_Reset);
 2259 
 2260         if (error != ERR_NONE) {
 2261             return error;
 2262         }
 2263         error = GSM_WaitForAutoLen(s, "ATE1\r", 0x00, 10, ID_EnableEcho);
 2264     }
 2265     if (error != ERR_NONE) {
 2266         smprintf(s, "Phone does not support enabled echo, it can not work with Gammu!\n");
 2267         smprintf(s, "It might be caused by other program using the modem.\n");
 2268         smprintf(s, "See <https://wammu.eu/docs/manual/faq/general.html#echo> for help.\n");
 2269         return error;
 2270     }
 2271 
 2272     /* Try whether phone supports mode switching as Motorola phones. */
 2273     smprintf(s, "Trying Motorola mode switch\n");
 2274     error = GSM_WaitForAutoLen(s, "AT+MODE=2\r", 0x00, 10, ID_ModeSwitch);
 2275 
 2276     if (error != ERR_NONE) {
 2277         smprintf(s, "Seems not to be supported\n");
 2278         Priv->Mode = FALSE;
 2279     } else {
 2280         smprintf(s, "Works, will use it\n");
 2281         Priv->Mode = TRUE;
 2282         Priv->CurrentMode = 2;
 2283     }
 2284     smprintf(s, "Enabling CME errors\n");
 2285 
 2286     /* Try numeric errors */
 2287     error = ATGEN_WaitForAutoLen(s, "AT+CMEE=1\r", 0x00, 10, ID_EnableErrorInfo);
 2288 
 2289     if (error != ERR_NONE) {
 2290         /* Try textual errors */
 2291         error = ATGEN_WaitForAutoLen(s, "AT+CMEE=2\r", 0x00, 10, ID_EnableErrorInfo);
 2292 
 2293         if (error != ERR_NONE) {
 2294             smprintf(s, "CME errors could not be enabled, some error types won't be detected.\n");
 2295         }
 2296     }
 2297 
 2298     /* Switch to GSM charset */
 2299     error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 2300     if (error != ERR_NONE && error != ERR_SECURITYERROR) return error;
 2301 
 2302     /* Get model, it is useful to know it now */
 2303     error = ATGEN_GetModel(s);
 2304     if (error != ERR_NONE && error != ERR_SECURITYERROR) return error;
 2305 
 2306     /* Get manufacturer, needed for some detection */
 2307     error = ATGEN_GetManufacturer(s);
 2308     if (error != ERR_NONE && error != ERR_SECURITYERROR) return error;
 2309     /* Clear error flag */
 2310     error = ERR_NONE;
 2311 
 2312     /* Mode switching cabaple phones can switch using AT+MODE */
 2313     if (!Priv->Mode) {
 2314         smprintf(s, "Checking for OBEX support\n");
 2315         /* We don't care about error here */
 2316         error = ATGEN_WaitForAutoLen(s, "AT+CPROT=?\r", 0x00, 20, ID_SetOBEX);
 2317         error = ERR_NONE;
 2318     /* Disabled by default as it usually fails to work */
 2319     } else {
 2320         /*
 2321          * Enable OBEX for Motorolas, they usually support this and
 2322          * AT+OBEX can fallback to pure AT.
 2323          *
 2324          * This usually does not work on Bluetooth and IrDA, as there
 2325          * you can access OBEX another way.
 2326          */
 2327 #ifdef GSM_ENABLE_ATOBEX_AUTO_MODE
 2328         if (s->ConnectionType != GCT_IRDAAT &&
 2329                 s->ConnectionType != GCT_BLUEAT &&
 2330                 !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NO_ATOBEX) &&
 2331                 !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_OBEX)
 2332                 ) {
 2333             smprintf(s, "Automatically enabling F_OBEX, please report bug if it causes problems\n");
 2334             GSM_AddPhoneFeature(s->Phone.Data.ModelInfo, F_OBEX);
 2335             GSM_AddPhoneFeature(s->Phone.Data.ModelInfo, F_MODE22);
 2336             GSM_AddPhoneFeature(s->Phone.Data.ModelInfo, F_IRMC_LEVEL_2);
 2337         }
 2338 #else
 2339         smprintf(s, "There is a chance that phone supports F_OBEX,F_MODE22, please report bug if it works\n");
 2340 #endif
 2341     }
 2342 
 2343     if (!GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_MOBEX) && !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_TSSPCSW) && !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NO_ATSYNCML)) {
 2344         smprintf(s, "Checking for SYNCML/OBEX support\n");
 2345         /* We don't care about error here */
 2346         error = ATGEN_WaitForAutoLen(s, "AT+SYNCML=?\r", 0x00, 20, ID_SetOBEX);
 2347         error = ERR_NONE;
 2348         /* We don't care about error here */
 2349         error = ATGEN_WaitForAutoLen(s, "AT$TSSPCSW=?\r", 0x00, 20, ID_SetOBEX);
 2350         error = ERR_NONE;
 2351     }
 2352 
 2353 #ifdef GSM_ENABLE_ATOBEX
 2354     if (Priv->Manufacturer == AT_Siemens) {
 2355         error = ATGEN_WaitForAutoLen(s, "AT^SQWE?\r", 0x00, 10, ID_GetProtocol);
 2356 
 2357         if (error == ERR_NONE) {
 2358 #ifdef GSM_ENABLE_ATOBEX_AUTO_MODE
 2359             if (s->ConnectionType != GCT_IRDAAT &&
 2360                     s->ConnectionType != GCT_BLUEAT &&
 2361                     !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NO_ATOBEX) &&
 2362                     !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_OBEX)
 2363                     ) {
 2364                 smprintf(s, "Phone seems to support Siemens like mode switching, adding OBEX feature.\n");
 2365                 GSM_AddPhoneFeature(s->Phone.Data.ModelInfo, F_OBEX);
 2366                 GSM_AddPhoneFeature(s->Phone.Data.ModelInfo, F_SQWE);
 2367             }
 2368 #else
 2369             smprintf(s, "There is a chance that phone supports F_OBEX,F_SQWE, please report bug if it works\n");
 2370 #endif
 2371 
 2372             /* Switch to mode 0 if we're in different mode */
 2373             if (Priv->SQWEMode != 0) {
 2374                 error = ATGEN_WaitForAutoLen(s, "AT^SQWE=0\r", 0x00, 10, ID_SetOBEX);
 2375 
 2376                 if (error != ERR_NONE) {
 2377                     return error;
 2378                 }
 2379                 Priv->SQWEMode = 0;
 2380             }
 2381         }
 2382         /* Clear error flag */
 2383         error = ERR_NONE;
 2384     }
 2385 #endif
 2386 
 2387   /* can we use CHUP to hangup calls (otherwise use ATH) */
 2388   ATGEN_WaitForAutoLen(s, "AT+CHUP=?\r", 0x00, 40, ID_CheckCHUP);
 2389 
 2390     s->Protocol.Data.AT.FastWrite = !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SLOWWRITE);
 2391     s->Protocol.Data.AT.CPINNoOK = GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_CPIN_NO_OK);
 2392 
 2393     return error;
 2394 }
 2395 
 2396 GSM_Error ATGEN_ReplyGetCharset(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2397 {
 2398     /*
 2399      * Reply we get here:
 2400      * AT+CSCS?
 2401      * +CSCS: "GSM"
 2402      * OK
 2403      *
 2404      * Or
 2405      *
 2406      * AT+CSCS?
 2407      * +CSCS:0
 2408      * OK
 2409      */
 2410     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 2411     const char      *line;
 2412     int         i = 0;
 2413 
 2414     switch (Priv->ReplyState) {
 2415         case AT_Reply_OK:
 2416             /* Can not use ATGEN_ParseReply safely here as we do not know charset yet */
 2417             line = GetLineString(msg->Buffer, &Priv->Lines, 2);
 2418             if (strcmp(line, "+CSCS:0") == 0) {
 2419                 smprintf(s, "WARNING: Charsets support broken! Assuming GSM as default!\n");
 2420                 Priv->Charset = AT_CHARSET_GSM;
 2421             }
 2422             /* First current charset: */
 2423             while (AT_Charsets[i].charset != 0) {
 2424                 if (strstr(line, AT_Charsets[i].text) != NULL) {
 2425                     Priv->Charset = AT_Charsets[i].charset;
 2426                     break;
 2427                 }
 2428                 /* We detect encoded UCS2 reply here so that we can handle encoding of values later. */
 2429                 if (strstr(line, "0055004300530032") != NULL) {
 2430                     Priv->Charset = AT_CHARSET_UCS2;
 2431                     Priv->EncodedCommands = TRUE;
 2432                     break;
 2433                 }
 2434                 i++;
 2435             }
 2436             if (Priv->Charset == 0) {
 2437                 smprintf(s, "Could not determine charset returned by phone, probably not supported!\n");
 2438                 return ERR_NOTSUPPORTED;
 2439             }
 2440             return ERR_NONE;
 2441         case AT_Reply_Error:
 2442             return ERR_NOTSUPPORTED;
 2443         case AT_Reply_CMSError:
 2444             return ATGEN_HandleCMSError(s);
 2445         case AT_Reply_CMEError:
 2446             return ATGEN_HandleCMEError(s);
 2447         default:
 2448             return ERR_UNKNOWNRESPONSE;
 2449     }
 2450 }
 2451 
 2452 GSM_Error ATGEN_ReplyGetCharsets(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2453 {
 2454     /* Reply we get here:
 2455         AT+CSCS=?
 2456         +CSCS: ("GSM","UCS2")
 2457 
 2458         OK
 2459      */
 2460     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 2461     const char  *line;
 2462     int         i = 0;
 2463     gboolean            IgnoredUTF8 = FALSE, IRAset = FALSE, GSMset = FALSE;
 2464 
 2465     switch (Priv->ReplyState) {
 2466         case AT_Reply_OK:
 2467             line = GetLineString(msg->Buffer, &Priv->Lines, 2);
 2468 
 2469             if (strcmp(line, "+CSCS:") == 0) {
 2470                 smprintf(s, "WARNING: Charsets support broken! Assuming that only GSM is supported!\n");
 2471                 Priv->NormalCharset = AT_CHARSET_GSM;
 2472                 Priv->IRACharset = AT_CHARSET_GSM;
 2473                 Priv->GSMCharset = AT_CHARSET_GSM;
 2474                 Priv->UnicodeCharset = AT_CHARSET_GSM;
 2475                 return ERR_NONE;
 2476             }
 2477             /* First find good charset for non-unicode: */
 2478             while (AT_Charsets[i].charset != 0) {
 2479                 if (strstr(line, AT_Charsets[i].text) != NULL) {
 2480                     Priv->NormalCharset = AT_Charsets[i].charset;
 2481                     Priv->IRACharset = AT_Charsets[i].charset;
 2482                     Priv->GSMCharset = AT_Charsets[i].charset;
 2483                     smprintf(s, "Chosen %s as normal charset\n", AT_Charsets[i].text);
 2484                     break;
 2485                 }
 2486                 i++;
 2487             }
 2488             /* Check if we have proper normal charset */
 2489             if (Priv->NormalCharset == 0) {
 2490                 smprintf(s, "Could not find supported charset in list returned by phone!\n");
 2491                 return ERR_UNKNOWNRESPONSE;
 2492             }
 2493             /* Then find good charset for unicode and IRA */
 2494             Priv->UnicodeCharset = 0;
 2495             while (AT_Charsets[i].charset != 0) {
 2496                 if ((Priv->UnicodeCharset == 0) && AT_Charsets[i].unicode && (strstr(line, AT_Charsets[i].text) != NULL)) {
 2497                     if ((AT_Charsets[i].charset == AT_CHARSET_UTF8 ||
 2498                         AT_Charsets[i].charset == AT_CHARSET_UTF_8) &&
 2499                             Priv->Manufacturer == AT_Motorola) {
 2500                         IgnoredUTF8 = TRUE;
 2501                         smprintf(s, "Skipped %s because it is usually wrongly implemented on Motorola phones\n", AT_Charsets[i].text);
 2502                     } else if ((AT_Charsets[i].charset == AT_CHARSET_UTF8 ||
 2503                         AT_Charsets[i].charset == AT_CHARSET_UTF_8) &&
 2504                             GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NO_UTF8)) {
 2505                         IgnoredUTF8 = TRUE;
 2506                         smprintf(s, "Skipped %s because it is reported to be broken on this phone\n", AT_Charsets[i].text);
 2507                     } else if ((AT_Charsets[i].charset != AT_CHARSET_UCS2 &&
 2508                             AT_Charsets[i].charset != AT_CHARSET_UCS_2) ||
 2509                             !GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NO_UCS2)) {
 2510                         Priv->UnicodeCharset = AT_Charsets[i].charset;
 2511                         smprintf(s, "Chosen %s as unicode charset\n", AT_Charsets[i].text);
 2512                     }
 2513                 }
 2514                 if (!IRAset && AT_Charsets[i].ira && (strstr(line, AT_Charsets[i].text) != NULL)) {
 2515                     Priv->IRACharset = AT_Charsets[i].charset;
 2516                     IRAset = TRUE;
 2517                 }
 2518                 if (!GSMset && AT_Charsets[i].GSM && (strstr(line, AT_Charsets[i].text) != NULL)) {
 2519                     Priv->GSMCharset = AT_Charsets[i].charset;
 2520                     GSMset = TRUE;
 2521                 }
 2522                 i++;
 2523             }
 2524             /* Fallback for unicode charset */
 2525             if (Priv->UnicodeCharset == 0) {
 2526                 if (IgnoredUTF8) {
 2527                     Priv->UnicodeCharset = AT_CHARSET_UTF8;
 2528                     smprintf(s, "Switched back to UTF8 charset, expect problems\n");
 2529                 } else {
 2530                     Priv->UnicodeCharset = Priv->NormalCharset;
 2531                 }
 2532             }
 2533             /* If we have unicode charset, it's better than GSM for IRA */
 2534             if (Priv->IRACharset == AT_CHARSET_GSM) {
 2535                 Priv->IRACharset = Priv->UnicodeCharset;
 2536             }
 2537             return ERR_NONE;
 2538         case AT_Reply_Error:
 2539             /* Phone does not support charsets, everything should
 2540              * be in GSM. */
 2541             smprintf(s, "INFO: assuming GSM charset\n");
 2542             Priv->IRACharset = AT_CHARSET_GSM;
 2543             Priv->GSMCharset = AT_CHARSET_GSM;
 2544             Priv->UnicodeCharset = AT_CHARSET_GSM;
 2545             Priv->NormalCharset = AT_CHARSET_GSM;
 2546             Priv->Charset = AT_CHARSET_GSM;
 2547             return ERR_NONE;
 2548         case AT_Reply_CMSError:
 2549             return ATGEN_HandleCMSError(s);
 2550         case AT_Reply_CMEError:
 2551             return ATGEN_HandleCMEError(s);
 2552         default:
 2553             return ERR_UNKNOWNRESPONSE;
 2554     }
 2555 }
 2556 
 2557 
 2558 GSM_Error ATGEN_SetCharset(GSM_StateMachine *s, GSM_AT_Charset_Preference Prefer)
 2559 {
 2560     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 2561     GSM_Error       error;
 2562     char            buffer[100];
 2563     char            buffer2[100];
 2564     char            buffer3[100];
 2565     int         i = 0;
 2566     GSM_AT_Charset      cset;
 2567     size_t len;
 2568 
 2569     /* Do we know current charset? */
 2570     if (Priv->Charset == 0) {
 2571         /* Get current charset */
 2572         error = ATGEN_WaitForAutoLen(s, "AT+CSCS?\r", 0x00, 10, ID_GetMemoryCharset);
 2573 
 2574         /* ERR_NOTSUPPORTED means that we do not know charset phone returned */
 2575         if (error != ERR_NONE && error != ERR_NOTSUPPORTED) {
 2576             return error;
 2577         }
 2578     }
 2579 
 2580     /* Do we know available charsets? */
 2581     if (Priv->NormalCharset == 0) {
 2582         /* Switch to GSM to be safe (UCS2 can give us encoded result) */
 2583         if (Priv->Charset == AT_CHARSET_UCS2 && Priv->EncodedCommands) {
 2584             error = ATGEN_WaitForAutoLen(s, "AT+CSCS=\"00470053004D\"\r", 0x00, 10, ID_SetMemoryCharset);
 2585 
 2586             if (error == ERR_NONE) {
 2587                 Priv->Charset = AT_CHARSET_GSM;
 2588             }
 2589         }
 2590         /* Get available charsets */
 2591         error = ATGEN_WaitForAutoLen(s, "AT+CSCS=?\r", 0x00, 10, ID_GetMemoryCharset);
 2592 
 2593         if (error != ERR_NONE) return error;
 2594     }
 2595 
 2596     /* Find charset we want */
 2597     if (Prefer == AT_PREF_CHARSET_UNICODE) {
 2598         cset = Priv->UnicodeCharset;
 2599     } else if (Prefer == AT_PREF_CHARSET_NORMAL) {
 2600         cset = Priv->NormalCharset;
 2601     } else if (Prefer == AT_PREF_CHARSET_GSM) {
 2602         cset = Priv->GSMCharset;
 2603     } else if (Prefer == AT_PREF_CHARSET_IRA) {
 2604         if (Priv->IRACharset == Priv->UnicodeCharset &&
 2605                 GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_CKPD_NO_UNICODE)) {
 2606             cset = Priv->NormalCharset;
 2607         } else {
 2608             cset = Priv->IRACharset;
 2609         }
 2610     } else if (Prefer == AT_PREF_CHARSET_RESET) {
 2611         cset = Priv->Charset;
 2612         Priv->Charset = 0;
 2613     } else {
 2614         return ERR_BUG;
 2615     }
 2616 
 2617     /* If we already have set our prefered charset there is nothing to do*/
 2618     if (Priv->Charset == cset) return ERR_NONE;
 2619 
 2620     /* Find text representation */
 2621     while (AT_Charsets[i].charset != 0) {
 2622         if (AT_Charsets[i].charset == cset) {
 2623             break;
 2624         }
 2625         i++;
 2626     }
 2627 
 2628     /* Should not happen! */
 2629     if (AT_Charsets[i].charset == 0) {
 2630         smprintf(s, "Could not find string representation for charset (%d)!\n",
 2631                 cset);
 2632         return ERR_BUG;
 2633     }
 2634 
 2635     /* And finally set the charset */
 2636     if (Priv->EncodedCommands && Priv->Charset == AT_CHARSET_UCS2) {
 2637         EncodeUnicode(buffer2, AT_Charsets[i].text, strlen(AT_Charsets[i].text));
 2638         EncodeHexUnicode(buffer3, buffer2, strlen(AT_Charsets[i].text));
 2639         len = sprintf(buffer, "AT+CSCS=\"%s\"\r", buffer3);
 2640     } else {
 2641         len = sprintf(buffer, "AT+CSCS=\"%s\"\r", AT_Charsets[i].text);
 2642     }
 2643     error = ATGEN_WaitFor(s, buffer, len, 0x00, 20, ID_SetMemoryCharset);
 2644 
 2645     if (error == ERR_NONE) {
 2646         Priv->Charset = cset;
 2647     }
 2648     else {
 2649         return error;
 2650     }
 2651 
 2652     /* Verify we have charset we wanted (this is especially needed to detect whether phone encodes also control information and not only data) */
 2653     error = ATGEN_WaitForAutoLen(s, "AT+CSCS?\r", 0x00, 10, ID_GetMemoryCharset);
 2654 
 2655     return error;
 2656 }
 2657 
 2658 GSM_Error ATGEN_ReplyGetIMEI(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2659 {
 2660     if (s->Phone.Data.Priv.ATGEN.ReplyState != AT_Reply_OK) return ERR_NOTSUPPORTED;
 2661 
 2662     if (GetLineLength(msg->Buffer, &s->Phone.Data.Priv.ATGEN.Lines, 2) > GSM_MAX_IMEI_LENGTH) {
 2663         smprintf(s, "IMEI too long!\n");
 2664         return ERR_MOREMEMORY;
 2665     }
 2666 
 2667     CopyLineString(s->Phone.Data.IMEI, msg->Buffer, &s->Phone.Data.Priv.ATGEN.Lines, 2);
 2668     /* Remove various prefies some phones add */
 2669     if (strncmp(s->Phone.Data.IMEI, "+CGSN: IMEI", 11) == 0) { /* Motorola */
 2670         memmove(s->Phone.Data.IMEI, s->Phone.Data.IMEI + 11, strlen(s->Phone.Data.IMEI + 11) + 1);
 2671     } else if (strncmp(s->Phone.Data.IMEI, "+CGSN: ", 7) == 0) {
 2672         memmove(s->Phone.Data.IMEI, s->Phone.Data.IMEI + 7, strlen(s->Phone.Data.IMEI + 7) + 1);
 2673     }
 2674     smprintf(s, "Received IMEI %s\n",s->Phone.Data.IMEI);
 2675     return ERR_NONE;
 2676 }
 2677 
 2678 GSM_Error ATGEN_GetIMEI (GSM_StateMachine *s)
 2679 {
 2680     GSM_Error error;
 2681 
 2682     if (s->Phone.Data.IMEI[0] != 0) return ERR_NONE;
 2683     smprintf(s, "Getting IMEI\n");
 2684     error = ATGEN_WaitForAutoLen(s, "AT+CGSN\r", 0x00, 20, ID_GetIMEI);
 2685 
 2686     return error;
 2687 }
 2688 
 2689 GSM_Error ATGEN_ReplyGetDateTime(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2690 {
 2691     GSM_Phone_Data *Data = &s->Phone.Data;
 2692     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 2693 
 2694     switch (Priv->ReplyState) {
 2695     case AT_Reply_OK:
 2696         return ATGEN_ParseReply(s,
 2697                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 2698                 "+CCLK: @d",
 2699                 Data->DateTime);
 2700     case AT_Reply_Error:
 2701         return ERR_NOTSUPPORTED;
 2702     case AT_Reply_CMSError:
 2703         return ATGEN_HandleCMSError(s);
 2704     case AT_Reply_CMEError:
 2705         return ATGEN_HandleCMEError(s);
 2706     default:
 2707         break;
 2708     }
 2709     return ERR_UNKNOWNRESPONSE;
 2710 }
 2711 
 2712 
 2713 GSM_Error ATGEN_ReplyGetAlarm(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2714 {
 2715     GSM_Phone_Data      *Data = &s->Phone.Data;
 2716     unsigned char       buffer[100];
 2717     GSM_Error       error;
 2718     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 2719     int i;
 2720     int location;
 2721     const char *str;
 2722 
 2723     switch (Priv->ReplyState) {
 2724     case AT_Reply_OK:
 2725         /* Try simple date string as alarm */
 2726         error = ATGEN_ParseReply(s,
 2727                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 2728                 "+CALA: @d",
 2729                 &(Data->Alarm->DateTime));
 2730         if (error == ERR_NONE) {
 2731             if (Data->Alarm->Location != 1) return ERR_INVALIDLOCATION;
 2732             return ERR_NONE;
 2733         }
 2734 
 2735         /* Ok we have something more complex, try to handle it */
 2736         i = 2;
 2737         /* Need to scan over all reply lines */
 2738         while (strcmp("OK", str = GetLineString(msg->Buffer, &Priv->Lines, i)) != 0) {
 2739             i++;
 2740             /**
 2741              * +CALA: [<time1>,<n1>,<type1>,[<text1>],[<recurr1>],<silent1>]
 2742              */
 2743             error = ATGEN_ParseReply(s, str,
 2744                     "+CALA: @d, @i, @s, @s, @s",
 2745                     &(Data->Alarm->DateTime),
 2746                     &location,
 2747                     buffer, sizeof(buffer),
 2748                     Data->Alarm->Text, sizeof(Data->Alarm->Text),
 2749                     buffer, sizeof(buffer));
 2750             if (error == ERR_NONE && location == Data->Alarm->Location) {
 2751                 /**
 2752                  * \todo This is not exact, repeating
 2753                  * can be set for only limited
 2754                  * set of days (eg. "4,5,6").
 2755                  */
 2756                 if (!strcmp(buffer, "\"1,2,3,4,5,6,7\"")) {
 2757                     Data->Alarm->Repeating = TRUE;
 2758                 } else {
 2759                     Data->Alarm->Repeating = FALSE;
 2760                 }
 2761                 return ERR_NONE;
 2762             }
 2763         }
 2764 
 2765         return ERR_EMPTY;
 2766     case AT_Reply_Error:
 2767         return ERR_NOTSUPPORTED;
 2768     case AT_Reply_CMSError:
 2769         return ATGEN_HandleCMSError(s);
 2770     case AT_Reply_CMEError:
 2771         return ATGEN_HandleCMEError(s);
 2772     default:
 2773         break;
 2774     }
 2775     return ERR_UNKNOWNRESPONSE;
 2776 }
 2777 
 2778 GSM_Error ATGEN_GetDateTime(GSM_StateMachine *s, GSM_DateTime *date_time)
 2779 {
 2780     GSM_Error       error;
 2781     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2782 
 2783     /* If phone encodes also values in command, we need normal charset */
 2784     if (Priv->EncodedCommands) {
 2785         error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 2786         if (error != ERR_NONE) return error;
 2787     }
 2788 
 2789     s->Phone.Data.DateTime = date_time;
 2790     smprintf(s, "Getting date & time\n");
 2791     error = ATGEN_WaitForAutoLen(s, "AT+CCLK?\r", 0x00, 40, ID_GetDateTime);
 2792 
 2793     return error;
 2794 }
 2795 
 2796 GSM_Error ATGEN_PrivSetDateTime(GSM_StateMachine *s, GSM_DateTime *date_time, gboolean set_timezone)
 2797 {
 2798     char            tz[8] = "";
 2799     char            req[128];
 2800     GSM_Error       error;
 2801     size_t len;
 2802 
 2803     if (set_timezone) {
 2804         sprintf(tz, "%+03i", date_time->Timezone / 3600);
 2805     }
 2806 
 2807     if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_FOUR_DIGIT_YEAR)) {
 2808         len = sprintf(req, "AT+CCLK=\"%04i/%02i/%02i,%02i:%02i:%02i%s\"\r",
 2809                  date_time->Year,
 2810                  date_time->Month ,
 2811                  date_time->Day,
 2812                  date_time->Hour,
 2813                  date_time->Minute,
 2814                  date_time->Second,
 2815                  tz);
 2816     } else {
 2817         len = sprintf(req, "AT+CCLK=\"%02i/%02i/%02i,%02i:%02i:%02i%s\"\r",
 2818                  (date_time->Year > 2000 ? date_time->Year-2000 : date_time->Year-1900),
 2819                  date_time->Month ,
 2820                  date_time->Day,
 2821                  date_time->Hour,
 2822                  date_time->Minute,
 2823                  date_time->Second,
 2824                  tz);
 2825     }
 2826     smprintf(s, "Setting date & time\n");
 2827 
 2828     error = ATGEN_WaitFor(s, req, len, 0x00, 40, ID_SetDateTime);
 2829     if (error == ERR_UNKNOWN) error = ERR_NOTSUPPORTED;
 2830 
 2831     if (set_timezone && (
 2832         s->Phone.Data.Priv.ATGEN.ReplyState == AT_Reply_CMEError
 2833         && ((error == ERR_INVALIDDATA
 2834         && s->Phone.Data.Priv.ATGEN.ErrorCode == 24) ||
 2835         (error == ERR_INVALIDLOCATION
 2836         && s->Phone.Data.Priv.ATGEN.ErrorCode == 21))
 2837         )) {
 2838         /*
 2839          * Some firmwares of Ericsson R320s don't like the timezone part,
 2840          * even though it is in its command reference. Similar issue
 2841          * exists for MC75
 2842          */
 2843         smprintf(s, "Retrying without timezone suffix\n");
 2844         error = ATGEN_PrivSetDateTime(s, date_time, FALSE);
 2845     }
 2846     return error;
 2847 }
 2848 
 2849 GSM_Error ATGEN_SetDateTime(GSM_StateMachine *s, GSM_DateTime *date_time)
 2850 {
 2851     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2852     GSM_Error       error;
 2853 
 2854     /* If phone encodes also values in command, we need normal charset */
 2855     if (Priv->EncodedCommands) {
 2856         error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 2857         if (error != ERR_NONE) return error;
 2858     }
 2859     return ATGEN_PrivSetDateTime(s, date_time, TRUE);
 2860 }
 2861 
 2862 GSM_Error ATGEN_GetAlarm(GSM_StateMachine *s, GSM_Alarm *Alarm)
 2863 {
 2864     GSM_Error       error;
 2865     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2866 
 2867     /* If phone encodes also values in command, we need normal charset */
 2868     if (Priv->EncodedCommands) {
 2869         error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 2870         if (error != ERR_NONE) return error;
 2871     }
 2872 
 2873     s->Phone.Data.Alarm = Alarm;
 2874     smprintf(s, "Getting alarm\n");
 2875     error = ATGEN_WaitForAutoLen(s, "AT+CALA?\r", 0x00, 40, ID_GetAlarm);
 2876 
 2877     return error;
 2878 }
 2879 
 2880 /* R320 only takes HH:MM. Do other phones understand full date? */
 2881 GSM_Error ATGEN_SetAlarm(GSM_StateMachine *s, GSM_Alarm *Alarm)
 2882 {
 2883     char            req[20]={0};
 2884     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2885     GSM_Error       error;
 2886     int         length = 0;
 2887 
 2888     if (Alarm->Location != 1) {
 2889         return ERR_INVALIDLOCATION;
 2890     }
 2891 
 2892     /* If phone encodes also values in command, we need normal charset */
 2893     if (Priv->EncodedCommands) {
 2894         error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 2895         if (error != ERR_NONE) return error;
 2896     }
 2897     smprintf(s, "Setting Alarm\n");
 2898     length = sprintf(req, "AT+CALA=\"%02i:%02i\"\r",Alarm->DateTime.Hour,Alarm->DateTime.Minute);
 2899     error = ATGEN_WaitFor(s, req, length, 0x00, 10, ID_SetAlarm);
 2900     return error;
 2901 }
 2902 
 2903 GSM_Error ATGEN_ReplyGetPacketNetworkLAC_CID(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 2904 {
 2905     GSM_NetworkInfo     *NetworkInfo = s->Phone.Data.NetworkInfo;
 2906     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 2907     int i, state;
 2908     int act;
 2909     char rac[8];
 2910     GSM_Error error;
 2911 
 2912     if (s->Phone.Data.RequestID != ID_GetNetworkInfo) {
 2913         smprintf(s, "Incoming LAC & CID info, ignoring\n");
 2914         return ERR_NONE;
 2915     }
 2916 
 2917     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 2918     case AT_Reply_OK:
 2919         break;
 2920     case AT_Reply_CMSError:
 2921             return ATGEN_HandleCMSError(s);
 2922     case AT_Reply_CMEError:
 2923         return ATGEN_HandleCMEError(s);
 2924     default:
 2925         return ERR_UNKNOWNRESPONSE;
 2926     }
 2927 
 2928     if (strcmp("OK", GetLineString(msg->Buffer, &Priv->Lines, 2)) == 0) {
 2929         NetworkInfo->PacketState = GSM_NoNetwork;
 2930         NetworkInfo->PacketLAC[0] = 0;
 2931         NetworkInfo->PacketCID[0] = 0;
 2932         return ERR_NONE;
 2933     }
 2934 
 2935     smprintf(s, "Network LAC & CID & state received\n");
 2936 
 2937     NetworkInfo->PacketLAC[0] = 0;
 2938     NetworkInfo->PacketCID[0] = 0;
 2939 
 2940     /* Full reply */
 2941     error = ATGEN_ParseReply(s,
 2942             GetLineString(msg->Buffer, &Priv->Lines, 2),
 2943             "+CGREG: @i, @i, @r, @r, @i, @r",
 2944             &i, /* Mode, ignored for now */
 2945             &state,
 2946             NetworkInfo->PacketLAC, sizeof(NetworkInfo->PacketLAC),
 2947             NetworkInfo->PacketCID, sizeof(NetworkInfo->PacketCID),
 2948             &act, /* Access Technology, ignored for now */
 2949             &rac, sizeof(rac) /* Routing Area Code, ignored for now */
 2950             );
 2951 
 2952     /* Reply without RAC */
 2953     if (error == ERR_UNKNOWNRESPONSE) {
 2954             error = ATGEN_ParseReply(s,
 2955             GetLineString(msg->Buffer, &Priv->Lines, 2),
 2956             "+CGREG: @i, @i, @r, @r, @i",
 2957             &i, /* Mode, ignored for now */
 2958             &state,
 2959             NetworkInfo->PacketLAC, sizeof(NetworkInfo->PacketLAC),
 2960             NetworkInfo->PacketCID, sizeof(NetworkInfo->PacketCID),
 2961             &act /* Access Technology, ignored for now */
 2962             );
 2963     }
 2964 
 2965     /* Reply without ACT/RAC */
 2966     if (error == ERR_UNKNOWNRESPONSE) {
 2967             error = ATGEN_ParseReply(s,
 2968             GetLineString(msg->Buffer, &Priv->Lines, 2),
 2969             "+CGREG: @i, @i, @r, @r",
 2970             &i, /* Mode, ignored for now */
 2971             &state,
 2972             NetworkInfo->PacketLAC, sizeof(NetworkInfo->PacketLAC),
 2973             NetworkInfo->PacketCID, sizeof(NetworkInfo->PacketCID));
 2974     }
 2975 
 2976     /* Reply without LAC/CID */
 2977     if (error == ERR_UNKNOWNRESPONSE) {
 2978         error = ATGEN_ParseReply(s,
 2979                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 2980                 "+CGREG: @i, @i",
 2981                 &i, /* Mode, ignored for now */
 2982                 &state);
 2983     }
 2984 
 2985     if (error != ERR_NONE) {
 2986         return error;
 2987     }
 2988 
 2989     /* Decode network state */
 2990     switch (state) {
 2991         case 0:
 2992             smprintf(s, "Not registered into any network. Not searching for network\n");
 2993             NetworkInfo->PacketState = GSM_NoNetwork;
 2994             break;
 2995         case 1:
 2996             smprintf(s, "Home network\n");
 2997             NetworkInfo->PacketState = GSM_HomeNetwork;
 2998             break;
 2999         case 2:
 3000             smprintf(s, "Not registered into any network. Searching for network\n");
 3001             NetworkInfo->PacketState = GSM_RequestingNetwork;
 3002             break;
 3003         case 3:
 3004             smprintf(s, "Registration denied\n");
 3005             NetworkInfo->PacketState = GSM_RegistrationDenied;
 3006             break;
 3007         case 4:
 3008             smprintf(s, "Unknown\n");
 3009             NetworkInfo->PacketState = GSM_NetworkStatusUnknown;
 3010             break;
 3011         case 5:
 3012             smprintf(s, "Registered in roaming network\n");
 3013             NetworkInfo->PacketState = GSM_RoamingNetwork;
 3014             break;
 3015         default:
 3016             smprintf(s, "Unknown: %d\n", state);
 3017             NetworkInfo->PacketState = GSM_NetworkStatusUnknown;
 3018             break;
 3019     }
 3020 
 3021     return ERR_NONE;
 3022 }
 3023 
 3024 GSM_Error ATGEN_ReplyGetNetworkLAC_CID(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3025 {
 3026     GSM_NetworkInfo     *NetworkInfo = s->Phone.Data.NetworkInfo;
 3027     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 3028     int i, state;
 3029     int act;
 3030     GSM_Error error;
 3031 
 3032     if (s->Phone.Data.RequestID != ID_GetNetworkInfo) {
 3033         smprintf(s, "Incoming LAC & CID info, ignoring\n");
 3034         return ERR_NONE;
 3035     }
 3036 
 3037     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 3038     case AT_Reply_OK:
 3039         break;
 3040     case AT_Reply_CMSError:
 3041             return ATGEN_HandleCMSError(s);
 3042     case AT_Reply_CMEError:
 3043         return ATGEN_HandleCMEError(s);
 3044     default:
 3045         return ERR_UNKNOWNRESPONSE;
 3046     }
 3047 
 3048     if (strcmp("OK", GetLineString(msg->Buffer, &Priv->Lines, 2)) == 0) {
 3049         NetworkInfo->State = GSM_NoNetwork;
 3050         NetworkInfo->LAC[0] = 0;
 3051         NetworkInfo->CID[0] = 0;
 3052         return ERR_NONE;
 3053     }
 3054 
 3055     smprintf(s, "Network LAC & CID & state received\n");
 3056 
 3057     NetworkInfo->LAC[0] = 0;
 3058     NetworkInfo->CID[0] = 0;
 3059 
 3060     /* Full reply */
 3061     error = ATGEN_ParseReply(s,
 3062             GetLineString(msg->Buffer, &Priv->Lines, 2),
 3063             "+CREG: @i, @i, @r, @r, @i",
 3064             &i, /* Mode, ignored for now */
 3065             &state,
 3066             NetworkInfo->LAC, sizeof(NetworkInfo->LAC),
 3067             NetworkInfo->CID, sizeof(NetworkInfo->CID),
 3068             &act  /* Access Technology, ignored for now */
 3069             );
 3070 
 3071     /* Reply without ACT */
 3072     if (error == ERR_UNKNOWNRESPONSE) {
 3073             error = ATGEN_ParseReply(s,
 3074             GetLineString(msg->Buffer, &Priv->Lines, 2),
 3075             "+CREG: @i, @i, @r, @r",
 3076             &i, /* Mode, ignored for now */
 3077             &state,
 3078             NetworkInfo->LAC, sizeof(NetworkInfo->LAC),
 3079             NetworkInfo->CID, sizeof(NetworkInfo->CID));
 3080     }
 3081 
 3082     /* Reply without mode */
 3083     if (error == ERR_UNKNOWNRESPONSE) {
 3084         error = ATGEN_ParseReply(s,
 3085                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 3086                 "+CREG: @i, @r, @r",
 3087                 &state,
 3088                 NetworkInfo->LAC, sizeof(NetworkInfo->LAC),
 3089                 NetworkInfo->CID, sizeof(NetworkInfo->CID));
 3090     }
 3091 
 3092     /* Reply without LAC/CID */
 3093     if (error == ERR_UNKNOWNRESPONSE) {
 3094         error = ATGEN_ParseReply(s,
 3095                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 3096                 "+CREG: @i, @i",
 3097                 &i, /* Mode, ignored for now */
 3098                 &state);
 3099     }
 3100 
 3101     if (error != ERR_NONE) {
 3102         return error;
 3103     }
 3104 
 3105     /* Decode network state */
 3106     switch (state) {
 3107         case 0:
 3108             smprintf(s, "Not registered into any network. Not searching for network\n");
 3109             NetworkInfo->State = GSM_NoNetwork;
 3110             break;
 3111         case 1:
 3112             smprintf(s, "Home network\n");
 3113             NetworkInfo->State = GSM_HomeNetwork;
 3114             break;
 3115         case 2:
 3116             smprintf(s, "Not registered into any network. Searching for network\n");
 3117             NetworkInfo->State = GSM_RequestingNetwork;
 3118             break;
 3119         case 3:
 3120             smprintf(s, "Registration denied\n");
 3121             NetworkInfo->State = GSM_RegistrationDenied;
 3122             break;
 3123         case 4:
 3124             smprintf(s, "Unknown\n");
 3125             NetworkInfo->State = GSM_NetworkStatusUnknown;
 3126             break;
 3127         case 5:
 3128             smprintf(s, "Registered in roaming network\n");
 3129             NetworkInfo->State = GSM_RoamingNetwork;
 3130             break;
 3131         default:
 3132             smprintf(s, "Unknown: %d\n", state);
 3133             NetworkInfo->State = GSM_NetworkStatusUnknown;
 3134             break;
 3135     }
 3136 
 3137     return ERR_NONE;
 3138 }
 3139 
 3140 GSM_Error ATGEN_ReplyGetNetworkCode(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3141 {
 3142     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3143     GSM_NetworkInfo     *NetworkInfo = s->Phone.Data.NetworkInfo;
 3144     int i;
 3145     GSM_Error error;
 3146 
 3147     switch (Priv->ReplyState) {
 3148     case AT_Reply_OK:
 3149         smprintf(s, "Network code received\n");
 3150         error = ATGEN_ParseReply(s,
 3151                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 3152                 "+COPS: @i, @i, @r",
 3153                 &i, /* Mode, ignored for now */
 3154                 &i, /* Format of reply, we set this */
 3155                 NetworkInfo->NetworkCode, sizeof(NetworkInfo->NetworkCode));
 3156 
 3157         /* Some Sony-Ericsson phones use this */
 3158         if (error == ERR_UNKNOWNRESPONSE) {
 3159             error = ATGEN_ParseReply(s,
 3160                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 3161                     "+COPS: @i, @i, @r, @i",
 3162                     &i, /* Mode, ignored for now */
 3163                     &i, /* Format of reply, we set this */
 3164                     NetworkInfo->NetworkCode, sizeof(NetworkInfo->NetworkCode),
 3165                     &i);
 3166         }
 3167 
 3168         if (error != ERR_NONE) {
 3169             /* Cleanup if something went wrong */
 3170             NetworkInfo->NetworkCode[0] = 0;
 3171             NetworkInfo->NetworkCode[1] = 0;
 3172 
 3173             return error;
 3174         }
 3175 
 3176         /* Split network code for country and operator */
 3177         if (strlen(NetworkInfo->NetworkCode) == 5) {
 3178             NetworkInfo->NetworkCode[6] = 0;
 3179             NetworkInfo->NetworkCode[5] = NetworkInfo->NetworkCode[4];
 3180             NetworkInfo->NetworkCode[4] = NetworkInfo->NetworkCode[3];
 3181             NetworkInfo->NetworkCode[3] = ' ';
 3182         }
 3183 
 3184         smprintf(s, "   Network code              : %s\n",
 3185                 NetworkInfo->NetworkCode);
 3186         smprintf(s, "   Network name for Gammu    : %s ",
 3187                 DecodeUnicodeString(GSM_GetNetworkName(NetworkInfo->NetworkCode)));
 3188         smprintf(s, "(%s)\n",
 3189                 DecodeUnicodeString(GSM_GetCountryName(NetworkInfo->NetworkCode)));
 3190         return ERR_NONE;
 3191     case AT_Reply_CMSError:
 3192         return ATGEN_HandleCMSError(s);
 3193     case AT_Reply_CMEError:
 3194         return ATGEN_HandleCMEError(s);
 3195     default:
 3196         break;
 3197     }
 3198     return ERR_UNKNOWNRESPONSE;
 3199 }
 3200 
 3201 GSM_Error ATGEN_ReplyGetNetworkName(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3202 {
 3203     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3204     GSM_NetworkInfo     *NetworkInfo = s->Phone.Data.NetworkInfo;
 3205     int i;
 3206     GSM_Error error;
 3207 
 3208     switch (Priv->ReplyState) {
 3209     case AT_Reply_OK:
 3210         smprintf(s, "Network name received\n");
 3211         error = ATGEN_ParseReply(s,
 3212                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 3213                 "+COPS: @i, @i, @s",
 3214                 &i, /* Mode, ignored for now */
 3215                 &i, /* Format of reply, we set this */
 3216                 NetworkInfo->NetworkName, sizeof(NetworkInfo->NetworkName));
 3217 
 3218         /* Some Sony-Ericsson phones use this */
 3219         if (error == ERR_UNKNOWNRESPONSE) {
 3220             error = ATGEN_ParseReply(s,
 3221                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 3222                     "+COPS: @i, @i, @s, @i",
 3223                     &i, /* Mode, ignored for now */
 3224                     &i, /* Format of reply, we set this */
 3225                     NetworkInfo->NetworkName, sizeof(NetworkInfo->NetworkName),
 3226                     &i);
 3227         }
 3228 
 3229         /* Cleanup if something went wrong */
 3230         if (error != ERR_NONE) {
 3231           smprintf(s, "WARNING: Failed to store network name - ERROR(%s)", GSM_ErrorName(error));
 3232             NetworkInfo->NetworkName[0] = 0;
 3233             NetworkInfo->NetworkName[1] = 0;
 3234         }
 3235 
 3236         return error;
 3237     case AT_Reply_CMSError:
 3238         return ATGEN_HandleCMSError(s);
 3239     case AT_Reply_CMEError:
 3240         return ATGEN_HandleCMEError(s);
 3241     default:
 3242         break;
 3243     }
 3244     return ERR_UNKNOWNRESPONSE;
 3245 }
 3246 
 3247 GSM_Error ATGEN_ReplyGetGPRSState(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3248 {
 3249     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3250     GSM_NetworkInfo     *NetworkInfo = s->Phone.Data.NetworkInfo;
 3251     int i;
 3252     GSM_Error error;
 3253 
 3254     switch (Priv->ReplyState) {
 3255     case AT_Reply_OK:
 3256         smprintf(s, "GPRS state received\n");
 3257         error = ATGEN_ParseReply(s,
 3258                 GetLineString(msg->Buffer, &Priv->Lines, 2),
 3259                 "+CGATT: @i",
 3260                 &i);
 3261 
 3262         if (error == ERR_NONE) {
 3263             if (i == 1) {
 3264                 NetworkInfo->GPRS = GSM_GPRS_Attached;
 3265             } else if (i == 0) {
 3266                 NetworkInfo->GPRS = GSM_GPRS_Detached;
 3267             } else {
 3268                 smprintf(s, "WARNING: Unknown GPRS state %d\n", i);
 3269                 error = ERR_UNKNOWN;
 3270             }
 3271         }
 3272         return error;
 3273     case AT_Reply_CMSError:
 3274         return ATGEN_HandleCMSError(s);
 3275     case AT_Reply_CMEError:
 3276         return ATGEN_HandleCMEError(s);
 3277     default:
 3278         break;
 3279     }
 3280     return ERR_UNKNOWNRESPONSE;
 3281 }
 3282 
 3283 GSM_Error ATGEN_GetNetworkInfo(GSM_StateMachine *s, GSM_NetworkInfo *netinfo)
 3284 {
 3285     GSM_Error error;
 3286 
 3287     s->Phone.Data.NetworkInfo = netinfo;
 3288 
 3289     netinfo->NetworkName[0] = 0;
 3290     netinfo->NetworkName[1] = 0;
 3291     netinfo->NetworkCode[0] = 0;
 3292     netinfo->GPRS = 0;
 3293 
 3294     smprintf(s, "Enable full network info\n");
 3295     error = ATGEN_WaitForAutoLen(s, "AT+CREG=2\r", 0x00, 40, ID_ConfigureNetworkInfo);
 3296 
 3297     if (error == ERR_UNKNOWN) {
 3298         /* Try basic info at least */
 3299         error = ATGEN_WaitForAutoLen(s, "AT+CREG=1\r", 0x00, 40, ID_ConfigureNetworkInfo);
 3300     }
 3301 
 3302     if (error != ERR_NONE) {
 3303         return error;
 3304     }
 3305 
 3306     smprintf(s, "Enable full packet network info\n");
 3307     error = ATGEN_WaitForAutoLen(s, "AT+CGREG=2\r", 0x00, 40, ID_ConfigureNetworkInfo);
 3308     if (error == ERR_UNKNOWN) {
 3309         /* Try basic info at least */
 3310         error = ATGEN_WaitForAutoLen(s, "AT+CGREG=1\r", 0x00, 40, ID_ConfigureNetworkInfo);
 3311     }
 3312     if (error != ERR_NONE) {
 3313         return error;
 3314     }
 3315 
 3316     smprintf(s, "Getting GPRS state\n");
 3317     error = ATGEN_WaitForAutoLen(s, "AT+CGATT?\r", 0x00, 40, ID_GetGPRSState);
 3318 
 3319     if (error != ERR_NONE) {
 3320         return error;
 3321     }
 3322     smprintf(s, "Getting network LAC and CID and state\n");
 3323     error = ATGEN_WaitForAutoLen(s, "AT+CREG?\r", 0x00, 40, ID_GetNetworkInfo);
 3324 
 3325     if (error != ERR_NONE) {
 3326         return error;
 3327     }
 3328     smprintf(s, "Getting packet network LAC and CID and state\n");
 3329     error = ATGEN_WaitForAutoLen(s, "AT+CGREG?\r", 0x00, 40, ID_GetNetworkInfo);
 3330 
 3331     if (error != ERR_NONE) {
 3332         return error;
 3333     }
 3334     if (netinfo->State == GSM_HomeNetwork          ||
 3335         netinfo->State == GSM_RoamingNetwork       ||
 3336         netinfo->PacketState == GSM_HomeNetwork    ||
 3337         netinfo->PacketState == GSM_RoamingNetwork
 3338         ) {
 3339         /* Set numeric format for AT+COPS? */
 3340         smprintf(s, "Setting short network name format\n");
 3341         error = ATGEN_WaitForAutoLen(s, "AT+COPS=3,2\r", 0x00, 40, ID_ConfigureNetworkInfo);
 3342 
 3343         /* Get operator code */
 3344         smprintf(s, "Getting network code\n");
 3345         error = ATGEN_WaitForAutoLen(s, "AT+COPS?\r", 0x00, 40, ID_GetNetworkCode);
 3346 
 3347         /* Set string format for AT+COPS? */
 3348         smprintf(s, "Setting long string network name format\n");
 3349         error = ATGEN_WaitForAutoLen(s, "AT+COPS=3,0\r", 0x00, 40, ID_ConfigureNetworkInfo);
 3350 
 3351         /* Get operator code */
 3352         smprintf(s, "Getting network code\n");
 3353         error = ATGEN_WaitForAutoLen(s, "AT+COPS?\r", 0x00, 40, ID_GetNetworkName);
 3354 
 3355         /* All information here is optional */
 3356         error = ERR_NONE;
 3357     }
 3358     return error;
 3359 }
 3360 
 3361 /**
 3362  * Stores available phonebook memories in PBKMemories.
 3363  *
 3364  * @todo Should parse reply, not copy it as is.
 3365  */
 3366 GSM_Error ATGEN_ReplyGetPBKMemories(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3367 {
 3368     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3369 
 3370     switch (Priv->ReplyState) {
 3371     case AT_Reply_OK:
 3372         break;
 3373     case AT_Reply_Error:
 3374         return ERR_NOTSUPPORTED;
 3375     case AT_Reply_CMSError:
 3376             return ATGEN_HandleCMSError(s);
 3377     case AT_Reply_CMEError:
 3378         return ATGEN_HandleCMEError(s);
 3379     default:
 3380         return ERR_UNKNOWNRESPONSE;
 3381     }
 3382 
 3383     if (GetLineLength(msg->Buffer, &Priv->Lines, 2) >= AT_PBK_MAX_MEMORIES) {
 3384         smprintf(s, "ERROR: Too long phonebook memories information received! (Recevided %d, AT_PBK_MAX_MEMORIES is %d\n",
 3385             GetLineLength(msg->Buffer, &Priv->Lines, 2), AT_PBK_MAX_MEMORIES);
 3386         return ERR_MOREMEMORY;
 3387     }
 3388     CopyLineString(Priv->PBKMemories, msg->Buffer, &Priv->Lines, 2);
 3389     smprintf(s, "PBK memories received: %s\n", s->Phone.Data.Priv.ATGEN.PBKMemories);
 3390     return ERR_NONE;
 3391 }
 3392 
 3393 GSM_Error ATGEN_ReplySetPBKMemory(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 3394 {
 3395     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 3396         case AT_Reply_OK:
 3397         case AT_Reply_Connect:
 3398             return ERR_NONE;
 3399         case AT_Reply_Error:
 3400             return ERR_NOTSUPPORTED;
 3401         case AT_Reply_CMSError:
 3402             return ATGEN_HandleCMSError(s);
 3403         case AT_Reply_CMEError:
 3404             return ATGEN_HandleCMEError(s);
 3405         default:
 3406             break;
 3407     }
 3408     return ERR_UNKNOWNRESPONSE;
 3409 }
 3410 
 3411 GSM_Error ATGEN_CheckSBNR(GSM_StateMachine *s)
 3412 {
 3413     GSM_Error   error;
 3414     char        req[] = "AT^SBNR=?\r";
 3415     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3416 
 3417     if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_SIEMENS_PBK)) {
 3418         smprintf(s, "Forcing AT^SBNR support\n");
 3419         Priv->PBKSBNR = AT_AVAILABLE;
 3420         return ERR_NONE;
 3421     }
 3422 
 3423     smprintf(s, "Checking availability of SBNR\n");
 3424     error = ATGEN_WaitForAutoLen(s, req, 0x00, 40, ID_GetMemory);
 3425     return error;
 3426 }
 3427 
 3428 GSM_Error ATGEN_CheckSPBR(GSM_StateMachine *s)
 3429 {
 3430     GSM_Error   error;
 3431     char        req[] = "AT+SPBR=?\r";
 3432 
 3433     smprintf(s, "Checking availability of SPBR\n");
 3434     error = ATGEN_WaitForAutoLen(s, req, 0x00, 40, ID_GetMemory);
 3435     return error;
 3436 }
 3437 
 3438 GSM_Error ATGEN_CheckMPBR(GSM_StateMachine *s)
 3439 {
 3440     GSM_Error   error;
 3441     char        req[] = "AT+MPBR=?\r";
 3442 
 3443     smprintf(s, "Checking availability of MPBR\n");
 3444     error = ATGEN_WaitForAutoLen(s, req, 0x00, 40, ID_GetMemory);
 3445     return error;
 3446 }
 3447 
 3448 
 3449 GSM_Error ATGEN_SetPBKMemory(GSM_StateMachine *s, GSM_MemoryType MemType)
 3450 {
 3451     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 3452     char            req[] = "AT+CPBS=\"XX\"\r";
 3453     GSM_Error       error;
 3454 
 3455     if (Priv->PBKMemory == MemType) return ERR_NONE;
 3456 
 3457     /* Zero values that are for actual memory */
 3458     Priv->MemorySize        = 0;
 3459     Priv->MemoryUsed        = 0;
 3460     Priv->FirstMemoryEntry      = -1;
 3461     Priv->NextMemoryEntry       = 0;
 3462     Priv->TextLength        = 0;
 3463     Priv->NumberLength      = 0;
 3464 
 3465     /* If phone encodes also values in command, we need normal charset */
 3466     error = ATGEN_SetCharset(s, AT_PREF_CHARSET_NORMAL);
 3467     if (error != ERR_NONE) return error;
 3468 
 3469     if (Priv->PBKMemories[0] == 0) {
 3470         error = ATGEN_WaitForAutoLen(s, "AT+CPBS=?\r", 0x00, 10, ID_SetMemoryType);
 3471 
 3472         if (error != ERR_NONE) {
 3473             /*
 3474              * We weren't able to read available memories, let's
 3475              * guess that phone supports all. This is TRUE at least
 3476              * for Samsung.
 3477              */
 3478             strcpy(s->Phone.Data.Priv.ATGEN.PBKMemories, "\"ME\",\"SM\",\"DC\",\"ON\",\"LD\",\"FD\",\"MC\",\"RC\"");
 3479             smprintf(s, "Falling back to default memories list: %s\n", s->Phone.Data.Priv.ATGEN.PBKMemories);
 3480         }
 3481     }
 3482 
 3483     switch (MemType) {
 3484         case MEM_SM:
 3485             req[9] = 'S'; req[10] = 'M';
 3486             break;
 3487         case MEM_ME:
 3488                 if (strstr(Priv->PBKMemories,"ME") != NULL) {
 3489                 req[9] = 'M'; req[10] = 'E';
 3490                 break;
 3491             }
 3492                 if (strstr(Priv->PBKMemories,"MT") != NULL) {
 3493                 req[9] = 'M'; req[10] = 'T';
 3494                 break;
 3495             }
 3496             return ERR_NOTSUPPORTED;
 3497         case MEM_RC:
 3498                 if (strstr(Priv->PBKMemories,"RC")==NULL) return ERR_NOTSUPPORTED;
 3499             req[9] = 'R'; req[10] = 'C';
 3500             break;
 3501         case MEM_MC:
 3502                 if (strstr(Priv->PBKMemories,"MC")==NULL) return ERR_NOTSUPPORTED;
 3503             req[9] = 'M'; req[10] = 'C';
 3504             break;
 3505         case MEM_ON:
 3506                 if (strstr(Priv->PBKMemories,"ON")==NULL) return ERR_NOTSUPPORTED;
 3507             req[9] = 'O'; req[10] = 'N';
 3508             break;
 3509         case MEM_FD:
 3510                 if (strstr(Priv->PBKMemories,"FD")==NULL) return ERR_NOTSUPPORTED;
 3511             req[9] = 'F'; req[10] = 'D';
 3512             break;
 3513         case MEM_QD:
 3514                 if (strstr(Priv->PBKMemories,"QD")==NULL) return ERR_NOTSUPPORTED;
 3515             req[9] = 'Q'; req[10] = 'D';
 3516             break;
 3517         case MEM_DC:
 3518             if (strstr(Priv->PBKMemories,"DC")!=NULL) {
 3519                 req[9] = 'D'; req[10] = 'C';
 3520                 break;
 3521             }
 3522             if (strstr(Priv->PBKMemories,"LD")!=NULL) {
 3523                 req[9] = 'L'; req[10] = 'D';
 3524                 break;
 3525             }
 3526             return ERR_NOTSUPPORTED;
 3527         default:
 3528             return ERR_NOTSUPPORTED;
 3529     }
 3530 
 3531     smprintf(s, "Setting memory type\n");
 3532     error = ATGEN_WaitForAutoLen(s, req, 0x00, 10, ID_SetMemoryType);
 3533 
 3534     if (error == ERR_NONE) {
 3535         Priv->PBKMemory = MemType;
 3536     }
 3537     if (MemType == MEM_ME) {
 3538         if (Priv->PBKSBNR == 0) {
 3539             ATGEN_CheckSBNR(s);
 3540         }
 3541         if (Priv->PBK_SPBR == 0) {
 3542             ATGEN_CheckSPBR(s);
 3543         }
 3544         if (Priv->PBK_MPBR == 0) {
 3545             ATGEN_CheckMPBR(s);
 3546         }
 3547     }
 3548     return error;
 3549 }
 3550 
 3551 GSM_Error ATGEN_ReplyGetCPBSMemoryStatus(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3552 {
 3553     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3554     unsigned char tmp[200]={0};
 3555     GSM_Error error;
 3556     const char *str;
 3557 
 3558     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 3559     case AT_Reply_OK:
 3560         smprintf(s, "Memory status received\n");
 3561         str = GetLineString(msg->Buffer, &Priv->Lines, 2);
 3562 
 3563         error = ATGEN_ParseReply(s, str,
 3564                     "+CPBS: @s, @i, @i",
 3565                     tmp, sizeof(tmp) / 2,
 3566                     &Priv->MemoryUsed,
 3567                     &Priv->MemorySize);
 3568         if (error == ERR_UNKNOWNRESPONSE) {
 3569             return ERR_NOTSUPPORTED;
 3570         }
 3571         return error;
 3572     case AT_Reply_CMSError:
 3573         return ATGEN_HandleCMSError(s);
 3574     case AT_Reply_CMEError:
 3575         return ATGEN_HandleCMEError(s);
 3576     default:
 3577         break;
 3578     }
 3579     return ERR_UNKNOWNRESPONSE;
 3580 }
 3581 
 3582 /**
 3583  * Parses reply from phone about available entries.
 3584  *
 3585  * Standard format:
 3586  * \verbatim
 3587  * +CPBR: (first-last),max_number_len,max_name_len
 3588  * \endverbatim
 3589  * \verbatim
 3590  * +CPBR: (location),max_number_len,max_name_len
 3591  * \endverbatim
 3592  *
 3593  * Some phones (eg. Motorola C350) reply is different:
 3594  * \verbatim
 3595  * +CPBR: first-last,max_number_len,max_name_len
 3596  * \endverbatim
 3597  *
 3598  * Some phones do not list positions (Sharp):
 3599  * \verbatim
 3600  * +CPBR: (),max_number_len,max_name_len
 3601  * \endverbatim
 3602  *
 3603  * Some phones (eg. Nokia 6600 slide) append some additional values to
 3604  * standard format.
 3605  *
 3606  * Samsung phones sometimes have additional number after standard format.
 3607  * I currently have no idea what does this number mean.
 3608  *
 3609  * \todo
 3610  * We currently guess memory size for Sharp to 1000.
 3611  */
 3612 GSM_Error ATGEN_ReplyGetCPBRMemoryInfo(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3613 {
 3614     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 3615     const char *str;
 3616     GSM_Error error;
 3617     int ignore;
 3618 
 3619     switch (Priv->ReplyState) {
 3620     case AT_Reply_OK:
 3621         smprintf(s, "Memory info received\n");
 3622 
 3623         str = GetLineString(msg->Buffer, &Priv->Lines, 2);
 3624 
 3625         /* Check for empty reply */
 3626         if (strcmp("OK", str) == 0) {
 3627             return ERR_UNKNOWN;
 3628         }
 3629 
 3630         /* Try standard format first */
 3631         error = ATGEN_ParseReply(s, str,
 3632                     "+CPBR: (@i-@i), @i, @i",
 3633                     &Priv->FirstMemoryEntry,
 3634                     &Priv->MemorySize,
 3635                     &Priv->NumberLength,
 3636                     &Priv->TextLength);
 3637         if (error == ERR_NONE) {
 3638             /* Calculate memory size from last position we got from phone */
 3639             Priv->MemorySize = Priv->MemorySize + 1 - Priv->FirstMemoryEntry;
 3640             return ERR_NONE;
 3641         }
 3642 
 3643         /* Try Motorola format then */
 3644         error = ATGEN_ParseReply(s, str,
 3645                     "+CPBR: @i-@i, @i, @i",
 3646                     &Priv->FirstMemoryEntry,
 3647                     &Priv->MemorySize,
 3648                     &Priv->NumberLength,
 3649                     &Priv->TextLength);
 3650         if (error == ERR_NONE) {
 3651             /* Calculate memory size from last position we got from phone */
 3652             Priv->MemorySize = Priv->MemorySize + 1 - Priv->FirstMemoryEntry;
 3653             return ERR_NONE;
 3654         }
 3655 
 3656         /* Try Sharp format */
 3657         error = ATGEN_ParseReply(s, str,
 3658                     "+CPBR: (), @i, @i",
 3659                     &Priv->NumberLength,
 3660                     &Priv->TextLength);
 3661         if (error == ERR_NONE) {
 3662             /* Hardcode size, we have no other choice here */
 3663             Priv->FirstMemoryEntry = 1;
 3664             Priv->MemorySize = 1000;
 3665             return ERR_NONE;
 3666         }
 3667 
 3668         /* Try single entry format */
 3669         error = ATGEN_ParseReply(s, str,
 3670                     "+CPBR: (@i), @i, @i",
 3671                     &Priv->FirstMemoryEntry,
 3672                     &Priv->NumberLength,
 3673                     &Priv->TextLength);
 3674         if (error == ERR_NONE) {
 3675             /* Hardcode size, we have no other choice here */
 3676             Priv->MemorySize = 1;
 3677             return ERR_NONE;
 3678         }
 3679 
 3680         /* Try Samsung format at the end */
 3681         error = ATGEN_ParseReply(s, str,
 3682                     "+CPBR: (@i-@i), @i, @i, @i",
 3683                     &Priv->FirstMemoryEntry,
 3684                     &Priv->MemorySize,
 3685                     &Priv->NumberLength,
 3686                     &Priv->TextLength,
 3687                     &ignore);
 3688         if (error == ERR_NONE) {
 3689             /* Calculate memory size from last position we got from phone */
 3690             Priv->MemorySize = Priv->MemorySize + 1 - Priv->FirstMemoryEntry;
 3691             return ERR_NONE;
 3692         }
 3693 
 3694 
 3695         /* Try standard format + unknown field */
 3696         error = ATGEN_ParseReply(s, str,
 3697                     "+CPBR: (@i-@i), @i, @i, @0",
 3698                     &Priv->FirstMemoryEntry,
 3699                     &Priv->MemorySize,
 3700                     &Priv->NumberLength,
 3701                     &Priv->TextLength);
 3702         if (error == ERR_NONE) {
 3703             /* Calculate memory size from last position we got from phone */
 3704             Priv->MemorySize = Priv->MemorySize + 1 - Priv->FirstMemoryEntry;
 3705             return ERR_NONE;
 3706         }
 3707 
 3708         /* Try cripled standard format */
 3709         error = ATGEN_ParseReply(s, str,
 3710                     "+CPBR: (@i-@i)",
 3711                     &Priv->FirstMemoryEntry,
 3712                     &Priv->MemorySize);
 3713         if (error == ERR_NONE) {
 3714             /* Calculate memory size from last position we got from phone */
 3715             Priv->MemorySize = Priv->MemorySize + 1 - Priv->FirstMemoryEntry;
 3716             return ERR_NONE;
 3717         }
 3718 
 3719         /* We don't get reply on first attempt on some Samsung phones */
 3720         if (Priv->Manufacturer == AT_Samsung) {
 3721             return ERR_NONE;
 3722         }
 3723         return ERR_UNKNOWNRESPONSE;
 3724     case AT_Reply_Error:
 3725         return ERR_UNKNOWN;
 3726     case AT_Reply_CMSError:
 3727             return ATGEN_HandleCMSError(s);
 3728     case AT_Reply_CMEError:
 3729         return ATGEN_HandleCMEError(s);
 3730     default:
 3731         return ERR_UNKNOWNRESPONSE;
 3732     }
 3733 }
 3734 
 3735 GSM_Error ATGEN_ReplyGetCPBRMemoryStatus(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3736 {
 3737     GSM_Error       error;
 3738     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 3739     int         line = 1;
 3740     const char          *str;
 3741     int         cur, last = -1;
 3742 
 3743     switch (Priv->ReplyState) {
 3744     case AT_Reply_OK:
 3745         smprintf(s, "Memory entries for status received\n");
 3746         /* Walk through lines with +CPBR: */
 3747         while (strcmp("OK", str = GetLineString(msg->Buffer, &Priv->Lines, line + 1)) != 0) {
 3748 
 3749             /* Parse reply */
 3750             error = ATGEN_ParseReply(s, str, "+CPBR: @i, @0", &cur);
 3751             if (error != ERR_NONE) {
 3752                 return error;
 3753             }
 3754 
 3755             /* Some phones wrongly return several lines with same location,
 3756              * we need to catch it here to get correct count. */
 3757             if (cur != last) {
 3758                 Priv->MemoryUsed++;
 3759             }
 3760             last = cur;
 3761             cur -= Priv->FirstMemoryEntry - 1;
 3762             if (cur == Priv->NextMemoryEntry || Priv->NextMemoryEntry == 0)
 3763                 Priv->NextMemoryEntry = cur + 1;
 3764 
 3765             /* Go to next line */
 3766             line++;
 3767         }
 3768         smprintf(s, "Memory status: Used: %d, Next: %d\n",
 3769                 Priv->MemoryUsed,
 3770                 Priv->NextMemoryEntry);
 3771         return ERR_NONE;
 3772     case AT_Reply_Error:
 3773         return ERR_UNKNOWN;
 3774     case AT_Reply_CMSError:
 3775             return ATGEN_HandleCMSError(s);
 3776     case AT_Reply_CMEError:
 3777         return ATGEN_HandleCMEError(s);
 3778     default:
 3779         return ERR_UNKNOWNRESPONSE;
 3780     }
 3781 }
 3782 
 3783 GSM_Error ATGEN_GetMemoryInfo(GSM_StateMachine *s, GSM_MemoryStatus *Status, GSM_AT_NeededMemoryInfo NeededInfo)
 3784 {
 3785     GSM_Error       error;
 3786     char            req[20]={'\0'};
 3787     int         start = 0,end = 0,memory_end = 0;
 3788     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 3789     gboolean        free_read = FALSE;
 3790     size_t len;
 3791     int step = 20;
 3792 
 3793     /* This can be NULL at this point */
 3794     if (Status != NULL) {
 3795         Status->MemoryUsed = 0;
 3796         Status->MemoryFree = 0;
 3797     }
 3798 
 3799     /* For reading we prefer unicode */
 3800     error = ATGEN_SetCharset(s, AT_PREF_CHARSET_UNICODE);
 3801     if (error != ERR_NONE) return error;
 3802 
 3803     Priv->MemorySize        = 0;
 3804     Priv->MemoryUsed        = 0;
 3805     /* Safe default values */
 3806     Priv->TextLength        = 20;
 3807     Priv->NumberLength      = 20;
 3808     Priv->FirstMemoryEntry      = 1;
 3809 
 3810     /*
 3811      * First we try AT+CPBS?. It should return size of memory and
 3812      * number of used entries, but some vendors do not support this
 3813      * (SE).
 3814      */
 3815     /*
 3816      * Some workaround for buggy mobile, that hangs after "AT+CPBS?" for other
 3817      * memory than SM.
 3818      */
 3819     if (!GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_BROKENCPBS) || (Priv->PBKMemory == MEM_SM)) {
 3820         smprintf(s, "Getting memory status\n");
 3821         error = ATGEN_WaitForAutoLen(s, "AT+CPBS?\r", 0x00, 40, ID_GetMemoryStatus);
 3822 
 3823         if (error == ERR_NONE) {
 3824             free_read = TRUE;
 3825         }
 3826     }
 3827 
 3828     /**
 3829      * Try to get memory size, first entry and length of entries
 3830      * this way.
 3831      */
 3832     smprintf(s, "Getting memory information\n");
 3833     if (Status != NULL && Status->MemoryType == MEM_ME && Priv->PBK_MPBR == AT_AVAILABLE) {
 3834         error = ATGEN_WaitForAutoLen(s, "AT+MPBR=?\r", 0x00, 40, ID_GetMemoryStatus);
 3835     } else {
 3836         error = ATGEN_WaitForAutoLen(s, "AT+CPBR=?\r", 0x00, 40, ID_GetMemoryStatus);
 3837     }
 3838 
 3839     /* Did we fail to get size in either way? */
 3840     if (error != ERR_NONE && Priv->MemorySize == 0) return error;
 3841     /* Fill in Status structure if we were asked for it */
 3842     if (Priv->MemorySize != 0 && Status != NULL) {
 3843         Status->MemoryUsed = Priv->MemoryUsed;
 3844         Status->MemoryFree = Priv->MemorySize - Priv->MemoryUsed;
 3845     }
 3846     if (((NeededInfo != AT_NextEmpty) &&
 3847             (NeededInfo != AT_Status || free_read)) || Status == NULL) {
 3848         return ERR_NONE;
 3849     }
 3850 
 3851     smprintf(s, "Getting memory status by reading values\n");
 3852 
 3853     Status->MemoryUsed      = 0;
 3854     Status->MemoryFree      = 0;
 3855     start               = Priv->FirstMemoryEntry;
 3856     Priv->NextMemoryEntry       = Priv->FirstMemoryEntry;
 3857     memory_end = Priv->MemorySize + Priv->FirstMemoryEntry - 1;
 3858 
 3859     while (1) {
 3860         /* Calculate end of next request */
 3861         end = start + step;
 3862         if (end > memory_end)
 3863             end = memory_end;
 3864 
 3865         /* Read next interval */
 3866         if (start == end) {
 3867             len = sprintf(req, "AT+CPBR=%i\r", start);
 3868         } else {
 3869             len = sprintf(req, "AT+CPBR=%i,%i\r", start, end);
 3870         }
 3871         error = ATGEN_WaitFor(s, req, len, 0x00, 50, ID_GetMemoryStatus);
 3872 
 3873         if (error == ERR_SECURITYERROR) {
 3874             /* Some Samsung phones fail to read more entries at once */
 3875             step = 0;
 3876             continue;
 3877         } else if (error == ERR_EMPTY) {
 3878             Priv->NextMemoryEntry = start;
 3879             if (NeededInfo == AT_NextEmpty) {
 3880                 return ERR_NONE;
 3881             }
 3882         } else if (error != ERR_NONE) {
 3883             return error;
 3884         }
 3885 
 3886         /* Do we already have first empty record? */
 3887         if (NeededInfo == AT_NextEmpty &&
 3888                 Priv->NextMemoryEntry != end + 1)
 3889             return ERR_NONE;
 3890 
 3891         /* Did we hit memory end? */
 3892         if (end == memory_end) {
 3893             Status->MemoryUsed = Priv->MemoryUsed;
 3894             Status->MemoryFree = Priv->MemorySize - Priv->MemoryUsed;
 3895             return ERR_NONE;
 3896         }
 3897 
 3898         /* Continue on next location */
 3899         start = end + 1;
 3900     }
 3901 }
 3902 
 3903 GSM_Error ATGEN_GetMemoryStatus(GSM_StateMachine *s, GSM_MemoryStatus *Status)
 3904 {
 3905     GSM_Error       error;
 3906     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 3907 
 3908     error = ATGEN_SetPBKMemory(s, Status->MemoryType);
 3909     if (error != ERR_NONE) return error;
 3910 
 3911     /* Catch errorneous 0 returned by some Siemens phones for ME. There is
 3912      * probably no way to get status there. */
 3913     if (Priv->PBKSBNR == AT_AVAILABLE && Status->MemoryType == MEM_ME && Status->MemoryFree == 0)
 3914         return ERR_NOTSUPPORTED;
 3915 
 3916     return ATGEN_GetMemoryInfo(s, Status, AT_Status);
 3917 }
 3918 
 3919 /**
 3920  * Parses reply on AT+CPBR=n.
 3921  *
 3922  * \todo Handle special replies from some phones:
 3923  * LG C1200:
 3924  * +CPBR: 23,"Primary Number",145,"Name",3,"0123456789",145,2,"0123456789",145,1,"E-Mail-Address without domain","Fax-Number",255
 3925  * 3 = Home Number
 3926  * 2 = Office Number
 3927  * 1 = Mobile Number
 3928  *
 3929  * Samsung SGH-P900 reply:
 3930  * +CPBR: 81,"#121#",129,"My Tempo",0
 3931  */
 3932 GSM_Error ATGEN_ReplyGetMemory(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 3933 {
 3934     GSM_Phone_ATGENData     *Priv = &s->Phone.Data.Priv.ATGEN;
 3935     GSM_MemoryEntry     *Memory = s->Phone.Data.Memory;
 3936     GSM_Error       error;
 3937     unsigned char       buffer[500];
 3938     int offset, i;
 3939     int number_type, types[10];
 3940 
 3941     switch (Priv->ReplyState) {
 3942     case AT_Reply_OK:
 3943         smprintf(s, "Phonebook entry received\n");
 3944         /* Check for empty entries */
 3945         if (strcmp("OK", GetLineString(msg->Buffer, &Priv->Lines, 2)) == 0) {
 3946             Memory->EntriesNum = 0;
 3947             return ERR_EMPTY;
 3948         }
 3949 
 3950         /* Set number type */
 3951         Memory->Entries[0].EntryType = PBK_Number_General;
 3952         Memory->Entries[0].Location = PBK_Location_Unknown;
 3953         Memory->Entries[0].VoiceTag = 0;
 3954         Memory->Entries[0].SMSList[0] = 0;
 3955 
 3956         /* Set name type */
 3957         Memory->Entries[1].EntryType = PBK_Text_Name;
 3958         Memory->Entries[1].Location = PBK_Location_Unknown;
 3959 
 3960         /* Try standard reply */
 3961         if (Priv->Manufacturer == AT_Motorola) {
 3962             /* Enable encoding guessing for Motorola */
 3963             error = ATGEN_ParseReply(s,
 3964                         GetLineString(msg->Buffer, &Priv->Lines, 2),
 3965                         "+CPBR: @i, @p, @I, @s",
 3966                         &Memory->Location,
 3967                         Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 3968                         &number_type,
 3969                         Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text));
 3970         } else {
 3971             error = ATGEN_ParseReply(s,
 3972                         GetLineString(msg->Buffer, &Priv->Lines, 2),
 3973                         "+CPBR: @i, @p, @I, @e",
 3974                         &Memory->Location,
 3975                         Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 3976                         &number_type,
 3977                         Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text));
 3978         }
 3979         if (error == ERR_NONE) {
 3980             smprintf(s, "Generic AT reply detected\n");
 3981             /* Adjust location */
 3982             Memory->Location = Memory->Location + 1 - Priv->FirstMemoryEntry;
 3983             /* Adjust number */
 3984             GSM_TweakInternationalNumber(Memory->Entries[0].Text, number_type);
 3985             /* Set number of entries */
 3986             Memory->EntriesNum = 2;
 3987             return ERR_NONE;
 3988         }
 3989 
 3990         /* Try reply with extra unknown number (maybe group?), seen on Samsung SGH-P900 */
 3991         error = ATGEN_ParseReply(s,
 3992                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 3993                     "+CPBR: @i, @p, @I, @e, @i",
 3994                     &Memory->Location,
 3995                     Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 3996                     &number_type,
 3997                     Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text),
 3998                     &i /* Don't know what this means */
 3999                     );
 4000         if (error == ERR_NONE) {
 4001             smprintf(s, "AT reply with extra number detected\n");
 4002             /* Adjust location */
 4003             Memory->Location = Memory->Location + 1 - Priv->FirstMemoryEntry;
 4004             /* Adjust number */
 4005             GSM_TweakInternationalNumber(Memory->Entries[0].Text, number_type);
 4006             /* Set number of entries */
 4007             Memory->EntriesNum = 2;
 4008             return ERR_NONE;
 4009         }
 4010 
 4011         /* Try reply with call date */
 4012         error = ATGEN_ParseReply(s,
 4013                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 4014                     "+CPBR: @i, @p, @I, @s, @d",
 4015                     &Memory->Location,
 4016                     Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 4017                     &number_type,
 4018                     Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text),
 4019                     &Memory->Entries[2].Date);
 4020         if (error == ERR_NONE) {
 4021             smprintf(s, "Reply with date detected\n");
 4022             /* Adjust location */
 4023             Memory->Location = Memory->Location + 1 - Priv->FirstMemoryEntry;
 4024             /* Adjust number */
 4025             GSM_TweakInternationalNumber(Memory->Entries[0].Text, number_type);
 4026             /* Set date type */
 4027             Memory->Entries[2].EntryType = PBK_Date;
 4028             Memory->Entries[2].Location = PBK_Location_Unknown;
 4029             /* Set number of entries */
 4030             Memory->EntriesNum = 3;
 4031             /* Check whether date is correct */
 4032             if (!CheckTime(&Memory->Entries[2].Date) || !CheckDate(&Memory->Entries[2].Date)) {
 4033                 smprintf(s, "Date looks invalid, ignoring!\n");
 4034                 Memory->EntriesNum = 2;
 4035             }
 4036             return ERR_NONE;
 4037         }
 4038 
 4039         /*
 4040          * Try reply with call date and some additional string.
 4041          * I have no idea what should be stored there.
 4042          * We store it in Entry 3, but do not use it for now.
 4043          * Seen on T630.
 4044          */
 4045         error = ATGEN_ParseReply(s,
 4046                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 4047                     "+CPBR: @i, @s, @p, @I, @s, @d",
 4048                     &Memory->Location,
 4049                     Memory->Entries[3].Text, sizeof(Memory->Entries[3].Text),
 4050                     Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 4051                     &number_type,
 4052                     Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text),
 4053                     &Memory->Entries[2].Date);
 4054         if (error == ERR_NONE) {
 4055             smprintf(s, "Reply with date detected\n");
 4056             /* Adjust location */
 4057             Memory->Location = Memory->Location + 1 - Priv->FirstMemoryEntry;
 4058             /* Adjust number */
 4059             GSM_TweakInternationalNumber(Memory->Entries[0].Text, number_type);
 4060             /* Set date type */
 4061             Memory->Entries[2].EntryType = PBK_Date;
 4062             /* Set number of entries */
 4063             Memory->EntriesNum = 3;
 4064             return ERR_NONE;
 4065         }
 4066 
 4067         /**
 4068          * Samsung format:
 4069          * location,"number",type,"0x02surname0x03","0x02firstname0x03","number",
 4070          * type,"number",type,"number",type,"number",type,"email","NA",
 4071          * "0x02note0x03",category?,x,x,x,ringtone?,"NA","photo"
 4072          *
 4073          * NA fields were empty
 4074          * x fields are some numbers, default is 1,65535,255,255,65535
 4075          *
 4076          * Samsung number types:
 4077          * 2 - fax
 4078          * 4 - cell
 4079          * 5 - other
 4080          * 6 - home
 4081          * 7 - office
 4082          */
 4083         if (Priv->Manufacturer == AT_Samsung) {
 4084             /* Parse reply */
 4085             error = ATGEN_ParseReply(s,
 4086                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 4087                     "+CPBR: @i,@p,@i,@S,@S,@p,@i,@p,@i,@p,@i,@p,@i,@s,@s,@S,@i,@i,@i,@i,@i,@s,@s",
 4088                     &Memory->Location,
 4089                     Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 4090                     &types[0],
 4091                     Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text), /* surname */
 4092                     Memory->Entries[2].Text, sizeof(Memory->Entries[2].Text), /* first name */
 4093                     Memory->Entries[3].Text, sizeof(Memory->Entries[3].Text),
 4094                     &types[3],
 4095                     Memory->Entries[4].Text, sizeof(Memory->Entries[4].Text),
 4096                     &types[4],
 4097                     Memory->Entries[5].Text, sizeof(Memory->Entries[5].Text),
 4098                     &types[5],
 4099                     Memory->Entries[6].Text, sizeof(Memory->Entries[6].Text),
 4100                     &types[6],
 4101                     Memory->Entries[7].Text, sizeof(Memory->Entries[7].Text), /* email */
 4102                     buffer, sizeof(buffer), /* We don't know this */
 4103                     Memory->Entries[8].Text, sizeof(Memory->Entries[8].Text), /* note */
 4104                     &Memory->Entries[9].Number, /* category */
 4105                     &number_type, /* We don't know this */
 4106                     &number_type, /* We don't know this */
 4107                     &number_type, /* We don't know this */
 4108                     &Memory->Entries[10].Number, /* ringtone ID */
 4109                     buffer, sizeof(buffer), /* We don't know this */
 4110                     Memory->Entries[11].Text, sizeof(Memory->Entries[11].Text) /* photo ID */
 4111                     );
 4112 
 4113             if (error == ERR_NONE) {
 4114                 smprintf(s, "Samsung reply detected\n");
 4115                 /* Set types */
 4116                 Memory->Entries[1].EntryType = PBK_Text_LastName;
 4117                 Memory->Entries[1].Location = PBK_Location_Unknown;
 4118                 Memory->Entries[2].EntryType = PBK_Text_FirstName;
 4119                 Memory->Entries[2].Location = PBK_Location_Unknown;
 4120                 Memory->Entries[7].EntryType = PBK_Text_Email;
 4121                 Memory->Entries[7].Location = PBK_Location_Unknown;
 4122                 Memory->Entries[8].EntryType = PBK_Text_Note;
 4123                 Memory->Entries[8].Location = PBK_Location_Unknown;
 4124                 Memory->Entries[9].EntryType = PBK_Category;
 4125                 Memory->Entries[9].Location = PBK_Location_Unknown;
 4126                 Memory->Entries[10].EntryType = PBK_RingtoneID;
 4127                 Memory->Entries[10].Location = PBK_Location_Unknown;
 4128                 Memory->Entries[11].EntryType = PBK_Text_PictureName;
 4129                 Memory->Entries[11].Location = PBK_Location_Unknown;
 4130 
 4131                 /* Adjust location */
 4132                 Memory->Location = Memory->Location + 1 - Priv->FirstMemoryEntry;
 4133 
 4134                 /* Shift entries when needed */
 4135                 offset = 0;
 4136 
 4137 #define SHIFT_ENTRIES(index) \
 4138     for (i = index - offset + 1; i < GSM_PHONEBOOK_ENTRIES; i++) { \
 4139         Memory->Entries[i - 1] = Memory->Entries[i]; \
 4140     } \
 4141     offset++;
 4142 
 4143 #define CHECK_TEXT(index) \
 4144                 if (UnicodeLength(Memory->Entries[index - offset].Text) == 0) { \
 4145                     smprintf(s, "Entry %d is empty\n", index); \
 4146                     SHIFT_ENTRIES(index); \
 4147                 }
 4148 #define CHECK_NUMBER(index) \
 4149                 if (UnicodeLength(Memory->Entries[index - offset].Text) == 0) { \
 4150                     smprintf(s, "Entry %d is empty\n", index); \
 4151                     SHIFT_ENTRIES(index); \
 4152                 } else { \
 4153                     Memory->Entries[index - offset].VoiceTag   = 0; \
 4154                     Memory->Entries[index - offset].SMSList[0] = 0; \
 4155                     switch (types[index]) { \
 4156                         case 2: \
 4157                             Memory->Entries[index - offset].EntryType  = PBK_Number_Fax; \
 4158                             Memory->Entries[index - offset].Location = PBK_Location_Unknown; \
 4159                             break; \
 4160                         case 4: \
 4161                             Memory->Entries[index - offset].EntryType  = PBK_Number_Mobile; \
 4162                             Memory->Entries[index - offset].Location = PBK_Location_Unknown; \
 4163                             break; \
 4164                         case 5: \
 4165                             Memory->Entries[index - offset].EntryType  = PBK_Number_Other; \
 4166                             Memory->Entries[index - offset].Location = PBK_Location_Unknown; \
 4167                             break; \
 4168                         case 6: \
 4169                             Memory->Entries[index - offset].EntryType  = PBK_Number_General; \
 4170                             Memory->Entries[index - offset].Location = PBK_Location_Home; \
 4171                             break; \
 4172                         case 7: \
 4173                             Memory->Entries[index - offset].EntryType  = PBK_Number_General; \
 4174                             Memory->Entries[index - offset].Location = PBK_Location_Work; \
 4175                             break; \
 4176                         default: \
 4177                             Memory->Entries[index - offset].EntryType  = PBK_Number_Other; \
 4178                             Memory->Entries[index - offset].Location = PBK_Location_Unknown; \
 4179                             smprintf(s, "WARNING: Unknown memory entry type %d\n", types[index]); \
 4180                             break; \
 4181                     } \
 4182                 }
 4183                 CHECK_NUMBER(0);
 4184                 CHECK_TEXT(1);
 4185                 CHECK_TEXT(2);
 4186                 CHECK_NUMBER(3);
 4187                 CHECK_NUMBER(4);
 4188                 CHECK_NUMBER(5);
 4189                 CHECK_NUMBER(6);
 4190                 CHECK_TEXT(7);
 4191                 CHECK_TEXT(8);
 4192                 if (Memory->Entries[10 - offset].Number == 65535) {
 4193                     SHIFT_ENTRIES(10);
 4194                 }
 4195                 CHECK_TEXT(11);
 4196 
 4197 #undef CHECK_NUMBER
 4198 #undef CHECK_TEXT
 4199 #undef SHIFT_ENTRIES
 4200                 /* Set number of entries */
 4201                 Memory->EntriesNum = 12 - offset;
 4202                 return ERR_NONE;
 4203             }
 4204 
 4205         }
 4206 
 4207         /*
 4208          * Nokia 2730 adds some extra fields to the end, we ignore
 4209          * them for now
 4210          */
 4211         error = ATGEN_ParseReply(s,
 4212                     GetLineString(msg->Buffer, &Priv->Lines, 2),
 4213                     "+CPBR: @i, @p, @I, @e, @0",
 4214                     &Memory->Location,
 4215                     Memory->Entries[0].Text, sizeof(Memory->Entries[0].Text),
 4216                     &number_type,
 4217                     Memory->Entries[1].Text, sizeof(Memory->Entries[1].Text));
 4218         if (error == ERR_NONE) {
 4219             smprintf(s, "Extended AT reply detected\n");
 4220             /* Adjust location */
 4221             Memory->Location = Memory->Location + 1 - Priv->FirstMemoryEntry;
 4222             /* Adjust number */
 4223             GSM_TweakInternationalNumber(Memory->Entries[0].Text, number_type);
 4224             /* Set number of entries */
 4225             Memory->EntriesNum = 2;
 4226             return ERR_NONE;
 4227         }
 4228 
 4229         return ERR_UNKNOWNRESPONSE;
 4230     case AT_Reply_CMEError:
 4231         if (Priv->ErrorCode == 100)
 4232             return ERR_EMPTY;
 4233         if (Priv->ErrorCode == 3)
 4234             return ERR_INVALIDLOCATION;
 4235         error = ATGEN_HandleCMEError(s);
 4236         if (error == ERR_MEMORY) {
 4237             smprintf(s, "Assuming that memory error means empty entry\n");
 4238             return ERR_EMPTY;
 4239         }
 4240         return error;
 4241     case AT_Reply_Error:
 4242         smprintf(s, "Error - too high location ?\n");
 4243         return ERR_INVALIDLOCATION;
 4244     case AT_Reply_CMSError:
 4245             return ATGEN_HandleCMSError(s);
 4246     default:
 4247         break;
 4248     }
 4249     return ERR_UNKNOWNRESPONSE;
 4250 }
 4251 
 4252 GSM_Error ATGEN_PrivGetMemory (GSM_StateMachine *s, GSM_MemoryEntry *entry, int endlocation)
 4253 {
 4254     GSM_Error       error;
 4255     char        req[20];
 4256     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 4257     size_t len;
 4258 
 4259     if (entry->Location == 0x00) return ERR_INVALIDLOCATION;
 4260 
 4261     /* For reading we prefer unicode */
 4262     error = ATGEN_SetCharset(s, AT_PREF_CHARSET_UNICODE);
 4263     if (error != ERR_NONE) return error;
 4264 
 4265     if (entry->MemoryType == MEM_ME) {
 4266         if (Priv->PBKSBNR == 0) {
 4267             ATGEN_CheckSBNR(s);
 4268         }
 4269         if (Priv->PBK_SPBR == 0) {
 4270             ATGEN_CheckSPBR(s);
 4271         }
 4272         if (Priv->PBK_MPBR == 0) {
 4273             ATGEN_CheckMPBR(s);
 4274         }
 4275         if (Priv->PBKSBNR == AT_AVAILABLE) {
 4276             /* FirstMemoryEntry is not applied here, it is always 0 */
 4277             len = sprintf(req, "AT^SBNR=\"vcf\",%i\r",entry->Location - 1);
 4278             goto read_memory;
 4279         }
 4280         if (Priv->PBK_SPBR == AT_AVAILABLE) {
 4281             error = ATGEN_SetPBKMemory(s, entry->MemoryType);
 4282             if (error != ERR_NONE) return error;
 4283 
 4284             /* FirstMemoryEntry is not applied here, it is always 1 */
 4285             len = sprintf(req, "AT+SPBR=%i\r", entry->Location);
 4286             goto read_memory;
 4287         }
 4288         if (Priv->PBK_MPBR == AT_AVAILABLE) {
 4289             error = ATGEN_SetPBKMemory(s, entry->MemoryType);
 4290             if (error != ERR_NONE) return error;
 4291 
 4292             if (Priv->MotorolaFirstMemoryEntry == -1) {
 4293                 ATGEN_CheckMPBR(s);
 4294             }
 4295             if (entry->Location > Priv->MotorolaMemorySize) {
 4296                 /* Reached end of memory, phone silently returns OK */
 4297                 return ERR_EMPTY;
 4298             }
 4299             len = sprintf(req, "AT+MPBR=%i\r", entry->Location + Priv->MotorolaFirstMemoryEntry - 1);
 4300             goto read_memory;
 4301         }
 4302     }
 4303 
 4304     error = ATGEN_SetPBKMemory(s, entry->MemoryType);
 4305     if (error != ERR_NONE) return error;
 4306 
 4307     if (Priv->FirstMemoryEntry == -1) {
 4308         error = ATGEN_GetMemoryInfo(s, NULL, AT_First);
 4309         if (error != ERR_NONE) return error;
 4310     }
 4311 
 4312     if (endlocation == 0) {
 4313         len = sprintf(req, "AT+CPBR=%i\r", entry->Location + Priv->FirstMemoryEntry - 1);
 4314     } else {
 4315         len = sprintf(req, "AT+CPBR=%i,%i\r", entry->Location + Priv->FirstMemoryEntry - 1, endlocation + Priv->FirstMemoryEntry - 1);
 4316     }
 4317 
 4318 read_memory:
 4319     s->Phone.Data.Memory=entry;
 4320     smprintf(s, "Getting phonebook entry\n");
 4321     error = ATGEN_WaitFor(s, req, len, 0x00, 30, ID_GetMemory);
 4322     return error;
 4323 }
 4324 
 4325 GSM_Error ATGEN_GetMemory (GSM_StateMachine *s, GSM_MemoryEntry *entry)
 4326 {
 4327     return ATGEN_PrivGetMemory(s, entry, 0);
 4328 }
 4329 
 4330 GSM_Error ATGEN_GetNextMemory (GSM_StateMachine *s, GSM_MemoryEntry *entry, gboolean start)
 4331 {
 4332     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 4333     GSM_Error       error;
 4334     int         step = 0;
 4335 
 4336     if (entry->MemoryType == MEM_ME) {
 4337         if (Priv->PBKSBNR == 0) {
 4338             ATGEN_CheckSBNR(s);
 4339         }
 4340         if (Priv->PBK_SPBR == 0) {
 4341             ATGEN_CheckSPBR(s);
 4342         }
 4343         if (Priv->PBK_MPBR == 0) {
 4344             ATGEN_CheckMPBR(s);
 4345         }
 4346     }
 4347     /* There are no status functions for SBNR */
 4348     if (entry->MemoryType != MEM_ME || Priv->PBKSBNR != AT_AVAILABLE) {
 4349         error = ATGEN_SetPBKMemory(s, entry->MemoryType);
 4350         if (error != ERR_NONE) return error;
 4351 
 4352         if (Priv->MemorySize == 0) {
 4353             error = ATGEN_GetMemoryInfo(s, NULL, AT_Total);
 4354             if (error != ERR_NONE) return error;
 4355         }
 4356     }
 4357 
 4358     if (start) {
 4359         entry->Location = 1;
 4360     } else {
 4361         entry->Location++;
 4362     }
 4363     while ((error = ATGEN_PrivGetMemory(s, entry, step == 0 ? 0 : MIN(Priv->MemorySize, entry->Location + step))) == ERR_EMPTY) {
 4364         entry->Location += step + 1;
 4365         if (Priv->PBK_MPBR == AT_AVAILABLE && entry->MemoryType == MEM_ME) {
 4366             if (entry->Location > Priv->MotorolaMemorySize) break;
 4367         } else {
 4368             if (entry->Location > Priv->MemorySize) break;
 4369         }
 4370         /* SBNR works only for one location */
 4371         if ((entry->MemoryType != MEM_ME || Priv->PBKSBNR != AT_AVAILABLE) &&
 4372                 Priv->PBK_MPBR != AT_AVAILABLE &&
 4373                 Priv->PBK_SPBR != AT_AVAILABLE) {
 4374             step = MIN(step + 2, 20);
 4375         }
 4376     }
 4377     if (error == ERR_INVALIDLOCATION) return ERR_EMPTY;
 4378     return error;
 4379 }
 4380 
 4381 GSM_Error ATGEN_DeleteAllMemory(GSM_StateMachine *s, GSM_MemoryType type)
 4382 {
 4383     GSM_Error       error;
 4384     unsigned char       req[100];
 4385     int         i;
 4386     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 4387     size_t len;
 4388 
 4389     error = ATGEN_SetPBKMemory(s, type);
 4390     if (error != ERR_NONE) return error;
 4391 
 4392     if (Priv->MemorySize == 0) {
 4393         error = ATGEN_GetMemoryInfo(s, NULL, AT_Total);
 4394         if (error != ERR_NONE) return error;
 4395     }
 4396 
 4397     if (Priv->FirstMemoryEntry == -1) {
 4398         error = ATGEN_GetMemoryInfo(s, NULL, AT_First);
 4399         if (error != ERR_NONE) return error;
 4400     }
 4401 
 4402 
 4403     smprintf(s, "Deleting all phonebook entries\n");
 4404     for (i = Priv->FirstMemoryEntry; i < Priv->FirstMemoryEntry + Priv->MemorySize; i++) {
 4405         len = sprintf(req, "AT+CPBW=%d\r",i);
 4406         error = ATGEN_WaitFor(s, req, len, 0x00, 40, ID_SetMemory);
 4407 
 4408         if (error != ERR_NONE) {
 4409             return error;
 4410         }
 4411     }
 4412     return ERR_NONE;
 4413 }
 4414 
 4415 GSM_Error ATGEN_ReplyDialVoice(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 4416 {
 4417     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 4418     case AT_Reply_OK:
 4419         smprintf(s, "Dial voice OK\n");
 4420         return ERR_NONE;
 4421     case AT_Reply_Error:
 4422         smprintf(s, "Dial voice error\n");
 4423         return ERR_UNKNOWN;
 4424     case AT_Reply_CMSError:
 4425             return ATGEN_HandleCMSError(s);
 4426     case AT_Reply_CMEError:
 4427             return ATGEN_HandleCMEError(s);
 4428     default:
 4429         break;
 4430     }
 4431     return ERR_UNKNOWNRESPONSE;
 4432 }
 4433 
 4434 GSM_Error ATGEN_DialService(GSM_StateMachine *s, char *number)
 4435 {
 4436     GSM_Error error;
 4437     char *req = NULL,*encoded = NULL;
 4438     unsigned char *tmp = NULL;
 4439     const char format[] = "AT+CUSD=%d,\"%s\",15\r";
 4440     size_t len = 0, allocsize, sevenlen = 0;
 4441 
 4442     len = strlen(number);
 4443     /*
 4444      * We need to allocate four times more memory for number here, because it
 4445      * might be encoded later to UCS2.
 4446      */
 4447     allocsize = 4 * (len + 1);
 4448     req = (char *)malloc(strlen(format) + allocsize + 1);
 4449 
 4450     if (req == NULL) {
 4451         return ERR_MOREMEMORY;
 4452     }
 4453     /* Prefer unicode to be able to deal with unicode response */
 4454     if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_USSD_GSM_CHARSET)) {
 4455         error = ATGEN_SetCharset(s, AT_PREF_CHARSET_GSM);
 4456     } else {
 4457         error = ATGEN_SetCharset(s, AT_PREF_CHARSET_UNICODE);
 4458     }
 4459 
 4460     if (error != ERR_NONE) {
 4461         free(req);
 4462         req = NULL;
 4463         return error;
 4464     }
 4465     encoded = (char *)malloc(allocsize);
 4466     tmp = (unsigned char *)malloc(allocsize);
 4467     if (tmp == NULL || encoded == NULL) {
 4468         free(req);
 4469         free(tmp);
 4470         free(encoded);
 4471         return ERR_MOREMEMORY;
 4472     }
 4473     if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_ENCODED_USSD)) {
 4474         /* This is against GSM specs, but Huawei seems to use this */
 4475         sevenlen = GSM_PackSevenBitsToEight(0, number, tmp, len);
 4476         EncodeHexBin(encoded, tmp, sevenlen);
 4477     } else {
 4478         EncodeUnicode(tmp, number, strlen(number));
 4479         error = ATGEN_EncodeText(s, tmp, len, encoded, allocsize, &len);
 4480     }
 4481     free(tmp);
 4482     if (error != ERR_NONE) {
 4483         free(req);
 4484         free(encoded);
 4485         return error;
 4486     }
 4487 
 4488     len = sprintf(req, format, s->Phone.Data.EnableIncomingUSSD ? 1 : 0, encoded);
 4489 
 4490     free(encoded);
 4491 
 4492     error = ATGEN_WaitFor(s, req, len, 0x00, 30, ID_GetUSSD);
 4493     free(req);
 4494     req = NULL;
 4495     return error;
 4496 }
 4497 
 4498 GSM_Error ATGEN_DialVoice(GSM_StateMachine *s, char *number, GSM_CallShowNumber ShowNumber)
 4499 {
 4500     GSM_Error error;
 4501     char buffer[GSM_MAX_NUMBER_LENGTH + 6] = {'\0'};
 4502     size_t length = 0;
 4503     int oldretry;
 4504     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 4505 
 4506     if (ShowNumber != GSM_CALL_DefaultNumberPresence) {
 4507         return ERR_NOTSUPPORTED;
 4508     }
 4509     if (strlen(number) > GSM_MAX_NUMBER_LENGTH) {
 4510         return ERR_MOREMEMORY;
 4511     }
 4512 
 4513     oldretry = s->ReplyNum;
 4514     s->ReplyNum = 1;
 4515     smprintf(s, "Making voice call\n");
 4516     length = sprintf(buffer, "ATDT%s;\r", number);
 4517     error = ATGEN_WaitFor(s, buffer, length, 0x00, 100, ID_DialVoice);
 4518 
 4519     if (error == ERR_INVALIDLOCATION || error == ERR_UNKNOWN) {
 4520         smprintf(s, "Making voice call without forcing to tone dial\n");
 4521         length = sprintf(buffer, "ATD%s;\r", number);
 4522         error = ATGEN_WaitFor(s, buffer, length, 0x00, 100, ID_DialVoice);
 4523     }
 4524     if (error == ERR_TIMEOUT && Priv->Manufacturer == AT_Samsung) {
 4525         smprintf(s, "Assuming voice call succeeded even without reply from phone\n");
 4526         return ERR_NONE;
 4527     }
 4528     s->ReplyNum = oldretry;
 4529     return error;
 4530 }
 4531 
 4532 GSM_Error ATGEN_ReplyEnterSecurityCode(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 4533 {
 4534     switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 4535     case AT_Reply_OK:
 4536         smprintf(s, "Security code was OK\n");
 4537         return ERR_NONE;
 4538     case AT_Reply_Error:
 4539         smprintf(s, "Incorrect security code\n");
 4540         return ERR_SECURITYERROR;
 4541     case AT_Reply_CMSError:
 4542             return ATGEN_HandleCMSError(s);
 4543     case AT_Reply_CMEError:
 4544             return ATGEN_HandleCMEError(s);
 4545     default:
 4546         break;
 4547     }
 4548     return ERR_UNKNOWNRESPONSE;
 4549 }
 4550 
 4551 GSM_Error ATGEN_EnterSecurityCode(GSM_StateMachine *s, GSM_SecurityCode *Code)
 4552 {
 4553     GSM_Error error;
 4554     GSM_SecurityCodeType Status;
 4555     unsigned char req[GSM_SECURITY_CODE_LEN + 30] = {'\0'};
 4556     size_t len;
 4557 
 4558     if (Code->Type == SEC_Pin2 &&
 4559             s->Phone.Data.Priv.ATGEN.Manufacturer == AT_Siemens) {
 4560         len = sprintf(req, "AT+CPIN2=\"%s\"\r", Code->Code);
 4561     } else {
 4562         error = ATGEN_GetSecurityStatus(s, &Status);
 4563         if (error != ERR_NONE) {
 4564             return error;
 4565         }
 4566         if (Status != Code->Type) {
 4567             smprintf(s, "Phone is expecting different security code!\n");
 4568             return ERR_SECURITYERROR;
 4569         }
 4570         if (Code->Type == SEC_Puk) {
 4571             if (Code->NewPIN[0] == 0) {
 4572                 smprintf(s, "Need new PIN code to enter PUK!\n");
 4573                 return ERR_SECURITYERROR;
 4574             }
 4575             len = sprintf(req, "AT+CPIN=\"%s\",\"%s\"\r" , Code->Code, Code->NewPIN);
 4576         } else {
 4577             len = sprintf(req, "AT+CPIN=\"%s\"\r" , Code->Code);
 4578         }
 4579 
 4580     }
 4581     smprintf(s, "Entering security code\n");
 4582     error = ATGEN_WaitFor(s, req, len, 0x00, 20, ID_EnterSecurityCode);
 4583     return error;
 4584 }
 4585 
 4586 GSM_Error ATGEN_ReplyGetSecurityStatus(GSM_Protocol_Message *msg, GSM_StateMachine *s)
 4587 {
 4588     GSM_Error error;
 4589     GSM_Phone_ATGENData *Priv = &s->Phone.Data.Priv.ATGEN;
 4590     GSM_SecurityCodeType *Status = s->Phone.Data.SecurityStatus;
 4591     char status[100] = {'\0'};
 4592 
 4593     if (Priv->ReplyState != AT_Reply_OK) {
 4594         switch (s->Phone.Data.Priv.ATGEN.ReplyState) {
 4595         case AT_Reply_Error:
 4596             return ERR_NOTSUPPORTED;
 4597         case AT_Reply_CMSError:
 4598             return ATGEN_HandleCMSError(s);
 4599         case AT_Reply_CMEError:
 4600             return ATGEN_HandleCMEError(s);
 4601         default:
 4602             return ERR_UNKNOWNRESPONSE;
 4603         }
 4604     }
 4605 
 4606     error = ATGEN_ParseReply(s,
 4607         GetLineString(msg->Buffer, &Priv->Lines, 2),
 4608         "+CPIN: @r",
 4609         status,
 4610         sizeof(status));
 4611     if (error != ERR_NONE) {
 4612         /* Alcatel mangled reply */
 4613         if (strcmp(GetLineString(msg->Buffer, &Priv->Lines, 2), "+CPIN: ") == 0) {
 4614             *Status = SEC_None;
 4615             smprintf(s, "nothing to enter\n");
 4616             return ERR_NONE;
 4617         }
 4618         return error;
 4619     }
 4620 
 4621     smprintf(s, "Security status received - ");
 4622     if (strstr(status, "READY")) {
 4623         *Status = SEC_None;
 4624         smprintf(s, "nothing to enter\n");
 4625         return ERR_NONE;
 4626     }
 4627     if (strstr(status, "PH-SIM PIN")) {
 4628         *Status = SEC_Phone;
 4629         smprintf(s, "Phone code needed\n");
 4630         return ERR_NONE;
 4631     }
 4632     if (strstr(status, "PH-NET PIN")) {
 4633         *Status = SEC_Network;
 4634         smprintf(s, "Network code needed\n");
 4635         return ERR_NONE;
 4636     }
 4637     if (strstr(status, "PH_SIM PIN")) {
 4638         smprintf(s, "no SIM inside or other error\n");
 4639         return ERR_UNKNOWN;
 4640     }
 4641     if (strstr(status, "SIM PIN2")) {
 4642         *Status = SEC_Pin2;
 4643         smprintf(s, "waiting for PIN2\n");
 4644         return ERR_NONE;
 4645     }
 4646     if (strstr(status, "SIM PUK2")) {
 4647         *Status = SEC_Puk2;
 4648         smprintf(s, "waiting for PUK2\n");
 4649         return ERR_NONE;
 4650     }
 4651     if (strstr(status, "SIM PIN")) {
 4652         *Status = SEC_Pin;
 4653         smprintf(s, "waiting for PIN\n");
 4654         return ERR_NONE;
 4655     }
 4656     if (strstr(status, "SIM PUK")) {
 4657         *Status = SEC_Puk;
 4658         smprintf(s, "waiting for PUK\n");
 4659         return ERR_NONE;
 4660     }
 4661     smprintf(s, "unknown\n");
 4662     return ERR_UNKNOWNRESPONSE;
 4663 }
 4664 
 4665 GSM_Error ATGEN_GetSecurityStatus(GSM_StateMachine *s, GSM_SecurityCodeType *Status)
 4666 {
 4667     GSM_Error error;
 4668 
 4669     s->Phone.Data.SecurityStatus = Status;
 4670 
 4671     smprintf(s, "Getting security code status\n");
 4672     /* Please note, that A2D doesn't return OK on the end.
 4673      * Because of it we try to read another reply after reading
 4674      * status.
 4675      */
 4676     error = ATGEN_WaitForAutoLen(s, "AT+CPIN?\r", 0x00, 40, ID_GetSecurityStatus);
 4677 
 4678     /* Read the possible left over OK */
 4679     GSM_WaitForOnce(s, NULL, 0x00, 0x00, 4);
 4680     return error;
 4681 }
 4682 
 4683 GSM_Error ATGEN_ReplyAnswerCall(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 4684 {
 4685     GSM_Call call;
 4686 
 4687     switch(s->Phone.Data.Priv.ATGEN.ReplyState) {
 4688         case AT_Reply_OK:
 4689             smprintf(s, "Calls answered\n");
 4690             call.CallIDAvailable = FALSE;
 4691             call.Status = GSM_CALL_CallEstablished;
 4692 
 4693             if (s->User.IncomingCall) {
 4694                 s->User.IncomingCall(s, &call, s->User.IncomingCallUserData);
 4695             }
 4696             return ERR_NONE;
 4697         case AT_Reply_CMSError:
 4698             return ATGEN_HandleCMSError(s);
 4699         case AT_Reply_CMEError:
 4700             return ATGEN_HandleCMEError(s);
 4701         default:
 4702             return ERR_UNKNOWN;
 4703     }
 4704 }
 4705 
 4706 GSM_Error ATGEN_AnswerCall(GSM_StateMachine *s, int ID UNUSED, gboolean all)
 4707 {
 4708     GSM_Error error;
 4709 
 4710     if (all) {
 4711         smprintf(s, "Answering all calls\n");
 4712         error = ATGEN_WaitForAutoLen(s, "ATA\r", 0x00, 40, ID_AnswerCall);
 4713         return error;
 4714     }
 4715     return ERR_NOTSUPPORTED;
 4716 }
 4717 
 4718 GSM_Error ATGEN_ReplyCancelCall(GSM_Protocol_Message *msg UNUSED, GSM_StateMachine *s)
 4719 {
 4720   GSM_Phone_ATGENData   *Priv = &s->Phone.Data.Priv.ATGEN;
 4721     GSM_Call call;