www.pudn.com > TAPIOfControl.rar > AdGSM.pas


(***** BEGIN LICENSE BLOCK ***** 
 * Version: MPL 1.1 
 * 
 * The contents of this file are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.mozilla.org/MPL/ 
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the 
 * License. 
 * 
 * The Original Code is TurboPower Async Professional 
 * 
 * The Initial Developer of the Original Code is 
 * TurboPower Software 
 * 
 * Portions created by the Initial Developer are Copyright (C) 1991-2002 
 * the Initial Developer. All Rights Reserved. 
 * 
 * Contributor(s): 
 * 
 * ***** END LICENSE BLOCK ***** *) 
 
{*********************************************************} 
{*                    ADGSM.PAS 4.06                     *} 
{*********************************************************} 
{* TApdGSMPhone component                                *} 
{*********************************************************} 
 
(* 
The TApdGSMPhone component in APRO 4 implements SMS text messaging through the 
text-mode interface defined in the GSM Technical Specification 07.05, version 
5.1.0, dated December 1996.  There are several variations of this spec, used 
throughout Nokia, Siemens, Ericsson, etc models. 
We have tested the Nokia 8290 in-house, but the Nokia 7190, 8890, 6210 and 9110 
models should work as well.  Phones from other manufacturers will also work, as 
long as they implement the text-mode interface.  About 1/4 of the current phones 
are capable of being connected to a PC (through IR or serial cable), about 1/3 
of those are text-mode only, 1/3 are PDU mode only, and the other 1/3 support 
both text and PDU mode.  Some phones (such as the Nokia 5190) support SMS, but 
they use a proprietary protocol, which APRO does not support.  Note also that 
APRO can't communicate successfully through an FBUS cable (usually found with 
Nokia phones). 
To test your phone, connect the phone to your PC through the serial cable or 
IR device (consult your phone's documentation for details on how to connect). 
Enter "AT" into a terminal window to verify the connection is established 
(you should receive "OK" from the phone), then enter "AT+CMGF=?". The 
response should contain a "1", indicating that it supports text-mode.  If both 
of these tests pass, then your phone meets the basic requirements. 
 
Command and response sequences are defined by the GSMXxx array consts, we 
iterate from 0 to High, so you can remove certain commands from the sequence 
by simply deleting that command from the array. 
 
PDU mode was added after 4.05 and did not undergo the normal testing cycle. 
Changes specific to PDU mode are marked with {!!.PDU} 
Much of the information on PDU format came from http://www.dreamfabric.com/sms/ 
with ParseAPDUMessage parsing out the PDU messages for the listing of messages. 
BuildPDUMessage will build a PDU message before sending. 
*) 
 
{Global defines potentially affecting this unit} 
{$I AWDEFINE.INC} 
 
unit adgsm; 
interface 
 
uses 
  Windows, Messages, SysUtils, Classes, Controls, Forms, 
  OoMisc, AdPort, AdPacket, AdExcept; 
 
{$IFDEF AproBCB} 
{*$HPPEMIT '' } 
{*$HPPEMIT '#undef GetMessage' } 
{*$HPPEMIT '#undef SendMessage' } 
{*$HPPEMIT '' } 
{$ENDIF} 
{$IFDEF VER110} 
  { BCB3 needs this included here } 
  {$I AdExcept.inc} 
{$ENDIF} 
const 
  ApdGSMResponse = WM_USER + 100; 
 
type 
  TGSMStates = (gsNone, 
                gsConfig, 
                gsSendAll, 
                gsListAll, 
                gsSend, 
                gsSendFStore, 
                gsWrite, 
                gsDelete, 
                gsNofify); 
 
  TApdSMSStatus = (srUnread, 
                   srRead, 
                   ssUnsent, 
                   ssSent, 
                   ssAll, 
                   ssUnknown); 
 
  { GSM modes } 
  TGSMMode = (gmDetect, gmPDU, gmText);                                 {!!.PDU} 
  TGSMModeSet = set of TGSMMode;                                        {!!.PDU} 
 
  TApdCustomGSMPhone = class; 
 
  TApdGSMNextMessageEvent = procedure (Pager : TApdCustomGSMPhone; 
                          ErrorCode : Integer; var NextMessageReady : Boolean) 
                          of object; 
  TApdGSMNewMessageEvent = procedure (Pager : TApdCustomGSMPhone; 
                         FIndex : Integer; Message : string) of object; 
  TApdGSMMessageListEvent = procedure (Sender : TObject) of object; 
  TApdGSMCompleteEvent = procedure (Pager : TApdCustomGSMPhone; 
                                    State : TGSMStates; 
                                    ErrorCode : Integer) of object; 
 
 
  TApdSMSMessage = class(TObject) 
  private 
    FMessageIndex: Integer; 
    FAddress: string; 
    FMessage: string; 
    FName: string; 
    FStatus: TApdSMSStatus; 
    FTimeStampStr: string; 
    FTimeStamp: TDateTime; 
    { private declarations } 
  protected                                                                      
    { protected declarations } 
    function GetMessageAsPDU : string;                                  {!!.PDU} 
    procedure SetMessageAsPDU (v : string);                             {!!.PDU} 
  public 
    { public declarations } 
    property Address : string 
      read FAddress write FAddress; 
    property Message : string 
      read FMessage write FMessage; 
    property MessageAsPDU : string                                      {!!.PDU} 
             read GetMessageAsPDU write SetMessageAsPDU;                {!!.PDU} 
    property MessageIndex : Integer 
      read FMessageIndex write FMessageIndex; 
    property Name : string 
      read FName write FName; 
    property Status : TApdSMSStatus 
      read FStatus write FStatus; 
    property TimeStamp : TDateTime 
      read FTimeStamp write FTimeStamp; 
    property TimeStampStr : string 
      read FTimeStampStr write FTimeStampStr; 
  end; 
 
  TApdMessageStore = class(TStringList) 
  private 
    { private declarations } 
    FCapacity : Integer; 
    FGSMPhone : TApdCustomGSMPhone; 
    {$IFDEF AproBCB} 
    function GetSMSMessage(Index: Integer): TApdSMSMessage; 
    {$ELSE} 
    function GetMessage(Index: Integer): TApdSMSMessage; 
    {$ENDIF} 
    procedure SetMessage(Index: Integer; const Value : TApdSMSMessage); 
    procedure SetMSCapacity(const Value: Integer); 
  protected 
    { protected declarations } 
    JustClearStore : Boolean; 
    function GetCapacity : Integer; override; 
    procedure ClearStore; 
  public 
    { public declarations } 
    constructor Create(GSMPhone : TApdCustomGSMPhone); 
    function AddMessage(const Dest, Msg : string) : Integer; 
    procedure Clear; override; 
    procedure Delete(PhoneIndex: Integer); override; 
    {$IFDEF AproBCB} 
    property Messages[Index: Integer]: TApdSMSMessage 
             read GetSMSMessage write SetMessage; default; 
    {$ELSE} 
    property Messages[Index: Integer]: TApdSMSMessage 
             read GetMessage write SetMessage; default; 
    {$ENDIF} 
    property Capacity : Integer read FCapacity write SetMSCapacity; 
  end; 
 
  TApdCustomGSMPhone = class(TApdBaseComponent) 
  private 
    { Private declarations } 
 
    FOnNewMessage : TApdGSMNewMessageEvent; 
    FOnNextMessage : TApdGSMNextMessageEvent; 
    FOnMessageList : TApdGSMMessageListEvent; 
    FOnGSMComplete: TApdGSMCompleteEvent; 
 
    FComPort: TApdCustomComPort; 
    FNeedNewMessage : Integer;           // Flag to get a new message    {!!.04} 
    FRecNewMess : String;                                                {!!.06} 
    FConnected : Boolean;                // Flag for connection status 
    FErrorCode: Integer; 
    FGSMState: TGSMStates; 
    FGSMMode: TGSMMode;                                                 {!!.PDU} 
    FHandle: THandle; 
    FMessage : string;                   // Defines Message to be sent 
    FMessageStore: TApdMessageStore; 
    FNotifyOnNewMessage: Boolean; 
    FQueryModemOnly: Boolean;                                            {!!.06} 
    // True won't sychronize message store 
    FQuickConnect: Boolean; 
    // True won't give list of messages when first connected 
    FConfigList: Boolean;                                                {!!.02} 
 
    FSMSAddress: string;                 // Phone number 
    FSMSCenter: string;                  // Service Center 
    FTempWriteMess: string;              // Temp store when WriteToMemory 
    FPDUMode : Boolean;                  // PDU Mode                    {!!.PDU} 
 
    ResponsePacket : TApdDataPacket; 
    ErrorPacket : TApdDataPacket; 
    NotifyPacket : TApdDataPacket;                                       {!!.02} 
    TempSMSMessage : TApdSMSMessage; 
 
    procedure SetMessage(const Value: string); 
    procedure SetCenter(const Value: string); 
    procedure SetNotifyOnNewMessage(const Value: Boolean); 
    procedure SetGSMMode(const NewMode : TGSMMode);                     {!!.PDU} 
 
  protected 
    { Protected declarations } 
    CmdIndex : Integer; 
    ResponseStr : string; 
    NotifyStr : string;                                                  {!!.02} 
    FSupportedGSMModes : TGSMModeSet;                                   {!!.PDU} 
    procedure CheckPort; 
    procedure WndProc(var Message: TMessage); 
    procedure Notification(AComponent : TComponent; 
                           Operation: TOperation); override; 
    procedure ResponseStringPacket(Sender: TObject; Data : string); 
    procedure NotifyStringPacket(Sender: TObject; Data : string);        {!!.02} 
 
    procedure SetPDUMode (v : Boolean);                                 {!!.PDU} 
    function GetGSMMode : TGSMMode;                                     {!!.PDU} 
 
    procedure ErrorStringPacket(Sender: TObject; Data : string); 
    procedure DoFail(const Msg: string; const ErrCode: Integer); 
 
    { these methods manage the phone's message store } 
    procedure DeleteFromMemoryIndex(PhoneIndex : Integer); 
    property Handle : THandle read FHandle; 
    procedure SetState(NewState : TGSMStates); 
  public 
    { Public declarations } 
    constructor Create(AOwner : TComponent); override; 
    destructor Destroy; override; 
    {$IFDEF AproBCB} 
    procedure SendSMSMessage; 
    {$ELSE} 
    procedure SendMessage; 
    {$ENDIF} 
    procedure SendAllMessages; 
    procedure ListAllMessages; 
    procedure Connect; 
    procedure SendFromMemory(TheIndex : Integer); 
    procedure WriteToMemory(const Dest, Msg: string); 
    procedure ProcessResponse; 
    procedure Synchronize; 
    procedure QueryModem; 
 
    function StatusToStr(StatusString : TApdSMSStatus) : string; 
 
    property ComPort : TApdCustomComPort 
      read FComPort write FComPort; 
    property SMSAddress : string 
      read FSMSAddress write FSMSAddress; 
    property SMSMessage : string 
      read FMessage write SetMessage; 
    property SMSCenter : string 
      read FSMSCenter write SetCenter; 
    property NotifyOnNewMessage : Boolean 
             read FNotifyOnNewMessage 
             write SetNotifyOnNewMessage default False;                  {!!.02} 
    property MessageStore : TApdMessageStore 
      read FMessageStore write FMessageStore; 
    property QuickConnect : Boolean 
      read FQuickConnect write FQuickConnect default False; 
    property GSMMode : TGSMMode                                         {!!.PDU} 
      read GetGSMMode write SetGSMMode;                                 {!!.PDU} 
    { read only properties } 
    property SMSErrorCode : Integer 
      read FErrorCode; 
    property GSMState : TGSMStates 
      read FGSMState; 
    property OnNewMessage : TApdGSMNewMessageEvent 
             read FOnNewMessage write FOnNewMessage; 
    property OnNextMessage : TApdGSMNextMessageEvent 
             read FOnNextMessage write FOnNextMessage; 
    property OnMessageList : TApdGSMMessageListEvent 
             read FOnMessageList write FOnMessageList; 
    property OnGSMComplete : TApdGSMCompleteEvent 
      read FOnGSMComplete write FOnGSMComplete; 
  end; 
 
  TApdGSMPhone = class(TApdCustomGSMPhone) 
  public 
    constructor Create(Owner : TComponent); override; 
    destructor Destroy; override; 
 
  published 
    property ComPort; 
    property QuickConnect; 
    property GSMMode;                                                   {!!.PDU} 
    property SMSAddress; 
    property SMSMessage; 
    property SMSCenter; 
    property NotifyOnNewMessage; 
 
    {published events} 
    property OnNewMessage; 
    property OnNextMessage; 
    property OnMessageList; 
    property OnGSMComplete; 
     
  end; 
   
  function StringToPDU (v : string) : string;                           {!!.PDU} 
  function PDUToString (v : string) : string;                           {!!.PDU} 
 
implementation 
 
const 
  { +CMS ERROR:  =  Message Service Failure Result Code } 
  GSMConfigAvail : array[0..6] of string = 
              ('E0',                // Turn off echo 
               '+CMGF=?',           // Support Text,PDU mode?           {!!.PDU} 
               '+CMGF=',            // Set appropriate mode 
               '+CSMS=0',           // Select Message Service 
               //'+CPMS=?',         // Preferred Message Storage         {!!.06} 
 
               {!!.PDU}             // Moved from GSMSendMessageCommands 
               '+CNMI= 2,1,0,1,0',  // New message indication =2,1,0,1,0 
                                    // or = 2,,2,,0 
               '+CSMP=,167,0,0',    // variable 17 for status 32 for no status 
 
               //'+CSCS?',          // Set Character Set                 {!!.06} 
               '+CSDH=1');          // Show Text Code Parameters 
               //'+CSMP?');         // Get Default values for SMS-SUBMIT {!!.06} 
  GSMConfigResponse : array[0..6] of string = 
              ('OK',               // No Response Expected, just OK 
               '+CMGF: ',          // +CMGF:[20](0,1), 0 is PDU, 1 is Text 
               'OK',               // No Response Expected, just OK 
               '+CSMS: ',          // Response StartString expected 
               //'+CPMS: ',        // Response StartString expected 
               'OK',               // No Response Expected, just OK 
               'OK',               // No Response Expected, just OK 
               //'+CSCS: ', // Show default character set like "PCCP437" {!!.06} 
               'OK');              // No Response Expected, just OK 
               //'+CSMP: ');       // Response StartString expected      {!!.06} 
 
  GSMSendMessageCommands : array[0..3] of string = 
    { Some commands moved to GSMConfigAvail and look at QueryModem procedure} 
               (//'+CNMI?',          // Default query for new message indication 
                //'+CNMI= 2,1,0,1,0', // New message indication =2,1,0,1,0 
                                      // or = 2,,2,,0 
                //'+CSCB?',           // Types of CBMs to be received by the ME 
                //'+CSMP=,167,0,0',   // variable 17 for status 32 for no status 
                '', 
                '+CSCA=',             // Service Center if FSMSCenter not empty 
                '+CMGS=',             // Send Destination address 
                '');                  // Sending message text 
 
  GSMSendMessageResponse : array[0..3] of string = 
               (//'+CNMI: ',          // Response StartString expected 
                //'OK',               // No Response Expected, just OK 
                //'+CSCB: ',          // Response StartString expected 
                //'OK',               // No Response Expected, just OK 
                'OK',                 // No Response Expected, just OK 
                'OK',                 // No Response Expected, just OK 
                #13#10,               // Response from phone to Send Message 
                '+CMGS: ');           // Response StartString expected 
 
  GSMListAllMessagesCommands : array[0..0] of string = 
               ('+CMGL');           // List Messages on Phone 
  GSMListAllMessagesResponse : array[0..0] of string = 
               ('+CMGL: ');         // Response StartString expected 
 
  GSMSendMessFromStorage : array[0..0] of string = 
               ('+CMSS=');          // Send Message from Storage 
  GSMSendMessFromStorageResp : array[0..0] of string = 
               (#13#10'OK');        // Response StartString expected 
 
  GSMWriteToMemoryCommands : array[0..1] of string = 
               ('+CMGW=',           // Write Message to Memory 
                '');                // Writing message text 
  GSMWriteToMemoryResponse : array[0..1] of string = 
               (#13#10,             // Response StartString expected 
                '+CMGW: '); 
  GSMDeleteAMessageCommand : array[0..0] of string = 
               ('+CMGD=');          // Delete Message from Memory 
  GSMDeleteAMessageResponse : array[0..0] of string = 
               (#13#10'OK');        // No Response Expected, just OK 
 
{ TApdSMSMessage } 
 
function TApdSMSMessage.GetMessageAsPDU : string; 
begin 
  Result := StringToPDU (FMessage); 
end; 
 
function PDUToString (v : string) : string; 
var 
  I, InLen, OutLen, OutPos : Integer; 
  TempByte, PrevByte : Byte; 
begin 
  { Check for empty input } 
  if v = '' then Exit; 
 
  { Init variables } 
  PrevByte := 0; 
  OutPos := 1; 
 
  { Set length of output string } 
  InLen := Length(v); 
  Assert(InLen <= 140, 'Input string greater than 140 characters'); 
  OutLen := (InLen * 8) div 7; 
  SetLength(Result, OutLen); 
 
  { Encode output string } 
  for I := 1 to InLen do begin 
    TempByte := Byte(v[I]); 
    TempByte := TempByte and not ($FF shl (7-((I-1) mod 7))); 
    TempByte := TempByte shl ((I-1) mod 7); 
    TempByte := TempByte or PrevByte; 
    Result[OutPos] := AnsiChar(TempByte); 
    Inc(OutPos); 
 
    { Set PrevByte for next round (or directly put it to Result) } 
    PrevByte := Byte(v[I]); 
    PrevByte := PrevByte shr (7-((I-1) mod 7)); 
    if (I mod 7) = 0 then begin 
      Result[OutPos] := AnsiChar(PrevByte); 
      Inc(OutPos); 
      PrevByte := 0; 
    end; 
  end; 
  if Result[Length(Result)] = #0 then 
    Result := Copy(Result, 1, pred(Length(Result))); 
end; 
 
procedure TApdSMSMessage.SetMessageAsPDU (v : string); 
begin 
  FMessage := PDUToString (v); 
end; 
 
function StringToPDU (v : string) : string; 
var 
  I, InLen, OutLen, OutPos : Integer; 
  RoundUp : Boolean; 
  TempByte, NextByte : Byte; 
begin 
  { Check for empty input } 
  if v = '' then Exit; 
 
  { Init OutPos } 
  OutPos := 1; 
 
  { Set length of output string } 
  InLen := Length(v); 
  Assert(InLen <= 160, 'Input string greater than 160 characters'); 
  RoundUp := (InLen * 7 mod 8) <> 0; 
  OutLen := InLen * 7 div 8; 
  if RoundUp then Inc(OutLen); 
  SetLength(Result, OutLen); 
 
  { Encode output string } 
  for I := 1 to InLen do begin 
    TempByte := Byte(v[I]); 
    Assert((TempByte and $80) = 0, 'Input string contains 8-bit data'); 
    if (I < InLen) then 
      NextByte := Byte(v[I+1]) 
    else 
      NextByte := 0; 
    TempByte := TempByte shr ((I-1) mod 8); 
    NextByte := NextByte shl (8 - ((I) mod 8)); 
    TempByte := TempByte or NextByte; 
    Result[OutPos] := AnsiChar(TempByte); 
    if I mod 8 <> 0 then 
      Inc(OutPos); 
  end; 
end; 
 
{ TApdCustomGSMPhone } 
 
{ opens the port, issues configuration commands } 
{Generates the OnMessageList (+CMGL) event when complete if not QuickConnect} 
procedure TApdCustomGSMPhone.Connect; 
var 
  Res : Integer; 
  ET : EventTimer; 
begin 
  if FGSMState > gsNone then begin 
    DoFail(secSMSBusy,-8100); 
    Exit; 
  end; 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    CheckPort; 
  end; 
  if FQueryModemOnly then                                                {!!.06} 
    exit;                                                                {!!.06} 
  if FNotifyOnNewMessage and not Assigned(NotifyPacket) then begin{!!.02}{!!.05} 
    NotifyPacket := TApdDataPacket.Create(Self);                         {!!.05} 
    NotifyPacket.OnStringPacket := NotifyStringPacket;                   {!!.05} 
    NotifyPacket.StartCond := scString;                                  {!!.05} 
    NotifyPacket.StartString := '+CMTI:';                                {!!.05} 
    NotifyPacket.EndCond := [ecString];                                  {!!.05} 
    NotifyPacket.EndString := #13;                                       {!!.05} 
    NotifyPacket.IncludeStrings := False;                                {!!.05} 
    NotifyPacket.ComPort := FComPort;                                    {!!.05} 
    NotifyPacket.Enabled := True;                                        {!!.02} 
    NotifyPacket.AutoEnable := True;                                     {!!.02} 
  end;                                                                   {!!.02} 
  FConfigList := True;                                                   {!!.02} 
  CmdIndex := 0; 
  ResponsePacket.StartString := GSMConfigResponse[CmdIndex]; 
  ResponsePacket.EndString := #13; 
  ResponsePacket.ComPort := FComPort;                                    {!!.05} 
  ResponsePacket.Enabled := True; 
  //DelayTicks(4, True);                                                 {!!.04} 
  SetState(gsConfig); 
  FComPort.Output := 'AT' + GSMConfigAvail[CmdIndex] + #13; 
  NewTimer(ET, 1080); // 60 second timer 
  repeat 
    Res := SafeYield; 
  until (FGSMState = gsNone) or (FGSMState = gsListAll) or (Res = wm_Quit) 
        or TimerExpired(ET); 
  if TimerExpired(ET) then begin 
    DoFail(secSMSTimedOut,-8101); 
    Exit; 
  end; 
end; 
 
constructor TApdCustomGSMPhone.Create(AOwner: TComponent); 
begin 
  inherited; 
  { Some of the initialization for the packets were moved to Checkport to make 
    sure there was a comport - Marked there with "!!.05" } 
 
  FConnected := False; 
  FNeedNewMessage := 0;                                                  {!!.04} 
  FRecNewMess := '';                                                     {!!.06} 
  FQueryModemOnly := False;                                              {!!.06} 
  FHandle := AllocateHWnd(WndProc); 
  FMessageStore := TApdMessageStore.Create(Self); 
  FComPort := SearchComPort(Owner); 
end; 
 
destructor TApdCustomGSMPhone.Destroy; 
begin 
  FConnected := False; 
  ResponsePacket.Free; 
  ErrorPacket.Free; 
  NotifyPacket.Free;                                                     {!!.02} 
  FMessageStore.Clear; 
  FMessageStore.Free; 
  DeallocateHwnd(FHandle); 
  inherited; 
end; 
 
{!!.PDU} 
procedure TApdCustomGSMPhone.SetPDUMode (v : Boolean); 
begin 
  if v <> FPDUMode then 
    FPDUMode := v; 
end; 
 
{ +CMS ERROR: message Service Failure Result Code } 
procedure TApdCustomGSMPhone.ErrorStringPacket(Sender: TObject; 
  Data: string); 
var 
  ErrorCode : Integer; 
  ErrorMessage : string; 
  Temp : string; 
begin 
  //Display Message Service Failure Result Code 
  Temp := Data; 
  if Temp <> '' then begin                                               {!!.06} 
    Temp := Copy(Data, Pos(' ', Data), Length(Data)); 
    ErrorCode := -8000 - StrToInt(Temp); 
    ErrorMessage := 'Phone error ';               {!!.06} 
  end else begin                                                         {!!.06} 
    ErrorCode := -8500;                                                  {!!.06} 
    ErrorMessage := 'Unknown Error';                                     {!!.06} 
  end;                                                                   {!!.06} 
  DoFail(ErrorMessage, ErrorCode);                                       {!!.06} 
  //raise Exception.Create('Exception ' + IntToStr(ErrorCode) + ' ' +    {!!.06} 
                          //ErrorMessage);                               {!!.06} 
end; 
 
procedure TApdCustomGSMPhone.ResponseStringPacket(Sender: TObject; 
  Data: string); 
begin 
  // Handle data from packet -State Machine- 
  ResponseStr := Data; 
  PostMessage(FHandle, ApdGSMResponse, 0,0); 
end; 
 
procedure TApdCustomGSMPhone.NotifyStringPacket(Sender: TObject;         {!!.02} 
  Data: string);                                                         {!!.02} 
var                                                                      {!!.02} 
  MessageIndex : Integer;                                                {!!.02} 
begin                                                                    {!!.02} 
  NotifyStr := Data;                                                     {!!.02} 
  if NotifyStr <> '' then begin                                          {!!.02} 
    if assigned(FOnNewMessage) then begin                                {!!.02} 
      NotifyStr := Copy(NotifyStr, 
        Pos('",', NotifyStr) + 2, Length(NotifyStr));                    {!!.02} 
      MessageIndex := StrToInt(NotifyStr);                               {!!.02} 
      //if Assigned(FOnNewMessage) then begin                            {!!.02} 
      { if OnNewMessage event then PostMessage to Synchronize/ListAllMessages } 
      FNeedNewMessage := MessageIndex;                                   {!!.04} 
      PostMessage(FHandle, ApdGSMResponse, 1,0);                         {!!.04} 
//      FOnNewMessage(Self, MessageIndex, TheMessage);           {!!.02} {!!.04} 
      //end;                                                     {!!.04} {!!.06} 
    end;                                                                 {!!.02} 
  end;                                                                   {!!.02} 
end;                                                                     {!!.02} 
 
{ issues +CMGL to list all messages with a Status of ssUnsend} 
procedure TApdCustomGSMPhone.ListAllMessages; 
var 
  Res : Integer; 
  ET : EventTimer; 
begin 
  if FQuickConnect then                                                  {!!.06} 
    exit;                                                                {!!.06} 
  if FGSMState > gsNone then begin 
    DoFail(secSMSBusy,-8100); 
    Exit; 
  end; 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    DoFail(secBadOperation,-8302); 
    Exit; 
  end; 
  CmdIndex := 0; 
  SetState(gsListAll); 
  FMessageStore.ClearStore; 
  FComPort.FlushInBuffer; 
  ResponsePacket.StartCond := scAnyData;                                 {!!.02} 
  //ResponsePacket.StartString := #13#10;                                {!!.02} 
  //ResponsePacket.StartString := '+CMGL: ';                             {!!.02} 
  ResponsePacket.EndString := #13#10'OK'#13#10;                          {!!.02} 
  ResponsePacket.IncludeStrings := True;                                 {!!.04} 
  ResponsePacket.Enabled := True; 
  //DelayTicks(4, True);                                                 {!!.04} 
  FComPort.Output := 'AT' + GSMListAllMessagesCommands[CmdIndex] + #13; 
  NewTimer(ET, 1080); // 60 second timer 
  DelayTicks(1, True);                                                   {!!.06} 
  repeat 
    Res := SafeYield; 
  until (FGSMState = gsNone) or (Res = wm_Quit) or TimerExpired(ET); 
  if TimerExpired(ET) then begin 
    DoFail(secSMSTimedOut,-8101); 
    Exit; 
  end; 
end; 
 
{ issues +CMSS to send message from storage indexed by Index } 
procedure TApdCustomGSMPhone.SendFromMemory(TheIndex: Integer); 
var 
  Res : Integer; 
  ET : EventTimer; 
begin 
  if FGSMState > gsNone then begin 
    DoFail(secSMSBusy,-8100); 
    Exit; 
  end; 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    FQuickConnect := False; 
    Connect; 
  end; 
  CmdIndex := 0; 
  SetState(gsSendFStore); 
  ResponsePacket.StartString := '+CMSS: '; 
  ResponsePacket.EndString := #13#10'OK'; 
  ResponsePacket.ComPort := FComPort; 
  ResponsePacket.Enabled := True; 
  //DelayTicks(4, True);                                                 {!!.04} 
  FComPort.Output := 'AT' + GSMSendMessFromStorage[CmdIndex] + 
                     IntToStr(TheIndex) + #13; 
  NewTimer(ET, 1080); // 60 second timer 
  repeat 
    Res := SafeYield; 
  until (FGSMState = gsNone) or (Res = wm_Quit) or TimerExpired(ET); 
  if TimerExpired(ET) then begin 
    DoFail(secSMSTimedOut,-8101); 
    Exit; 
  end; 
end; 
 
{ issues +CMGS to send message without placing the message in memory } 
{$IFDEF AproBCB} 
procedure TApdCustomGSMPhone.SendSMSMessage; 
{$ELSE} 
procedure TApdCustomGSMPhone.SendMessage; 
{$ENDIF} 
var 
  Res : Integer; 
  ET : EventTimer; 
begin 
  if FGSMState > gsNone then begin 
    DoFail(secSMSBusy,-8100); 
    Exit; 
  end; 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    Connect; 
  end; 
  //if FNotifyOnNewMessage then                                   {!!.01}{!!.06} 
  //  CmdIndex := 0                                               {!!.01}{!!.06} 
  //else                                                          {!!.01}{!!.06} 
    //CmdIndex := 1; // Handled in the gsConfig                          {!!.06} 
  CmdIndex := 0;                                                         {!!.06} 
  ResponsePacket.EndString := #13;                                       {!!.01} 
  ResponsePacket.StartString := GSMSendMessageResponse[CmdIndex]; 
  ResponsePacket.Enabled := True; 
  //DelayTicks(4, True);                                                 {!!.04} 
  SetState(gsSend); 
  FComPort.Output := 'AT' + GSMSendMessageCommands[CmdIndex] + #13; 
  NewTimer(ET, 1080); // 60 second timer 
  repeat 
    Res := SafeYield; 
  until (FGSMState = gsNone) or (Res = wm_Quit) or TimerExpired(ET); 
  if TimerExpired(ET) then begin 
    DoFail(secSMSTimedOut,-8101); 
    Exit; 
  end; 
end; 
 
procedure TApdCustomGSMPhone.SetMessage(const Value: string); 
begin 
  if Length(Value) > 160 then begin 
    DoFail(secSMSTooLong,-8102); 
    Exit; 
  end; 
  FMessage := Value; 
end; 
 
{ enables/disables new message notification (+CNMI), new messages provided 
  through OnNewMessage event } 
procedure TApdCustomGSMPhone.SetNotifyOnNewMessage(const Value: Boolean); 
begin 
  FNotifyOnNewMessage := Value; 
     // Return True for then +CMTI for New Message Indications to TE 
end; 
 
{ TApdMessageStore } 
function TApdMessageStore.AddMessage(const Dest, Msg: string): Integer; 
var 
  SMS : TApdSMSMessage; 
begin 
  SMS := nil; 
  try 
    SMS := TApdSMSMessage.Create; 
    SMS.Message := Msg; 
    SMS.Address := Dest; 
    SMS.TimeStampStr := FormatDateTime('yy/mm/dd/hh:mm:ss', Now); 
    SMS.Status := ssUnsent; 
 
    Result := AddObject(SMS.TimeStampStr, SMS); 
  finally 
    SMS.Free; 
  end; 
end; 
 
{ Clear all Messages } 
procedure TApdMessageStore.Clear; 
begin 
 inherited;exit; 
  // Clear All messages 
  while Count > 0 do begin 
    if Objects[Count - 1] <> nil then begin 
      TApdSMSMessage(Objects[Count - 1]).Free; 
      Objects[Count - 1] := nil; 
    end; 
    Delete(Count - 1); 
  end; 
end; 
 
{ Just clears our message store - not the phone } 
procedure TApdMessageStore.ClearStore; 
begin 
  JustClearStore := True; 
  Clear; 
  JustClearStore := False; 
end; 
 
{ Create Message Store } 
constructor TApdMessageStore.Create(GSMPhone : TApdCustomGSMPhone); 
begin 
  FGSMPhone := GSMPhone; 
  JustClearStore := False; 
end; 
 
{ issues +CMGD to Delete Message at Index } 
procedure TApdMessageStore.Delete(PhoneIndex: Integer); 
var 
  I : Integer; 
begin 
  if (FGSMPhone.FConnected) and not(JustClearStore) then 
    FGSMPhone.DeleteFromMemoryIndex(PhoneIndex) 
  else begin 
    for I := 0 to pred(Count) do 
      if Messages[I].MessageIndex = PhoneIndex then 
        Break; 
    if I < Count then begin 
      if Objects[I] <> nil then begin 
        TApdSMSMessage(Objects[I]).Free; 
        Objects[I] := nil; 
      end; 
      inherited Delete(I); 
    end; 
  end; 
end; 
 
{ Return the phone's message store capacity } 
function TApdMessageStore.GetCapacity: Integer; 
begin 
  result := FCapacity; 
end; 
 
{ Get message number of Index } 
{$IFDEF AproBCB} 
function TApdMessageStore.GetSMSMessage(Index: Integer): TApdSMSMessage; 
{$ELSE} 
function TApdMessageStore.GetMessage(Index: Integer): TApdSMSMessage; 
{$ENDIF} 
begin 
  Result := TApdSMSMessage(Objects[Index]); 
end; 
 
{ issues + to send all messages with a Status of ssUnsent } 
procedure TApdCustomGSMPhone.SendAllMessages; 
var 
  Res : Integer; 
  HighIndex: Integer; 
  I: Integer; 
begin 
  if FGSMState > gsNone then begin 
    DoFail(secSMSBusy,-8100); 
    Exit; 
  end; 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    FQuickConnect := False; 
    Connect; 
    repeat 
      Res := SafeYield; 
    until (FGSMState = gsNone) or (Res = wm_Quit); 
  end; 
  CmdIndex := 0; 
  { tell the GSMPhone to send all messages in it's store } 
  HighIndex := FMessageStore.GetCapacity; 
  for I := 0 to HighIndex do begin 
    SendFromMemory(I); 
  end; 
end; 
 
procedure TApdCustomGSMPhone.ProcessResponse; 
 
  { Convert String to Integer } 
  function GSMStrToInt(Str : string) : Integer; 
  begin 
    while Pos(' ', Str) > 0 do 
      Str[Pos(' ', Str)] := '0'; 
    Result := StrToInt(Str); 
  end; 
 
var 
  S : string; 
  STemp : string; 
  MsgRdy : Boolean; 
  PDULength : Integer;                                                  {!!.PDU} 
 
  { Get next field in message } 
  function GetNextField( aDelimiter: String ): String; 
  var 
    aDelimiterPosition: Integer; 
  begin 
  {To retrieve the text from position 1 until the occurance of aDelimiter 
   while advancing the pointer in S so that we can just call this function 
   again and again. } 
    if S[1] = '"' then begin 
      aDelimiter := '"' + aDelimiter; 
    end; 
    aDelimiterPosition := Pos( aDelimiter, S ); 
    Result := Copy( S, 1, aDelimiterPosition - 1 ); 
 
    if ( Result <> '' ) and ( Result[ 1 ] = '"' ) then 
      Result := Copy( Result, 2, Length( Result ) ); 
    if ( Result <> '' ) and ( Result[ Length( Result ) ] = '"' )then 
      Result := Copy( Result, 1, Length( Result ) - 1 ); 
 
    S := Copy( S, aDelimiterPosition + Length( aDelimiter ), Length( S )); 
  end; 
 
  { Build a PDU message !!.PDU} 
  function BuildPDUMessage : string; 
  var 
    TheLength, I: Integer; 
    TheNextOctet, S: string; 
    T: ShortString; 
  begin 
    TheLength := Length(FSMSCenter); 
    if Odd(TheLength) then 
      TheLength := TheLength + 1; 
    if FSMSCenter = '' then 
      // No SMSCenter needed 
      FTempWriteMess := '00' 
    else begin 
      // Add the length of SMSCenter in octets 
      FTempWriteMess := '0' + IntToStr((TheLength Div 2) + 1); 
      if Length(FSMSCenter) > 10 then begin 
        FTempWriteMess := FTempWriteMess + '91'; // International format 
      end else begin 
        FTempWriteMess := FTempWriteMess + '81'; // Let phone handle format 
      end; 
      I := 1; 
      // Add SMSCenter to PDU message 
      while I < Length(FSMSCenter) do begin 
        TheNextOctet := FSMSCenter[I+1] + FSMSCenter[I]; 
        FTempWriteMess := FTempWriteMess + TheNextOctet; 
        I := I + 2; 
      end; 
      if Odd(Length(FSMSCenter)) then 
        FTempWriteMess := FTempWriteMess + 'F' + FSMSCenter[Length(FSMSCenter)]; 
    end; 
    //FirstOctet 
    FTempWriteMess := FTempWriteMess + '11'; 
    // Lets the phone set the message reference number itself 
    FTempWriteMess := FTempWriteMess + '00'; 
    TheLength := Length(FSMSAddress); 
    S := Format ('%02.2x', [TheLength]); 
    FTempWriteMess := FTempWriteMess + S; 
    // Add SMSAddress to PDU message 
    if Length(FSMSAddress) > 10 then 
      FTempWriteMess := FTempWriteMess + '91' // International format 
    else 
      FTempWriteMess := FTempWriteMess + '81'; // Let phone handle format 
    I := 1; 
    while I < Length(FSMSAddress) do begin 
      TheNextOctet := FSMSAddress[I+1] + FSMSAddress[I]; 
      FTempWriteMess := FTempWriteMess + TheNextOctet; 
      I := I + 2; 
    end; 
    if Odd(Length(FSMSAddress)) then 
      FTempWriteMess := FTempWriteMess + 'F' + FSMSAddress[I]; 
    // Protocol identifier 
    FTempWriteMess := FTempWriteMess + '00'; 
    // Datacoding scheme 
    FTempWriteMess := FTempWriteMess + '00'; 
    // Optional validity period. 
    FTempWriteMess := FTempWriteMess + 'AA'; //"AA" means 4 days. 
 
    // Length of SMS message converted to HEX 
    TheLength := Length(FMessage); 
    S := Format ('%02.2x', [TheLength]); 
    FTempWriteMess := FTempWriteMess + S; 
    T := ''; 
    // Add SMSMessage after transformation to PDU string 
    S := StringToPDU(FMessage); 
    for I := 1 to Length(S) do begin  
        T := T + IntToHex(Byte(S[I]), 2); 
    end; 
    FTempWriteMess := FTempWriteMess + T; 
    PDULength := Length(FTempWriteMess) DIV 2 - 1; 
  end; 
 
  { Parse PDU message } {!!.PDU} 
  function ParseAPDUMessage : Boolean; 
  var 
    St, TempS, TempStaID, U: String; 
    T, TheMessage: ShortString; 
    NumLength, I, TempI, MessageLength : Integer; 
  begin 
    // Make sure we are not at the end of the last message 
    if Pos (#13#10, S) = 1 then 
      GetNextField (#13#10); 
    // Make sure we are at the beginning of a message 
    if Pos('+CMGL:', S) = 1 then begin 
      // Build the SMS message while reading 
      TempSMSMessage := TApdSMSMessage.Create; 
      // format is +CMGL: Index, status, address, address name, timestamp 
      S := Copy(S, Pos(':', S) + 1, Length(S)); 
      // extract the index number 
      TempSMSMessage.FMessageIndex := GSMStrToInt(GetNextField(',')); 
      //Let the phone handle the status 
      //TempSMSMessage.FStatus := GSMStrToInt(GetNextField(',')); 
      GetNextField(#13#10); 
      // Assign PDU stuff to TempS 
      TempS := GetNextField(#13#10); 
      NumLength := StrToInt(Copy(TempS, 0, 2)); 
      I := 0; 
      //NumLength is length of center number, in octets 
      if NumLength > 0 then begin 
        // Next two digits is Type-of-Address octet 
        I := StrToInt(Copy(TempS, 3, 2)); 
        // I is the Type-of-Address octet 
        case I of 
          91 : begin 
 
          end; 
          81 : begin 
 
          end; 
        end; // End Case 
        // NumLength = length of center phone number 
        TempStaID := ''; 
        I := 5; 
        while I < (NumLength*2 + 3) do begin 
          if TempS[I] = 'F' then 
            TempStaID := TempStaID + TempS[I+1] 
          else 
            TempStaID := TempStaID + TempS[I+1] + TempS[I]; 
          I := I + 2; 
        end; 
        // TempStaID is the Center Address at this point 
      end else begin 
        STemp := Copy(STemp, 3, Length(STemp)); 
      end; 
      // TempI = octet is First Octet of SMS-Deliver PDU 
      TempI := StrToInt(Copy(TempS, I, 2)); 
      // Get next octet 
      TempS := Copy(TempS, I+2, Length(TempS)); 
      St := Copy(TempS, 0, 2); 
      case TempI of 
        4 : begin // This PDU is a SMS delivery 
 
        end; 
        11: begin // Message ready to send 
          if St = '00' then begin 
          // The "00" lets the phone set the message reference number. 
            TempS := Copy(TempS, 3, Length(TempS)); 
            St := Copy(TempS, 0, 2); 
          end; 
        end; 
        24: begin // Status report indication returned 
 
        end; 
      end; // End Case 
      //Address-Length. Length of the sender number (0B hex = 11 dec) 
      NumLength := (StrToInt('$' + St)); 
      // Next two digits is Type-of-Address octet 
      I := StrToInt('$' + Copy(TempS, 3, 2)); 
      case I of 
        133 : begin 
          // 85 in Hex = Voice mail ? 
        end; 
        145 : begin 
          // 91 in Hex 
        end; 
        129 : begin 
          // 81 in Hex 
        end; 
        200 : begin 
          // C8 in Hex 
        end; 
      end; // End Case 
      // NumLength = Length of phone number 
      TempStaID := ''; 
      if Odd(NumLength) then 
        NumLength := NumLength + 1; 
      // Change NumLength to Octets 
      NumLength := NumLength Div 2; 
      I := 5; 
      while I < (NumLength*2 + 5) do begin 
        if TempS[I] = 'F' then 
          TempStaID := TempStaID + TempS[I+1] 
        else 
          TempStaID := TempStaID + TempS[I+1] + TempS[I]; 
        I := I + 2; 
      end; 
      // TempStaID is the StationID at this point 
      TempSMSMessage.FAddress := TempStaID; 
      // Protocol identifier of "00" and Data coding scheme of "00" 
      // are the 2 octets or the 4 added to I, below  
      I := I + 4; // start of time stamp 
      St := ''; 
      if TempS[I] = 'A' then begin 
        // No time stamp - not sent yet 
        TempI := I + 2; 
      end else begin 
        St := TempS[I+1]  + TempS[I]    + '/' +    // Year 
              TempS[I+3]  + TempS[I+2]  + '/' +    // Month 
              TempS[I+5]  + TempS[I+4]  + ',' +    // Day 
              TempS[I+7]  + TempS[I+6]  + ':' +    // Hours 
              TempS[I+9]  + TempS[I+8]  + ':' +    // Minutes 
              TempS[I+11] + TempS[I+10] + '+' +    // Seconds 
              TempS[I+13] + TempS[I+12];           // Time Zone 
        // 14 is length of time stamp - Keeping track where we are in TempS 
        TempI := I + 14; 
        TempSMSMessage.FTimeStampStr := St; 
      end; 
      // Get next octet 
      St := TempS[TempI] + TempS[TempI + 1]; 
      // MessageLength 
      MessageLength := StrToInt('$' + St); 
      // U is the message 
      U := Copy(TempS,TempI+2,Length(TempS)); 
      // NumLength is the length of the message 
      NumLength := Length(U); 
      // Set pointer to start of message 
      I := 1; 
      T := ''; 
      while I < NumLength do begin 
        St := '$' + U[I] + U[I+1]; 
        TempI := StrToInt(St); 
        T := T + char(TempI); 
        I := I + 2; 
      end; 
      // Change message to string form 
      TheMessage := PDUToString(T); 
      if MessageLength = Length(TheMessage) then 
        // Store message 
        TempSMSMessage.FMessage := TheMessage; 
    end; // else our PDU should handle longer messages 
    if TempSMSMessage.FMessageIndex = FNeedNewMessage then 
    // if new message notify wanted, then assign it 
      FRecNewMess := TempSMSMessage.Message; 
    MessageStore.AddObject(TempSMSMessage.TimeStampStr, TempSMSMessage); 
    Result := Pos('+CMGL:', S) > 0; 
  end; 
 
  { Parse a message } 
  function ParseAMessage : Boolean; 
  begin 
    if Pos (#13#10, S) = 1 then 
      GetNextField (#13#10); 
    if Pos('+CMGL:', S) = 1 then begin 
      // first line of the message 
      // format is +CMGL: Index, status, address, address name, timestamp 
      TempSMSMessage := TApdSMSMessage.Create; 
      S := Copy(S, Pos(':', S) + 1, Length(S)); 
      // extract the index number 
      TempSMSMessage.FMessageIndex := GSMStrToInt(GetNextField(',')); 
      // extract the status 
      STemp := GetNextField( ',' ); 
      if STemp = 'STO UNSENT' then TempSMSMessage.FStatus := ssUnsent 
      else if STemp = 'STO SENT' then TempSMSMessage.FStatus := ssSent 
      else if STemp = 'ALL' then TempSMSMessage.FStatus := ssAll 
      else if STemp = 'REC UNREAD' then TempSMSMessage.FStatus := srUnread 
      else if STemp = 'REC READ' then TempSMSMessage.FStatus := srRead 
      else TempSMSMessage.FStatus := ssUnknown;                          {!!.01} 
 
      // Read the address field with quotes (if any) 
      TempSMSMessage.FAddress := GetNextField( ',' ); 
      // Name (??) field  followed by , 
      TempSMSMessage.FName := GetNextField( ',' ); 
      // DateTime Field followed by , 
      TempSMSMessage.FTimeStampStr := GetNextField( ',' ); 
      if TempSMSMessage.FTimeStampStr = '' then 
        TempSMSMessage.FTimeStampStr := ''; 
      // Message Type   followed by , 
      STemp := GetNextField( ',' ); 
      // Message Length followed by #13#10 
      STemp := GetNextField( #13#10 ); 
      // Message        followed by #13#10 
      TempSMSMessage.Message := GetNextField( #13#10 ); 
    end else begin 
      // Message more than 1 line??? 
      TempSMSMessage.Message := TempSMSMessage.Message + #13#10 + 
                      GetNextField( #13#10 );   
    end; 
    if TempSMSMessage.FMessageIndex = FNeedNewMessage then               {!!.06} 
        FRecNewMess := TempSMSMessage.Message;                           {!!.06} 
    MessageStore.AddObject(TempSMSMessage.TimeStampStr, TempSMSMessage); 
    Result := Pos('+CMGL:', S) > 0;                                      {!!.04} 
  end; 
 
begin 
  //State machine response handle 
  case FGSMState of 
 
    gsConfig: begin 
                S := ResponseStr;                                       {!!.PDU} 
                inc(CmdIndex); 
                STemp := '';                                            {!!.PDU} 
                if CmdIndex > High(GSMConfigAvail) then begin 
                    // generate the OnComplete event, we're done configuring 
                  ResponseStr := '';                                     {!!.02} 
                  if not FQuickConnect then begin                        {!!.02} 
                    // State set to gsNone in PostMessage below 
                    //FGSMState := gsNone;                        {!!.02}{!!.06} 
                    // PostMessage calls ListAllMessages 
                    PostMessage(FHandle, ApdGSMResponse, 1,0);           {!!.06} 
                    //ListAllMessages;                            {!!.02}{!!.06} 
                  end;                                                   {!!.02} 
                  FGSMState := gsConfig;                                 {!!.02} 
                  if Assigned(FOnGSMComplete) then                       {!!.02} 
                    FOnGSMComplete(Self, FGSMState, FErrorCode);         {!!.02} 
                  FGSMState := gsNone; 
                  FConfigList := False;                                  {!!.02} 
                  ResponseStr := ''; 
                end else begin 
                  // send the next command 
                  ResponsePacket.StartString := GSMConfigResponse[CmdIndex]; 
                  ResponsePacket.EndString := #13; 
                  ResponsePacket.Enabled := True; 
                  //DelayTicks(4, True);                                 {!!.04} 
                  STemp := GSMConfigAvail[CmdIndex];                     {!!.06} 
                  // Detect, PDU, or Text 
                  if STemp = '+CMGF=' then begin                        {!!.PDU} 
                    STemp := Copy(S, Pos('(',S)+1, Length(S));          {!!.PDU} 
                    if Length(STemp) < 3 then begin                     {!!.PDU} 
                      if (FGSMMode = gmPDU) and (STemp[1] = '0') then   {!!.PDU} 
                        SetPDUMode(True)                                {!!.PDU} 
                      else                                              {!!.PDU} 
                        if (STemp[1] = '1') then                        {!!.PDU} 
                          SetPDUMode(False);                            {!!.PDU} 
                    end else begin                                      {!!.PDU} 
                      if (FGSMMode = gmPDU) and (STemp[3] = '1') then   {!!.PDU} 
                        SetPDUMode(True)                                {!!.PDU} 
                      else                                              {!!.PDU} 
                        SetPDUMode(False);                              {!!.PDU} 
                    end;                                                {!!.PDU} 
                    STemp := GSMConfigAvail[CmdIndex];                  {!!.PDU} 
                  end;                                                  {!!.PDU} 
                  if STemp = '+CMGF=' then begin                        {!!.PDU} 
                    if FPDUMode then                                    {!!.PDU} 
                      STemp := STemp + '0'                              {!!.PDU} 
                    else                                                {!!.PDU} 
                      STemp := STemp + '1';                             {!!.PDU} 
                    if QuickConnect then                                {!!.PDU} 
                      CmdIndex := High(GSMConfigAvail);                 {!!.PDU} 
                  end;                                                  {!!.PDU} 
                  if Copy(STemp, 1, 6) = '+CSMP=' then begin             {!!.06} 
                    if NotifyOnNewMessage then                           {!!.06} 
                      STemp := '+CSMP= 32' +                             {!!.06} 
                                    Copy(STemp, 7, Length(STemp))        {!!.06} 
                    else                                                 {!!.06} 
                      STemp := '+CSMP= 17' +                             {!!.06} 
                                    Copy(STemp, 7, Length(STemp));       {!!.06} 
                  end;                                                   {!!.06} 
                  FComPort.Output := 'AT' + STemp + #13; 
                end; 
              end; 
 
    gsSend: begin 
              inc(CmdIndex); 
              if CmdIndex > High(GSMSendMessageResponse) then begin 
                // Sent message - see if another message is ready 
                if Assigned(FOnNextMessage) then begin 
                  MsgRdy := False; 
                  FOnNextMessage(Self, FErrorCode, MsgRdy); 
                  if MsgRdy then begin 
                    { Will inc(CmdIndex) and start with the CSCA setting } 
                    CmdIndex := 0; // start sending over                 {!!.06} 
                    PostMessage(FHandle, ApdGSMResponse, 2,0);           {!!.06} 
                  end; 
                end; 
                // generate the OnComplete event, we're done 
                if Assigned(FOnGSMComplete) then begin 
                  FOnGSMComplete(Self, FGSMState, FErrorCode); 
                end; 
                FGSMState := gsNone; 
                ResponseStr := '';                                       {!!.02} 
              end else begin 
 
                if CmdIndex = High(GSMSendMessageCommands) - 1 then begin 
                  // send the next command 
                  ResponsePacket.StartString := 
                      GSMSendMessageResponse[CmdIndex]; 
                  ResponsePacket.EndString := '>'#32; 
                  ResponsePacket.Enabled := True; 
                  if FPDUMode then begin                                {!!.PDU} 
                    BuildPDUMessage;                                    {!!.PDU} 
                    STemp := IntToStr(PDULength);                       {!!.PDU} 
                  end else begin                                        {!!.PDU} 
                    //DelayTicks(4, True);                               {!!.04} 
                    STemp := SMSAddress; 
                    if STemp = '' then begin                             {!!.02} 
                      DoFail(secBadOperation,-8302);                     {!!.02} 
                      Exit;                                              {!!.02} 
                    end;                                                 {!!.02} 
                    if STemp[1] <> '"' then {begin}              {!!.01} {!!.06} 
                      STemp := '"' + STemp + '"';                {!!.01} {!!.06} 
                    //else                                               {!!.01} 
                      //if S[2] <> '+' then                              {!!.01} 
                        //S := '"+' + S + '"';                           {!!.01} 
                    //end;                                               {!!.01} 
                    STemp := STemp + #13#10; 
                  end; 
                  FComPort.Output := 'AT' + GSMSendMessageCommands[CmdIndex] 
                                        + STemp + #13;                   {!!.06} 
                end else begin 
                  if CmdIndex = High(GSMSendMessageCommands) then begin 
                    ResponsePacket.StartString := 
                        GSMSendMessageResponse[CmdIndex]; 
                    ResponsePacket.EndString := #13#10; 
                    ResponsePacket.Enabled := True; 
                    //DelayTicks(4, True);                               {!!.04} 
                    if FPDUMode then begin                              {!!.PDU} 
                      STemp := FTempWriteMess;                          {!!.PDU} 
                    end else                                            {!!.PDU} 
                      STemp := FMessage; 
                    if STemp[Length(STemp)] <> #26 then 
                      STemp := STemp + #26; 
                    FComPort.Output := STemp; 
                  end else begin 
                    ResponsePacket.StartString := 
                        GSMSendMessageResponse[CmdIndex]; 
                    ResponsePacket.Enabled := True; 
                    //DelayTicks(4, True);                               {!!.04} 
                    if GSMSendMessageCommands[CmdIndex] = '+CSCA=' then begin 
                      if (FSMSCenter <> '') and 
                      (GSMSendMessageCommands[CmdIndex]='+CSCA=') then begin 
                        if FSMSCenter[1] = '"' then {begin}              {!!.01} 
                          FSMSCenter := copy(FSMSCenter, 2, Length(FSMSCenter)); 
                          {*if FSMSCenter[2] = '+' then 
                            {FSMSCenter := copy(FSMSCenter, 3, 
                                               {Length(FSMSCenter));*}   {!!.01} 
                        if FSMSCenter[Length(FSMSCenter)] = '"' then     {!!.01} 
                          FSMSCenter := copy(FSMSCenter, 1,              {!!.01} 
                                               Length(FSMSCenter)-1);    {!!.01} 
                        //end;                                           {!!.01} 
                        FComport.Output := 'AT' + 
                                GSMSendMessageCommands[CmdIndex]+'"'     {!!.01} 
                                           + FSMSCenter + '"' + #13 
                      end else begin 
                        inc(CmdIndex); 
                        ResponsePacket.StartString := 
                            GSMSendMessageResponse[CmdIndex]; 
                        ResponsePacket.EndString := '>'#32; 
                        ResponsePacket.Enabled := True; 
                        //DelayTicks(4, True);                           {!!.04} 
                        if FPDUMode then begin                          {!!.PDU} 
                          BuildPDUMessage;                              {!!.PDU} 
                          S := IntToStr(PDULength);                     {!!.PDU} 
                        end else begin                                  {!!.PDU} 
                          S := SMSAddress; 
                          if S = '' then begin                           {!!.02} 
                            DoFail(secBadOperation,-8302);               {!!.02} 
                            Exit;                                        {!!.02} 
                          end;                                           {!!.02} 
                          if S[1] <> '"' then {begin}                    {!!.01} 
                            S := '"' + S + '"';                          {!!.01} 
                          //else                                         {!!.01} 
                            //if S[2] <> '+' then                        {!!.01} 
                              //S := '"+' + S + '"';                     {!!.01} 
                          //end;                                         {!!.01} 
                        end;                                            {!!.PDU} 
                        S := S + #13; 
                        FComPort.Output := 'AT' + 
                                   GSMSendMessageCommands[CmdIndex] + S; 
                      end 
                    end else 
                      FComPort.Output := 'AT' + STemp + #13; 
                  end 
                end 
              end; 
              STemp := ''; 
            end; 
    gsListAll : begin 
                  // Just sent +CMGL, successful so far, send the next command 
                  S := ResponseStr; 
                  if Length(S) > 8 then begin                            {!!.02} 
                    if FGSMMode = gmPDU then begin                       {!!.06} 
                      while ParseAPDUMessage do                          {!!.06} 
                        DelayTicks(1, True);                             {!!.06} 
                    end else                                             {!!.06} 
                      while ParseAMessage do                             {!!.02} 
                        DelayTicks(1, True);                             {!!.02} 
                  end; 
                  if FNeedNewMessage > 0 then begin                      {!!.04} 
                    if Assigned(FOnNewMessage) then                      {!!.04} 
                      FOnNewMessage 
                        (Self, FNeedNewMessage, FRecNewMess);            {!!.06} 
                    FNeedNewMessage := 0;                                {!!.04} 
                    FRecNewMess := '';                                   {!!.06} 
                  end else begin                                         {!!.04} 
                    if Assigned(FOnMessageList) then 
                      FOnMessageList(Self); 
                    if Assigned(FOnGSMComplete) and not FConfigList then {!!.02} 
                      FOnGSMComplete(Self, FGSMState, FErrorCode); 
                  end; 
                  ResponsePacket.IncludeStrings := False; 
                  ResponsePacket.StartCond := scString;                  {!!.02} 
                  FGSMState := gsNone; 
                  ResponseStr := '';                                     {!!.02} 
                end; 
 
    gsSendFStore : begin 
                     //Just sent +CMSS, successful so far, send the next command 
                     inc(CmdIndex); 
                     if CmdIndex > High(GSMSendMessFromStorage) then begin 
                       // generate the OnComplete event, we're done 
                       //FMessageStore.ClearStore;                       {!!.06} 
                       //Synchronize;                                    {!!.06} 
                       if Assigned(FOnMessageList) then                   
                         FOnMessageList(Self); 
                       if Assigned(FOnGSMComplete) then 
                         FOnGSMComplete(Self, FGSMState, FErrorCode); 
                       FGSMState := gsNone; 
                       ResponseStr := '';                                {!!.02} 
                     end else 
                       // send the next command if there is one 
                       FComPort.Output := 'AT' + 
                                     GSMSendMessFromStorage[CmdIndex] + #13; 
                   end; 
    gsWrite : begin 
                // Just sent +CMGW, successful so far, send the next command 
                inc(CmdIndex); 
                if CmdIndex > High(GSMWriteToMemoryCommands) then begin 
                  //FMessageStore.ClearStore;                            {!!.06} 
                  //Synchronize;                                         {!!.06} 
                  if Assigned(FOnMessageList) then 
                    FOnMessageList(Self);                               
                  // generate the OnComplete event, we're done configuring 
                  if Assigned(FOnGSMComplete) then 
                    FOnGSMComplete(Self, FGSMState, FErrorCode);  
                  // PostMessage will synchronize and list all messages    
                  PostMessage(FHandle, ApdGSMResponse, 1,0);             {!!.06} 
                  FGSMState := gsNone;                                   {!!.02} 
                  ResponseStr := '';                                     {!!.02} 
                end else 
                  if (CmdIndex < High(GSMWriteToMemoryCommands)) then begin 
                    BuildPDUMessage;                                    {!!.PDU} 
                    STemp := IntToStr(PDULength);                       {!!.PDU} 
                    inc(CmdIndex);                                      {!!.PDU} 
                    FComPort.Output := 'AT' + 
                      GSMWriteToMemoryCommands[CmdIndex] + STemp + #13; 
                  end else begin 
                    // send the next command 
                    ResponsePacket.StartString := 
                        GSMWriteToMemoryResponse[CmdIndex]; 
                    ResponsePacket.EndString := #13#10; 
                    ResponsePacket.Enabled := True; 
                    if FTempWriteMess[Length(FTempWriteMess)] <> #26 then 
                        FTempWriteMess := FTempWriteMess + #26; 
                    FComPort.Output := FTempWriteMess; 
                  end; 
              end; 
    gsDelete : begin 
                 { Just sent +CMGD=,successful so far, send the next command} 
                 inc(CmdIndex); 
                 if CmdIndex > High(GSMDeleteAMessageCommand) then begin 
                   // generate the OnComplete event, we're done 
                   //ResponsePacket.IncludeStrings := False;             {!!.01} 
                   //FMessageStore.ClearStore;                           {!!.06} 
                   //Synchronize;                                        {!!.06} 
                   if Assigned(FOnMessageList) then 
                     FOnMessageList(Self);                                
                   if Assigned(FOnGSMComplete) then 
                     FOnGSMComplete(Self, FGSMState, FErrorCode); 
                   // State set to gsNone in PostMessage below 
                   //FGSMState := gsNone;                         {!!.02}{!!.06} 
                   ResponseStr := '';                                    {!!.02} 
                   // PostMessage will synchronize and list all messages 
                   PostMessage(FHandle, ApdGSMResponse, 1,0);            {!!.06} 
                 end else 
                   // send the next command 
                   FComPort.Output := 'AT' + GSMDeleteAMessageCommand[CmdIndex] 
                                      + #13; 
               end; 
  end; // End Case 
end; 
 
{ issues +CMGW to write message secified by FMessage and DestAddr to the phone 
  memory, memory location returned as function result } 
procedure TApdCustomGSMPhone.WriteToMemory(const Dest, Msg: string); 
var 
  S   : string; 
  Res : Integer; 
  ET  : EventTimer; 
begin 
  if FGSMState > gsNone then begin 
    DoFail(secSMSBusy,-8100); 
    Exit; 
  end; 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    FQuickConnect := False; 
    Connect; 
    repeat 
      Res := SafeYield; 
    until (FGSMState = gsNone) or (Res = wm_Quit); 
  end; 
  SetState(gsWrite); 
  ResponsePacket.StartString := GSMWriteToMemoryResponse[CmdIndex]; 
  ResponsePacket.EndString := '>'#32; 
  ResponsePacket.Enabled := True; 
  //DelayTicks(4, True);                                                 {!!.04} 
  S := Dest; 
  if S[1] <> '"' then begin 
    if S[1] = '+' then 
      S := '"' + S + '"'; 
  end; 
  if FGSMMode = gmPDU then begin                                        {!!.PDU} 
    FSMSAddress := Dest;                                                {!!.PDU} 
    FMessage := Msg;                                                    {!!.PDU} 
    PostMessage(FHandle, ApdGSMResponse, 0,0);                          {!!.PDU} 
    CmdIndex := CmdIndex - 2;                                           {!!.PDU} 
  end else begin 
    FComPort.Output := 'AT' + GSMWriteToMemoryCommands[CmdIndex] + S + #13; 
    NewTimer(ET, 1080); // 60 second timer 
    repeat 
      Res := SafeYield; 
    until (FGSMState = gsNone) or (Res = wm_Quit) or TimerExpired(ET); 
    if TimerExpired(ET) then begin 
      DoFail(secSMSTimedOut,-8101); 
      Exit; 
    end; 
  end; 
end; 
 
procedure TApdCustomGSMPhone.WndProc(var Message: TMessage); 
begin 
  with Message do begin 
    if Msg = ApdGSMResponse then begin 
      Message.Result := 1;                                               {!!.06} 
      case WParam of                                                     {!!.04} 
      0 : ProcessResponse; 
      1 : begin                                                          {!!.04} 
            SetState(gsNone);                                            {!!.06} 
            DelayTicks(1, True);                                         {!!.06} 
            ListAllMessages;                                             {!!.04} 
          end;                                                           {!!.04} 
{Begin !!.06} 
      2 : begin 
            SetState(gsNone); 
            DelayTicks(1, True); 
            {$IFDEF AproBCB} 
            SendSMSMessage; 
            {$ELSE} 
            SendMessage; 
            {$ENDIF} 
          end; 
{End !!.06} 
      end; // End Case 
    end else 
      Result := DefWindowProc(FHandle, Msg, wParam, lParam); 
  end; 
end; 
 
procedure TApdCustomGSMPhone.DeleteFromMemoryIndex(PhoneIndex: Integer); 
var 
  Res : Integer; 
begin 
  { Remove a single message from the phone's message store } 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    FQuickConnect := False; 
    Connect; 
    repeat 
      Res := SafeYield; 
    until (FGSMState = gsNone) or (Res = wm_Quit); 
  end; 
  CmdIndex := 0; 
  SetState(gsDelete); 
  //ResponsePacket.IncludeStrings := True;                               {!!.01} 
  ResponsePacket.StartString := GSMDeleteAMessageCommand[CmdIndex]; 
  ResponsePacket.EndString := #13#10'OK'; 
  ResponsePacket.Enabled := True; 
  //DelayTicks(4, True);                                                 {!!.04} 
  FComPort.Output := 'AT' + GSMDeleteAMessageCommand[CmdIndex] + 
                     IntToStr(PhoneIndex) + #13; 
  // go to process response 
  PostMessage(FHandle, ApdGSMResponse, 0,0);                             {!!.06} 
end; 
 
procedure TApdCustomGSMPhone.CheckPort; 
begin 
  { Make sure comport is open } 
  if Assigned(FComPort) then begin 
    if (FComPort.DeviceLayer = dlWinSock) then 
      { we can't do GSM through Winsock... } 
      raise ECannotUseWithWinSock.Create(ecCannotUseWithWinSock, False); 
    if not(FComPort.Open) then 
    { open the port, let the TApdComPort raise any exceptions } 
      FComPort.Open := True; 
    if FQueryModemOnly then                                              {!!.06} 
      exit;                                                              {!!.06} 
    FGSMState := gsNone; 
    FConnected := True; 
    { Create a response packet, there is a port now } 
    if not Assigned(ResponsePacket) then begin                           {!!.05} 
      ResponsePacket := TApdDataPacket.Create(Self);                     {!!.05} 
      ResponsePacket.OnStringPacket := ResponseStringPacket;             {!!.05} 
      ResponsePacket.IncludeStrings := False;                            {!!.05} 
      ResponsePacket.StartCond := scString;                              {!!.05} 
      ResponsePacket.EndCond := [ecString];                              {!!.05} 
      ResponsePacket.AutoEnable := False;                                {!!.05} 
    end;     
    ResponsePacket.ComPort := FComPort;                                  {!!.05} 
    ResponsePacket.Enabled := False;                                     {!!.05} 
    { Create an error packet, there is a port now } 
    if not Assigned(ErrorPacket) then begin                              {!!.05} 
      ErrorPacket := TApdDataPacket.Create(Self);                        {!!.05} 
      ErrorPacket.OnStringPacket := ErrorStringPacket;                   {!!.05} 
      ErrorPacket.IncludeStrings := False;                               {!!.05} 
      ErrorPacket.StartCond := scString;                                 {!!.05} 
      //ErrorPacket.StartString := #13#10'ERROR';                        {!!.05} 
      ErrorPacket.StartString := #13#10'+CMS ERROR:';                    {!!.06} 
      ErrorPacket.EndCond := [ecString];                                 {!!.05} 
      ErrorPacket.EndString := #13;                                      {!!.05} 
      ErrorPacket.ComPort := FComPort;                                   {!!.05} 
      ErrorPacket.Enabled := True;                                       {!!.05} 
      ErrorPacket.AutoEnable := True;                                    {!!.05} 
    end;                                                                 {!!.05} 
  end else 
    raise EPortNotAssigned.Create(ecPortNotAssigned, False); 
end; 
 
procedure TApdCustomGSMPhone.Synchronize; 
var 
  Res : Integer; 
begin 
  // tell the phone to give us the message store 
  if not FConnected then begin 
    { Do connection/configuration stuff } 
    FQuickConnect := False; 
    Connect; 
    repeat 
      Res := SafeYield; 
    until (FGSMState = gsNone) or (Res = wm_Quit); 
  end; 
  FGSMState := gsNone;                                                   {!!.02} 
  ListAllMessages; 
end; 
 
procedure TApdCustomGSMPhone.QueryModem; 
var 
  ErrorPacketExists: Boolean; 
  ResponsePacketExists: Boolean; 
begin 
{ QueryModem is designed to test the phone/modem to see what value/result 
  comes back. The responses may vary, so you can view them in the log. You can 
  set logging from the TApdComPort component. Set the Logging property to tlOn 
  and the LogName property to path and filename where you want the log stored. 
  For example: C:\GSM.LOG; } 
  FQueryModemOnly := True;                                               {!!.06} 
  Connect;                                                               {!!.06} 
  ErrorPacketExists := False; 
  ResponsePacketExists := False; 
  if Assigned(ErrorPacket) then begin 
    ErrorPacket.AutoEnable := False;                                     {!!.06} 
    ErrorPacket.Enabled := False;                                        {!!.06} 
    ErrorPacketExists := True; 
  end; 
  if Assigned(ResponsePacket) then begin 
    ResponsePacket.Enabled := False;                                     {!!.06} 
    ResponsePacketExists := True; 
  end; 
  FComPort.Output := 'ATI3'#13;                                          {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FComPort.Output := 'AT+CMGF=?'#13;                                     {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FComPort.Output := 'AT+CSMS=?'#13;                                     {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FComPort.Output := 'AT+CNMI=?'#13;                                     {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FComPort.Output := 'AT+CSMP?'#13;                                      {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FComPort.Output := 'AT+CSDH?'#13;                                      {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FComPort.Output := 'AT+CPMS?'#13;                                      {!!.06} 
  DelayTicks(9, True);                                                   {!!.06} 
  FQueryModemOnly := False;                                              {!!.06} 
  if ErrorPacketExists then begin                                        {!!.06} 
    ErrorPacket.Enabled := True;                                         {!!.06} 
    ErrorPacket.AutoEnable := True;                                      {!!.06} 
  end;                                                                   {!!.06} 
  if ResponsePacketExists then                                           {!!.06} 
    ResponsePacket.Enabled := True;                                      {!!.06} 
end; 
 
procedure TApdMessageStore.SetMSCapacity(const Value: Integer); 
begin 
  FCapacity := Value; 
end; 
 
procedure TApdMessageStore.SetMessage(Index: Integer; 
                                      const Value: TApdSMSMessage); 
begin 
  Strings[Index] := Value.FTimeStampStr; 
  Objects[Index] := Value; 
end; 
 
{ Notification } 
procedure TApdCustomGSMPhone.Notification(AComponent: TComponent; 
  Operation: TOperation); 
begin 
  inherited; 
  if Operation = opRemove then begin 
    if AComponent = FComPort then 
      FComPort := nil; 
  end else begin 
    if (AComponent is TApdCustomComPort) and (FComPort = nil) then 
      FComPort := TApdCustomComPort(AComponent); 
  end; 
end; 
 
{ Set the Service Center Address/Phone Number } 
procedure TApdCustomGSMPhone.SetCenter(const Value: string); 
begin 
  FSMSCenter := Value; 
end; 
 
{ TApdGSMPhone - TApdSMSStatus to string conversion} 
function TApdCustomGSMPhone.StatusToStr(StatusString: TApdSMSStatus): string; 
const 
    StatusStrings : array[0..ord(High(TApdSMSStatus))] of string =       {!!.01} 
              ('REC UNREAD', 'REC READ', 'STO UNSENT', 'STO SENT', 'ALL', 
               'Unknown');                                               {!!.01} 
begin 
  if ord(StatusString) > High(StatusStrings) then begin 
    Result := StatusStrings[High(StatusStrings)];                        {!!.02} 
    //DoFail(ecSMSUnknownStatus,-8103);                                  {!!.02} 
    Exit; 
  end; 
  Result := StatusStrings[Ord(StatusString)]; 
end; 
 
procedure TApdCustomGSMPhone.DoFail(const Msg: string; 
  const ErrCode: Integer); 
begin 
  if Assigned(FOnGSMComplete) then begin 
    FOnGSMComplete(Self, FGSMState, ErrCode); 
    FGSMState := gsNone; 
  end else begin 
    //raise EApdGSMPhoneException.Create(Msg, ErrCode); 
    FGSMState := gsNone; 
    raise EApdGSMPhoneException.Create(ErrCode, Msg); 
  end; 
end; 
 
 
procedure TApdCustomGSMPhone.SetGSMMode(const NewMode : TGSMMode);      {!!.PDU} 
begin 
  FGSMMode := NewMode; 
end; 
 
function TApdCustomGSMPhone.GetGSMMode : TGSMMode; 
begin 
  Result := FGSMMode; 
end; 
 
procedure TApdCustomGSMPhone.SetState(NewState : TGSMStates); 
begin 
  FGSMState := NewState; 
end; 
 
{ TApdGSMPhone } 
 
constructor TApdGSMPhone.Create(Owner: TComponent); 
begin 
  inherited; 
end; 
 
destructor TApdGSMPhone.Destroy; 
begin 
  inherited; 
end; 
 
end.