diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ebd21a --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/GSMComm.sln b/GSMComm.sln new file mode 100644 index 0000000..bc3c4bc --- /dev/null +++ b/GSMComm.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDUConverter", "PDUConverter\PDUConverter.csproj", "{5E657EFE-0A30-466D-B025-FF177E23A727}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GSMCommShared", "GSMCommShared\GSMCommShared.csproj", "{71EA4054-A98A-46BA-84FA-81652DB72B72}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GSMCommunication", "GSMCommunication\GSMCommunication.csproj", "{32B76F1E-B022-47C6-86EC-28639D348067}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GSMCommServer", "GSMCommServer\GSMCommServer.csproj", "{67CB92ED-436B-4005-A6D6-092503D6E496}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5E657EFE-0A30-466D-B025-FF177E23A727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E657EFE-0A30-466D-B025-FF177E23A727}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E657EFE-0A30-466D-B025-FF177E23A727}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E657EFE-0A30-466D-B025-FF177E23A727}.Release|Any CPU.Build.0 = Release|Any CPU + {71EA4054-A98A-46BA-84FA-81652DB72B72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71EA4054-A98A-46BA-84FA-81652DB72B72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71EA4054-A98A-46BA-84FA-81652DB72B72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71EA4054-A98A-46BA-84FA-81652DB72B72}.Release|Any CPU.Build.0 = Release|Any CPU + {32B76F1E-B022-47C6-86EC-28639D348067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32B76F1E-B022-47C6-86EC-28639D348067}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32B76F1E-B022-47C6-86EC-28639D348067}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32B76F1E-B022-47C6-86EC-28639D348067}.Release|Any CPU.Build.0 = Release|Any CPU + {67CB92ED-436B-4005-A6D6-092503D6E496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67CB92ED-436B-4005-A6D6-092503D6E496}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67CB92ED-436B-4005-A6D6-092503D6E496}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67CB92ED-436B-4005-A6D6-092503D6E496}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/GSMCommServer/GSMCommServer.csproj b/GSMCommServer/GSMCommServer.csproj new file mode 100644 index 0000000..a2bea1a --- /dev/null +++ b/GSMCommServer/GSMCommServer.csproj @@ -0,0 +1,82 @@ + + + + {67CB92ED-436B-4005-A6D6-092503D6E496} + 2 + Debug + AnyCPU + GSMCommServer + Library + GsmComm + v4.0 + + + bin\Debug\ + true + DEBUG;TRACE + false + 4 + full + prompt + AnyCPU + true + + + bin\Release\ + false + TRACE + true + 4 + pdbonly + prompt + AnyCPU + + + + + .\GSMCommServerReferences\GSMCommShared.dll + + + + .\GSMCommServerReferences\GSMCommunication.dll + + + .\GSMCommServerReferences\PDUConverter.dll + + + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + + \ No newline at end of file diff --git a/GSMCommServer/GSMCommServerReferences/GSMCommShared.dll b/GSMCommServer/GSMCommServerReferences/GSMCommShared.dll new file mode 100644 index 0000000..09feab4 Binary files /dev/null and b/GSMCommServer/GSMCommServerReferences/GSMCommShared.dll differ diff --git a/GSMCommServer/GSMCommServerReferences/GSMCommunication.dll b/GSMCommServer/GSMCommServerReferences/GSMCommunication.dll new file mode 100644 index 0000000..2939cc4 Binary files /dev/null and b/GSMCommServer/GSMCommServerReferences/GSMCommunication.dll differ diff --git a/GSMCommServer/GSMCommServerReferences/PDUConverter.dll b/GSMCommServer/GSMCommServerReferences/PDUConverter.dll new file mode 100644 index 0000000..baee9ee Binary files /dev/null and b/GSMCommServer/GSMCommServerReferences/PDUConverter.dll differ diff --git a/GSMCommServer/Properties/AssemblyInfo.cs b/GSMCommServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bba6242 --- /dev/null +++ b/GSMCommServer/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyCompany("Stefan Mayr")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCopyright("Copyright © 2004-2011 Stefan Mayr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyFileVersion("1.21.0.0")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyTitle("GSMComm Server")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyVersion("1.21.0.0")] +[assembly: CompilationRelaxations(8)] +[assembly: ComVisible(false)] +[assembly: Guid("51344c27-1895-4355-9289-a25D0880918f")] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows=true)] diff --git a/GSMCommServer/Server/AuthorizationModule.cs b/GSMCommServer/Server/AuthorizationModule.cs new file mode 100644 index 0000000..ff220a1 --- /dev/null +++ b/GSMCommServer/Server/AuthorizationModule.cs @@ -0,0 +1,53 @@ +using System; +using System.Net; +using System.Runtime.Remoting.Channels; +using System.Security.Principal; + +/// +/// Implements the authorization module for the server. +/// +namespace GsmComm.Server +{ + public class AuthorizationModule : IAuthorizeRemotingConnection + { + private bool allowAnonymous; + + /// + /// Initializes a new instance of the module. + /// + /// Specifies if users authenticated anonymously can + /// connect to the current channel. + public AuthorizationModule(bool allowAnonymous) + { + this.allowAnonymous = allowAnonymous; + } + + /// + /// Gets a Boolean value that indicates whether the network address of the client is + /// authorized to connect on the current channel. + /// + /// The that identifies the network address of the client. + /// true if the network address of the client is authorized; otherwise, false. + public bool IsConnectingEndPointAuthorized(EndPoint endPoint) + { + return true; + } + + /// + /// Gets a Boolean value that indicates whether the user identity of the client is + /// authorized to connect on the current channel. + /// + /// The that represents the user identity of the client. + /// true if the user identity of the client is authorized; otherwise, false. + public bool IsConnectingIdentityAuthorized(IIdentity identity) + { + bool flag = true; + WindowsIdentity windowsIdentity = identity as WindowsIdentity; + if (windowsIdentity != null && windowsIdentity.IsAnonymous && !this.allowAnonymous) + { + flag = false; + } + return flag; + } + } +} \ No newline at end of file diff --git a/GSMCommServer/Server/MessageSendErrorEventArgs.cs b/GSMCommServer/Server/MessageSendErrorEventArgs.cs new file mode 100644 index 0000000..62a1a46 --- /dev/null +++ b/GSMCommServer/Server/MessageSendErrorEventArgs.cs @@ -0,0 +1,46 @@ +using System; + +/// +/// Provides data for the error events that deal with message sending. +/// +namespace GsmComm.Server +{ + public class MessageSendErrorEventArgs : MessageSendEventArgs + { + private Exception exception; + + /// + /// Gets the exception that caused the error. + /// + public Exception Exception + { + get + { + return this.exception; + } + } + + /// + /// Initializes a new instance of the . + /// + /// The message that failed sending. + /// The destination the message was attempted to send to. + /// The exception that caused the error. + public MessageSendErrorEventArgs(string message, string destination, Exception exception) : base(message, destination) + { + this.exception = exception; + } + + /// + /// Initializes a new instance of the . + /// + /// The message that failed sending. + /// The destination the message was attempted to send to. + /// The exception that caused the error. + /// The name of the user from which the action started. + public MessageSendErrorEventArgs(string message, string destination, Exception exception, string userName) : base(message, destination, userName) + { + this.exception = exception; + } + } +} \ No newline at end of file diff --git a/GSMCommServer/Server/MessageSendErrorEventHandler.cs b/GSMCommServer/Server/MessageSendErrorEventHandler.cs new file mode 100644 index 0000000..7763f4b --- /dev/null +++ b/GSMCommServer/Server/MessageSendErrorEventHandler.cs @@ -0,0 +1,11 @@ +using System; + +/// +/// The method that handles the event. +/// +/// The origin of the event. +/// The associated with the event. +namespace GsmComm.Server +{ + public delegate void MessageSendErrorEventHandler(object sender, MessageSendErrorEventArgs e); +} \ No newline at end of file diff --git a/GSMCommServer/Server/MessageSendEventArgs.cs b/GSMCommServer/Server/MessageSendEventArgs.cs new file mode 100644 index 0000000..e512328 --- /dev/null +++ b/GSMCommServer/Server/MessageSendEventArgs.cs @@ -0,0 +1,74 @@ +using System; + +/// +/// Provides data for the events that deal with message sending. +/// +namespace GsmComm.Server +{ + public class MessageSendEventArgs : EventArgs + { + private string message; + + private string destination; + + private string userName; + + /// + /// Gets the destination the message is being or was sent to. + /// + public string Destination + { + get + { + return this.destination; + } + } + + /// + /// Gets the message that is being sent or was sent. + /// + public string Message + { + get + { + return this.message; + } + } + + /// + /// Gets the user name from which the action started. + /// + public string UserName + { + get + { + return this.userName; + } + } + + /// + /// Initializes a new instance of the . + /// + /// The message that is being sent or was sent. + /// The destination the message is being or was sent to. + public MessageSendEventArgs(string message, string destination) + { + this.message = message; + this.destination = destination; + this.userName = string.Empty; + } + + /// + /// Initializes a new instance of the . + /// + /// The message that is being sent or was sent. + /// The destination the message is being or was sent to. + /// The name of the user from which the action started. + public MessageSendEventArgs(string message, string destination, string userName) + { + this.message = message; + this.destination = destination; + this.userName = userName; + } + } +} \ No newline at end of file diff --git a/GSMCommServer/Server/MessageSendEventHandler.cs b/GSMCommServer/Server/MessageSendEventHandler.cs new file mode 100644 index 0000000..15e5347 --- /dev/null +++ b/GSMCommServer/Server/MessageSendEventHandler.cs @@ -0,0 +1,12 @@ +using System; + +/// +/// The method that handles the and +/// events. +/// +/// The origin of the event. +/// The associated with the event. +namespace GsmComm.Server +{ + public delegate void MessageSendEventHandler(object sender, MessageSendEventArgs e); +} \ No newline at end of file diff --git a/GSMCommServer/Server/SmsSender.cs b/GSMCommServer/Server/SmsSender.cs new file mode 100644 index 0000000..289231d --- /dev/null +++ b/GSMCommServer/Server/SmsSender.cs @@ -0,0 +1,193 @@ +using GsmComm.GsmCommunication; +using GsmComm.Interfaces; +using GsmComm.PduConverter; +using System; +using System.Threading; + +/// +/// Implements a remotable object to send SMS messages. +/// +namespace GsmComm.Server +{ + public class SmsSender : MarshalByRefObject, ISmsSender + { + private GsmCommMain comm; + + private bool disposed; + + /// + /// Initializes a new instance of the class. + /// + /// The COM port to connect to. + /// The baud rate to use. + /// The communictaion timeout. + public SmsSender(string portName, int baudRate, int timeout) + { + this.disposed = false; + this.comm = new GsmCommMain(portName, baudRate, timeout); + this.ConnectEvents(); + try + { + this.comm.Open(); + } + catch (Exception exception) + { + this.DisconnectEvents(); + this.comm = null; + throw; + } + } + + private void comm_MessageSendComplete(object sender, MessageEventArgs e) + { + if (this.MessageSendComplete != null) + { + string userDataText = e.Pdu.UserDataText; + string empty = string.Empty; + if (e.Pdu is SmsSubmitPdu) + { + empty = (e.Pdu as SmsSubmitPdu).DestinationAddress; + } + MessageSendEventArgs messageSendEventArg = new MessageSendEventArgs(userDataText, empty, this.GetIdentityName()); + this.MessageSendComplete(this, messageSendEventArg); + } + } + + private void comm_MessageSendFailed(object sender, MessageErrorEventArgs e) + { + if (this.MessageSendFailed != null) + { + string userDataText = e.Pdu.UserDataText; + string empty = string.Empty; + if (e.Pdu is SmsSubmitPdu) + { + empty = (e.Pdu as SmsSubmitPdu).DestinationAddress; + } + MessageSendErrorEventArgs messageSendErrorEventArg = new MessageSendErrorEventArgs(userDataText, empty, e.Exception, this.GetIdentityName()); + this.MessageSendFailed(this, messageSendErrorEventArg); + } + } + + private void comm_MessageSendStarting(object sender, MessageEventArgs e) + { + if (this.MessageSendStarting != null) + { + string userDataText = e.Pdu.UserDataText; + string empty = string.Empty; + if (e.Pdu is SmsSubmitPdu) + { + empty = (e.Pdu as SmsSubmitPdu).DestinationAddress; + } + MessageSendEventArgs messageSendEventArg = new MessageSendEventArgs(userDataText, empty, this.GetIdentityName()); + this.MessageSendStarting(this, messageSendEventArg); + } + } + + private void ConnectEvents() + { + this.comm.MessageSendStarting += new GsmCommMain.MessageEventHandler(this.comm_MessageSendStarting); + this.comm.MessageSendComplete += new GsmCommMain.MessageEventHandler(this.comm_MessageSendComplete); + this.comm.MessageSendFailed += new GsmCommMain.MessageErrorEventHandler(this.comm_MessageSendFailed); + } + + private void DisconnectEvents() + { + this.comm.MessageSendStarting -= new GsmCommMain.MessageEventHandler(this.comm_MessageSendStarting); + this.comm.MessageSendComplete -= new GsmCommMain.MessageEventHandler(this.comm_MessageSendComplete); + this.comm.MessageSendFailed -= new GsmCommMain.MessageErrorEventHandler(this.comm_MessageSendFailed); + } + + private string GetIdentityName() + { + return Thread.CurrentPrincipal.Identity.Name; + } + + /// + /// Determines how long the remoting object lives. + /// + /// Always null so that the object lives forever. + public override object InitializeLifetimeService() + { + return null; + } + + /// + /// Sends an SMS message. + /// + /// The message to send. + /// The destination (phone number) to which the message should be sent. + public void SendMessage(string message, string destination) + { + lock (this) + { + if (!this.disposed) + { + SmsSubmitPdu smsSubmitPdu = new SmsSubmitPdu(message, destination); + this.comm.SendMessage(smsSubmitPdu); + } + else + { + throw new ObjectDisposedException("SmsSender"); + } + } + } + + /// + /// Sends an SMS message. + /// + /// The message to send. + /// The destination (phone number) to which the message should be sent. + /// Specifies if the message should be sent as Unicode. + public void SendMessage(string message, string destination, bool unicode) + { + SmsSubmitPdu smsSubmitPdu; + lock (this) + { + if (!this.disposed) + { + if (!unicode) + { + smsSubmitPdu = new SmsSubmitPdu(message, destination); + } + else + { + smsSubmitPdu = new SmsSubmitPdu(message, destination, 8); + } + this.comm.SendMessage(smsSubmitPdu); + } + else + { + throw new ObjectDisposedException("SmsSender"); + } + } + } + + /// + /// Stops the SMS sender and releases its resources. + /// + public void Shutdown() + { + if (!this.disposed) + { + this.comm.Close(); + this.DisconnectEvents(); + this.disposed = true; + } + } + + /// + /// The event that occurs after a successful message transfer. + /// + public event MessageSendEventHandler MessageSendComplete; + + /// + /// The event that occurs after a failed message transfer. + /// + public event MessageSendErrorEventHandler MessageSendFailed; + + /// + /// The event that occurs immediately before transferring a new message. + /// + public event MessageSendEventHandler MessageSendStarting; + } +} \ No newline at end of file diff --git a/GSMCommServer/Server/SmsServer.cs b/GSMCommServer/Server/SmsServer.cs new file mode 100644 index 0000000..a85a158 --- /dev/null +++ b/GSMCommServer/Server/SmsServer.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Tcp; + +/// +/// Implements a server for sending SMS messages remotely. +/// +/// +/// The server uses .NET remoting with a TCP channel to publish an object. +/// After starting, the server can be accessed by default at tcp://(servername):2000/SMSSender. +/// +namespace GsmComm.Server +{ + public class SmsServer : IDisposable + { + private SmsSender smsSender; + + private IChannel channel; + + private ObjRef objRefSmsSender; + + private int networkPort; + + private string uri; + + private bool isSecured; + + private AuthorizationModule authModule; + + private bool allowAnonymous; + + private string portName; + + private int baudRate; + + private int timeout; + + /// + /// Gets or sets whether anonymous users can connect when the server is secured. + /// + /// If this property is changed, while the server is running, the server must be restarted. + public bool AllowAnonymous + { + get + { + return this.allowAnonymous; + } + set + { + this.allowAnonymous = value; + } + } + + /// + /// Gets or sets the baud rate to use when communicating with the phone. + /// + /// If this property is changed, while the server is running, the server must be restarted. + public int BaudRate + { + get + { + return this.baudRate; + } + set + { + this.baudRate = value; + } + } + + /// + /// Gets or sets a value that indicates whether security is enabled for the server. + /// + /// + /// When security is enabled, only user identities authenticated by Windows are allowed to + /// connect to the server and the communication between server and client is encrypted. + /// Clients must also have security enabled to be able to connect to the server. + /// Additionally, access may be allowed or denied for specific users when in secure mode. + /// This is currently determined by the property that specifies whether + /// anonymous users can connect or not. + /// If this property is changed, while the server is running, the server must be restarted. + /// + public bool IsSecured + { + get + { + return this.isSecured; + } + set + { + this.isSecured = value; + } + } + + /// + /// Gets or sets the network port for the SMS server to listen for requests. + /// + /// If this property is changed, while the server is running, the server must be restarted. + public int NetworkPort + { + get + { + return this.networkPort; + } + set + { + this.networkPort = value; + } + } + + /// + /// Gets or sets the COM port where the phone is connected. + /// + /// If this property is changed, while the server is running, the server must be restarted. + public string PortName + { + get + { + return this.portName; + } + set + { + this.portName = value; + } + } + + /// + /// Gets or sets the timeout when communicating with the phone. + /// + /// If this property is changed, while the server is running, the server must be restarted. + public int Timeout + { + get + { + return this.timeout; + } + set + { + this.timeout = value; + } + } + + /// + /// Gets or sets the URI under which the SMS sender is available. + /// + /// If this property is changed, while the server is running, the server must be restarted. + public string Uri + { + get + { + return this.uri; + } + set + { + this.uri = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SmsServer() + { + this.smsSender = null; + this.channel = null; + this.objRefSmsSender = null; + this.networkPort = 2000; + this.uri = "SMSSender"; + this.isSecured = false; + this.authModule = null; + this.allowAnonymous = false; + this.portName = "COM1"; + this.baudRate = 19200; + this.timeout = 300; + } + + private void ConnectEvents() + { + this.smsSender.MessageSendStarting += new MessageSendEventHandler(this.smsSender_MessageSendStarting); + this.smsSender.MessageSendComplete += new MessageSendEventHandler(this.smsSender_MessageSendComplete); + this.smsSender.MessageSendFailed += new MessageSendErrorEventHandler(this.smsSender_MessageSendFailed); + } + + private void DisconnectEvents() + { + this.smsSender.MessageSendStarting -= new MessageSendEventHandler(this.smsSender_MessageSendStarting); + this.smsSender.MessageSendComplete -= new MessageSendEventHandler(this.smsSender_MessageSendComplete); + this.smsSender.MessageSendFailed -= new MessageSendErrorEventHandler(this.smsSender_MessageSendFailed); + } + + /// + /// Disposes of the host. + /// + public void Dispose() + { + this.StopInternal(); + } + + /// + /// Finalizes the class. + /// + protected override void Finalize() + { + try + { + this.Dispose(); + } + finally + { + this.Finalize(); + } + } + + /// + /// Tells if the remoting server is currently running. + /// + /// true if the server is running, false otherwise. + public bool IsRunning() + { + return this.smsSender != null; + } + + private void smsSender_MessageSendComplete(object sender, MessageSendEventArgs e) + { + if (this.MessageSendComplete != null) + { + this.MessageSendComplete(this, e); + } + } + + private void smsSender_MessageSendFailed(object sender, MessageSendErrorEventArgs e) + { + if (this.MessageSendFailed != null) + { + this.MessageSendFailed(this, e); + } + } + + private void smsSender_MessageSendStarting(object sender, MessageSendEventArgs e) + { + if (this.MessageSendStarting != null) + { + this.MessageSendStarting(this, e); + } + } + + /// + /// Starts the server. + /// + /// Server is already running. + public void Start() + { + if (this.smsSender == null) + { + IDictionary hashtables = new Hashtable(); + hashtables["port"] = this.networkPort; + hashtables["name"] = string.Concat("SMSSenderTCPChannel", this.networkPort.ToString()); + if (this.isSecured) + { + hashtables["secure"] = "true"; + this.authModule = new AuthorizationModule(this.allowAnonymous); + } + TcpServerChannel tcpServerChannel = new TcpServerChannel(hashtables, null, this.authModule); + SmsSender smsSender = null; + ObjRef objRef = null; + try + { + ChannelServices.RegisterChannel(tcpServerChannel, this.isSecured); + try + { + smsSender = new SmsSender(this.portName, this.baudRate, this.timeout); + objRef = RemotingServices.Marshal(smsSender, this.uri); + } + catch (Exception exception) + { + ChannelServices.UnregisterChannel(tcpServerChannel); + throw; + } + } + catch (Exception exception1) + { + tcpServerChannel.StopListening(null); + throw; + } + this.channel = tcpServerChannel; + this.smsSender = smsSender; + this.objRefSmsSender = objRef; + this.ConnectEvents(); + return; + } + else + { + throw new InvalidOperationException("Server is already running."); + } + } + + /// + /// Stops the server. + /// + /// Server is not running. + public void Stop() + { + if (this.smsSender != null) + { + this.StopInternal(); + return; + } + else + { + throw new InvalidOperationException("Server is not running."); + } + } + + private void StopInternal() + { + if (this.smsSender != null) + { + try + { + RemotingServices.Disconnect(this.smsSender); + this.smsSender.Shutdown(); + this.DisconnectEvents(); + } + finally + { + this.objRefSmsSender = null; + this.smsSender = null; + } + } + if (this.channel != null) + { + try + { + ChannelServices.UnregisterChannel(this.channel); + } + finally + { + this.channel = null; + this.authModule = null; + } + } + } + + /// + /// The event that occurs after a successful message transfer. + /// + public event MessageSendEventHandler MessageSendComplete; + + /// + /// The event that occurs after a failed message transfer. + /// + public event MessageSendErrorEventHandler MessageSendFailed; + + /// + /// The event that occurs immediately before transferring a new message. + /// + public event MessageSendEventHandler MessageSendStarting; + } +} \ No newline at end of file diff --git a/GSMCommShared/CommException.cs b/GSMCommShared/CommException.cs new file mode 100644 index 0000000..f9835d7 --- /dev/null +++ b/GSMCommShared/CommException.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.Serialization; + +/// +/// General exception that gets thrown upon a communication error with the device. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class CommException : ApplicationException + { + private string commTrace; + + /// + /// Gets the communication trace associated with the exception. + /// + public string CommTrace + { + get + { + return this.commTrace; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// A message that describes the error. + public CommException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and a communication trace. + /// + /// A message that describes the error. + /// The communication that occurred right before the error. + public CommException(string message, string commTrace) : base(message) + { + this.commTrace = commTrace; + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The exception that is the cause of the current exception. + public CommException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, a communication trace and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The communication that occurred right before the error. + /// The exception that is the cause of the current exception. + public CommException(string message, string commTrace, Exception innerException) : base(message, innerException) + { + this.commTrace = commTrace; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + protected CommException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/GSMCommShared/GSMCommShared.csproj b/GSMCommShared/GSMCommShared.csproj new file mode 100644 index 0000000..4ef338f --- /dev/null +++ b/GSMCommShared/GSMCommShared.csproj @@ -0,0 +1,57 @@ + + + + {71ea4054-a98a-46ba-84fa-81652db72b72} + 2 + Debug + AnyCPU + GSMCommShared + Library + GsmComm + v4.0 + + + bin\Debug\ + true + DEBUG;TRACE + false + 4 + full + prompt + AnyCPU + + + bin\Release\ + false + TRACE + true + 4 + pdbonly + prompt + AnyCPU + + + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + + \ No newline at end of file diff --git a/GSMCommShared/GsmCommunication/CommException.cs b/GSMCommShared/GsmCommunication/CommException.cs new file mode 100644 index 0000000..f9835d7 --- /dev/null +++ b/GSMCommShared/GsmCommunication/CommException.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.Serialization; + +/// +/// General exception that gets thrown upon a communication error with the device. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class CommException : ApplicationException + { + private string commTrace; + + /// + /// Gets the communication trace associated with the exception. + /// + public string CommTrace + { + get + { + return this.commTrace; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// A message that describes the error. + public CommException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and a communication trace. + /// + /// A message that describes the error. + /// The communication that occurred right before the error. + public CommException(string message, string commTrace) : base(message) + { + this.commTrace = commTrace; + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The exception that is the cause of the current exception. + public CommException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, a communication trace and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The communication that occurred right before the error. + /// The exception that is the cause of the current exception. + public CommException(string message, string commTrace, Exception innerException) : base(message, innerException) + { + this.commTrace = commTrace; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + protected CommException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/GSMCommShared/GsmCommunication/MessageServiceErrorException.cs b/GSMCommShared/GsmCommunication/MessageServiceErrorException.cs new file mode 100644 index 0000000..203cb47 --- /dev/null +++ b/GSMCommShared/GsmCommunication/MessageServiceErrorException.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; + +/// +/// The exception that gets thrown when there is an error with the message service. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class MessageServiceErrorException : CommException + { + private int errorCode; + + /// + /// Gets the error code reported by the network. + /// + public int ErrorCode + { + get + { + return this.errorCode; + } + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code and a communication trace. + /// + /// A message that describes the error. + /// The error code reported by the network. + /// The communication that occurred right before the error. + /// This exception gets thrown when an action can not be executed due to a message service error. + public MessageServiceErrorException(string message, int errorCode, string commTrace) : base(message, commTrace) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code, a communication trace and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The error code reported by the network. + /// The communication that occurred right before the error. + /// The exception that is the cause of the current exception. + /// This exception gets thrown when an action can not be executed due to a message service error. + public MessageServiceErrorException(string message, int errorCode, string commTrace, Exception innerException) : base(message, commTrace, innerException) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + protected MessageServiceErrorException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/GSMCommShared/GsmCommunication/MobileEquipmentErrorException.cs b/GSMCommShared/GsmCommunication/MobileEquipmentErrorException.cs new file mode 100644 index 0000000..c9e0614 --- /dev/null +++ b/GSMCommShared/GsmCommunication/MobileEquipmentErrorException.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; + +/// +/// The exception that gets thrown when the device detects an internal error. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class MobileEquipmentErrorException : CommException + { + private int errorCode; + + /// + /// Gets the error code reported by the device. + /// + public int ErrorCode + { + get + { + return this.errorCode; + } + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code and a communication trace. + /// + /// A message that describes the error. + /// The error code reported by the device. + /// The communication that occurred right before the error. + /// This exception gets thrown when an action can not be executed due to an internal device error. + public MobileEquipmentErrorException(string message, int errorCode, string commTrace) : base(message, commTrace) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code, a communication trace and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The error code reported by the device. + /// The communication that occurred right before the error. + /// The exception that is the cause of the current exception. + /// This exception gets thrown when an action can not be executed due to an internal device error. + public MobileEquipmentErrorException(string message, int errorCode, string commTrace, Exception innerException) : base(message, commTrace, innerException) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + protected MobileEquipmentErrorException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/GSMCommShared/Interfaces/ISmsSender.cs b/GSMCommShared/Interfaces/ISmsSender.cs new file mode 100644 index 0000000..338d0c4 --- /dev/null +++ b/GSMCommShared/Interfaces/ISmsSender.cs @@ -0,0 +1,25 @@ +using System; + +/// +/// Defines the interface for an SMS Sender. +/// +namespace GsmComm.Interfaces +{ + public interface ISmsSender + { + /// + /// Sends an SMS message. + /// + /// The message to send. + /// The destination (phone number) to which the message should be sent. + void SendMessage(string message, string destination); + + /// + /// Sends an SMS message. + /// + /// The message to send. + /// The destination (phone number) to which the message should be sent. + /// Specifies if the message should be sent as Unicode. + void SendMessage(string message, string destination, bool unicode); + } +} \ No newline at end of file diff --git a/GSMCommShared/MessageServiceErrorException.cs b/GSMCommShared/MessageServiceErrorException.cs new file mode 100644 index 0000000..203cb47 --- /dev/null +++ b/GSMCommShared/MessageServiceErrorException.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; + +/// +/// The exception that gets thrown when there is an error with the message service. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class MessageServiceErrorException : CommException + { + private int errorCode; + + /// + /// Gets the error code reported by the network. + /// + public int ErrorCode + { + get + { + return this.errorCode; + } + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code and a communication trace. + /// + /// A message that describes the error. + /// The error code reported by the network. + /// The communication that occurred right before the error. + /// This exception gets thrown when an action can not be executed due to a message service error. + public MessageServiceErrorException(string message, int errorCode, string commTrace) : base(message, commTrace) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code, a communication trace and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The error code reported by the network. + /// The communication that occurred right before the error. + /// The exception that is the cause of the current exception. + /// This exception gets thrown when an action can not be executed due to a message service error. + public MessageServiceErrorException(string message, int errorCode, string commTrace, Exception innerException) : base(message, commTrace, innerException) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + protected MessageServiceErrorException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/GSMCommShared/MobileEquipmentErrorException.cs b/GSMCommShared/MobileEquipmentErrorException.cs new file mode 100644 index 0000000..c9e0614 --- /dev/null +++ b/GSMCommShared/MobileEquipmentErrorException.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; + +/// +/// The exception that gets thrown when the device detects an internal error. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class MobileEquipmentErrorException : CommException + { + private int errorCode; + + /// + /// Gets the error code reported by the device. + /// + public int ErrorCode + { + get + { + return this.errorCode; + } + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code and a communication trace. + /// + /// A message that describes the error. + /// The error code reported by the device. + /// The communication that occurred right before the error. + /// This exception gets thrown when an action can not be executed due to an internal device error. + public MobileEquipmentErrorException(string message, int errorCode, string commTrace) : base(message, commTrace) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with a specified error + /// message, an error code, a communication trace and a reference to the inner exception that is the cause of this exception. + /// + /// A message that describes the error. + /// The error code reported by the device. + /// The communication that occurred right before the error. + /// The exception that is the cause of the current exception. + /// This exception gets thrown when an action can not be executed due to an internal device error. + public MobileEquipmentErrorException(string message, int errorCode, string commTrace, Exception innerException) : base(message, commTrace, innerException) + { + this.errorCode = errorCode; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + protected MobileEquipmentErrorException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/GSMCommShared/Properties/AssemblyInfo.cs b/GSMCommShared/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..543a3bf --- /dev/null +++ b/GSMCommShared/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyCompany("Stefan Mayr")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCopyright("Copyright © 2004-2011 Stefan Mayr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyFileVersion("1.21.0.0")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyTitle("GSMComm Shared Implementations")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyVersion("1.21.0.0")] +[assembly: CompilationRelaxations(8)] +[assembly: ComVisible(false)] +[assembly: Guid("c43ba0b1-3ffa-4b02-8fbd-2d5997e9b5c5")] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows=true)] diff --git a/GSMCommunication/GSMCommunication.csproj b/GSMCommunication/GSMCommunication.csproj new file mode 100644 index 0000000..e7d0d86 --- /dev/null +++ b/GSMCommunication/GSMCommunication.csproj @@ -0,0 +1,250 @@ + + + + {32b76f1e-b022-47c6-86ec-28639d348067} + 2 + Debug + AnyCPU + GSMCommunication + Library + GsmComm + v4.0 + + + bin\Debug\ + true + DEBUG;TRACE + false + 4 + full + prompt + AnyCPU + + + bin\Release\ + false + TRACE + true + 4 + pdbonly + prompt + AnyCPU + + + + .\GSMCommunicationReferences\PDUConverter.dll + + + + .\GSMCommunicationReferences\GSMCommShared.dll + + + + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + + \ No newline at end of file diff --git a/GSMCommunication/GSMCommunicationReferences/GSMCommShared.dll b/GSMCommunication/GSMCommunicationReferences/GSMCommShared.dll new file mode 100644 index 0000000..09feab4 Binary files /dev/null and b/GSMCommunication/GSMCommunicationReferences/GSMCommShared.dll differ diff --git a/GSMCommunication/GSMCommunicationReferences/PDUConverter.dll b/GSMCommunication/GSMCommunicationReferences/PDUConverter.dll new file mode 100644 index 0000000..baee9ee Binary files /dev/null and b/GSMCommunication/GSMCommunicationReferences/PDUConverter.dll differ diff --git a/GSMCommunication/GsmCommunication/AddressData.cs b/GSMCommunication/GsmCommunication/AddressData.cs new file mode 100644 index 0000000..a815098 --- /dev/null +++ b/GSMCommunication/GsmCommunication/AddressData.cs @@ -0,0 +1,47 @@ +using System; + +/// +/// Contains network address data. +/// +namespace GsmComm.GsmCommunication +{ + public class AddressData + { + private string address; + + private int typeOfAddress; + + /// + /// Gets the network address. + /// + public string Address + { + get + { + return this.address; + } + } + + /// + /// Gets the type of the . + /// + public int TypeOfAddress + { + get + { + return this.typeOfAddress; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The network address + /// The type of the given address + public AddressData(string address, int typeOfAddress) + { + this.address = address; + this.typeOfAddress = typeOfAddress; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/BatteryChargeInfo.cs b/GSMCommunication/GsmCommunication/BatteryChargeInfo.cs new file mode 100644 index 0000000..eb460c1 --- /dev/null +++ b/GSMCommunication/GsmCommunication/BatteryChargeInfo.cs @@ -0,0 +1,53 @@ +using System; + +/// +/// Contains the ME battery charging status and charge level. +/// +namespace GsmComm.GsmCommunication +{ + public class BatteryChargeInfo + { + private int bcs; + + private int bcl; + + /// + /// Gets the battery charge level. + /// + /// Usual values are in the range from 0 (empty) to 100 (full). + public int BatteryChargeLevel + { + get + { + return this.bcl; + } + } + + /// + /// Gets the battery charging status. + /// + /// Usual values are 0 for "not charging" and 1 for "charging". + public int BatteryChargingStatus + { + get + { + return this.bcs; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The battery charging status, usually 0 for "not charging" and 1 for "charging". + /// + /// + /// The battery charge level, usually in the range of 0 (empty) to 100 (full). + /// + public BatteryChargeInfo(int batteryChargingStatus, int batteryChargeLevel) + { + this.bcs = batteryChargingStatus; + this.bcl = batteryChargeLevel; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/CbmIndicationStyle.cs b/GSMCommunication/GsmCommunication/CbmIndicationStyle.cs new file mode 100644 index 0000000..cea16f3 --- /dev/null +++ b/GSMCommunication/GsmCommunication/CbmIndicationStyle.cs @@ -0,0 +1,30 @@ +/// +/// Specifies the possible indication settings for new cell broadcast messages (CBMs). +/// +namespace GsmComm.GsmCommunication +{ + public enum CbmIndicationStyle + { + /// + /// No CBM indications are routed to the TE. + /// + Disabled, + /// + /// If CBM is stored into ME/TA, indication of the memory location is routed to the TE. + /// + RouteMemoryLocation, + /// + /// New CBMs are routed directly to the TE. + /// + /// If ME supports data coding groups which define special routing also for messages other than + /// class 3 (e.g. SIM specific messages), ME may choose not to route messages of such data coding schemes + /// into TE (indication of a stored CBM may be given as with . + RouteMessage, + /// + /// Class 3 CBMs are routed directly to TE using the same indications as with . + /// If CBM storage is supported, messages of other classes result in indication as with + /// . + /// + RouteSpecial + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/Charset.cs b/GSMCommunication/GsmCommunication/Charset.cs new file mode 100644 index 0000000..4b4c8d8 --- /dev/null +++ b/GSMCommunication/GsmCommunication/Charset.cs @@ -0,0 +1,35 @@ +using System; + +/// +/// Lists some common character sets. +/// +namespace GsmComm.GsmCommunication +{ + public class Charset + { + /// The UCS2 (Unicode) character set + public const string Ucs2 = "UCS2"; + + /// The GSM character set + public const string Gsm = "GSM"; + + /// The PCCP437 character set + public const string Pccp437 = "PCCP437"; + + /// The PCDN character set + public const string Pcdn = "PCDN"; + + /// The IRA character set + public const string Ira = "IRA"; + + /// The ISO 8859-1 character set + public const string Iso8859_1 = "8859-1"; + + /// The characters encoded as hex + public const string Hex = "HEX"; + + public Charset() + { + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/DecodedShortMessage.cs b/GSMCommunication/GsmCommunication/DecodedShortMessage.cs new file mode 100644 index 0000000..515147e --- /dev/null +++ b/GSMCommunication/GsmCommunication/DecodedShortMessage.cs @@ -0,0 +1,78 @@ +using GsmComm.PduConverter; +using System; + +/// +/// Represents a short message from the phone in its decoded state. +/// +namespace GsmComm.GsmCommunication +{ + public class DecodedShortMessage + { + private int index; + + private SmsPdu data; + + private PhoneMessageStatus status; + + private string storage; + + /// + /// Gets the decoded message. + /// + public SmsPdu Data + { + get + { + return this.data; + } + } + + /// + /// Gets the index where the message is saved in the device in the . + /// + public int Index + { + get + { + return this.index; + } + } + + /// + /// Gets the parsed message status. + /// + public PhoneMessageStatus Status + { + get + { + return this.status; + } + } + + /// + /// Gets the phone storage the message was read from. + /// + public string Storage + { + get + { + return this.storage; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The index where the message is saved in the device in the . + /// The decoded message. + /// The parsed message status. + /// The phone storage the message was read from. + public DecodedShortMessage(int index, SmsPdu data, PhoneMessageStatus status, string storage) + { + this.index = index; + this.data = data; + this.status = status; + this.storage = storage; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/DeleteFlag.cs b/GSMCommunication/GsmCommunication/DeleteFlag.cs new file mode 100644 index 0000000..cb8a54e --- /dev/null +++ b/GSMCommunication/GsmCommunication/DeleteFlag.cs @@ -0,0 +1,30 @@ +/// +/// Lists the possible delete flags for AT+CMGD. +/// +namespace GsmComm.GsmCommunication +{ + public enum DeleteFlag + { + /// Delete the message specified in index. + DeleteSpecified, + /// + /// Delete all read messages from preferred message storage, leaving unread messages and stored mobile + /// originated messages (whether sent or not) untouched. + /// + DeleteRead, + /// + /// Delete all read messages from preferred message storage and sent mobile originated messages, + /// leaving unread messages and unsent mobile originated messages untouched. + /// + DeleteReadAndSent, + /// + /// Delete all read messages from preferred message storage, sent and unsent mobile originated messages + /// leaving unread messages untouched. + /// + DeleteReadSentAndUnsent, + /// + /// Delete all messages from preferred message storage including unread messages. + /// + DeleteAll + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/DeleteScope.cs b/GSMCommunication/GsmCommunication/DeleteScope.cs new file mode 100644 index 0000000..190a92e --- /dev/null +++ b/GSMCommunication/GsmCommunication/DeleteScope.cs @@ -0,0 +1,17 @@ +/// +/// Lists the possible scopes for deleting short messages. +/// +namespace GsmComm.GsmCommunication +{ + public enum DeleteScope + { + /// Delete all read messages. + Read = 1, + /// Delete all read and sent messages. + ReadAndSent = 2, + /// Delete all read, sent and unsent messages + ReadSentAndUnsent = 3, + /// Delete all messages including unread messages. + All = 4 + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/GsmCommMain.cs b/GSMCommunication/GsmCommunication/GsmCommMain.cs new file mode 100644 index 0000000..e198f8e --- /dev/null +++ b/GSMCommunication/GsmCommunication/GsmCommMain.cs @@ -0,0 +1,1543 @@ +using GsmComm.PduConverter; +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Remoting.Messaging; + +/// +/// Interacts with a mobile phone to execute various functions. +/// +namespace GsmComm.GsmCommunication +{ + public class GsmCommMain + { + /// + /// The default port to connect to. + /// + public const string DefaultPortName = "COM1"; + + /// + /// The default baud rate to use. + /// + public const int DefaultBaudRate = 19200; + + /// + /// The default communication timeout. + /// + public const int DefaultTimeout = 300; + + private GsmPhone theDevice; + + private static bool versionInfoLogged; + + private LogLevel logLevel; + + /// + /// Gets the baud rate in use. + /// + public int BaudRate + { + get + { + return this.theDevice.BaudRate; + } + } + + /// + /// Gets or sets the delay in milliseconds between the checks to verify that the connection + /// to the phone is still alive. + /// + public int ConnectionCheckDelay + { + get + { + return this.theDevice.ConnectionCheckDelay; + } + set + { + this.theDevice.ConnectionCheckDelay = value; + } + } + + /// + /// Get or sets the current log level for this instance. + /// + public LogLevel LogLevel + { + get + { + return this.logLevel; + } + set + { + this.logLevel = value; + this.theDevice.LogLevel = value; + } + } + + /// + /// Gets COM port currently connected to. + /// + public string PortName + { + get + { + return this.theDevice.PortName; + } + } + + /// + /// Gets the current communication timeout. + /// + public int Timeout + { + get + { + return this.theDevice.Timeout; + } + } + + static GsmCommMain() + { + GsmCommMain.versionInfoLogged = false; + } + + /// + /// Initializes a new instance of the class using default parameters. + /// + /// Uses the default values: port=COM1, baud rate=19200, timeout=300ms. + public GsmCommMain() + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone("COM1", 19200, 300); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Initializes a new instance of the class using the specified parameters. + /// + /// The communication (COM) port to use. + /// Uses the default values: baud rate=19200, timeout=300ms. + public GsmCommMain(string portName) + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone(portName, 19200, 300); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Initializes a new instance of the class using the specified parameters. + /// + /// The communication (COM) port to use. + /// Uses the default values: baud rate=19200, timeout=300ms. + public GsmCommMain(int portNumber) + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone(portNumber, 19200, 300); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Initializes a new instance of the class using the specified parameters. + /// + /// The communication (COM) port to use. + /// The baud rate (speed) to use. + /// Uses the default values: timeout=300ms. + public GsmCommMain(string portName, int baudRate) + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone(portName, baudRate, 300); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Initializes a new instance of the class using the specified parameters. + /// + /// The communication (COM) port to use. + /// The baud rate (speed) to use. + /// Uses the default values: timeout=300ms. + public GsmCommMain(int portNumber, int baudRate) + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone(portNumber, baudRate, 300); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Initializes a new instance of the class using the specified parameters. + /// + /// The communication (COM) port to use. + /// The baud rate (speed) to use. + /// The communication timeout in milliseconds. + public GsmCommMain(string portName, int baudRate, int timeout) + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone(portName, baudRate, timeout); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Initializes a new instance of the class using the specified parameters. + /// + /// The communication (COM) port to use. + /// The baud rate (speed) to use. + /// The communication timeout in milliseconds. + public GsmCommMain(int portNumber, int baudRate, int timeout) + { + this.logLevel = LogLevel.Verbose; + this.theDevice = new GsmPhone(portNumber, baudRate, timeout); + this.theDevice.LogLevel = this.logLevel; + } + + /// + /// Acknowledges a new received short message that was directly routed to the application. + /// + /// Acknowledges are required for most received messages if + /// returns true. The acknowledge requirement can be changed with the + /// method. + /// + public void AcknowledgeNewMessage() + { + this.theDevice.AcknowledgeNewMessage(); + } + + private void AsyncCallback(IAsyncResult ar) + { + AsyncResult asyncResult = (AsyncResult)ar; + if (asyncResult.AsyncDelegate as GsmCommMain.MessageEventHandler == null) + { + if (asyncResult.AsyncDelegate as GsmCommMain.MessageErrorEventHandler == null) + { + this.LogIt(LogLevel.Warning, string.Concat("AsyncCallback got unknown delegate: ", asyncResult.AsyncDelegate.GetType().ToString())); + return; + } + else + { + this.LogIt(LogLevel.Info, "Ending async MessageErrorEventHandler call"); + GsmCommMain.MessageErrorEventHandler asyncDelegate = (GsmCommMain.MessageErrorEventHandler)asyncResult.AsyncDelegate; + asyncDelegate.EndInvoke(ar); + return; + } + } + else + { + this.LogIt(LogLevel.Info, "Ending async MessageEventHandler call"); + GsmCommMain.MessageEventHandler messageEventHandler = (GsmCommMain.MessageEventHandler)asyncResult.AsyncDelegate; + messageEventHandler.EndInvoke(ar); + return; + } + } + + /// + /// Closes the connection to the device. + /// + /// You can check the current connection state with the method. + /// + /// + /// + public void Close() + { + this.theDevice.Close(); + this.DisconnectEvents(); + } + + private void ConnectEvents() + { + this.theDevice.LoglineAdded += new LoglineAddedEventHandler(this.theDevice_LoglineAdded); + this.theDevice.ReceiveProgress += new ProgressEventHandler(this.theDevice_ReceiveProgress); + this.theDevice.ReceiveComplete += new ProgressEventHandler(this.theDevice_ReceiveComplete); + this.theDevice.MessageReceived += new MessageReceivedEventHandler(this.theDevice_MessageReceived); + this.theDevice.PhoneConnected += new EventHandler(this.theDevice_PhoneConnected); + this.theDevice.PhoneDisconnected += new EventHandler(this.theDevice_PhoneDisconnected); + } + + /// + /// Creates a new phonebook entry. + /// + /// The entry to create. + /// The storage to save the entry. + /// The property of the entry is ignored, + /// the entry is always saved in the first free location. All other properties must be set + /// correctly. + /// + public void CreatePhonebookEntry(PhonebookEntry entry, string storage) + { + this.theDevice.SelectPhonebookStorage(storage); + this.theDevice.WritePhonebookEntry(entry); + } + + /// + /// Decodes a received short message. + /// + /// The message to decode + /// The decoded message as object. + public SmsPdu DecodeReceivedMessage(ShortMessage message) + { + IncomingSmsPdu incomingSmsPdu = null; + try + { + incomingSmsPdu = IncomingSmsPdu.Decode(message.Data, true, message.Length); + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, string.Concat("IncomingSmsPdu decoder can't decode message: ", exception.Message)); + this.LogIt(LogLevel.Error, string.Concat(" Message data = \"", message.Data, "\"")); + int length = message.Length; + this.LogIt(LogLevel.Error, string.Concat(" Message length = ", length.ToString())); + throw; + } + return incomingSmsPdu; + } + + /// + /// Decodes a short message read from the phone. + /// + /// The message to decode. + /// The decoded short message. + /// + /// Use this function to decode messages that were read with the function. + /// + /// + /// There is no decoder available that can handle the message's status. + public SmsPdu DecodeShortMessage(ShortMessageFromPhone message) + { + PhoneMessageStatus status = (PhoneMessageStatus)message.Status; + SmsPdu smsPdu = null; + PhoneMessageStatus phoneMessageStatu = status; + if (phoneMessageStatu == PhoneMessageStatus.ReceivedUnread || phoneMessageStatu == PhoneMessageStatus.ReceivedRead) + { + try + { + smsPdu = this.DecodeReceivedMessage(message); + } + catch (Exception exception) + { + this.LogIt(LogLevel.Warning, "Unable to decode message with specified status - trying other variants."); + smsPdu = this.DecodeStoredMessage(message); + } + } + else if (phoneMessageStatu == PhoneMessageStatus.StoredUnsent || phoneMessageStatu == PhoneMessageStatus.StoredSent) + { + try + { + smsPdu = this.DecodeStoredMessage(message); + } + catch (Exception exception1) + { + this.LogIt(LogLevel.Warning, "Unable to decode message with specified status - trying other variants."); + smsPdu = this.DecodeReceivedMessage(message); + } + } + else + { + string[] str = new string[5]; + str[0] = "No decoder available for message of status \""; + str[1] = status.ToString(); + str[2] = "\" (index "; + int index = message.Index; + str[3] = index.ToString(); + str[4] = ")."; + string str1 = string.Concat(str); + this.LogIt(LogLevel.Error, str1); + throw new CommException(str1); + } + return smsPdu; + } + + private SmsPdu DecodeStoredMessage(ShortMessage message) + { + OutgoingSmsPdu outgoingSmsPdu = null; + try + { + outgoingSmsPdu = OutgoingSmsPdu.Decode(message.Data, true, message.Length); + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, string.Concat("OutgoingSmsPdu decoder can't decode message: ", exception.Message)); + this.LogIt(LogLevel.Error, string.Concat(" Message data = \"", message.Data, "\"")); + int length = message.Length; + this.LogIt(LogLevel.Error, string.Concat(" Message length = ", length.ToString())); + throw; + } + return outgoingSmsPdu; + } + + /// + /// Deletes all phonebook entries. + /// + /// The storage to use. + /// To delete single entries, use the function. + /// + /// + public void DeleteAllPhonebookEntries(string storage) + { + this.theDevice.SelectPhonebookStorage(storage); + PhonebookEntry[] phonebookEntryArray = this.theDevice.ReadPhonebookEntries(); + PhonebookEntry[] phonebookEntryArray1 = phonebookEntryArray; + for (int i = 0; i < (int)phonebookEntryArray1.Length; i++) + { + PhonebookEntry phonebookEntry = phonebookEntryArray1[i]; + this.theDevice.DeletePhonebookEntry(phonebookEntry.Index); + } + } + + /// + /// Deletes the specified short message. + /// + /// The index of the message to delete. + /// The storage to use. + /// + /// To delete a group of messages, use the function. + /// + /// + public void DeleteMessage(int index, string storage) + { + this.LogIt(LogLevel.Info, "Deleting message..."); + this.theDevice.SelectReadStorage(storage); + this.theDevice.DeleteMessage(index); + } + + /// + /// Deletes the specified group of messages. + /// + /// Specifies the messages that are affected by this command. + /// The storage to use. + /// + /// To delete a single message, use the function. + /// + /// + public void DeleteMessages(DeleteScope scope, string storage) + { + int num = scope; + DeleteFlag deleteFlag = (DeleteFlag)Enum.Parse(typeof(DeleteFlag), num.ToString()); + string[] str = new string[5]; + str[0] = "Deleting \""; + str[1] = scope.ToString(); + str[2] = "\" messages in storage \""; + str[3] = storage; + str[4] = "\"..."; + this.LogIt(LogLevel.Info, string.Concat(str)); + this.theDevice.SelectReadStorage(storage); + this.theDevice.DeleteMessage(1, deleteFlag); + } + + /// + /// Deletes a phonebook entry. + /// + /// The index of the entry to delete. + /// The storage to use. + /// To delete all phonebook entries at once, use the + /// function. + /// + /// + /// + public void DeletePhonebookEntry(int index, string storage) + { + this.theDevice.SelectPhonebookStorage(storage); + this.theDevice.DeletePhonebookEntry(index); + } + + /// + /// Disables all message notifications. + /// + /// + /// Call this function after a call to to disable this functionality + /// again. + /// It's highly recommended to disable notifications again before closing the connection to + /// the phone. If it doesn't get disabled, the phone will still try to send notifications, which will not be + /// successful. The phone may buffer unsuccessful notifications, but you should generally not rely + /// on this. + /// + /// + public void DisableMessageNotifications() + { + this.theDevice.SetMessageIndications(new MessageIndicationSettings(0, 0, 0, 0, 0)); + } + + /// + /// Disables all message routings. + /// + /// + /// Call this function after a call to to disable this functionality + /// again. + /// CAUTION: It's highly recommended to disable routing again before closing the connection to + /// the phone. If it doesn't get disabled, the phone will still try to route messages on, which will not be + /// successful. You may lose messages in such a case if the phone doesn't buffer unsuccessful routings. + /// + /// + /// + public void DisableMessageRouting() + { + this.theDevice.SetMessageIndications(new MessageIndicationSettings(0, 0, 0, 0, 0)); + } + + /// + /// Disables the SMS batch mode. + /// + /// Disables the SMS batch mode previously enabled with + /// or . + /// + public void DisableSmsBatchMode() + { + this.theDevice.SetMoreMessagesToSend(MoreMessagesMode.Disabled); + } + + private void DisconnectEvents() + { + this.theDevice.LoglineAdded -= new LoglineAddedEventHandler(this.theDevice_LoglineAdded); + this.theDevice.ReceiveProgress -= new ProgressEventHandler(this.theDevice_ReceiveProgress); + this.theDevice.ReceiveComplete -= new ProgressEventHandler(this.theDevice_ReceiveComplete); + this.theDevice.MessageReceived -= new MessageReceivedEventHandler(this.theDevice_MessageReceived); + this.theDevice.PhoneConnected -= new EventHandler(this.theDevice_PhoneConnected); + this.theDevice.PhoneDisconnected -= new EventHandler(this.theDevice_PhoneDisconnected); + } + + /// + /// Enables notifications of new received short messages. + /// + /// + /// When a new message is received that is either a standard SMS message or a status report, + /// the event is fired. The + /// in this event must be cast to a object, which contains the memory + /// location of the message that was saved in the phone. You can then use the + /// method (for example) to read the new message. + /// The supported notification settings vary between different phone models. Therefore the phone is + /// queried first and then the supported settings of the phone are compared to the settings needed for + /// the notification functionality to work. If a specific setting is not supported and there is no + /// alternative setting possible, an exception will be raised. + /// To disable message notifications, use the function. + /// + /// EnableMessageNotifications can't be used with at the same time. + /// Disable one functionality before using the other. + /// + /// + /// + /// + public void EnableMessageNotifications() + { + MessageIndicationSupport supportedIndications = this.theDevice.GetSupportedIndications(); + MessageIndicationMode messageIndicationMode = MessageIndicationMode.BufferAndFlush; + if (!supportedIndications.SupportsMode(messageIndicationMode)) + { + if (supportedIndications.SupportsMode(MessageIndicationMode.SkipWhenReserved)) + { + messageIndicationMode = MessageIndicationMode.SkipWhenReserved; + } + else + { + if (supportedIndications.SupportsMode(MessageIndicationMode.ForwardAlways)) + { + messageIndicationMode = MessageIndicationMode.ForwardAlways; + } + else + { + throw new CommException("The phone does not support any of the required message indication modes."); + } + } + } + SmsDeliverIndicationStyle smsDeliverIndicationStyle = SmsDeliverIndicationStyle.RouteMemoryLocation; + if (supportedIndications.SupportsDeliverStyle(smsDeliverIndicationStyle)) + { + CbmIndicationStyle cbmIndicationStyle = CbmIndicationStyle.Disabled; + SmsStatusReportIndicationStyle smsStatusReportIndicationStyle = SmsStatusReportIndicationStyle.RouteMemoryLocation; + if (!supportedIndications.SupportsStatusReportStyle(smsStatusReportIndicationStyle)) + { + this.LogIt(LogLevel.Warning, "Attention: The phone does not support notification about new status reports. As a fallback it will be disabled."); + smsStatusReportIndicationStyle = SmsStatusReportIndicationStyle.Disabled; + } + IndicationBufferSetting indicationBufferSetting = IndicationBufferSetting.Flush; + if (!supportedIndications.SupportsBufferSetting(indicationBufferSetting)) + { + if (supportedIndications.SupportsBufferSetting(IndicationBufferSetting.Clear)) + { + indicationBufferSetting = IndicationBufferSetting.Clear; + } + else + { + throw new CommException("The phone does not support any of the required buffer settings."); + } + } + MessageIndicationSettings messageIndicationSetting = new MessageIndicationSettings(messageIndicationMode, smsDeliverIndicationStyle, cbmIndicationStyle, smsStatusReportIndicationStyle, indicationBufferSetting); + this.theDevice.SetMessageIndications(messageIndicationSetting); + return; + } + else + { + throw new CommException("The phone does not support notification for standard SMS (SMS-DELIVER) messages. "); + } + } + + /// + /// Enables direct routing of new received short messages to the application. + /// + /// + /// When a new message is received that is either a standard SMS message or a status report, + /// the event is fired. The + /// in this event must be cast to a object which can then be decoded using + /// . + /// CAUTION: Because the messages are forwared directly, they are not saved in the phone. + /// If for some reason the message must be saved it must explicitly be done afterwards. Either by using + /// the or functions + /// to write the message back to the phone or by storing the message somewhere else for later use. + /// It may be necessary to acknlowledge new routed messages to the phone, either because this + /// is desired for reliable message transfer or because it is preconfigured in the phone. Use + /// to find out if acknowledgements must be done. To do the actual + /// acknowledge, use . + /// The supported routing settings vary between different phone models. Therefore the phone is + /// queried first and then the supported settings of the phone are compared to the settings needed for + /// the routing functionality to work. If a specific setting is not supported and there is no + /// alternative setting possible, an exception will be raised. + /// To disable message routing, use the function. + /// EnableMessageRouting can't be used with at the same time. + /// Disable one functionality before using the other. + /// + /// + /// + /// + /// + /// + public void EnableMessageRouting() + { + MessageIndicationSupport supportedIndications = this.theDevice.GetSupportedIndications(); + MessageIndicationMode messageIndicationMode = MessageIndicationMode.BufferAndFlush; + if (!supportedIndications.SupportsMode(messageIndicationMode)) + { + if (supportedIndications.SupportsMode(MessageIndicationMode.SkipWhenReserved)) + { + messageIndicationMode = MessageIndicationMode.SkipWhenReserved; + } + else + { + if (supportedIndications.SupportsMode(MessageIndicationMode.ForwardAlways)) + { + messageIndicationMode = MessageIndicationMode.ForwardAlways; + } + else + { + throw new CommException("The phone does not support any of the required message indication modes."); + } + } + } + SmsDeliverIndicationStyle smsDeliverIndicationStyle = SmsDeliverIndicationStyle.RouteMessage; + if (supportedIndications.SupportsDeliverStyle(smsDeliverIndicationStyle)) + { + CbmIndicationStyle cbmIndicationStyle = CbmIndicationStyle.Disabled; + SmsStatusReportIndicationStyle smsStatusReportIndicationStyle = SmsStatusReportIndicationStyle.RouteMessage; + if (!supportedIndications.SupportsStatusReportStyle(smsStatusReportIndicationStyle)) + { + this.LogIt(LogLevel.Warning, "Attention: The phone does not support routing of new status reports. As a fallback it will be disabled."); + smsStatusReportIndicationStyle = SmsStatusReportIndicationStyle.Disabled; + } + IndicationBufferSetting indicationBufferSetting = IndicationBufferSetting.Flush; + if (!supportedIndications.SupportsBufferSetting(indicationBufferSetting)) + { + if (supportedIndications.SupportsBufferSetting(IndicationBufferSetting.Clear)) + { + indicationBufferSetting = IndicationBufferSetting.Clear; + } + else + { + throw new CommException("The phone does not support any of the required buffer settings."); + } + } + MessageIndicationSettings messageIndicationSetting = new MessageIndicationSettings(messageIndicationMode, smsDeliverIndicationStyle, cbmIndicationStyle, smsStatusReportIndicationStyle, indicationBufferSetting); + this.theDevice.SetMessageIndications(messageIndicationSetting); + return; + } + else + { + throw new CommException("The phone does not support routing of standard SMS (SMS-DELIVER) messages."); + } + } + + /// + /// Enables the SMS batch mode permanently. + /// + /// + /// When this feature is enabled (and supported by the network), multiple messages can be sent much + /// faster, as the SMS link is kept open between the messages. + /// If there is no message sent for 1-5 seconds (the exact value is up to the phones's implementation) + /// after the last sent SMS message, the SMS link is closed but the batch mode is kept enabled. You have + /// to explicitely disable it with . + /// If you don't want to care about turning the batch mode off when done with sending, + /// consider using instead. + /// + public void EnablePermanentSmsBatchMode() + { + this.theDevice.SetMoreMessagesToSend(MoreMessagesMode.Permanent); + } + + /// + /// Enables the SMS batch mode temporarily. + /// + /// + /// When this feature is enabled (and supported by the network), multiple messages can be sent much + /// faster, as the SMS link is kept open between the messages. + /// If there is no message sent for 1-5 seconds (the exact value is up to the phones's implementation) + /// after the last sent SMS message, the SMS link is closed and the batch mode is disabled + /// automatically. You have to re-enable it before you send the next batch of messages, but you + /// also don't have to care about turning it off when finished with sending. + /// + public void EnableTemporarySmsBatchMode() + { + this.theDevice.SetMoreMessagesToSend(MoreMessagesMode.Temporary); + } + + /// + /// Enters a password at the phone which is necessary before it can operated. + /// + /// The SIM PIN, SIM PUK or other password required. + /// Get the current PIN status with to check + /// whether a password must be entered. + public void EnterPin(string pin) + { + this.theDevice.EnterPin(pin); + } + + /// + /// Finds phonebook entries. + /// + /// The text in the entry to find. + /// The storage to search. + /// An array of phonebook entries matching the specified criteria. + /// The device executes the actual search. If you need the storage information with the results, + /// use the function instead. + /// + /// + public PhonebookEntry[] FindPhonebookEntries(string findtext, string storage) + { + new ArrayList(); + this.theDevice.SelectPhonebookStorage(storage); + return this.theDevice.FindPhonebookEntries(findtext); + } + + /// + /// Finds phonebook entries and saves the storage where they came from. + /// + /// The text in the entry to find. + /// The storage to search. + /// An array of phonebook entries matching the specified criteria. + /// The device executes the actual search. If you don't need the storage information with the + /// results, use the function instead. + /// + /// + public PhonebookEntryWithStorage[] FindPhonebookEntriesWithStorage(string findtext, string storage) + { + ArrayList arrayLists = new ArrayList(); + this.theDevice.SelectPhonebookStorage(storage); + PhonebookEntry[] phonebookEntryArray = this.theDevice.FindPhonebookEntries(findtext); + for (int i = 0; i < (int)phonebookEntryArray.Length; i++) + { + PhonebookEntry phonebookEntry = phonebookEntryArray[i]; + arrayLists.Add(new PhonebookEntryWithStorage(phonebookEntry, storage)); + } + PhonebookEntryWithStorage[] phonebookEntryWithStorageArray = new PhonebookEntryWithStorage[arrayLists.Count]; + arrayLists.CopyTo(phonebookEntryWithStorageArray, 0); + return phonebookEntryWithStorageArray; + } + + /// + /// Gets the phone's battery charging status. + /// + /// A object containing the battery details. + public BatteryChargeInfo GetBatteryCharge() + { + return this.theDevice.GetBatteryCharge(); + } + + /// + /// Gets the currenty selected text mode character set. + /// + /// A string containing the name of the currently selected character set. + /// + /// + /// + /// + public string GetCurrentCharacterSet() + { + return this.theDevice.GetCurrentCharacterSet(); + } + + /// + /// AT+COPS. Gets the currently selected network operator. + /// + /// An object containing the data or null if there is no current operator. + public OperatorInfo GetCurrentOperator() + { + return this.theDevice.GetCurrentOperator(); + } + + /// + /// Gets the memory status of the specified message storage. + /// + /// The storage to return the status for + /// An object containing the memory status of the specified storage. + public MemoryStatus GetMessageMemoryStatus(string storage) + { + return this.theDevice.SelectReadStorage(storage); + } + + /// + /// Gets the device's supported message storages. + /// + /// A object that contains details about the supported storages. + public MessageStorageInfo GetMessageStorages() + { + return this.theDevice.GetMessageStorages(); + } + + /// + /// Determines the current mode to select a network operator. + /// + /// The current mode. + public OperatorSelectionMode GetOperatorSelectionMode() + { + int operatorSelectionMode = this.theDevice.GetOperatorSelectionMode(); + if (Enum.IsDefined(typeof(OperatorSelectionMode), operatorSelectionMode)) + { + OperatorSelectionMode operatorSelectionMode1 = (OperatorSelectionMode)Enum.Parse(typeof(OperatorSelectionMode), operatorSelectionMode.ToString()); + return operatorSelectionMode1; + } + else + { + throw new CommException(string.Concat("Unknown operator selection mode ", operatorSelectionMode)); + } + } + + /// + /// Gets the entire phonebook of the selected storage. + /// + /// The storage to read the data from. + /// An array of phonebook entries. If you need the storage information with the + /// results, use the function instead. + /// + /// + /// + public PhonebookEntry[] GetPhonebook(string storage) + { + new ArrayList(); + this.theDevice.SelectPhonebookStorage(storage); + return this.theDevice.ReadPhonebookEntries(); + } + + /// + /// Gets the memory status of the specified phonebook storage. + /// + /// The storage to return the status for + /// An object containing the memory status of the specified storage. + public MemoryStatusWithStorage GetPhonebookMemoryStatus(string storage) + { + this.theDevice.SelectPhonebookStorage(storage); + return this.theDevice.GetPhonebookMemoryStatus(); + } + + /// + /// Gets the device's supported phonebook storages. + /// + /// An array of supported storages in coded form, usually "SM" for SIM, "ME" for + /// phone, etc. + public string[] GetPhonebookStorages() + { + return this.theDevice.GetPhonebookStorages(); + } + + /// + /// Gets the entire phonebook of the selected storage and saves the storage where the entries came from. + /// + /// The storage to read the data from. + /// An array of phonebook entries. If you don't need the storage information with the + /// results, use the function instead. + /// + /// + /// + public PhonebookEntryWithStorage[] GetPhonebookWithStorage(string storage) + { + ArrayList arrayLists = new ArrayList(); + this.theDevice.SelectPhonebookStorage(storage); + PhonebookEntry[] phonebookEntryArray = this.theDevice.ReadPhonebookEntries(); + for (int i = 0; i < (int)phonebookEntryArray.Length; i++) + { + PhonebookEntry phonebookEntry = phonebookEntryArray[i]; + arrayLists.Add(new PhonebookEntryWithStorage(phonebookEntry, storage)); + } + PhonebookEntryWithStorage[] phonebookEntryWithStorageArray = new PhonebookEntryWithStorage[arrayLists.Count]; + arrayLists.CopyTo(phonebookEntryWithStorageArray, 0); + return phonebookEntryWithStorageArray; + } + + /// + /// Returns a value indicating whether some password must be entered at the phone or not. + /// + /// The current PIN status as one of the values. + public PinStatus GetPinStatus() + { + return this.theDevice.GetPinStatus(); + } + + /// + /// Enables access to the protocol level of the current connection. + /// + /// An object that sends and receives data at the protocol level. + /// This method enables execution of custom commands that are not directly supported. It also disables execution of background + /// operations that would usually take place, such as checking whether the phone is still connected. + /// The method must be called as soon as execution of the custom commands is completed, + /// and allows for normal operations to continue. Execution of other commands besides from is not allowed + /// until is called. + /// + /// + public IProtocol GetProtocol() + { + return this.theDevice.GetProtocol(); + } + + /// + /// Gets the signal quality as calculated by the phone. + /// + /// A object containing the signal details. + public SignalQualityInfo GetSignalQuality() + { + return this.theDevice.GetSignalQuality(); + } + + /// + /// Gets the current SMS batch mode setting. + /// + /// The current mode. + public MoreMessagesMode GetSmsBatchModeSetting() + { + return this.theDevice.GetMoreMessagesToSend(); + } + + /// + /// Gets the SMS Service Center Address. + /// + /// The current SMSC address + /// This command returns the SMSC address, through which SMS messages are transmitted. + /// In text mode, this setting is used by SMS sending and SMS writing commands. In PDU mode, this setting is + /// used by the same commands, but only when the length of the SMSC address coded into the PDU data equals + /// zero. + public AddressData GetSmscAddress() + { + return this.theDevice.GetSmscAddress(); + } + + /// + /// Returns the MSISDNs related to the subscriber. + /// + /// An array of objects with one for each MSISDN + /// (Mobile Subscriber ISDN Number), depending on the services subscribed. + /// + /// This information can be stored in the SIM/UICC or in the MT. + /// If the command is supported by the phone but no number can be retrieved, + /// an empty array is returned. + /// + public SubscriberInfo[] GetSubscriberNumbers() + { + return this.theDevice.GetSubscriberNumbers(); + } + + /// + /// Gets the phone's supported text mode character sets. + /// + /// A string array containing the names of the phone's supportet text mode character sets. + /// + /// + /// + /// + public string[] GetSupportedCharacterSets() + { + return this.theDevice.GetSupportedCharacterSets(); + } + + /// + /// Gathers information that identifiy the connected device. + /// + /// An object containing data about the device. + public IdentificationInfo IdentifyDevice() + { + this.LogIt(LogLevel.Info, "Identifying device..."); + IdentificationInfo identificationInfo = new IdentificationInfo(); + identificationInfo.Manufacturer = this.theDevice.RequestManufacturer(); + identificationInfo.Model = this.theDevice.RequestModel(); + identificationInfo.Revision = this.theDevice.RequestRevision(); + identificationInfo.SerialNumber = this.theDevice.RequestSerialNumber(); + this.LogIt(LogLevel.Info, string.Concat("Manufacturer: ", identificationInfo.Manufacturer)); + this.LogIt(LogLevel.Info, string.Concat("Model: ", identificationInfo.Model)); + this.LogIt(LogLevel.Info, string.Concat("Revision: ", identificationInfo.Revision)); + this.LogIt(LogLevel.Info, string.Concat("Serial number: ", identificationInfo.SerialNumber)); + return identificationInfo; + } + + /// + /// Checks if it is required to acknowledge new directly routed incoming messages. + /// + /// true if directly routed incoming messages need to be acknowledged, false if not. + public bool IsAcknowledgeRequired() + { + int num = 0; + int num1 = 0; + int num2 = 0; + int num3 = 0; + this.theDevice.GetCurrentMessageService(out num, out num1, out num2, out num3); + return num == 1; + } + + /// + /// Determines if there is actually a device connected that responds to commands. + /// + /// true if there is a device connected and responsive, otherwise false. + /// + /// You can use this function after opening the port with to verify that there is really a device connected + /// before processding. + /// + /// + /// + public bool IsConnected() + { + return this.theDevice.IsConnected(); + } + + /// + /// Determines if the port is currently open. + /// + /// true if the port is open, otherwise false. + /// The port is open after a auccessful call to and must be closed with + /// . + /// This function does not check if there is actually a device connected, use the + /// function for that. + /// + /// + /// + /// + public bool IsOpen() + { + return this.theDevice.IsOpen(); + } + + /// + /// Lists the network operators detected by the phone. + /// + /// An array of objects containing the data of each operator. + /// If you want to determine the current operator, use the method. + public OperatorInfo2[] ListOperators() + { + return this.theDevice.ListOperators(); + } + + private void LogIt(LogLevel level, string text) + { + if (this.LoglineAdded != null && level <= this.logLevel) + { + DateTime now = DateTime.Now; + text = string.Concat(now.ToString("HH:mm:ss.fff"), " ", text); + this.LoglineAdded(this, new LoglineAddedEventArgs(level, text)); + } + } + + private void LogVersionInfo() + { + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + Version version = executingAssembly.GetName().Version; + string str = version.ToString(2); + string str1 = version.ToString(); + string imageRuntimeVersion = executingAssembly.ImageRuntimeVersion; + string str2 = string.Format("GSMComm {0} (Build {1} for .NET {2})", str, str1, imageRuntimeVersion); + this.LogIt(LogLevel.Info, str2); + } + + private void OnMessageSendComplete(OutgoingSmsPdu pdu) + { + if (this.MessageSendComplete != null) + { + this.LogIt(LogLevel.Info, "Firing async MessageSendComplete event."); + MessageEventArgs messageEventArg = new MessageEventArgs(pdu); + this.MessageSendComplete.BeginInvoke(this, messageEventArg, new AsyncCallback(this.AsyncCallback), null); + } + } + + private void OnMessageSendFailed(OutgoingSmsPdu pdu, Exception exception) + { + if (this.MessageSendFailed != null) + { + this.LogIt(LogLevel.Info, "Firing async MessageSendFailed event."); + MessageErrorEventArgs messageErrorEventArg = new MessageErrorEventArgs(pdu, exception); + this.MessageSendFailed.BeginInvoke(this, messageErrorEventArg, new AsyncCallback(this.AsyncCallback), null); + } + } + + private void OnMessageSendStarting(OutgoingSmsPdu pdu) + { + if (this.MessageSendStarting != null) + { + this.LogIt(LogLevel.Info, "Firing async MessageSendStarting event."); + MessageEventArgs messageEventArg = new MessageEventArgs(pdu); + this.MessageSendStarting.BeginInvoke(this, messageEventArg, new AsyncCallback(this.AsyncCallback), null); + } + } + + /// + /// Opens the connection to the device. + /// + /// You can check the current connection state with the method. + /// + /// + /// + public void Open() + { + if (!GsmCommMain.versionInfoLogged) + { + this.LogVersionInfo(); + GsmCommMain.versionInfoLogged = true; + } + this.ConnectEvents(); + this.theDevice.Open(); + } + + /// + /// Reads a single short message. + /// + /// The index of the message to read. + /// The storage to look in for the message. + /// A object containing the message at the index specified. + public DecodedShortMessage ReadMessage(int index, string storage) + { + this.LogIt(LogLevel.Info, "Reading message..."); + this.theDevice.SelectReadStorage(storage); + ShortMessageFromPhone shortMessageFromPhone = this.theDevice.ReadMessage(index); + if (!Enum.IsDefined(typeof(PhoneMessageStatus), shortMessageFromPhone.Status)) + { + int status = shortMessageFromPhone.Status; + throw new CommException(string.Concat("Unknown message status \"", status.ToString(), "\"!")); + } + else + { + int num = shortMessageFromPhone.Status; + PhoneMessageStatus phoneMessageStatu = (PhoneMessageStatus)Enum.Parse(typeof(PhoneMessageStatus), num.ToString()); + DecodedShortMessage decodedShortMessage = new DecodedShortMessage(shortMessageFromPhone.Index, this.DecodeShortMessage(shortMessageFromPhone), phoneMessageStatu, storage); + return decodedShortMessage; + } + } + + /// + /// Reads and decodes short messages from phone. + /// + /// The status of the messages to read. + /// The storage to look in for the messages. + /// An array of decoded messages. + /// As the decoded version of the message is not always guaranteed to + /// be exactly the same when encoded back, do not use this function if you want + /// to save the message in their original form. Use instead + /// for that case. + /// + /// + public DecodedShortMessage[] ReadMessages(PhoneMessageStatus status, string storage) + { + this.LogIt(LogLevel.Info, "Reading messages..."); + ArrayList arrayLists = new ArrayList(); + this.theDevice.SelectReadStorage(storage); + ShortMessageFromPhone[] shortMessageFromPhoneArray = this.theDevice.ListMessages(status); + for (int i = 0; i < (int)shortMessageFromPhoneArray.Length; i++) + { + ShortMessageFromPhone shortMessageFromPhone = shortMessageFromPhoneArray[i]; + try + { + if (!Enum.IsDefined(typeof(PhoneMessageStatus), shortMessageFromPhone.Status)) + { + int num = shortMessageFromPhone.Status; + throw new CommException(string.Concat("Unknown message status \"", num.ToString(), "\"!")); + } + else + { + int num1 = shortMessageFromPhone.Status; + PhoneMessageStatus phoneMessageStatu = (PhoneMessageStatus)Enum.Parse(typeof(PhoneMessageStatus), num1.ToString()); + arrayLists.Add(new DecodedShortMessage(shortMessageFromPhone.Index, this.DecodeShortMessage(shortMessageFromPhone), phoneMessageStatu, storage)); + } + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, string.Concat("Error while decoding a message: ", exception.Message, " The message was ignored.")); + } + } + DecodedShortMessage[] decodedShortMessageArray = new DecodedShortMessage[arrayLists.Count]; + arrayLists.CopyTo(decodedShortMessageArray); + return decodedShortMessageArray; + } + + /// + /// Reads short messages in their original form from phone. + /// + /// The status of the messages to read. + /// The storage to look in for the messages. + /// An array of undecoded short messages, as read from the phone. + /// This function is intended to download the messages exactly as they are + /// returned from the phone, e.g. for a backup. If you want to use the messages + /// directly, e.g. displaying them to the user without saving them, use the + /// function instead. + /// If you want to decode the saved messages later, you can use the + /// function. + /// You can import the saved message back to the phone using the + /// function. + /// + /// + /// + /// + /// + public ShortMessageFromPhone[] ReadRawMessages(PhoneMessageStatus status, string storage) + { + this.LogIt(LogLevel.Info, "Reading messages..."); + new ArrayList(); + this.theDevice.SelectReadStorage(storage); + return this.theDevice.ListMessages(status); + } + + /// + /// Disables access to the protocol level of the current connection. + /// + /// This method must be called as soon as the execution of the custom commands initiated + /// by is completed and allows for normal operations to continue. + /// + public void ReleaseProtocol() + { + this.theDevice.ReleaseProtocol(); + } + + /// + /// Enables or disables the requirement to acknowledge new received short messages + /// that are directly routed to the application. + /// + /// Set to true to require acknowledgements, set to false + /// to turn off the requirement. + /// It depends on the phone when this setting can actually be changed. + /// Because of this, it is recommended to execute + /// after a call to RequireAcknowledge to verify the new setting. + /// + public void RequireAcknowledge(bool require) + { + int num = 0; + int num1 = 0; + int num2 = 0; + int num3; + if (require) + { + num3 = 1; + } + else + { + num3 = 0; + } + int num4 = num3; + this.theDevice.SelectMessageService(num4, out num, out num1, out num2); + } + + /// + /// Resets all settings that are not stored in a profile to their factory defaults. + /// + /// This function is useful if you don't know the state your phone is and + /// want to set it up from scratch. + public void ResetToDefaultConfig() + { + this.theDevice.ResetToDefaultConfig(); + } + + /// Selects the text mode character set. + /// The character set to use. + /// + /// The class contains some common character sets. + /// To get a list of the character sets that the phone supports, use . + /// + /// + /// + public void SelectCharacterSet(string charset) + { + this.theDevice.SelectCharacterSet(charset); + } + + /// + /// Sends a short message. + /// + /// The object containing the message to send. + /// Indicates whether an exception should be + /// thrown upon an error. + /// + /// This method sends the short message contained in the PDU object. + /// The message reference returned by the phone is stored in the MessageReference + /// property of the PDU object. If there is an error, an exception will be thrown. + /// Additionally, this function also fires the event + /// upon an error and the event upon success. + /// Set the throwExceptions parameter to false if your message handling + /// uses only the events fired by this method. + /// To send multiple messages in succession, use the function. + /// + /// + public void SendMessage(OutgoingSmsPdu pdu, bool throwExceptions) + { + this.LogIt(LogLevel.Info, "Sending message..."); + this.OnMessageSendStarting(pdu); + byte num = 0; + try + { + num = this.theDevice.SendMessage(pdu.ToString(), pdu.ActualLength); + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, string.Concat("Error while sending the message: ", exception.Message)); + this.OnMessageSendFailed(pdu, exception); + if (!throwExceptions) + { + return; + } + else + { + throw; + } + } + if (pdu.MessageReference == 0) + { + pdu.MessageReference = num; + } + this.LogIt(LogLevel.Info, "Message sent successfully."); + this.OnMessageSendComplete(pdu); + } + + /// + /// Sends a short message. + /// + /// The object containing the message to send. + /// + /// This method sends the short message contained in the PDU object. + /// The message reference returned by the phone is stored in the MessageReference + /// property of the PDU object. If there is an error, an exception will be thrown. + /// Additionally, this function also fires the event + /// upon an error and the event upon success. + /// To send multiple messages in succession, use the function. + /// + /// + public void SendMessage(OutgoingSmsPdu pdu) + { + this.SendMessage(pdu, true); + } + + /// + /// Sends multiple messages in succession. Sending stops at the first error. + /// + /// The messages to send. + /// + /// + /// + /// + public void SendMessages(OutgoingSmsPdu[] pdus) + { + if (pdus != null) + { + if ((int)pdus.Length != 0) + { + int length = (int)pdus.Length; + this.LogIt(LogLevel.Info, string.Concat(length.ToString(), " message(s) to send.")); + for (int i = 0; i < (int)pdus.Length; i++) + { + OutgoingSmsPdu outgoingSmsPdu = pdus[i]; + string[] str = new string[5]; + str[0] = "Sending message "; + int num = i + 1; + str[1] = num.ToString(); + str[2] = " of "; + int length1 = (int)pdus.Length; + str[3] = length1.ToString(); + str[4] = "..."; + this.LogIt(LogLevel.Info, string.Concat(str)); + this.SendMessage(outgoingSmsPdu); + } + return; + } + else + { + this.LogIt(LogLevel.Warning, "Nothing to do!"); + return; + } + } + else + { + this.LogIt(LogLevel.Error, "Failed. Message array is null."); + throw new ArgumentNullException("pdus"); + } + } + + /// + /// Sets the new SMS service center address. + /// + /// An object containing the new address + /// This command changes the SMSC address, through which SMS messages are transmitted. + /// In text mode, this setting is used by SMS sending and SMS writing commands. In PDU mode, this setting is + /// used by the same commands, but only when the length of the SMSC address coded into the PDU data equals + /// zero. + public void SetSmscAddress(AddressData data) + { + this.theDevice.SetSmscAddress(data); + } + + /// + /// Sets the new SMS service center address. + /// + /// The new SMSC address + /// This command changes the SMSC address, through which SMS messages are transmitted. + /// In text mode, this setting is used by SMS sending and SMS writing commands. In PDU mode, this setting is + /// used by the same commands, but only when the length of the SMSC address coded into the PDU data equals + /// zero. + public void SetSmscAddress(string address) + { + this.theDevice.SetSmscAddress(address); + } + + private void theDevice_LoglineAdded(object sender, LoglineAddedEventArgs e) + { + if (this.LoglineAdded != null) + { + this.LoglineAdded(this, e); + } + } + + private void theDevice_MessageReceived(object sender, MessageReceivedEventArgs e) + { + if (this.MessageReceived != null) + { + this.MessageReceived(this, e); + } + } + + private void theDevice_PhoneConnected(object sender, EventArgs e) + { + if (this.PhoneConnected != null) + { + this.PhoneConnected(this, e); + } + } + + private void theDevice_PhoneDisconnected(object sender, EventArgs e) + { + if (this.PhoneDisconnected != null) + { + this.PhoneDisconnected(this, e); + } + } + + private void theDevice_ReceiveComplete(object sender, ProgressEventArgs e) + { + if (this.ReceiveComplete != null) + { + this.ReceiveComplete(this, e); + } + } + + private void theDevice_ReceiveProgress(object sender, ProgressEventArgs e) + { + if (this.ReceiveProgress != null) + { + this.ReceiveProgress(this, e); + } + } + + /// + /// Stores a raw short message in the specified storage. + /// + /// The message to store. + /// The storage to store the message in. + /// The index of the message. If the index could not be retrieved, zero is returned. + /// + /// This function is useful for importing messages that were previously exported with . + /// + /// + public int WriteRawMessage(ShortMessageFromPhone message, string storage) + { + this.theDevice.SelectWriteStorage(storage); + return this.theDevice.WriteMessageToMemory(message.Data, message.Length, message.Status); + } + + /// + /// Stores a raw short message in the specified storage. + /// + /// The message to store. + /// The storage to store the message in. + /// The status to set for the message. + /// The index of the message. If the index could not be retrieved, zero is returned. + public int WriteRawMessage(ShortMessage message, string storage, int status) + { + this.theDevice.SelectWriteStorage(storage); + return this.theDevice.WriteMessageToMemory(message.Data, message.Length, status); + } + + /// + /// Stores a raw short message in the specified storage without setting a specific message status. + /// + /// The message to store. + /// The storage to store the message in. + /// The index of the message. If the index could not be retrieved, zero is returned. + /// + /// The message is stored with a predefined status set in the phone. + /// + /// + /// + public int WriteRawMessageWithoutStatus(ShortMessage message, string storage) + { + this.theDevice.SelectWriteStorage(storage); + return this.theDevice.WriteMessageToMemory(message.Data, message.Length); + } + + /// + /// The event that occurs when a new line was added to the log. + /// + public event LoglineAddedEventHandler LoglineAdded; + + /// + /// The event that occurs when a new message was received. + /// + public event MessageReceivedEventHandler MessageReceived; + + /// + /// The event that occurs after a successful message transfer. + /// + public event GsmCommMain.MessageEventHandler MessageSendComplete; + + /// + /// The event that occurs after a failed message transfer. + /// + public event GsmCommMain.MessageErrorEventHandler MessageSendFailed; + + /// + /// The event that occurs immediately before transferring a new message. + /// + public event GsmCommMain.MessageEventHandler MessageSendStarting; + + /// The event that occurs when the phone is connected. + public event EventHandler PhoneConnected; + + /// The event that occurs when the phone is disconnected. + public event EventHandler PhoneDisconnected; + + /// The event that occurs when receiving from the phone is completed. + /// This event is only fired by reading operations that may take longer to complete. + public event ProgressEventHandler ReceiveComplete; + + /// The event that occurs when new data was received from the phone. + /// This event is only fired by reading operations that may take longer to complete. + public event ProgressEventHandler ReceiveProgress; + + /// + /// The method that handles the event. + /// + /// The origin of the event. + /// The associated with the event. + public delegate void MessageErrorEventHandler(object sender, MessageErrorEventArgs e); + + /// + /// The method that handles the and + /// events. + /// + /// The origin of the event. + /// The associated with the event. + public delegate void MessageEventHandler(object sender, MessageEventArgs e); + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/GsmPhone.cs b/GSMCommunication/GsmCommunication/GsmPhone.cs new file mode 100644 index 0000000..74485b2 --- /dev/null +++ b/GSMCommunication/GsmCommunication/GsmPhone.cs @@ -0,0 +1,3262 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO.Ports; +using System.Runtime.Remoting.Messaging; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Timers; + +/// +/// Interacts with a mobile phone at a low level to execute various functions. +/// +namespace GsmComm.GsmCommunication +{ + public class GsmPhone : IProtocol + { + private const int receiveTimeout = 5000; + + private const string commOK = "\r\nOK\r\n"; + + private const string commError = "\r\nERROR\r\n"; + + private const string messageServiceErrorPattern = "\\r\\n\\+CMS ERROR: (\\d+)\\r\\n"; + + private const string mobileEquipmentErrorPattern = "\\r\\n\\+CME ERROR: (\\d+)\\r\\n"; + + private SerialPort port; + + private string portName; + + private int baudRate; + + private int timeout; + + private bool logEnabled; + + private LogLevel logLevel; + + private int commThreadCheckDelay; + + private Queue rawQueue; + + private Queue inputQueue; + + private ManualResetEvent terminateCommThread; + + private ManualResetEvent dataReceived; + + private ManualResetEvent noData; + + private Thread commThread; + + private string dataToSend; + + private ManualResetEvent sendNow; + + private ManualResetEvent receiveNow; + + private ManualResetEvent sendDone; + + private bool connectionState; + + private AutoResetEvent checkConnection; + + private ManualResetEvent logThreadInitialized; + + private ManualResetEvent terminateLogThread; + + private Queue logQueue; + + private Thread logThread; + + /// + /// Gets the baud rate. + /// + public int BaudRate + { + get + { + return this.baudRate; + } + } + + /// + /// Gets or sets the delay in milliseconds between the checks to verify that the connection + /// to the phone is still alive. + /// + public int ConnectionCheckDelay + { + get + { + return this.commThreadCheckDelay; + } + set + { + int num; + if (value < 1000) + { + num = 1000; + } + else + { + num = value; + } + int num1 = num; + this.commThreadCheckDelay = num1; + } + } + + /// + /// Get or sets the current log level for this instance. + /// + public LogLevel LogLevel + { + get + { + return this.logLevel; + } + set + { + this.logLevel = value; + } + } + + /// + /// Gets the COM port. + /// + public string PortName + { + get + { + return this.portName; + } + } + + /// + /// Gets the communication timeout. + /// + public int Timeout + { + get + { + return this.timeout; + } + } + + /// + /// Initializing a new instance of the class. + /// + /// The communication (COM) port to use. + /// The baud rate (speed) to use. + /// The communication timeout in milliseconds. + public GsmPhone(string portName, int baudRate, int timeout) + { + this.commThreadCheckDelay = 10000; + if (portName != null) + { + if (portName == null || portName.Length != 0) + { + if (baudRate >= 0) + { + if (timeout >= 0) + { + this.port = null; + this.portName = portName; + this.baudRate = baudRate; + this.timeout = timeout; + this.logEnabled = true; + this.logLevel = LogLevel.Verbose; + this.rawQueue = new Queue(); + this.inputQueue = Queue.Synchronized(this.rawQueue); + this.terminateCommThread = new ManualResetEvent(false); + this.dataReceived = new ManualResetEvent(false); + this.noData = new ManualResetEvent(false); + this.commThread = null; + this.dataToSend = string.Empty; + this.sendNow = new ManualResetEvent(false); + this.receiveNow = new ManualResetEvent(false); + this.sendDone = new ManualResetEvent(false); + this.connectionState = false; + this.checkConnection = new AutoResetEvent(false); + this.logThreadInitialized = new ManualResetEvent(false); + this.terminateLogThread = new ManualResetEvent(false); + this.logQueue = new Queue(); + this.logThread = null; + return; + } + else + { + throw new ArgumentException("timeout must not be negative.", "timeout"); + } + } + else + { + throw new ArgumentException("baudRate must not be negative.", "baudRate"); + } + } + else + { + throw new ArgumentException("portName must not be an empty string."); + } + } + else + { + throw new ArgumentNullException("portName"); + } + } + + /// + /// Initializing a new instance of the class. + /// + /// The communication (COM) port to use. + /// The baud rate (speed) to use. + /// The communication timeout in milliseconds. + public GsmPhone(int portNumber, int baudRate, int timeout) : this(string.Concat("COM", portNumber.ToString()), baudRate, timeout) + { + } + + /// + /// AT+CNMA. Confirms reception of a new message (SMS-DELIVER or SMS-STATUS-REPORT) which is routed + /// directly to the TE. This acknowledgement command shall be used when "service" parameter + /// of the function equals 1. + /// + /// This sends a positive acknowledgement to the network. + public void AcknowledgeNewMessage() + { + lock (this) + { + this.AcknowledgeNewMessage(true); + } + } + + /// + /// AT+CNMA. Confirms reception of a new message (SMS-DELIVER or SMS-STATUS-REPORT) which is routed + /// directly to the TE. This acknowledgement command shall be used when "service" parameter + /// of the function equals 1. + /// + /// Specifies whether the message was received correctly. + /// Setting this parameter to true, will send a positive (RP-ACK) acknowledgement to the network. + /// Setting this parameter to false, will send a negative (RP-ERROR) acknowledgement to the network. + /// + /// + /// If ME does not get acknowledgement within required time (network timeout), ME should send RP-ERROR to + /// the network. ME/TA shall automatically disable routing to TE. + /// If command is executed, but no acknowledgement is expected, or some other ME related error occurs, + /// a is raised. + /// + public void AcknowledgeNewMessage(bool ok) + { + byte num; + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + string str = "AT+CNMA="; + if (ok) + { + num = 0; + } + else + { + num = 2; + } + string str1 = string.Concat(str, num); + this.ExecAndReceiveMultiple(str1); + } + } + + /// + /// AT+CMGF. Activates the PDU mode. Device must support it or the call will fail. + /// + private void ActivatePduMode() + { + this.LogIt(LogLevel.Info, "Activating PDU mode..."); + this.ExecAndReceiveMultiple("AT+CMGF=0"); + } + + /// + /// AT+CMGF. Activates the text mode. Device must support it or the call will fail. + /// + private void ActivateTextMode() + { + this.LogIt(LogLevel.Info, "Activating text mode..."); + this.ExecAndReceiveMultiple("AT+CMGF=1"); + } + + private void AsyncCallback(IAsyncResult ar) + { + AsyncResult asyncResult = (AsyncResult)ar; + if (asyncResult.AsyncDelegate as MessageReceivedEventHandler == null) + { + if (asyncResult.AsyncDelegate as EventHandler == null) + { + if (asyncResult.AsyncDelegate as ProgressEventHandler == null) + { + this.LogIt(LogLevel.Warning, string.Concat("AsyncCallback got unknown delegate: ", asyncResult.AsyncDelegate.GetType().ToString())); + return; + } + else + { + ProgressEventHandler asyncDelegate = (ProgressEventHandler)asyncResult.AsyncDelegate; + asyncDelegate.EndInvoke(ar); + return; + } + } + else + { + this.LogIt(LogLevel.Info, "Ending async EventHandler call"); + EventHandler eventHandler = (EventHandler)asyncResult.AsyncDelegate; + eventHandler.EndInvoke(ar); + return; + } + } + else + { + this.LogIt(LogLevel.Info, "Ending async MessageReceivedEventHandler call"); + MessageReceivedEventHandler messageReceivedEventHandler = (MessageReceivedEventHandler)asyncResult.AsyncDelegate; + messageReceivedEventHandler.EndInvoke(ar); + return; + } + } + + private void CheckConnection() + { + if (Monitor.TryEnter(this)) + { + try + { + bool flag = this.IsConnectedInternal(); + this.SetNewConnectionState(flag); + } + finally + { + Monitor.Exit(this); + } + return; + } + else + { + this.LogIt(LogLevel.Verbose, "Object locked - connection check not performed."); + return; + } + } + + /// + /// Closes the connection to the device. + /// + /// You can check the current connection state with the method. + /// + /// + /// + /// Port not open. + public void Close() + { + lock (this) + { + if (this.IsOpen()) + { + this.TerminateCommThread(); + try + { + this.ClosePort(); + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Warning, "Closing the port failed. The exeption will not be rethrown to allow proper shutdown."); + this.LogIt(LogLevel.Warning, "Error details:"); + this.LogItShow(LogLevel.Warning, exception.ToString(), ""); + } + this.TerminateLogThread(); + } + else + { + throw new InvalidOperationException("Port not open."); + } + } + } + + private void ClosePort() + { + if (!this.port.IsOpen) + { + this.LogIt(LogLevel.Warning, "Attempted to close a closed serial connection. Ignored."); + } + else + { + this.LogIt(LogLevel.Info, "Closing serial connection."); + this.port.Close(); + this.port.DataReceived -= new SerialDataReceivedEventHandler(this.port_DataReceived); + if (this.connectionState) + { + this.connectionState = false; + this.OnPhoneDisconnected(); + return; + } + } + } + + private void CommThread() + { + this.LogIt(LogLevel.Info, "Communication thread started."); + System.Timers.Timer timer = new System.Timers.Timer((double)this.commThreadCheckDelay); + timer.Elapsed += new ElapsedEventHandler(this.connectionTimer_Elapsed); + timer.AutoReset = false; + timer.Start(); + object[] objArray = new object[1]; + objArray[0] = this.commThreadCheckDelay; + this.LogIt(LogLevel.Info, "Connection to phone will be checked every {0} ms.", objArray); + WaitHandle[] waitHandleArray = new WaitHandle[4]; + waitHandleArray[0] = this.terminateCommThread; + waitHandleArray[1] = this.sendNow; + waitHandleArray[2] = this.receiveNow; + waitHandleArray[3] = this.checkConnection; + WaitHandle[] waitHandleArray1 = waitHandleArray; + while (true) + { + int num = WaitHandle.WaitAny(waitHandleArray1); + if (num == 0) + { + break; + } + if (num != 1) + { + if (num != 2) + { + if (num == 3) + { + timer.Stop(); + this.CheckConnection(); + timer.Start(); + } + } + else + { + if (this.CommThreadReceive()) + { + timer.Stop(); + this.checkConnection.Reset(); + timer.Start(); + } + } + } + else + { + string str = this.dataToSend; + this.sendNow.Reset(); + this.inputQueue.Clear(); + this.dataReceived.Reset(); + try + { + this.SendInternal(str, true); + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, "Error while sending data to the phone."); + this.LogIt(LogLevel.Error, "Error details:"); + this.LogIt(LogLevel.Error, exception.ToString()); + } + this.sendDone.Set(); + } + } + this.LogIt(LogLevel.Info, "Communication thread is terminating."); + if (!this.terminateCommThread.WaitOne(0, false)) + { + this.LogIt(LogLevel.Warning, "Communication thread terminates without a stop signal!"); + } + timer.Stop(); + timer.Dispose(); + this.checkConnection.Reset(); + this.receiveNow.Reset(); + this.dataReceived.Reset(); + this.noData.Reset(); + this.inputQueue.Clear(); + this.sendNow.Reset(); + this.sendDone.Reset(); + this.dataToSend = string.Empty; + } + + private bool CommThreadReceive() + { + bool flag; + string str = null; + this.receiveNow.Reset(); + this.noData.Reset(); + bool flag1 = false; + MessageIndicationHandlers messageIndicationHandler = new MessageIndicationHandlers(); + StringBuilder stringBuilder = new StringBuilder(); + do + { + flag = false; + try + { + if (this.ReceiveInternal(out str)) + { + stringBuilder.Append(str); + flag = true; + } + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, "Error while receiving data from the phone."); + this.LogIt(LogLevel.Error, "Error details:"); + this.LogIt(LogLevel.Error, exception.ToString()); + stringBuilder = new StringBuilder(); + flag = false; + break; + } + try + { + if (!flag && messageIndicationHandler.IsIncompleteUnsolicitedMessage(stringBuilder.ToString())) + { + this.LogIt(LogLevel.Info, "Incomplete unsolicited message found, reading on after sleep."); + Thread.Sleep(this.timeout); + flag = true; + } + } + catch (Exception exception3) + { + Exception exception2 = exception3; + this.LogIt(LogLevel.Error, "Error while checking received data for unsolicited messages."); + this.LogIt(LogLevel.Error, "Received data was:"); + this.LogItShow(LogLevel.Error, stringBuilder.ToString(), ">> "); + this.LogIt(LogLevel.Error, "This data will not be processed further."); + this.LogIt(LogLevel.Error, "Error details:"); + this.LogIt(LogLevel.Error, exception2.ToString()); + stringBuilder = new StringBuilder(); + flag = false; + } + } + while (flag); + if (stringBuilder.Length <= 0) + { + this.noData.Set(); + flag1 = false; + } + else + { + this.inputQueue.Enqueue(stringBuilder.ToString()); + string str1 = this.MakeQueueString(this.inputQueue); + if (this.HandleUnsolicitedMessages(ref str1)) + { + this.inputQueue.Clear(); + this.inputQueue.Enqueue(str1); + } + this.dataReceived.Set(); + flag1 = true; + } + return flag1; + } + + private void connectionTimer_Elapsed(object sender, ElapsedEventArgs e) + { + this.checkConnection.Set(); + } + + private void CreateCommThread() + { + if (!this.IsCommThreadRunning()) + { + this.terminateCommThread.Reset(); + this.commThread = new Thread(new ThreadStart(this.CommThread)); + this.commThread.Name = "GsmPhone comm thread"; + this.commThread.Start(); + return; + } + else + { + this.LogIt(LogLevel.Warning, "Comm thread already created, ignoring call to CreateCommThread."); + return; + } + } + + private void CreateLogThread() + { + if (this.logThread == null || !this.logThread.IsAlive) + { + this.logThreadInitialized.Reset(); + this.terminateLogThread.Reset(); + this.logThread = new Thread(new ThreadStart(this.LogThread)); + this.logThread.Name = "GsmPhone log thread"; + this.logThread.Start(); + this.logThreadInitialized.WaitOne(); + this.LogIt(LogLevel.Info, "Log thread started."); + return; + } + else + { + this.LogIt(LogLevel.Warning, "Log thread already created, ignoring call to CreateLogThread."); + return; + } + } + + /// + /// Decodes a data stream with phonebook entries into objects. + /// + /// The entries to decode + /// The string the lines start with + private PhonebookEntry[] DecodePhonebookStream(string input, string prefix) + { + this.LogIt(LogLevel.Info, "Decoding phonebook entries..."); + ArrayList arrayLists = new ArrayList(); + Regex regex = new Regex(string.Concat(Regex.Escape(prefix), "(\\d+),\"(.+)\",(\\d+),\"(.+)\".*\\r\\n")); + for (Match i = regex.Match(input); i.Success; i = i.NextMatch()) + { + int num = int.Parse(i.Groups[1].Value); + string value = i.Groups[2].Value; + int num1 = int.Parse(i.Groups[3].Value); + string str = i.Groups[4].Value; + arrayLists.Add(new PhonebookEntry(num, value, num1, str)); + string[] strArrays = new string[9]; + strArrays[0] = "Entry: index="; + strArrays[1] = num.ToString(); + strArrays[2] = ", number=\""; + strArrays[3] = value; + strArrays[4] = "\", type="; + strArrays[5] = num1.ToString(); + strArrays[6] = ", text=\""; + strArrays[7] = str; + strArrays[8] = "\""; + this.LogIt(LogLevel.Info, string.Concat(strArrays)); + } + if (arrayLists.Count == 1) + { + this.LogIt(LogLevel.Info, "1 entry decoded."); + } + else + { + int count = arrayLists.Count; + this.LogIt(LogLevel.Info, string.Concat(count.ToString(), " entries decoded.")); + } + PhonebookEntry[] phonebookEntryArray = new PhonebookEntry[arrayLists.Count]; + arrayLists.CopyTo(phonebookEntryArray, 0); + return phonebookEntryArray; + } + + /// + /// AT+CMGD. Deletes the specified SMS message from the current read/delete storage. + /// + /// The index of the message to delete. + public void DeleteMessage(int index) + { + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Deleting message with index ", index.ToString(), "...")); + this.ExecAndReceiveMultiple(string.Concat("AT+CMGD=", index.ToString())); + } + } + + /// + /// AT+CMGD. Deletes the specified SMS message from the current read/delete storage. + /// + /// The index of the message to delete. + /// The delete flag, this controls the behaviour of the delete command. + public void DeleteMessage(int index, DeleteFlag delflag) + { + lock (this) + { + this.VerifyValidConnection(); + string[] str = new string[5]; + str[0] = "Deleting message with index "; + str[1] = index.ToString(); + str[2] = ", using delflag "; + str[3] = delflag.ToString(); + str[4] = "..."; + this.LogIt(LogLevel.Info, string.Concat(str)); + int num = delflag; + string str1 = string.Concat("AT+CMGD=", index.ToString(), ",", num.ToString()); + this.ExecAndReceiveMultiple(str1); + } + } + + /// + /// AT+CPBW. Deletes a phonebook entry. + /// + /// The index of the entry to delete. + /// In this case it does not matter whether the specified index is valid. + /// If the entry does not exist, no error is returned. + public void DeletePhonebookEntry(int index) + { + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Deleting phonebook entry ", index.ToString(), "...")); + this.ExecAndReceiveMultiple(string.Concat("AT+CPBW=", index.ToString())); + } + } + + private bool DispatchLog() + { + LoglineAddedEventArgs loglineAddedEventArg = null; + lock (this.logQueue) + { + if (this.logQueue.Count > 0) + { + loglineAddedEventArg = (LoglineAddedEventArgs)this.logQueue.Dequeue(); + } + } + if (loglineAddedEventArg == null) + { + return false; + } + else + { + try + { + if (this.LoglineAdded != null) + { + this.LoglineAdded(this, loglineAddedEventArg); + } + } + catch (Exception exception) + { + } + return true; + } + } + + /// + /// AT+CPIN. Enters a password at the phone which is necessary before it can operated. + /// + /// The SIM PIN, SIM PUK or other password required. + /// Get the current PIN status with to check + /// whether a password must be entered. + public void EnterPin(string pin) + { + lock (this) + { + this.VerifyValidConnection(); + object[] objArray = new object[1]; + objArray[0] = pin; + this.LogIt(LogLevel.Info, "Entering PIN...", objArray); + this.logEnabled = false; + try + { + string str = string.Format("AT+CPIN=\"{0}\"", pin); + this.ExecAndReceiveMultiple(str); + } + finally + { + this.logEnabled = true; + } + } + } + + private string ExecCommandInternal(string command, string receiveErrorMessage) + { + string str = null; + string str1 = string.Concat(command, "\r"); + this.receiveNow.Reset(); + this.SendInternal(str1, false); + StringBuilder stringBuilder = new StringBuilder(); + while (this.receiveNow.WaitOne(this.timeout, false)) + { + this.receiveNow.Reset(); + this.ReceiveInternal(out str); + stringBuilder.Append(str); + } + string str2 = stringBuilder.ToString(); + if (str2.Length != 0) + { + if (this.IsSuccess(str2)) + { + if (str2.EndsWith("\r\nOK\r\n")) + { + str2 = str2.Remove(str2.LastIndexOf("\r\nOK\r\n"), "\r\nOK\r\n".Length); + } + if (str2.StartsWith(str1)) + { + str2 = str2.Substring(str1.Length); + } + return str2; + } + else + { + this.HandleCommError(str2); + return null; + } + } + else + { + this.HandleRecvError(str2, receiveErrorMessage); + return null; + } + } + + /// + /// AT+CPBF. Searches for the specified text in the phonebook. + /// + /// The text to find. + /// An array of objects containing + /// the specified text + public PhonebookEntry[] FindPhonebookEntries(string findText) + { + PhonebookEntry[] phonebookEntryArray; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Searching phonebook entry \"", findText, "\"...")); + string str = string.Concat("AT+CPBF=\"", findText, "\""); + string str1 = this.ExecAndReceiveMultiple(str); + phonebookEntryArray = this.DecodePhonebookStream(str1, "+CPBF: "); + } + return phonebookEntryArray; + } + + /// + /// AT+CBC. Gets the ME battery charging status and charge level. + /// + /// A object containing the battery details. + public BatteryChargeInfo GetBatteryCharge() + { + BatteryChargeInfo batteryChargeInfo; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting battery charge..."); + string str = this.ExecAndReceiveMultiple("AT+CBC"); + Regex regex = new Regex("\\+CBC: (\\d+),(\\d+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + batteryChargeInfo = null; + } + else + { + int num = int.Parse(match.Groups[1].Value); + int num1 = int.Parse(match.Groups[2].Value); + this.LogIt(LogLevel.Info, string.Concat("Battery charging status = ", num.ToString())); + this.LogIt(LogLevel.Info, string.Concat("Battery charge level = ", num1.ToString())); + BatteryChargeInfo batteryChargeInfo1 = new BatteryChargeInfo(num, num1); + batteryChargeInfo = batteryChargeInfo1; + } + } + return batteryChargeInfo; + } + + /// + /// AT+CSCS. Retrives the currently selected character set. + /// + /// The current character set. + /// + /// + /// + /// + public string GetCurrentCharacterSet() + { + string empty; + string str; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Retrieving current character set..."); + string str1 = this.ExecAndReceiveMultiple("AT+CSCS?"); + Regex regex = new Regex("\\+CSCS: \"([^\"]+)\""); + Match match = regex.Match(str1); + if (!match.Success) + { + empty = string.Empty; + this.HandleCommError(str1); + } + else + { + empty = match.Groups[1].Value; + } + this.LogIt(LogLevel.Info, string.Concat("Current character set is \"", empty, "\".")); + str = empty; + } + return str; + } + + /// + /// AT+CSMS. Gets the supported message types along with the current service setting. + /// + /// Specifies the compatibility level of the SMS AT commands. + /// The requirement of service setting 1 depends on specific commands. + /// + /// ME supports mobile terminated messages + /// ME supports mobile originated messages + /// ME supports broadcast type messages + public void GetCurrentMessageService(out int service, out int mt, out int mo, out int bm) + { + lock (this) + { + this.VerifyValidConnection(); + string str = this.ExecAndReceiveMultiple("AT+CSMS?"); + Regex regex = new Regex("\\+CSMS: (\\d+),(\\d+),(\\d+),(\\d+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + service = int.Parse(match.Groups[1].Value); + mt = int.Parse(match.Groups[2].Value); + mo = int.Parse(match.Groups[3].Value); + bm = int.Parse(match.Groups[4].Value); + } + } + } + + /// + /// AT+COPS. Gets the currently selected network operator. + /// + /// An object containing the data or null if there is no current operator. + public OperatorInfo GetCurrentOperator() + { + OperatorInfo operatorInfo; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting current operator..."); + string str = this.ExecAndReceiveMultiple("AT+COPS?"); + Regex regex = new Regex("\\+COPS: (\\d+)(?:,(\\d+),\"(.+)\")?(?:,(.+))?"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + int.Parse(match.Groups[1].Value); + if (match.Groups.Count <= 1) + { + this.LogIt(LogLevel.Info, "There is no operator currently selected!"); + operatorInfo = null; + } + else + { + int num = int.Parse(match.Groups[2].Value); + string value = match.Groups[3].Value; + string empty = string.Empty; + if (match.Groups.Count > 3) + { + empty = match.Groups[4].Value; + } + object[] objArray = new object[3]; + objArray[0] = num; + objArray[1] = value; + objArray[2] = empty; + this.LogIt(LogLevel.Info, "format={0}, oper=\"{1}\", act=\"{2}\"", objArray); + if (Enum.IsDefined(typeof(OperatorFormat), num)) + { + OperatorFormat operatorFormat = (OperatorFormat)Enum.Parse(typeof(OperatorFormat), num.ToString()); + OperatorInfo operatorInfo1 = new OperatorInfo(operatorFormat, value, empty); + operatorInfo = operatorInfo1; + } + else + { + throw new CommException(string.Concat("Unknown operator format ", num.ToString()), str); + } + } + } + } + return operatorInfo; + } + + /// + /// AT+CNMI. Gets the current message notification settings. + /// + /// A structure containing the detailed settings. + public MessageIndicationSettings GetMessageIndications() + { + MessageIndicationSettings messageIndicationSetting; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting current message indications..."); + string str = this.ExecAndReceiveMultiple("AT+CNMI?"); + Regex regex = new Regex("\\+CNMI: (\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + MessageIndicationSettings messageIndicationSetting1 = new MessageIndicationSettings(); + messageIndicationSetting1.Mode = int.Parse(match.Groups[1].Value); + messageIndicationSetting1.DeliverStyle = int.Parse(match.Groups[2].Value); + messageIndicationSetting1.CellBroadcastStyle = int.Parse(match.Groups[3].Value); + messageIndicationSetting1.StatusReportStyle = int.Parse(match.Groups[4].Value); + messageIndicationSetting1.BufferSetting = int.Parse(match.Groups[5].Value); + object[] mode = new object[5]; + mode[0] = messageIndicationSetting1.Mode; + mode[1] = messageIndicationSetting1.DeliverStyle; + mode[2] = messageIndicationSetting1.CellBroadcastStyle; + mode[3] = messageIndicationSetting1.StatusReportStyle; + mode[4] = messageIndicationSetting1.BufferSetting; + this.LogIt(LogLevel.Info, string.Format("mode={0:g}, mt={1:g}, bm={2:g}, ds={3:g}, bfr={4:g}", mode)); + messageIndicationSetting = messageIndicationSetting1; + } + } + return messageIndicationSetting; + } + + /// + /// Returns the message service error code in the input string. + /// + /// The data received + /// The error code + /// Use the method to check if the string + /// contains a message service error message. + /// Input string does not contain a message service error code + private int GetMessageServiceErrorCode(string input) + { + Regex regex = new Regex("\\r\\n\\+CMS ERROR: (\\d+)\\r\\n"); + Match match = regex.Match(input); + if (!match.Success || match.Groups[1].Captures.Count <= 0) + { + throw new ArgumentException("The input string does not contain a message service error code."); + } + else + { + return int.Parse(match.Groups[1].Captures[0].ToString()); + } + } + + /// + /// AT+CPMS. Gets the supported message storages. + /// + /// A object that contains details about the supported storages. + public MessageStorageInfo GetMessageStorages() + { + MessageStorageInfo messageStorageInfo; + lock (this) + { + this.VerifyValidConnection(); + ArrayList arrayLists = new ArrayList(); + this.LogIt(LogLevel.Info, "Enumerating supported message storages..."); + string str = this.ExecAndReceiveMultiple("AT+CPMS=?"); + int num = str.IndexOf("+CPMS: "); + if (num < 0) + { + this.HandleCommError(str); + } + else + { + str = str.Substring(num + "+CPMS: ".Length); + } + Regex regex = new Regex("\\((?:\"(\\w+)\"(?(?!\\)),))+\\)"); + for (Match i = regex.Match(str); i.Success; i = i.NextMatch()) + { + int count = i.Groups[1].Captures.Count; + string[] value = new string[count]; + for (int j = 0; j < count; j++) + { + value[j] = i.Groups[1].Captures[j].Value; + } + arrayLists.Add(value); + } + } + return messageStorageInfo; + } + + /// + /// Returns the mobile equipment error code in the input string. + /// + /// The data received + /// The mobile equipment error code + /// Use the method to check if the string + /// contains mobile equipment error message. + /// Input string does not contain a mobile equipment error code + private int GetMobileEquipmentErrorCode(string input) + { + Regex regex = new Regex("\\r\\n\\+CME ERROR: (\\d+)\\r\\n"); + Match match = regex.Match(input); + if (!match.Success || match.Groups[1].Captures.Count <= 0) + { + throw new ArgumentException("The input string does not contain a mobile equipment error code."); + } + else + { + return int.Parse(match.Groups[1].Captures[0].ToString()); + } + } + + /// + /// AT+CMMS. Gets the current SMS batch mode setting. + /// + /// The current mode. + public MoreMessagesMode GetMoreMessagesToSend() + { + MoreMessagesMode moreMessagesMode; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting more messages mode..."); + string str = string.Format("AT+CMMS?", new object[0]); + string str1 = this.ExecAndReceiveMultiple(str); + Regex regex = new Regex("\\+CMMS: (\\d+)"); + Match match = regex.Match(str1); + if (!match.Success) + { + this.HandleCommError(str1); + throw new CommException("Unexpected response.", str1); + } + else + { + int num = int.Parse(match.Groups[1].Value); + object[] objArray = new object[1]; + objArray[0] = num; + this.LogIt(LogLevel.Info, "mode=\"{0}\"", objArray); + if (Enum.IsDefined(typeof(MoreMessagesMode), num)) + { + MoreMessagesMode moreMessagesMode1 = (MoreMessagesMode)Enum.Parse(typeof(MoreMessagesMode), num.ToString()); + moreMessagesMode = moreMessagesMode1; + } + else + { + throw new CommException(string.Concat("Unknown more messages mode ", num.ToString(), "."), str1); + } + } + } + return moreMessagesMode; + } + + /// + /// AT+COPS. Determines the current mode to select a network operator. + /// + /// The current mode, see for possible values. + public int GetOperatorSelectionMode() + { + int num; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting current operator selection mode..."); + string str = this.ExecAndReceiveMultiple("AT+COPS?"); + Regex regex = new Regex("\\+COPS: (\\d+)(?:,(\\d+),\"(.+)\")?(?:,(.+))?"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + int num1 = int.Parse(match.Groups[1].Value); + object[] objArray = new object[1]; + objArray[0] = num1.ToString(); + this.LogIt(LogLevel.Info, "Current mode is {0}.", objArray); + num = num1; + } + } + return num; + } + + /// + /// AT+CPBS. Gets the memory status of the currently selected phonebook storage. + /// + /// The memory status of the currently selected storage. + public MemoryStatusWithStorage GetPhonebookMemoryStatus() + { + MemoryStatusWithStorage memoryStatusWithStorage; + int used; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting phonebook memory status..."); + string str = this.ExecAndReceiveMultiple("AT+CPBS?"); + MemoryStatusWithStorage memoryStatusWithStorage1 = this.ParsePhonebookMemoryStatus(str); + if (memoryStatusWithStorage1.Total > 0) + { + used = (int)((double)memoryStatusWithStorage1.Used / (double)memoryStatusWithStorage1.Total * 100); + } + else + { + used = 0; + } + int num = used; + object[] storage = new object[4]; + storage[0] = memoryStatusWithStorage1.Storage; + storage[1] = memoryStatusWithStorage1.Used; + storage[2] = memoryStatusWithStorage1.Total; + storage[3] = num; + this.LogIt(LogLevel.Info, string.Format("Memory status: storage=\"{0}\" {1}/{2} ({3}% used)", storage)); + memoryStatusWithStorage = memoryStatusWithStorage1; + } + return memoryStatusWithStorage; + } + + /// + /// AT+CPBR. Queries the size of the currently selected phonebook. + /// + /// Receives the lower bound of the phonebook + /// Receives the upper bound of the phonebook + /// Receives the maximum number length, 0 if unknown + /// Receives the maximum text length, 0 if unknown + public void GetPhonebookSize(out int lowerBound, out int upperBound, out int nLength, out int tLength) + { + int num; + int num1; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting phonebook size..."); + string str = this.ExecAndReceiveMultiple("AT+CPBR=?"); + Regex regex = new Regex("\\+CPBR: \\((\\d+)-(\\d+)\\)\\,(\\d*),(\\d*)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + } + lowerBound = int.Parse(match.Groups[1].Value); + upperBound = int.Parse(match.Groups[2].Value); + int& numPointer = nLength; + if (match.Groups[3].Value != "") + { + num = int.Parse(match.Groups[3].Value); + } + else + { + num = 0; + } + *(numPointer) = num; + int& numPointer1 = tLength; + if (match.Groups[4].Value != "") + { + num1 = int.Parse(match.Groups[4].Value); + } + else + { + num1 = 0; + } + *(numPointer1) = num1; + string[] strArrays = new string[8]; + strArrays[0] = "lowerBound="; + strArrays[1] = lowerBound.ToString(); + strArrays[2] = ", upperBound="; + strArrays[3] = upperBound.ToString(); + strArrays[4] = ", nLength="; + strArrays[5] = nLength.ToString(); + strArrays[6] = ", tLength="; + strArrays[7] = tLength.ToString(); + this.LogIt(LogLevel.Info, string.Concat(strArrays)); + } + } + + /// + /// AT+CPBS. Gets the supported phonebook storages. + /// + /// An array of the supported storages + public string[] GetPhonebookStorages() + { + string[] strArrays; + string str; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Enumerating supported storages..."); + string str1 = this.ExecAndReceiveMultiple("AT+CPBS=?"); + string[] value = null; + Regex regex = new Regex("\\+CPBS: \\((?:\"(\\w+)\"(?(?!\\)),))+\\)"); + Match match = regex.Match(str1); + if (!match.Success) + { + this.HandleCommError(str1); + } + else + { + int count = match.Groups[1].Captures.Count; + value = new string[count]; + for (int i = 0; i < count; i++) + { + value[i] = match.Groups[1].Captures[i].Value; + } + } + string empty = string.Empty; + int num = 0; + while (num < (int)value.Length) + { + string str2 = empty; + if (empty == string.Empty) + { + str = ""; + } + else + { + str = ", "; + } + empty = string.Concat(str2, str, value[num]); + num++; + } + this.LogIt(LogLevel.Info, string.Concat("Supported storages: ", empty)); + strArrays = value; + } + return strArrays; + } + + /// + /// AT+CPIN. Returns a value indicating whether some password must be entered at the phone or not. + /// + /// The current PIN status as one of the values. + public PinStatus GetPinStatus() + { + PinStatus status; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting PIN status..."); + string str = string.Format("AT+CPIN?", new object[0]); + string str1 = this.ExecAndReceiveMultiple(str); + Regex regex = new Regex("\\+CPIN: (.+)\\r\\n"); + Match match = regex.Match(str1); + if (!match.Success) + { + this.HandleCommError(str1); + throw new CommException("Unexpected response.", str1); + } + else + { + string value = match.Groups[1].Value; + object[] objArray = new object[1]; + objArray[0] = value; + this.LogIt(LogLevel.Info, "status=\"{0}\"", objArray); + status = this.MapPinStatusStringToStatus(value); + } + } + return status; + } + + /// + /// Enables access to the protocol level of the current connection. + /// + /// An object that sends and receives data at the protocol level. + /// This method enables execution of custom commands that are not directly supported. It also disables execution of background + /// operations that would usually take place, such as checking whether the phone is still connected. + /// The method must be called as soon as execution of the custom commands is completed, + /// and allows for normal operations to continue. Execution of other commands besides from is not allowed + /// until is called. + /// + /// + public IProtocol GetProtocol() + { + Monitor.Enter(this); + return this; + } + + /// + /// AT+CSQ. Gets the signal quality as calculated by the ME. + /// + /// A object containing the signal details. + public SignalQualityInfo GetSignalQuality() + { + SignalQualityInfo signalQualityInfo; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting signal quality..."); + string str = this.ExecAndReceiveMultiple("AT+CSQ"); + Regex regex = new Regex("\\+CSQ: (\\d+),(\\d+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + signalQualityInfo = null; + } + else + { + int num = int.Parse(match.Groups[1].Value); + int num1 = int.Parse(match.Groups[2].Value); + this.LogIt(LogLevel.Info, string.Concat("Signal strength = ", num.ToString())); + this.LogIt(LogLevel.Info, string.Concat("Bit error rate = ", num1.ToString())); + SignalQualityInfo signalQualityInfo1 = new SignalQualityInfo(num, num1); + signalQualityInfo = signalQualityInfo1; + } + } + return signalQualityInfo; + } + + /// + /// AT+CSCA. Gets the SMS Service Center Address. + /// + /// The current SMSC address + /// This command returns the SMSC address, through which SMS messages are transmitted. + /// In text mode, this setting is used by SMS sending and SMS writing commands. In PDU mode, this setting is + /// used by the same commands, but only when the length of the SMSC address coded into the PDU data equals + /// zero. + public AddressData GetSmscAddress() + { + AddressData addressDatum; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting SMSC address..."); + string str = "AT+CSCA?"; + string str1 = this.ExecAndReceiveMultiple(str); + Regex regex = new Regex("\\+CSCA: \"(.*)\",(\\d+)"); + Match match = regex.Match(str1); + if (!match.Success) + { + this.HandleCommError(str1); + throw new CommException("Unexpected response.", str1); + } + else + { + string value = match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + object[] objArray = new object[2]; + objArray[0] = value; + objArray[1] = num.ToString(); + this.LogIt(LogLevel.Info, "address=\"{0}\", typeOfAddress={1}", objArray); + AddressData addressDatum1 = new AddressData(value, num); + addressDatum = addressDatum1; + } + } + return addressDatum; + } + + /// + /// AT+CNUM. Returns the MSISDNs related to the subscriber. + /// + /// An array of objects with one for each MSISDN + /// (Mobile Subscriber ISDN Number), depending on the services subscribed. + /// + /// This information can be stored in the SIM/UICC or in the MT. + /// If the command is supported by the phone but no number can be retrieved, + /// an empty array is returned. + /// + public SubscriberInfo[] GetSubscriberNumbers() + { + SubscriberInfo[] subscriberInfoArray; + string empty; + int num; + int num1; + int num2; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting subscriber numbers..."); + string str = this.ExecAndReceiveMultiple("AT+CNUM"); + Regex regex = new Regex("\\+CNUM: (.*),\"(.*)\",(\\d+)(?:,(\\d+),(\\d+)(?:,(\\d+))?)?(?:\\r\\n)?"); + Match match = regex.Match(str); + List subscriberInfos = new List(); + while (match.Success) + { + if (match.Groups[1].Captures.Count <= 0 || match.Groups[1].Length <= 0) + { + empty = string.Empty; + } + else + { + empty = match.Groups[1].Value; + } + string str1 = empty; + string value = match.Groups[2].Value; + int num3 = int.Parse(match.Groups[3].Value); + if (match.Groups[4].Captures.Count <= 0 || match.Groups[4].Length <= 0) + { + num = -1; + } + else + { + num = int.Parse(match.Groups[4].Value); + } + int num4 = num; + if (match.Groups[5].Captures.Count <= 0 || match.Groups[5].Length <= 0) + { + num1 = -1; + } + else + { + num1 = int.Parse(match.Groups[5].Value); + } + int num5 = num1; + if (match.Groups[6].Captures.Count <= 0 || match.Groups[6].Length <= 0) + { + num2 = -1; + } + else + { + num2 = int.Parse(match.Groups[6].Value); + } + int num6 = num2; + object[] objArray = new object[6]; + objArray[0] = str1; + objArray[1] = value; + objArray[2] = num3; + objArray[3] = num4; + objArray[4] = num5; + objArray[5] = num6; + this.LogIt(LogLevel.Info, "alpha=\"{0}\",number=\"{1}\",type={2},speed={3},service={4},itc={5}", objArray); + SubscriberInfo subscriberInfo = new SubscriberInfo(str1, value, num3, num4, num5, num6); + subscriberInfos.Add(subscriberInfo); + match = match.NextMatch(); + } + SubscriberInfo[] subscriberInfoArray1 = new SubscriberInfo[subscriberInfos.Count]; + subscriberInfos.CopyTo(subscriberInfoArray1); + subscriberInfoArray = subscriberInfoArray1; + } + return subscriberInfoArray; + } + + /// + /// AT+CSCS. Retrieves the phone's supported character sets. + /// + /// A string array containing the supported character sets. + /// + /// + /// + /// + public string[] GetSupportedCharacterSets() + { + string[] strArrays; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Enumerating supported character sets..."); + string str = this.ExecAndReceiveMultiple("AT+CSCS=?"); + string[] value = null; + Regex regex = new Regex("\\+CSCS: \\((?:\"([^\"]+)\"(?(?!\\)),))+\\)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + } + else + { + int count = match.Groups[1].Captures.Count; + value = new string[count]; + for (int i = 0; i < count; i++) + { + value[i] = match.Groups[1].Captures[i].Value; + } + } + string str1 = this.MakeArrayString(value); + this.LogIt(LogLevel.Info, string.Concat("Supported character sets: ", str1)); + strArrays = value; + } + return strArrays; + } + + /// + /// AT+CNMI. Gets the supported new message indications from the phone. + /// + /// A object containing information about the supported + /// indications. + public MessageIndicationSupport GetSupportedIndications() + { + MessageIndicationSupport messageIndicationSupport; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Getting supported message indications..."); + string str = this.ExecAndReceiveMultiple("AT+CNMI=?"); + Regex regex = new Regex("\\+CNMI: \\(([\\d,-])+\\),\\(([\\d,-]+)\\),\\(([\\d,-]+)\\),\\(([\\d,-]+)\\),\\(([\\d,-]+)\\)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + string empty = string.Empty; + string empty1 = string.Empty; + string str1 = string.Empty; + string empty2 = string.Empty; + string str2 = string.Empty; + foreach (Capture capture in match.Groups[1].Captures) + { + empty = string.Concat(empty, capture.Value); + } + foreach (Capture capture1 in match.Groups[2].Captures) + { + empty1 = string.Concat(empty1, capture1.Value); + } + foreach (Capture capture2 in match.Groups[3].Captures) + { + str1 = string.Concat(str1, capture2.Value); + } + foreach (Capture capture3 in match.Groups[4].Captures) + { + empty2 = string.Concat(empty2, capture3.Value); + } + foreach (Capture capture4 in match.Groups[5].Captures) + { + str2 = string.Concat(str2, capture4.Value); + } + object[] objArray = new object[5]; + objArray[0] = empty; + objArray[1] = empty1; + objArray[2] = str1; + objArray[3] = empty2; + objArray[4] = str2; + this.LogIt(LogLevel.Info, "mode=\"{0}\", deliver=\"{1}\", cellBroadcast=\"{2}\", statusReport=\"{3}\", buffer=\"{4}\"", objArray); + MessageIndicationSupport messageIndicationSupport1 = new MessageIndicationSupport(empty, empty1, str1, empty2, str2); + messageIndicationSupport = messageIndicationSupport1; + } + } + return messageIndicationSupport; + } + + /// + /// Executes the specified command and reads multiple times from the phone + /// until a specific pattern is detected in the response. + /// + /// The command to execute. + /// The regular expression pattern that the received data must match to stop + /// reading. + /// The response received. + /// + /// + /// + public string ExecAndReceiveAnything(string command, string pattern) + { + string str = string.Concat(command, "\r"); + this.Send(str); + string str1 = this.ReceiveAnything(pattern); + if (str1.StartsWith(str)) + { + str1 = str1.Substring(str.Length); + } + return str1; + } + + /// Executes the specified command and reads multiple times from the phone + /// until one of the defined message termination patterns is detected in the response. + /// The command to execute. + /// The response received. + /// + /// + /// + public string ExecAndReceiveMultiple(string command) + { + + string str = string.Concat(command, "\r"); + this.Send(str); + string str1 = this.ReceiveMultiple(); + if (str1.EndsWith("\r\nOK\r\n")) + { + str1 = str1.Remove(str1.LastIndexOf("\r\nOK\r\n"), "\r\nOK\r\n".Length); + } + if (str1.StartsWith(str)) + { + str1 = str1.Substring(str.Length); + } + return str1; + } + + /// Executes the specified command and reads a single response. + /// The command to execute. + /// The response received. + /// + /// This method returns whatever response comes in from the phone during a single read operation. + /// The response received may not be complete. + /// If you want to ensure that always complete responses are read, use instead. + /// + /// + /// + /// + /// + public string ExecCommand(string command) + { + return this.ExecCommand(command, "No answer from phone."); + } + + /// + /// Executes the specified command and reads a single response. + /// The command to execute. + /// The message text for the exception if no data is received. + /// The response received. + /// + /// This method returns whatever response comes in from the phone during a single read operation. + /// The response received may not be complete. + /// If you want to ensure that always complete responses are read, use instead. + /// + /// + /// + /// + /// + public string ExecCommand(string command, string receiveErrorMessage) + { + string str = null; + string str1 = string.Concat(command, "\r"); + this.Send(str1); + if (this.Receive(out str)) + { + this.LogItShow(LogLevel.Verbose, str, ">> "); + if (this.IsSuccess(str)) + { + if (str.EndsWith("\r\nOK\r\n")) + { + str = str.Remove(str.LastIndexOf("\r\nOK\r\n"), "\r\nOK\r\n".Length); + } + if (str.StartsWith(str1)) + { + str = str.Substring(str1.Length); + } + return str; + } + else + { + this.HandleCommError(str); + return null; + } + } + else + { + this.HandleRecvError(str, receiveErrorMessage); + return null; + } + } + + /// Receives raw string data. + /// The data received. + /// true if reception was successful, otherwise false. + /// + public bool GsmComm.GsmCommunication.IProtocol.Receive(out string input) + { + input = string.Empty; + if (!this.IsCommThreadRunning()) + { + throw new InvalidOperationException("Communication thread is not running."); + } + else + { + WaitHandle[] waitHandleArray = new WaitHandle[2]; + waitHandleArray[0] = this.dataReceived; + waitHandleArray[1] = this.noData; + WaitHandle[] waitHandleArray1 = waitHandleArray; + int num = WaitHandle.WaitAny(waitHandleArray1, 5000, false); + if (num == 0) + { + this.dataReceived.Reset(); + if (this.inputQueue.Count <= 0) + { + this.LogIt(LogLevel.Warning, "Nothing in input queue"); + } + else + { + while (this.inputQueue.Count > 0) + { + input = string.Concat(input, (string)this.inputQueue.Dequeue()); + } + } + } + this.noData.Reset(); + return input.Length > 0; + } + } + + /// Reads multiple times from the phone until a specific pattern is detected in the response. + /// The response received. + /// The regular expression pattern that the received data must match to + /// stop reading. Can be an empty string if the pattern should not be checked. + /// + /// + /// + public string GsmComm.GsmCommunication.IProtocol.ReceiveAnything(string pattern) + { + string str = null; + string empty = string.Empty; + int tickCount = Environment.TickCount; + this.OnReceiveProgress(empty.Length); + int num = 0; + int num1 = 0; + while (!this.IsSuccess(empty) && !this.IsCommError(empty) && !this.IsMessageServiceError(empty) && !this.IsMobileEquipmentError(empty) && num < 6) + { + if (!this.Receive(out str)) + { + num++; + if (num <= 1) + { + continue; + } + this.LogIt(LogLevel.Info, string.Concat("Waiting for response from phone (", num.ToString(), " empty read(s)).")); + } + else + { + empty = string.Concat(empty, str); + num = 0; + num1++; + this.OnReceiveProgress(empty.Length); + if (pattern.Length <= 0 || !Regex.IsMatch(empty, pattern)) + { + continue; + } + break; + } + } + int tickCount1 = Environment.TickCount - tickCount; + this.LogItShow(LogLevel.Verbose, empty, ">> "); + this.OnReceiveComplete(empty.Length); + if (num < 6) + { + if (tickCount1 > this.timeout) + { + object[] objArray = new object[2]; + int length = empty.Length; + objArray[0] = length.ToString(); + objArray[1] = tickCount1.ToString(); + this.LogIt(LogLevel.Info, "{0} characters received after {1} ms.", objArray); + } + if (pattern.Length != 0 || this.IsSuccess(empty)) + { + this.HandleUnsolicitedMessages(ref empty); + return empty; + } + else + { + this.HandleCommError(empty); + return string.Empty; + } + } + else + { + string[] strArrays = new string[5]; + strArrays[0] = "Timeout after "; + strArrays[1] = num.ToString(); + strArrays[2] = " empty reading attempts within "; + strArrays[3] = tickCount1.ToString(); + strArrays[4] = " ms."; + this.LogIt(LogLevel.Error, string.Concat(strArrays)); + throw new CommException(string.Concat("No data received from phone after waiting for ", tickCount1.ToString(), " ms.")); + } + } + + /// Reads multiple times from the phone until one of the defined + /// message termination patterns is detected in the response. + /// The response received. + /// + /// + /// + public string GsmComm.GsmCommunication.IProtocol.ReceiveMultiple() + { + return this.ReceiveAnything(string.Empty); + } + + /// Sends raw string data. + /// The data to send. + /// + public void GsmComm.GsmCommunication.IProtocol.Send(string output) + { + if (!this.IsCommThreadRunning()) + { + throw new InvalidOperationException("Communication thread is not running."); + } + else + { + this.dataToSend = output; + this.sendDone.Reset(); + this.sendNow.Set(); + this.sendDone.WaitOne(); + return; + } + } + + /// + /// Handles a communication error. + /// + /// The data received. + /// Call this function when communicating and the response is not a success response. This + /// function checks if the response contains an error message. In any case a + /// or an exception derived from it is thrown based on the type of error. + /// + /// Always thrown based on type of error. + private void HandleCommError(string input) + { + if (!this.IsCommError(input)) + { + if (!this.IsMessageServiceError(input)) + { + if (!this.IsMobileEquipmentError(input)) + { + if (input.Length != 0) + { + this.LogIt(LogLevel.Error, "Failed. Unexpected response."); + this.LogIt(LogLevel.Error, "Received input:"); + this.LogItShow(LogLevel.Error, input, ""); + throw new CommException(string.Concat("Unexpected response received from phone:", Environment.NewLine, Environment.NewLine, input)); + } + else + { + this.LogIt(LogLevel.Error, "Failed. No answer from phone."); + throw new CommException("No answer from phone."); + } + } + else + { + int mobileEquipmentErrorCode = this.GetMobileEquipmentErrorCode(input); + this.LogIt(LogLevel.Error, string.Concat("Failed. Phone reports mobile equipment (ME) error ", mobileEquipmentErrorCode.ToString(), ".")); + this.LogItShow(LogLevel.Error, input, ""); + throw new MobileEquipmentErrorException(string.Concat("Mobile equipment error ", mobileEquipmentErrorCode.ToString(), " occurred."), mobileEquipmentErrorCode, input); + } + } + else + { + int messageServiceErrorCode = this.GetMessageServiceErrorCode(input); + this.LogIt(LogLevel.Error, string.Concat("Failed. Phone reports message service (MS) error ", messageServiceErrorCode.ToString(), ".")); + this.LogItShow(LogLevel.Error, input, ""); + throw new MessageServiceErrorException(string.Concat("Message service error ", messageServiceErrorCode.ToString(), " occurred."), messageServiceErrorCode, input); + } + } + else + { + string str = "The phone reports an unspecified error. This typically happens when a command is not supported by the device, a command is not valid for the current state or if a parameter is incorrect."; + this.LogIt(LogLevel.Error, string.Concat("Failed. ", str, " The response received was: ")); + this.LogItShow(LogLevel.Error, input, ""); + throw new CommException(str, input); + } + } + + private void HandleRecvError(string input, string userMessage) + { + if (input.Length <= 0) + { + this.LogIt(LogLevel.Error, "Failed. Receiving error."); + } + else + { + this.LogIt(LogLevel.Error, "Failed. Receiving error. Data until error:"); + this.LogItShow(LogLevel.Error, input, ""); + } + throw new CommException(userMessage); + } + + private bool HandleUnsolicitedMessages(ref string bigInput) + { + string str = null; + MessageIndicationHandlers messageIndicationHandler = new MessageIndicationHandlers(); + if (!messageIndicationHandler.IsUnsolicitedMessage(bigInput)) + { + return false; + } + else + { + this.LogItShow(LogLevel.Verbose, bigInput, ">> "); + IMessageIndicationObject messageIndicationObject = messageIndicationHandler.HandleUnsolicitedMessage(ref bigInput, out str); + this.LogIt(LogLevel.Info, string.Concat("Unsolicited message: ", str)); + this.OnMessageReceived(messageIndicationObject); + return true; + } + } + + private bool IsCommError(string input) + { + return input.IndexOf("\r\nERROR\r\n") >= 0; + } + + private bool IsCommThreadRunning() + { + if (this.commThread == null) + { + return false; + } + else + { + return this.commThread.IsAlive; + } + } + + /// + /// Checks if there is a device connected and responsive. + /// + /// true if there is really a device connected and it responds to commands, false otherwise. + /// + /// You can use this function after opening the port with to verify that there is really a device connected + /// before processding. + /// + /// + /// + public bool IsConnected() + { + bool flag; + lock (this) + { + flag = this.connectionState; + } + return flag; + } + + /// + /// AT. Checks if there is a device connected and responsive. + /// + /// true if there is really a device connected and it responds to commands, false otherwise. + /// + /// Bypasses the communication thread and does a direct send/receive. + /// + private bool IsConnectedInternal() + { + bool flag; + lock (this) + { + try + { + this.ExecCommandInternal("AT", "No phone connected."); + } + catch (Exception exception) + { + flag = false; + return flag; + } + flag = true; + } + return flag; + } + + private bool IsMessageServiceError(string input) + { + return Regex.IsMatch(input, "\\r\\n\\+CMS ERROR: (\\d+)\\r\\n"); + } + + /// + /// Checks if there is a mobile equipment error message in the input string. + /// + /// The data received + /// true if there is a mobile equipment error message in the string, otherwiese false. + private bool IsMobileEquipmentError(string input) + { + return Regex.IsMatch(input, "\\r\\n\\+CME ERROR: (\\d+)\\r\\n"); + } + + /// + /// Checks if the communication to the device is open. + /// + /// true if the port is open, false otherwise. + /// The port is open after a auccessful call to and must be closed with + /// . + /// This function does not check if there is actually a device connected, use the + /// function for that. + /// + /// + /// + /// + public bool IsOpen() + { + bool flag; + bool isOpen; + lock (this) + { + if (this.port == null) + { + isOpen = false; + } + else + { + isOpen = this.port.IsOpen; + } + flag = isOpen; + } + return flag; + } + + private bool IsSuccess(string input) + { + return input.IndexOf("\r\nOK\r\n") >= 0; + } + + /// + /// AT+CMGL. Reads SMS messages from the current read/delete storage using the PDU mode. + /// + /// The message status + /// An array of objects representing the messages read. + /// Always switches to PDU mode at the beginning. + public ShortMessageFromPhone[] ListMessages(PhoneMessageStatus status) + { + ShortMessageFromPhone[] shortMessageFromPhoneArray; + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + this.LogIt(LogLevel.Info, string.Concat("Reading messages, requesting status \"", status.ToString(), "\"...")); + string str = string.Concat("AT+CMGL=", (int)status); + string str1 = this.ExecAndReceiveMultiple(str); + ArrayList arrayLists = new ArrayList(); + Regex regex = new Regex("\\+CMGL: (\\d+),(\\d+),(?:\"(\\w*)\")?,(\\d+)\\r\\n(\\w+)"); + for (Match i = regex.Match(str1); i.Success; i = i.NextMatch()) + { + int num = int.Parse(i.Groups[1].Value); + int num1 = int.Parse(i.Groups[2].Value); + string value = i.Groups[3].Value; + int num2 = int.Parse(i.Groups[4].Value); + string value1 = i.Groups[5].Value; + string[] strArrays = new string[8]; + strArrays[0] = "index="; + strArrays[1] = num.ToString(); + strArrays[2] = ", stat="; + strArrays[3] = num1.ToString(); + strArrays[4] = ", alpha=\""; + strArrays[5] = value; + strArrays[6] = "\", length="; + strArrays[7] = num2.ToString(); + this.LogIt(LogLevel.Info, string.Concat(strArrays)); + ShortMessageFromPhone shortMessageFromPhone = new ShortMessageFromPhone(num, num1, value, num2, value1); + arrayLists.Add(shortMessageFromPhone); + } + int count = arrayLists.Count; + this.LogIt(LogLevel.Info, string.Concat(count.ToString(), " message(s) read.")); + ShortMessageFromPhone[] shortMessageFromPhoneArray1 = new ShortMessageFromPhone[arrayLists.Count]; + arrayLists.CopyTo(shortMessageFromPhoneArray1, 0); + shortMessageFromPhoneArray = shortMessageFromPhoneArray1; + } + return shortMessageFromPhoneArray; + } + + /// + /// Lists the network operators detected by the phone. + /// + /// An array of objects containing the data of each operator. + /// If you want to determine the current operator, use the method. + public OperatorInfo2[] ListOperators() + { + OperatorInfo2[] operatorInfo2Array; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Listing operators..."); + string str = this.ExecAndReceiveMultiple("AT+COPS=?"); + ArrayList arrayLists = new ArrayList(); + if (Regex.IsMatch(str, "\\+COPS: .*")) + { + Regex regex = new Regex("\\((\\d+),(?:\"([^\\(\\)\\,]+)\")?,(?:\"([^\\(\\)\\,]+)\")?,(?:\"(\\d+)\")?(?:,([^\\(\\)\\,]+))?\\)"); + Match match = regex.Match(str); + while (match.Success) + { + int num = int.Parse(match.Groups[1].Value); + string value = match.Groups[2].Value; + string value1 = match.Groups[3].Value; + string str1 = match.Groups[4].Value; + string value2 = match.Groups[5].Value; + object[] objArray = new object[5]; + objArray[0] = num; + objArray[1] = value; + objArray[2] = value1; + objArray[3] = str1; + objArray[4] = value2; + this.LogIt(LogLevel.Info, "stat={0}, longAlpha=\"{1}\", shortAlpha=\"{2}\", numeric=\"{3}\", act=\"{4}\"", objArray); + if (Enum.IsDefined(typeof(OperatorStatus), num)) + { + OperatorStatus operatorStatu = (OperatorStatus)Enum.Parse(typeof(OperatorStatus), num.ToString()); + OperatorInfo2 operatorInfo2 = new OperatorInfo2(operatorStatu, value, value1, str1, value2); + arrayLists.Add(operatorInfo2); + match = match.NextMatch(); + } + else + { + throw new CommException(string.Concat("Unknown operator status ", num.ToString(), "."), str); + } + } + int count = arrayLists.Count; + this.LogIt(LogLevel.Info, string.Concat(count.ToString(), " operator(s) enumerated.")); + OperatorInfo2[] operatorInfo2Array1 = new OperatorInfo2[arrayLists.Count]; + arrayLists.CopyTo(operatorInfo2Array1, 0); + operatorInfo2Array = operatorInfo2Array1; + } + else + { + this.HandleCommError(str); + operatorInfo2Array = null; + } + } + return operatorInfo2Array; + } + + private void LogIt(LogLevel level, string text) + { + if (this.LoglineAdded != null && this.logEnabled && level <= this.logLevel) + { + DateTime now = DateTime.Now; + string str = string.Format("{0} [gsmphone] {1}", now.ToString("HH:mm:ss.fff"), text); + LoglineAddedEventArgs loglineAddedEventArg = new LoglineAddedEventArgs(level, str); + lock (this.logQueue) + { + this.logQueue.Enqueue(loglineAddedEventArg); + } + } + } + + private void LogIt(LogLevel level, string text, params object[] args) + { + if (this.LoglineAdded != null && this.logEnabled && level <= this.logLevel) + { + string str = string.Format(text, args); + DateTime now = DateTime.Now; + string str1 = string.Format("{0} [gsmphone] {1}", now.ToString("HH:mm:ss.fff"), str); + LoglineAddedEventArgs loglineAddedEventArg = new LoglineAddedEventArgs(level, str1); + lock (this.logQueue) + { + this.logQueue.Enqueue(loglineAddedEventArg); + } + } + } + + private void LogItShow(LogLevel level, string data, string prefix, bool hideEmptyLines) + { + string[] strArrays = this.SplitLines(data); + if ((int)strArrays.Length != 0) + { + bool flag = false; + for (int i = 0; i < (int)strArrays.Length; i++) + { + if (!hideEmptyLines || hideEmptyLines && strArrays[i].Length > 0) + { + if (flag) + { + this.LogIt(level, strArrays[i].PadLeft(strArrays[i].Length + prefix.Length, ' ')); + } + else + { + this.LogIt(level, string.Concat(prefix, strArrays[i])); + flag = true; + } + } + } + return; + } + else + { + return; + } + } + + private void LogItShow(LogLevel level, string data, string prefix) + { + this.LogItShow(level, data, prefix, false); + } + + private void LogMemoryStatus(MemoryStatus status) + { + int used; + if (status.Total > 0) + { + used = (int)((double)status.Used / (double)status.Total * 100); + } + else + { + used = 0; + } + int num = used; + this.LogIt(LogLevel.Info, string.Format("Memory status: {0}/{1} ({2}% used)", status.Used, status.Total, num)); + } + + private void LogThread() + { + this.logQueue.Clear(); + this.logThreadInitialized.Set(); + while (!this.terminateLogThread.WaitOne(100, false)) + { + while (this.DispatchLog()) + { + } + } + while (this.DispatchLog()) + { + } + this.logQueue.Clear(); + } + + private string MakeArrayString(string[] array) + { + StringBuilder stringBuilder = new StringBuilder(); + string[] strArrays = array; + for (int i = 0; i < (int)strArrays.Length; i++) + { + string str = strArrays[i]; + if (stringBuilder.Length > 0) + { + stringBuilder.Append(", "); + } + stringBuilder.Append(str); + } + return stringBuilder.ToString(); + } + + private string MakeQueueString(Queue queue) + { + StringBuilder stringBuilder = new StringBuilder(); + lock (queue.SyncRoot) + { + foreach (string str in queue) + { + stringBuilder.Append(str); + } + } + return stringBuilder.ToString(); + } + + private PinStatus MapPinStatusStringToStatus(string s) + { + PinStatus pinStatu = PinStatus.Ready; + Dictionary strs = new Dictionary(); + strs["READY"] = PinStatus.Ready; + strs["SIM PIN"] = PinStatus.SimPin; + strs["SIM PUK"] = PinStatus.SimPuk; + strs["PH-SIM PIN"] = PinStatus.PhoneToSimPin; + strs["PH-FSIM PIN"] = PinStatus.PhoneToFirstSimPin; + strs["PH-FSIM PUK"] = PinStatus.PhoneToFirstSimPuk; + strs["SIM PIN2"] = PinStatus.SimPin2; + strs["SIM PUK2"] = PinStatus.SimPuk2; + strs["PH-NET PIN"] = PinStatus.PhoneToNetworkPin; + strs["PH-NET PUK"] = PinStatus.PhoneToNetworkPuk; + strs["PH-NETSUB PIN"] = PinStatus.PhoneToNetworkSubsetPin; + strs["PH-NETSUB PUK"] = PinStatus.PhoneToNetworkSubsetPuk; + strs["PH-SP PIN"] = PinStatus.PhoneToServiceProviderPin; + strs["PH-SP PUK"] = PinStatus.PhoneToServiceProviderPuk; + strs["PH-CORP PIN"] = PinStatus.PhoneToCorporatePin; + strs["PH-CORP PUK"] = PinStatus.PhoneToCorporatePuk; + if (!strs.TryGetValue(s, out pinStatu)) + { + throw new CommException(string.Concat("Unknown PIN status \"", s, "\".")); + } + else + { + return pinStatu; + } + } + + private void OnMessageReceived(IMessageIndicationObject obj) + { + if (this.MessageReceived == null) + { + this.LogIt(LogLevel.Info, "No event handlers for MessageReceived event, message is ignored."); + return; + } + else + { + this.LogIt(LogLevel.Info, "Firing async MessageReceived event."); + MessageReceivedEventArgs messageReceivedEventArg = new MessageReceivedEventArgs(obj); + this.MessageReceived.BeginInvoke(this, messageReceivedEventArg, new AsyncCallback(this.AsyncCallback), null); + return; + } + } + + private void OnPhoneConnected() + { + if (this.PhoneConnected != null) + { + this.LogIt(LogLevel.Info, "Firing async PhoneConnected event."); + this.PhoneConnected.BeginInvoke(this, EventArgs.Empty, new AsyncCallback(this.AsyncCallback), null); + } + } + + private void OnPhoneDisconnected() + { + if (this.PhoneDisconnected != null) + { + this.LogIt(LogLevel.Info, "Firing async PhoneDisconnected event."); + this.PhoneDisconnected.BeginInvoke(this, EventArgs.Empty, new AsyncCallback(this.AsyncCallback), null); + } + } + + private void OnReceiveComplete(int bytesReceived) + { + if (this.ReceiveComplete != null) + { + ProgressEventArgs progressEventArg = new ProgressEventArgs(bytesReceived); + this.ReceiveComplete.BeginInvoke(this, progressEventArg, new AsyncCallback(this.AsyncCallback), null); + } + } + + private void OnReceiveProgress(int bytesReceived) + { + if (this.ReceiveProgress != null) + { + ProgressEventArgs progressEventArg = new ProgressEventArgs(bytesReceived); + this.ReceiveProgress.BeginInvoke(this, progressEventArg, new AsyncCallback(this.AsyncCallback), null); + } + } + + /// + /// Opens the connection to the device. + /// + /// You can check the current connection state with the method. + /// + /// + /// + /// Connection to device already open. + /// Unable to open the port. + public void Open() + { + lock (this) + { + if (!this.IsOpen()) + { + this.CreateLogThread(); + try + { + this.OpenPort(this.portName, this.baudRate, this.timeout); + this.LogIt(LogLevel.Info, string.Concat("Port ", this.portName.ToString(), " now open.")); + } + catch (Exception exception1) + { + Exception exception = exception1; + string str = string.Concat("Unable to open port ", this.portName, ": ", exception.Message); + this.LogIt(LogLevel.Error, str); + this.TerminateLogThread(); + throw new CommException(str, exception); + } + this.CheckConnection(); + try + { + this.CreateCommThread(); + } + catch (Exception exception3) + { + Exception exception2 = exception3; + this.ClosePort(); + this.TerminateLogThread(); + throw new CommException("Unable to create communication thread.", exception2); + } + } + else + { + throw new InvalidOperationException("Port already open."); + } + } + } + + private void OpenPort(string portName, int baudRate, int timeout) + { + this.LogIt(LogLevel.Info, "Initializing serial connection..."); + this.LogIt(LogLevel.Info, string.Concat(" Port = ", portName)); + this.LogIt(LogLevel.Info, string.Concat(" Baud rate = ", baudRate.ToString())); + this.LogIt(LogLevel.Info, string.Concat(" Timeout = ", timeout.ToString())); + if (this.port != null) + { + if (this.port.IsOpen) + { + throw new CommException("Port already open."); + } + } + else + { + SerialPortFixer.Execute(portName); + this.port = new SerialPort(); + } + try + { + this.port.PortName = portName; + this.port.BaudRate = baudRate; + this.port.DataBits = 8; + this.port.StopBits = StopBits.One; + this.port.Parity = Parity.None; + this.port.ReadTimeout = timeout; + this.port.WriteTimeout = timeout; + this.port.Encoding = Encoding.GetEncoding(1252); + this.port.DataReceived += new SerialDataReceivedEventHandler(this.port_DataReceived); + this.port.Open(); + this.port.DtrEnable = true; + this.port.RtsEnable = true; + } + catch (Exception exception) + { + if (this.port.IsOpen) + { + this.port.Close(); + } + throw; + } + } + + /// + /// Parses the memory status response of the CPMS command. + /// + /// A response to the +CPMS set command + /// A object containing the status information of the storages. + /// + /// Data for the ReceiveStorage (mem3) or for the WriteStorage (mem2) is null if there is no information available about it. + /// + private MessageMemoryStatus ParseMessageMemoryStatus(string input) + { + MessageMemoryStatus messageMemoryStatu; + Regex regex = new Regex("\\+CPMS: (\\d+),(\\d+)(?:,(\\d+),(\\d+))?(?:,(\\d+),(\\d+))?"); + Match match = regex.Match(input); + if (!match.Success) + { + messageMemoryStatu = this.TryParseMessageMemoryStatus2(input); + if (messageMemoryStatu == null) + { + this.HandleCommError(input); + return null; + } + } + else + { + messageMemoryStatu = new MessageMemoryStatus(); + int num = int.Parse(match.Groups[1].Value); + int num1 = int.Parse(match.Groups[2].Value); + messageMemoryStatu.ReadStorage = new MemoryStatus(num, num1); + if (match.Groups[3].Captures.Count > 0 && match.Groups[4].Captures.Count > 0) + { + int num2 = int.Parse(match.Groups[3].Value); + int num3 = int.Parse(match.Groups[4].Value); + messageMemoryStatu.WriteStorage = new MemoryStatus(num2, num3); + } + if (match.Groups[5].Captures.Count > 0 && match.Groups[6].Captures.Count > 0) + { + int num4 = int.Parse(match.Groups[5].Value); + int num5 = int.Parse(match.Groups[6].Value); + messageMemoryStatu.ReceiveStorage = new MemoryStatus(num4, num5); + } + } + return messageMemoryStatu; + } + + /// + /// Parses the memory status response of the CPBS command. + /// + /// A response to the +CPBS set command + /// A object containing the memory details. + private MemoryStatusWithStorage ParsePhonebookMemoryStatus(string input) + { + Regex regex = new Regex("\\+CPBS: \"(\\w+)\",(\\d+),(\\d+)"); + Match match = regex.Match(input); + if (!match.Success) + { + this.HandleCommError(input); + return null; + } + else + { + string value = match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + int num1 = int.Parse(match.Groups[3].Value); + MemoryStatusWithStorage memoryStatusWithStorage = new MemoryStatusWithStorage(value, num, num1); + return memoryStatusWithStorage; + } + } + + private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) + { + if (e.EventType == SerialData.Chars) + { + this.receiveNow.Set(); + } + } + + /// + /// AT+CMGR. Reads a single SMS message from the current read/delete storage using the PDU mode. + /// + /// The index of the message to read. + /// A object containing the message at the index specified. + /// Always switches to PDU mode at the beginning. + public ShortMessageFromPhone ReadMessage(int index) + { + ShortMessageFromPhone shortMessageFromPhone; + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + this.LogIt(LogLevel.Info, string.Concat("Reading message from index ", index.ToString(), "...")); + string str = this.ExecAndReceiveMultiple(string.Concat("AT+CMGR=", index.ToString())); + Regex regex = new Regex("\\+CMGR: (\\d+),(?:\"(\\w*)\")?,(\\d+)\\r\\n(\\w+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + int num = int.Parse(match.Groups[1].Value); + string value = match.Groups[2].Value; + int num1 = int.Parse(match.Groups[3].Value); + string value1 = match.Groups[4].Value; + string[] strArrays = new string[6]; + strArrays[0] = "stat="; + strArrays[1] = num.ToString(); + strArrays[2] = ", alpha=\""; + strArrays[3] = value; + strArrays[4] = "\", length="; + strArrays[5] = num1.ToString(); + this.LogIt(LogLevel.Info, string.Concat(strArrays)); + ShortMessageFromPhone shortMessageFromPhone1 = new ShortMessageFromPhone(index, num, value, num1, value1); + shortMessageFromPhone = shortMessageFromPhone1; + } + } + return shortMessageFromPhone; + } + + /// + /// Gets the entire phonebook of the currently selected phonebook storage. + /// + /// An array of objects. + public PhonebookEntry[] ReadPhonebookEntries() + { + int num = 0; + int num1 = 0; + int num2 = 0; + int num3 = 0; + PhonebookEntry[] phonebookEntryArray; + lock (this) + { + this.GetPhonebookSize(out num, out num1, out num2, out num3); + phonebookEntryArray = this.ReadPhonebookEntries(num, num1); + } + return phonebookEntryArray; + } + + /// + /// AT+CPBR. Gets the specified range of phonebook entries. + /// + /// The first entry to get + /// The last entry to get + /// An array of objects + public PhonebookEntry[] ReadPhonebookEntries(int lowerBound, int upperBound) + { + PhonebookEntry[] phonebookEntryArray; + lock (this) + { + this.VerifyValidConnection(); + object[] objArray = new object[5]; + objArray[0] = "Getting phonebook from index "; + objArray[1] = lowerBound; + objArray[2] = " to "; + objArray[3] = upperBound; + objArray[4] = "..."; + this.LogIt(LogLevel.Info, string.Concat(objArray)); + string str = string.Concat("AT+CPBR=", lowerBound.ToString(), ",", upperBound.ToString()); + string str1 = this.ExecAndReceiveMultiple(str); + phonebookEntryArray = this.DecodePhonebookStream(str1, "+CPBR: "); + } + return phonebookEntryArray; + } + + /// + /// Receives raw data as a string. + /// + /// Receives the data received. + /// true if data was received, otherwise false. + private bool ReceiveInternal(out string input) + { + StringBuilder stringBuilder = new StringBuilder(); + for (string i = this.port.ReadExisting(); i.Length > 0; i = this.port.ReadExisting()) + { + stringBuilder.Append(i); + } + input = stringBuilder.ToString(); + return input.Length > 0; + } + + /// + /// Disables access to the protocol level of the current connection. + /// + /// This method must be called as soon as the execution of the custom commands initiated + /// by is completed and allows for normal operations to continue. + /// + public void ReleaseProtocol() + { + Monitor.Exit(this); + } + + /// + /// AT+CGMI. Requests manufacturer identification. + /// + /// The product manufacturer identification. + public string RequestManufacturer() + { + string str; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Requesting manufacturer..."); + string str1 = this.ExecAndReceiveMultiple("AT+CGMI"); + string str2 = this.TrimLineBreaks(str1); + this.LogIt(LogLevel.Info, string.Concat("Manufacturer = \"", str2, "\"")); + str = str2; + } + return str; + } + + /// + /// AT+CGMM. Requests model identification. + /// + /// The product model identification. + public string RequestModel() + { + string str; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Requesting model..."); + string str1 = this.ExecAndReceiveMultiple("AT+CGMM"); + string str2 = this.TrimLineBreaks(str1); + this.LogIt(LogLevel.Info, string.Concat("Model = \"", str2, "\"")); + str = str2; + } + return str; + } + + /// + /// AT+CGMR. Requests revision identification. + /// + /// The product revision identification. + public string RequestRevision() + { + string str; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Requesting revision..."); + string str1 = this.ExecAndReceiveMultiple("AT+CGMR"); + string str2 = this.TrimLineBreaks(str1); + this.LogIt(LogLevel.Info, string.Concat("Revision = \"", str2, "\"")); + str = str2; + } + return str; + } + + /// + /// AT+CGSN. Requests serial number identification. + /// + /// The product serial number. + public string RequestSerialNumber() + { + string str; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Requesting serial number..."); + string str1 = this.ExecAndReceiveMultiple("AT+CGSN"); + string str2 = this.TrimLineBreaks(str1); + this.LogIt(LogLevel.Info, string.Concat("Serial Number = \"", str2, "\"")); + str = str2; + } + return str; + } + + /// + /// ATZ. Settings that are not stored in a profile will be reset to their factory defaults. + /// + public void ResetToDefaultConfig() + { + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Resetting to default configuration..."); + this.ExecAndReceiveMultiple("ATZ"); + } + } + + /// + /// AT+CSCS. Selects the character set. + /// + /// The character set to use. + /// This command informs the data card of which character set is used by the TE. The data card is + /// then able to convert character strings correctly between TE and ME character sets. When the data card-TE + /// interface is set to 8-bit operation and the TE uses a 7-bit alphabet, the highest bit shall be set to + /// zero. This setting affects text mode SMS data and alpha fields in the phone book memory. If the ME is + /// using the GSM default alphabet, its characters shall be padded with the 8th bit (zero) before + /// converting them to hexadecimal numbers (that is, a 7-bit alphabet is not packed in the SMS-style + /// packing). + /// + /// + /// + public void SelectCharacterSet(string charset) + { + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Selecting character set \"", charset, "\"...")); + this.ExecAndReceiveMultiple(string.Concat("AT+CSCS=\"", charset, "\"")); + } + } + + /// + /// AT+CSMS. Selects the specified messaging service. + /// + /// The service to select. Specifies the compatibility level of the SMS AT commands. + /// The requirement of service setting 1 depends on specific commands. + /// + /// ME supports mobile terminated messages + /// ME supports mobile originated messages + /// ME supports broadcast type messages + public void SelectMessageService(int service, out int mt, out int mo, out int bm) + { + lock (this) + { + this.VerifyValidConnection(); + string str = this.ExecAndReceiveMultiple(string.Concat("AT+CSMS=", service.ToString())); + Regex regex = new Regex("\\+CSMS: (\\d+),(\\d+),(\\d+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.HandleCommError(str); + throw new CommException("Unexpected response.", str); + } + else + { + mt = int.Parse(match.Groups[1].Value); + mo = int.Parse(match.Groups[2].Value); + bm = int.Parse(match.Groups[3].Value); + } + } + } + + /// + /// AT+CPBS. Selects the storage to use for phonebook operations. + /// + /// The storage to use. + /// The memory status of the selected storage. + public void SelectPhonebookStorage(string storage) + { + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Selecting \"", storage, "\" phonebook memory...")); + this.ExecAndReceiveMultiple(string.Concat("AT+CPBS=\"", storage, "\"")); + } + } + + /// + /// AT+CPMS. Selects the storage to use for read and delete operations. + /// + /// The storage to use + /// The memory status of the selected storage. + /// This selects the preferred message storage "mem1" on the device. + public MemoryStatus SelectReadStorage(string storage) + { + MemoryStatus memoryStatu; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Selecting \"", storage, "\" as read storage...")); + string str = this.ExecAndReceiveMultiple(string.Concat("AT+CPMS=\"", storage, "\"")); + MessageMemoryStatus messageMemoryStatu = this.ParseMessageMemoryStatus(str); + MemoryStatus readStorage = messageMemoryStatu.ReadStorage; + this.LogMemoryStatus(readStorage); + memoryStatu = readStorage; + } + return memoryStatu; + } + + /// + /// AT+CPMS. Selects the storage to use for write and send operations. + /// + /// The storage to use + /// The memory status of the selected storage + /// This selects the preferred message storage "mem2" on the device. Additionaly, the "mem1" storage + /// is also set to the same storage since the read storage must also be set when selecting the + /// write storage. + public MemoryStatus SelectWriteStorage(string storage) + { + MemoryStatus memoryStatu; + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, string.Concat("Selecting \"", storage, "\" as write storage...")); + string str = storage; + string str1 = storage; + string[] strArrays = new string[5]; + strArrays[0] = "AT+CPMS=\""; + strArrays[1] = str; + strArrays[2] = "\",\""; + strArrays[3] = str1; + strArrays[4] = "\""; + string str2 = string.Concat(strArrays); + string str3 = this.ExecAndReceiveMultiple(str2); + MessageMemoryStatus messageMemoryStatu = this.ParseMessageMemoryStatus(str3); + MemoryStatus writeStorage = messageMemoryStatu.WriteStorage; + this.LogMemoryStatus(writeStorage); + memoryStatu = writeStorage; + } + return memoryStatu; + } + + /// + /// Sends raw data as a string. + /// + /// The data to send. + /// Specifies if the data sent should be logged. + /// Send and receive buffers are cleared before sending. + private void SendInternal(string output, bool logIt) + { + if (logIt) + { + this.LogItShow(LogLevel.Verbose, output, "<< "); + } + this.port.DiscardOutBuffer(); + this.port.DiscardInBuffer(); + this.port.Write(output); + } + + /// + /// AT+CMGS. Sends an SMS message using PDU mode. + /// + /// The PDU stream to send + /// The actual length of the PDU (not counting the SMSC data) + /// The message reference + /// Always switches to PDU mode at the beginning. + public byte SendMessage(string pdu, int actualLength) + { + byte num; + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + this.LogIt(LogLevel.Info, "Sending message..."); + this.ExecAndReceiveAnything(string.Concat("AT+CMGS=", actualLength.ToString()), "\\r\\n> "); + this.Send(string.Concat(pdu, (char)26)); + string str = this.ReceiveMultiple(); + byte num1 = 0; + Regex regex = new Regex("\\+CMGS: (\\d+)(,(\\w+))*"); + Match match = regex.Match(str); + if (!match.Success) + { + this.LogIt(LogLevel.Warning, string.Concat("Could not get message reference. Answer received was: \"", str, "\"")); + } + else + { + num1 = (byte)int.Parse(match.Groups[1].Value); + this.LogIt(LogLevel.Info, string.Concat("Message reference = ", num1.ToString())); + } + num = num1; + } + return num; + } + + /// + /// AT+CNMI. Selects the procedure for indicating new messages received from the network. + /// + /// A structure containing the + /// detailed settings. + /// The function switches to the PDU mode before setting the notifications. This + /// causes all short messages, that are directly routed, to be presented in PDU mode. If the mode + /// is changed (such as a switch to the text mode), all indications (containing a message) following the + /// change are sent in the new mode. + /// + public void SetMessageIndications(MessageIndicationSettings settings) + { + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + this.LogIt(LogLevel.Info, "Setting message notifications..."); + object[] mode = new object[5]; + mode[0] = settings.Mode; + mode[1] = settings.DeliverStyle; + mode[2] = settings.CellBroadcastStyle; + mode[3] = settings.StatusReportStyle; + mode[4] = settings.BufferSetting; + string str = string.Format("AT+CNMI={0},{1},{2},{3},{4}", mode); + this.ExecAndReceiveMultiple(str); + } + } + + /// + /// AT+CMMS. Sets the SMS batch mode. + /// + /// The new mode to set. + public void SetMoreMessagesToSend(MoreMessagesMode mode) + { + lock (this) + { + this.VerifyValidConnection(); + object[] objArray = new object[1]; + objArray[0] = mode; + this.LogIt(LogLevel.Info, "Setting more messages mode {0}...", objArray); + string str = string.Format("AT+CMMS={0}", (int)mode); + this.ExecAndReceiveMultiple(str); + } + } + + private void SetNewConnectionState(bool newState) + { + if (newState != this.connectionState) + { + this.connectionState = newState; + if (!this.connectionState) + { + this.LogIt(LogLevel.Info, "Phone disconnected."); + this.OnPhoneDisconnected(); + } + else + { + this.LogIt(LogLevel.Info, "Phone connected."); + this.OnPhoneConnected(); + return; + } + } + } + + /// + /// AT+CSCA. Sets the SMS Service Center Address. + /// + /// An object containing the new address + /// This command changes the SMSC address, through which SMS messages are transmitted. + /// In text mode, this setting is used by SMS sending and SMS writing commands. In PDU mode, this setting is + /// used by the same commands, but only when the length of the SMSC address coded into the PDU data equals + /// zero. + public void SetSmscAddress(AddressData data) + { + lock (this) + { + this.VerifyValidConnection(); + object[] address = new object[2]; + address[0] = data.Address; + int typeOfAddress = data.TypeOfAddress; + address[1] = typeOfAddress.ToString(); + this.LogIt(LogLevel.Info, "Setting SMSC address to \"{0}\", type {1}...", address); + int num = data.TypeOfAddress; + string str = string.Format("AT+CSCA=\"{0}\",{1}", data.Address, num.ToString()); + this.ExecAndReceiveMultiple(str); + } + } + + /// + /// AT+CSCA. Sets the SMS Service Center Address. + /// + /// The new SMSC address + /// This command changes the SMSC address, through which SMS messages are transmitted. + /// In text mode, setting is used by send and write commands. In PDU mode, setting is used by the same + /// commands, but only when the length of the SMSC address coded into the PDU data equals zero. + public void SetSmscAddress(string address) + { + lock (this) + { + this.VerifyValidConnection(); + object[] objArray = new object[1]; + objArray[0] = address; + this.LogIt(LogLevel.Info, "Setting SMSC address to \"{0}\"...", objArray); + string str = string.Format("AT+CSCA=\"{0}\"", address); + this.ExecAndReceiveMultiple(str); + } + } + + private string[] SplitLines(string data) + { + char chr = '\r'; + char chr1 = '\n'; + char chr2 = '\r'; + data = data.Replace(string.Concat(chr.ToString(), chr1.ToString()), chr2.ToString()); + char[] chrArray = new char[1]; + chrArray[0] = '\r'; + return data.Split(chrArray); + } + + private void TerminateCommThread() + { + if (this.IsCommThreadRunning()) + { + this.terminateCommThread.Set(); + try + { + if (!this.commThread.Join(10000)) + { + this.LogIt(LogLevel.Warning, "Communication thread did not exit within the timeout, aborting thread."); + this.commThread.Abort(); + } + } + catch (Exception exception1) + { + Exception exception = exception1; + this.LogIt(LogLevel.Error, string.Concat("Error while terminating comm thread: ", exception.Message)); + } + this.commThread = null; + } + } + + private void TerminateLogThread() + { + if (this.logThread != null && this.logThread.IsAlive) + { + this.LogIt(LogLevel.Info, "Log thread is terminating."); + this.terminateLogThread.Set(); + try + { + if (!this.logThread.Join(10000)) + { + this.logThread.Abort(); + } + } + catch (Exception exception) + { + } + this.logThread = null; + } + } + + /// + /// Removes all leading and trailing line termination characters from a string. + /// + /// The string to trim. + /// The modified string. + private string TrimLineBreaks(string input) + { + char[] chrArray = new char[2]; + chrArray[0] = '\r'; + chrArray[1] = '\n'; + return input.Trim(chrArray); + } + + /// + /// Tries to parse an alternative memory status response of the CPMS command. + /// + /// A response to the +CPMS set command + /// A object containing the status information of the storages + /// if successful, otherwise null. + private MessageMemoryStatus TryParseMessageMemoryStatus2(string input) + { + MessageMemoryStatus messageMemoryStatu = null; + Regex regex = new Regex("\\+CPMS: \"(\\w+)\",(\\d+),(\\d+),\"(\\w+)\",(\\d+),(\\d+)(?:,\"(\\w+)\",(\\d+),(\\d+))?"); + Match match = regex.Match(input); + if (match.Success) + { + messageMemoryStatu = new MessageMemoryStatus(); + string value = match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + int num1 = int.Parse(match.Groups[3].Value); + string str = match.Groups[4].Value; + int num2 = int.Parse(match.Groups[5].Value); + int num3 = int.Parse(match.Groups[6].Value); + messageMemoryStatu.ReadStorage = new MemoryStatusWithStorage(value, num, num1); + messageMemoryStatu.WriteStorage = new MemoryStatusWithStorage(str, num2, num3); + if (match.Groups[7].Captures.Count > 0) + { + string value1 = match.Groups[7].Value; + int num4 = int.Parse(match.Groups[8].Value); + int num5 = int.Parse(match.Groups[9].Value); + messageMemoryStatu.ReceiveStorage = new MemoryStatusWithStorage(value1, num4, num5); + } + } + return messageMemoryStatu; + } + + private void VerifyValidConnection() + { + if (this.IsOpen()) + { + if (this.connectionState) + { + return; + } + else + { + this.LogIt(LogLevel.Error, "No phone connected."); + throw new CommException("No phone connected."); + } + } + else + { + this.LogIt(LogLevel.Error, "Port not open."); + throw new InvalidOperationException("Port not open."); + } + } + + /// + /// AT+CMGW. Stores an SMS message in the current write/send storage using PDU mode. + /// + /// The message in PDU format + /// The actual length of the PDU (not counting the SMSC data) + /// The status that the message should get when saved. + /// The index of the message. If the index could not be retrieved, zero is returned. + /// The message is saved with a status predefined by the phone + /// This function always switches to the PDU mode at the beginning. + public int WriteMessageToMemory(string pdu, int actualLength, int status) + { + int memoryPart2; + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + this.LogIt(LogLevel.Info, "Writing message to memory..."); + this.ExecAndReceiveAnything(string.Concat("AT+CMGW=", actualLength.ToString(), ",", status.ToString()), "\\r\\n> "); + memoryPart2 = this.WriteMessageToMemoryPart2(pdu); + } + return memoryPart2; + } + + /// + /// AT+CMGW. Stores an SMS message in the current write/send storage using PDU mode. + /// + /// The message in PDU format + /// The actual length of the PDU (not counting the SMSC data) + /// The index of the message. If the index could not be retrieved, zero is returned. + /// The message is saved with a status predefined by the phone + /// This function always switches to the PDU mode at the beginning. + public int WriteMessageToMemory(string pdu, int actualLength) + { + int memoryPart2; + lock (this) + { + this.VerifyValidConnection(); + this.ActivatePduMode(); + this.LogIt(LogLevel.Info, "Writing message to memory..."); + this.ExecAndReceiveAnything(string.Concat("AT+CMGW=", actualLength.ToString()), "\\r\\n> "); + memoryPart2 = this.WriteMessageToMemoryPart2(pdu); + } + return memoryPart2; + } + + private int WriteMessageToMemoryPart2(string pdu) + { + this.Send(string.Concat(pdu, (char)26)); + string str = this.ReceiveMultiple(); + int num = 0; + Regex regex = new Regex("\\+CMGW: (\\d+)"); + Match match = regex.Match(str); + if (!match.Success) + { + this.LogIt(LogLevel.Warning, string.Concat("Could not get message index. Answer received was: \"", str, "\"")); + } + else + { + num = int.Parse(match.Groups[1].Value); + this.LogIt(LogLevel.Info, string.Concat("Message index = ", num.ToString())); + } + return num; + } + + /// + /// AT+CPBW. Creates a new phonebook entry. + /// + /// The entry to write. + /// The property of the entry is ignored, + /// the entry is always saved in the first free location. All other properties must be set + /// correctly. + public void WritePhonebookEntry(PhonebookEntry entry) + { + lock (this) + { + this.VerifyValidConnection(); + this.LogIt(LogLevel.Info, "Writing phonebook entry..."); + object[] number = new object[7]; + number[0] = "AT+CPBW=,\""; + number[1] = entry.Number; + number[2] = "\","; + number[3] = entry.Type; + number[4] = ",\""; + number[5] = entry.Text; + number[6] = "\""; + string str = string.Concat(number); + this.ExecAndReceiveMultiple(str); + } + } + + /// + /// The event that occurs when a new line was added to the log. + /// + public event LoglineAddedEventHandler LoglineAdded; + + /// + /// The event that occurs when a new SMS message was received. + /// + public event MessageReceivedEventHandler MessageReceived; + + /// The event that occurs when the phone is connected. + public event EventHandler PhoneConnected; + + /// The event that occurs when the phone is disconnected. + public event EventHandler PhoneDisconnected; + + /// The event that occurs when receiving from the phone is completed. + /// This event is only fired by reading operations that may take longer to complete. + public event ProgressEventHandler ReceiveComplete; + + /// The event that occurs when new data was received from the phone. + /// This event is only fired by reading operations that may take longer to complete. + public event ProgressEventHandler ReceiveProgress; + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/IMessageIndicationObject.cs b/GSMCommunication/GsmCommunication/IMessageIndicationObject.cs new file mode 100644 index 0000000..836ed48 --- /dev/null +++ b/GSMCommunication/GsmCommunication/IMessageIndicationObject.cs @@ -0,0 +1,10 @@ +/// +/// The interface identifying an object as being used for indicating new incoming messages. +/// +namespace GsmComm.GsmCommunication +{ + public interface IMessageIndicationObject + { + + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/IProtocol.cs b/GSMCommunication/GsmCommunication/IProtocol.cs new file mode 100644 index 0000000..ea9da43 --- /dev/null +++ b/GSMCommunication/GsmCommunication/IProtocol.cs @@ -0,0 +1,91 @@ +using System; + +/// +/// Provides an interface for low-level access to the device. +/// +namespace GsmComm.GsmCommunication +{ + public interface IProtocol + { + /// + /// Executes the specified command and reads multiple times from the phone + /// until a specific pattern is detected in the response. + /// + /// The command to execute. + /// The regular expression pattern that the received data must match to stop + /// reading. + /// The response received. + /// + /// + /// + string ExecAndReceiveAnything(string command, string pattern); + + /// Executes the specified command and reads multiple times from the phone + /// until one of the defined message termination patterns is detected in the response. + /// The command to execute. + /// The response received. + /// + /// + /// + string ExecAndReceiveMultiple(string command); + + /// Executes the specified command and reads a single response. + /// The command to execute. + /// The response received. + /// + /// This method returns whatever response comes in from the phone during a single read operation. + /// The response received may not be complete. + /// If you want to ensure that always complete responses are read, use instead. + /// + /// + /// + /// + /// + string ExecCommand(string command); + + /// + /// Executes the specified command and reads a single response. + /// The command to execute. + /// The message text for the exception if no data is received. + /// The response received. + /// + /// This method returns whatever response comes in from the phone during a single read operation. + /// The response received may not be complete. + /// If you want to ensure that always complete responses are read, use instead. + /// + /// + /// + /// + /// + string ExecCommand(string command, string receiveErrorMessage); + + /// + /// Receives raw string data. + /// The data received. + /// true if reception was successful, otherwise false. + /// + bool Receive(out string input); + + /// Reads multiple times from the phone until a specific pattern is detected in the response. + /// The response received. + /// The regular expression pattern that the received data must match to + /// stop reading. Can be an empty string if the pattern should not be checked. + /// + /// + /// + string ReceiveAnything(string pattern); + + /// Reads multiple times from the phone until one of the defined + /// message termination patterns is detected in the response. + /// The response received. + /// + /// + /// + string ReceiveMultiple(); + + /// Sends raw string data. + /// The data to send. + /// + void Send(string output); + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/IdentificationInfo.cs b/GSMCommunication/GsmCommunication/IdentificationInfo.cs new file mode 100644 index 0000000..84d6369 --- /dev/null +++ b/GSMCommunication/GsmCommunication/IdentificationInfo.cs @@ -0,0 +1,79 @@ +using System; + +/// +/// Contains information that identify a mobile phone. +/// +namespace GsmComm.GsmCommunication +{ + public struct IdentificationInfo + { + private string manufacturer; + + private string model; + + private string revision; + + private string serialNumber; + + /// + /// Gets or sets the manufacturer. + /// + public string Manufacturer + { + get + { + return this.manufacturer; + } + set + { + this.manufacturer = value; + } + } + + /// + /// Gets or sets the model. + /// + public string Model + { + get + { + return this.model; + } + set + { + this.model = value; + } + } + + /// + /// Gets or sets the revision. + /// + public string Revision + { + get + { + return this.revision; + } + set + { + this.revision = value; + } + } + + /// + /// Gets or sets the serial number. + /// + public string SerialNumber + { + get + { + return this.serialNumber; + } + set + { + this.serialNumber = value; + } + } + + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/IndicationBufferSetting.cs b/GSMCommunication/GsmCommunication/IndicationBufferSetting.cs new file mode 100644 index 0000000..83f3621 --- /dev/null +++ b/GSMCommunication/GsmCommunication/IndicationBufferSetting.cs @@ -0,0 +1,19 @@ +/// +/// Specifies what should happen to the TA's indication buffer. +/// +namespace GsmComm.GsmCommunication +{ + public enum IndicationBufferSetting + { + /// + /// The TA buffer of unsolicited result codes is flushed to the TE when an + /// other than is entered. + /// + Flush, + /// + /// TA buffer of unsolicited result codes defined within this command is cleared when an + /// other than is entered. + /// + Clear + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/LogLevel.cs b/GSMCommunication/GsmCommunication/LogLevel.cs new file mode 100644 index 0000000..b38e28b --- /dev/null +++ b/GSMCommunication/GsmCommunication/LogLevel.cs @@ -0,0 +1,25 @@ +/// +/// Specifies the level of an entry written to the log. +/// +namespace GsmComm.GsmCommunication +{ + public enum LogLevel + { + /// + /// Error + /// + Error, + /// + /// Warning + /// + Warning, + /// + /// Information + /// + Info, + /// + /// Additional information + /// + Verbose + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/LoglineAddedEventArgs.cs b/GSMCommunication/GsmCommunication/LoglineAddedEventArgs.cs new file mode 100644 index 0000000..3520c08 --- /dev/null +++ b/GSMCommunication/GsmCommunication/LoglineAddedEventArgs.cs @@ -0,0 +1,62 @@ +using System; + +/// +/// Provides data for the event. +/// +namespace GsmComm.GsmCommunication +{ + public class LoglineAddedEventArgs + { + private LogLevel level; + + private string text; + + /// + /// Gets the log level. + /// + public LogLevel Level + { + get + { + return this.level; + } + } + + /// + /// Gets the log text. + /// + public string Text + { + get + { + return this.text; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The log level. + /// The log text. + public LoglineAddedEventArgs(LogLevel level, string text) + { + if (Enum.IsDefined(typeof(LogLevel), level)) + { + if (text != null) + { + this.level = level; + this.text = text; + return; + } + else + { + throw new ArgumentNullException("text"); + } + } + else + { + throw new ArgumentException(string.Concat("Invalid log level \"", level, "\".")); + } + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/LoglineAddedEventHandler.cs b/GSMCommunication/GsmCommunication/LoglineAddedEventHandler.cs new file mode 100644 index 0000000..960bc9d --- /dev/null +++ b/GSMCommunication/GsmCommunication/LoglineAddedEventHandler.cs @@ -0,0 +1,11 @@ +using System; + +/// +/// The method that handles the event. +/// +/// The origin of the event. +/// The data for the event. +namespace GsmComm.GsmCommunication +{ + public delegate void LoglineAddedEventHandler(object sender, LoglineAddedEventArgs e); +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MemoryLocation.cs b/GSMCommunication/GsmCommunication/MemoryLocation.cs new file mode 100644 index 0000000..32b6a97 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MemoryLocation.cs @@ -0,0 +1,47 @@ +using System; + +/// +/// Contains the memory location of a saved message. +/// +namespace GsmComm.GsmCommunication +{ + public class MemoryLocation : IMessageIndicationObject + { + private string storage; + + private int index; + + /// + /// Gets the message index within the specified . + /// + public int Index + { + get + { + return this.index; + } + } + + /// + /// Gets the storage where the message is saved. + /// + public string Storage + { + get + { + return this.storage; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The storage where the message is saved. + /// The message index within the specified storage. + public MemoryLocation(string storage, int index) + { + this.storage = storage; + this.index = index; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MemoryStatus.cs b/GSMCommunication/GsmCommunication/MemoryStatus.cs new file mode 100644 index 0000000..8e99aef --- /dev/null +++ b/GSMCommunication/GsmCommunication/MemoryStatus.cs @@ -0,0 +1,47 @@ +using System; + +/// +/// Contains the memory status of a specific storage. +/// +namespace GsmComm.GsmCommunication +{ + public class MemoryStatus + { + private int used; + + private int total; + + /// + /// Gets the total capacity of the storage. + /// + public int Total + { + get + { + return this.total; + } + } + + /// + /// Gets the number of messages in the storage. + /// + public int Used + { + get + { + return this.used; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of messages in the storage + /// The total capacity of the storage + public MemoryStatus(int used, int total) + { + this.used = used; + this.total = total; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MemoryStatusWithStorage.cs b/GSMCommunication/GsmCommunication/MemoryStatusWithStorage.cs new file mode 100644 index 0000000..b14e0ad --- /dev/null +++ b/GSMCommunication/GsmCommunication/MemoryStatusWithStorage.cs @@ -0,0 +1,34 @@ +using System; + +/// +/// Contains the memory status of a specific storage, including the storage type. +/// +namespace GsmComm.GsmCommunication +{ + public class MemoryStatusWithStorage : MemoryStatus + { + private string storage; + + /// + /// Gets the storage that this memory status applies to. + /// + public string Storage + { + get + { + return this.storage; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The storage that this memory status applies to + /// The number of messages in the storage + /// The total capacity of the storage + public MemoryStatusWithStorage(string storage, int used, int total) : base(used, total) + { + this.storage = storage; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageErrorEventArgs.cs b/GSMCommunication/GsmCommunication/MessageErrorEventArgs.cs new file mode 100644 index 0000000..1b96df5 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageErrorEventArgs.cs @@ -0,0 +1,34 @@ +using GsmComm.PduConverter; +using System; + +/// +/// Provides data for the error events that deal with message sending. +/// +namespace GsmComm.GsmCommunication +{ + public class MessageErrorEventArgs : MessageEventArgs + { + private Exception exception; + + /// + /// Gets the exception that caused the error. + /// + public Exception Exception + { + get + { + return this.exception; + } + } + + /// + /// Initializes a new instance of the . + /// + /// The message that failed sending. + /// The exception that caused the error. + public MessageErrorEventArgs(OutgoingSmsPdu pdu, Exception exception) : base(pdu) + { + this.exception = exception; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageEventArgs.cs b/GSMCommunication/GsmCommunication/MessageEventArgs.cs new file mode 100644 index 0000000..1ef02d5 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageEventArgs.cs @@ -0,0 +1,33 @@ +using GsmComm.PduConverter; +using System; + +/// +/// Provides data for the events that deal with message sending. +/// +namespace GsmComm.GsmCommunication +{ + public class MessageEventArgs : EventArgs + { + private OutgoingSmsPdu pdu; + + /// + /// The message that was dealt with. + /// + public OutgoingSmsPdu Pdu + { + get + { + return this.pdu; + } + } + + /// + /// Initializes a new instance of the . + /// + /// The message that was dealt with. + public MessageEventArgs(OutgoingSmsPdu pdu) + { + this.pdu = pdu; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageIndicationHandlers.cs b/GSMCommunication/GsmCommunication/MessageIndicationHandlers.cs new file mode 100644 index 0000000..9c6a1c4 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageIndicationHandlers.cs @@ -0,0 +1,349 @@ +using GsmComm.PduConverter; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +/// +/// This class contains the handlers for unsolicited messages sent by the phone. It is for use by the GsmPhone +/// class only and must not be made public. +/// +namespace GsmComm.GsmCommunication +{ + internal class MessageIndicationHandlers + { + private const string deliverMemoryIndication = "\\+CMTI: \"(\\w+)\",(\\d+)"; + + private const string deliverMemoryIndicationStart = "\\+CMTI: "; + + private const string deliverPduModeIndication = "\\+CMT: (\\w*),(\\d+)\\r\\n(\\w+)"; + + private const string deliverPduModeIndicationStart = "\\+CMT: "; + + private const string statusReportMemoryIndication = "\\+CDSI: \"(\\w+)\",(\\d+)"; + + private const string statusReportMemoryIndicationStart = "\\+CDSI: "; + + private const string statusReportPduModeIndication = "\\+CDS: (\\d+)\\r\\n(\\w+)"; + + private const string statusReportPduModeIndicationStart = "\\+CDS: "; + + private List messages; + + public MessageIndicationHandlers() + { + this.messages = new List(); + MessageIndicationHandlers.UnsoMessage unsoMessage = new MessageIndicationHandlers.UnsoMessage("\\+CMTI: \"(\\w+)\",(\\d+)", new MessageIndicationHandlers.UnsoHandler(this.HandleDeliverMemoryIndication)); + unsoMessage.StartPattern = "\\+CMTI: "; + unsoMessage.Description = "New SMS-DELIVER received (indicated by memory location)"; + this.messages.Add(unsoMessage); + MessageIndicationHandlers.UnsoMessage unsoCompleteChecker = new MessageIndicationHandlers.UnsoMessage("\\+CMT: (\\w*),(\\d+)\\r\\n(\\w+)", new MessageIndicationHandlers.UnsoHandler(this.HandleDeliverPduModeIndication)); + unsoCompleteChecker.StartPattern = "\\+CMT: "; + unsoCompleteChecker.Description = "New SMS-DELIVER received (indicated by PDU mode version)"; + unsoCompleteChecker.CompleteChecker = new MessageIndicationHandlers.UnsoCompleteChecker(this.IsCompleteDeliverPduModeIndication); + this.messages.Add(unsoCompleteChecker); + MessageIndicationHandlers.UnsoMessage unsoMessage1 = new MessageIndicationHandlers.UnsoMessage("\\+CDSI: \"(\\w+)\",(\\d+)", new MessageIndicationHandlers.UnsoHandler(this.HandleStatusReportMemoryIndication)); + unsoMessage1.StartPattern = "\\+CDSI: "; + unsoMessage1.Description = "New SMS-STATUS-REPORT received (indicated by memory location)"; + this.messages.Add(unsoMessage1); + MessageIndicationHandlers.UnsoMessage unsoCompleteChecker1 = new MessageIndicationHandlers.UnsoMessage("\\+CDS: (\\d+)\\r\\n(\\w+)", new MessageIndicationHandlers.UnsoHandler(this.HandleStatusReportPduModeIndication)); + unsoCompleteChecker1.StartPattern = "\\+CDS: "; + unsoCompleteChecker1.Description = "New SMS-STATUS-REPORT received (indicated by PDU mode version)"; + unsoCompleteChecker1.CompleteChecker = new MessageIndicationHandlers.UnsoCompleteChecker(this.IsCompleteStatusReportPduModeIndication); + this.messages.Add(unsoCompleteChecker1); + } + + private IMessageIndicationObject HandleDeliverMemoryIndication(ref string input) + { + Regex regex = new Regex("\\+CMTI: \"(\\w+)\",(\\d+)"); + Match match = regex.Match(input); + if (match.Success) + { + string value = match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + MemoryLocation memoryLocation = new MemoryLocation(value, num); + input = input.Remove(match.Index, match.Length); + return memoryLocation; + } + else + { + throw new ArgumentException("Input string does not contain an SMS-DELIVER memory location indication."); + } + } + + private IMessageIndicationObject HandleDeliverPduModeIndication(ref string input) + { + Regex regex = new Regex("\\+CMT: (\\w*),(\\d+)\\r\\n(\\w+)"); + Match match = regex.Match(input); + if (match.Success) + { + string value = match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + string str = match.Groups[3].Value; + ShortMessage shortMessage = new ShortMessage(value, num, str); + input = input.Remove(match.Index, match.Length); + return shortMessage; + } + else + { + throw new ArgumentException("Input string does not contain an SMS-DELIVER PDU mode indication."); + } + } + + private IMessageIndicationObject HandleStatusReportMemoryIndication(ref string input) + { + Regex regex = new Regex("\\+CDSI: \"(\\w+)\",(\\d+)"); + Match match = regex.Match(input); + if (match.Success) + { + string value = match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + MemoryLocation memoryLocation = new MemoryLocation(value, num); + input = input.Remove(match.Index, match.Length); + return memoryLocation; + } + else + { + throw new ArgumentException("Input string does not contain an SMS-STATUS-REPORT memory location indication."); + } + } + + private IMessageIndicationObject HandleStatusReportPduModeIndication(ref string input) + { + Regex regex = new Regex("\\+CDS: (\\d+)\\r\\n(\\w+)"); + Match match = regex.Match(input); + if (match.Success) + { + int num = int.Parse(match.Groups[1].Value); + string value = match.Groups[2].Value; + ShortMessage shortMessage = new ShortMessage(string.Empty, num, value); + input = input.Remove(match.Index, match.Length); + return shortMessage; + } + else + { + throw new ArgumentException("Input string does not contain an SMS-STATUS-REPORT PDU mode indication."); + } + } + + /// + /// Handles an unsolicited message of the specified input string. + /// + /// The input string to handle, the unsolicited message will be removed + /// Receives a textual description of the message, may be empty + /// The message indication object generated from the message + /// Input string does not match any of the supported + /// unsolicited messages + public IMessageIndicationObject HandleUnsolicitedMessage(ref string input, out string description) + { + IMessageIndicationObject messageIndicationObject; + List.Enumerator enumerator = this.messages.GetEnumerator(); + try + { + while (enumerator.MoveNext()) + { + MessageIndicationHandlers.UnsoMessage current = enumerator.Current; + if (!current.IsMatch(input)) + { + continue; + } + IMessageIndicationObject messageIndicationObject1 = current.Handler(ref input); + description = current.Description; + messageIndicationObject = messageIndicationObject1; + return messageIndicationObject; + } + throw new ArgumentException("Input string does not match any of the supported unsolicited messages."); + } + finally + { + enumerator.Dispose(); + } + return messageIndicationObject; + } + + private bool IsCompleteDeliverPduModeIndication(string input) + { + Regex regex = new Regex("\\+CMT: (\\w*),(\\d+)\\r\\n(\\w+)"); + Match match = regex.Match(input); + if (match.Success) + { + match.Groups[1].Value; + int num = int.Parse(match.Groups[2].Value); + string value = match.Groups[3].Value; + if (BcdWorker.CountBytes(value) <= 0) + { + return false; + } + else + { + int num1 = BcdWorker.GetByte(value, 0); + int num2 = num * 2 + num1 * 2 + 2; + return value.Length >= num2; + } + } + else + { + return false; + } + } + + private bool IsCompleteStatusReportPduModeIndication(string input) + { + Regex regex = new Regex("\\+CDS: (\\d+)\\r\\n(\\w+)"); + Match match = regex.Match(input); + if (match.Success) + { + int num = int.Parse(match.Groups[1].Value); + string value = match.Groups[2].Value; + if (BcdWorker.CountBytes(value) <= 0) + { + return false; + } + else + { + int num1 = BcdWorker.GetByte(value, 0); + int num2 = num * 2 + num1 * 2 + 2; + return value.Length >= num2; + } + } + else + { + return false; + } + } + + public bool IsIncompleteUnsolicitedMessage(string input) + { + bool flag = false; + foreach (MessageIndicationHandlers.UnsoMessage message in this.messages) + { + if (!message.IsStartMatch(input) || message.IsMatch(input)) + { + continue; + } + flag = true; + break; + } + return flag; + } + + public bool IsUnsolicitedMessage(string input) + { + bool flag = false; + foreach (MessageIndicationHandlers.UnsoMessage message in this.messages) + { + if (!message.IsMatch(input)) + { + continue; + } + flag = true; + break; + } + return flag; + } + + private delegate bool UnsoCompleteChecker(string input); + + private delegate IMessageIndicationObject UnsoHandler(ref string input); + + private class UnsoMessage + { + private string pattern; + + private string startPattern; + + private string description; + + private MessageIndicationHandlers.UnsoHandler handler; + + private MessageIndicationHandlers.UnsoCompleteChecker completeChecker; + + public MessageIndicationHandlers.UnsoCompleteChecker CompleteChecker + { + get + { + return this.completeChecker; + } + set + { + this.completeChecker = value; + } + } + + public string Description + { + get + { + return this.description; + } + set + { + this.description = value; + } + } + + public MessageIndicationHandlers.UnsoHandler Handler + { + get + { + return this.handler; + } + set + { + this.handler = value; + } + } + + public string Pattern + { + get + { + return this.pattern; + } + set + { + this.pattern = value; + } + } + + public string StartPattern + { + get + { + return this.startPattern; + } + set + { + this.startPattern = value; + } + } + + public UnsoMessage(string pattern, MessageIndicationHandlers.UnsoHandler handler) + { + this.pattern = pattern; + this.startPattern = pattern; + this.description = string.Empty; + this.handler = handler; + this.completeChecker = null; + } + + public bool IsMatch(string input) + { + bool flag; + if (this.completeChecker == null) + { + flag = Regex.IsMatch(input, this.pattern); + } + else + { + flag = this.completeChecker(input); + } + return flag; + } + + public bool IsStartMatch(string input) + { + return Regex.IsMatch(input, this.startPattern); + } + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageIndicationMode.cs b/GSMCommunication/GsmCommunication/MessageIndicationMode.cs new file mode 100644 index 0000000..424459a --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageIndicationMode.cs @@ -0,0 +1,30 @@ +/// +/// Specifies the possible modes for for new message indications. +/// +namespace GsmComm.GsmCommunication +{ + public enum MessageIndicationMode + { + /// + /// Buffer unsolicited result codes in the TA. If TA result code buffer is full, indications can be + /// buffered in some other place or the oldest indications may be discarded and replaced with the new + /// received indications. + /// + DoNotForward, + /// + /// Discard indication and reject new received message unsolicited result codes when TA-TE link is + /// reserved (e.g. in on-line data mode). Otherwise forward them directly to the TE. + /// + SkipWhenReserved, + /// + /// Buffer unsolicited result codes in the TA when TA-TE link is reserved (e.g. in on-line data mode) and + /// flush them to the TE after reservation. Otherwise forward them directly to the TE. + /// + BufferAndFlush, + /// + /// Forward unsolicited result codes directly to the TE. TA-TE link specific inband technique used to + /// embed result codes and data when TA is in on-line data mode. + /// + ForwardAlways + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageIndicationSettings.cs b/GSMCommunication/GsmCommunication/MessageIndicationSettings.cs new file mode 100644 index 0000000..6f25138 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageIndicationSettings.cs @@ -0,0 +1,142 @@ +using System; + +/// +/// Specifies the settings for new message notifications. +/// +namespace GsmComm.GsmCommunication +{ + public struct MessageIndicationSettings + { + private int mode; + + private int mt; + + private int bm; + + private int ds; + + private int bfr; + + /// + /// Specifies how the indication buffer should be handled when indications are activated, i.e. + /// when is set to any value except . + /// + /// + /// You can use one of the values to set this property. + /// + public int BufferSetting + { + get + { + return this.bfr; + } + set + { + this.bfr = value; + } + } + + /// + /// Specifies how new Cell Broadcast messages should be indicated. + /// + /// + /// You can use one of the values to set this property. + /// + public int CellBroadcastStyle + { + get + { + return this.bm; + } + set + { + this.bm = value; + } + } + + /// + /// Specifies how new SMS-DELIVER messages should be indicated. + /// + /// + /// You can use one of the values to set this property. + /// + public int DeliverStyle + { + get + { + return this.mt; + } + set + { + this.mt = value; + } + } + + /// + /// Specifies the general indication mode. + /// + /// + /// You can use one of the values to set this property. + /// + public int Mode + { + get + { + return this.mode; + } + set + { + this.mode = value; + } + } + + /// + /// Specifies how new SMS-STATUS-REPORT messages should be indicated. + /// + /// + /// You can use one of the values to set this property. + /// + public int StatusReportStyle + { + get + { + return this.ds; + } + set + { + this.ds = value; + } + } + + /// + /// Initializes a new instance of the structure. + /// + /// Specifies the general indication mode. + /// Specifies how new SMS-DELIVER messages should be indicated. + /// Specifies how new Cell Broadcast messages should be indicated. + /// Specifies how new SMS-STATUS-REPORT messages should be indicated. + /// Specifies how the indication buffer should be handled when indications are activated, i.e. + /// when is set to any value except . + public MessageIndicationSettings(int mode, int mt, int bm, int ds, int bfr) + { + this.mode = mode; + this.mt = mt; + this.bm = bm; + this.ds = ds; + this.bfr = bfr; + } + + /// + /// Initializes a new instance of the structure. + /// + /// Specifies the general indication mode. + /// Specifies how new SMS-DELIVER messages should be indicated. + /// Specifies how new Cell Broadcast messages should be indicated. + /// Specifies how new SMS-STATUS-REPORT messages should be indicated. + /// Specifies how the indication buffer should be handled when indications are activated, i.e. + /// when is set to any value except . + public MessageIndicationSettings(MessageIndicationMode mode, SmsDeliverIndicationStyle mt, CbmIndicationStyle bm, SmsStatusReportIndicationStyle ds, IndicationBufferSetting bfr) : this(mode, mt, bm, ds, bfr) + { + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageIndicationSupport.cs b/GSMCommunication/GsmCommunication/MessageIndicationSupport.cs new file mode 100644 index 0000000..7ddae21 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageIndicationSupport.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections; +using System.Text.RegularExpressions; + +/// +/// Contains information about the supported new message indications of the phone. +/// +namespace GsmComm.GsmCommunication +{ + public class MessageIndicationSupport + { + private string mode; + + private string deliver; + + private string cellBroadcast; + + private string statusReport; + + private string buffer; + + /// + /// Gets a string representation of the supported buffer handling settings. + /// + public string BufferHandling + { + get + { + return this.buffer; + } + } + + /// + /// Gets a string representation of the supported cell broadcast indication styles. + /// + public string CellBroadcastStyles + { + get + { + return this.cellBroadcast; + } + } + + /// + /// Gets a string representation of the supported deliver indication modes. + /// + public string DeliverStyles + { + get + { + return this.deliver; + } + } + + /// + /// Gets a string representation of the supported indication modes. + /// + public string Modes + { + get + { + return this.mode; + } + } + + /// + /// Gets a string representation of the supported status report indication styles. + /// + public string StatusReportStyles + { + get + { + return this.statusReport; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// A string representation of the phone's supported indication modes + /// A string representation of the phones's supported standard SMS (SMS-DELIVER) styles + /// A string representation of the phones's supported cell broadcast styles + /// The phones's supported status report (SMS-STATUS-REPORT) styles + /// The phones's supported buffer handling settings + public MessageIndicationSupport(string mode, string deliver, string cellBroadcast, string statusReport, string buffer) + { + this.mode = mode; + this.deliver = deliver; + this.cellBroadcast = cellBroadcast; + this.statusReport = statusReport; + this.buffer = buffer; + } + + private ArrayList ParseArrayAsString(string s) + { + ArrayList arrayLists = new ArrayList(); + Regex regex = new Regex("(?:(\\d+),?)+"); + Match match = regex.Match(s); + if (match.Success) + { + foreach (Capture capture in match.Groups[1].Captures) + { + arrayLists.Add(int.Parse(capture.Value)); + } + } + Regex regex1 = new Regex("(\\d+)-(\\d+)"); + Match match1 = regex1.Match(s); + if (match1.Success) + { + int num = int.Parse(match1.Groups[1].Value); + int num1 = int.Parse(match1.Groups[2].Value); + for (int i = num; i <= num1; i++) + { + arrayLists.Add(i); + } + } + return arrayLists; + } + + /// + /// Checks if a specific buffer handling setting is supported. + /// + /// The setting to check + /// true if the setting is supported, false otherwise. + public bool SupportsBufferSetting(int setting) + { + ArrayList arrayLists = this.ParseArrayAsString(this.buffer); + return arrayLists.Contains(setting); + } + + /// + /// Checks if a specific buffer handling setting is supported. + /// + /// The setting to check + /// true if the setting is supported, false otherwise. + public bool SupportsBufferSetting(IndicationBufferSetting setting) + { + return this.SupportsBufferSetting(setting); + } + + /// + /// Checks if a specific cell broadcast indication style is supported. + /// + /// The style to check + /// true if the style is supported, false otherwise. + public bool SupportsCellBroadcastStyle(int style) + { + ArrayList arrayLists = this.ParseArrayAsString(this.cellBroadcast); + return arrayLists.Contains(style); + } + + /// + /// Checks if a specific cell broadcast indication style is supported. + /// + /// The style to check + /// true if the style is supported, false otherwise. + public bool SupportsCellBroadcastStyle(CbmIndicationStyle style) + { + return this.SupportsCellBroadcastStyle(style); + } + + /// + /// Checks if a specific SMS-DELIVER indication style is supported. + /// + /// The style to check + /// true if the style is supported, false otherwise. + public bool SupportsDeliverStyle(int style) + { + ArrayList arrayLists = this.ParseArrayAsString(this.deliver); + return arrayLists.Contains(style); + } + + /// + /// Checks if a specific SMS-DELIVER indication style is supported. + /// + /// The style to check + /// true if the style is supported, false otherwise. + public bool SupportsDeliverStyle(SmsDeliverIndicationStyle style) + { + return this.SupportsDeliverStyle(style); + } + + /// + /// Checks if a specific indication mode is supported. + /// + /// The mode to check + /// true if the mode is supported, false otherwise. + public bool SupportsMode(int mode) + { + ArrayList arrayLists = this.ParseArrayAsString(this.mode); + return arrayLists.Contains(mode); + } + + /// + /// Checks if a specific indication mode is supported. + /// + /// The mode to check + /// true if the mode is supported, false otherwise. + public bool SupportsMode(MessageIndicationMode mode) + { + return this.SupportsMode(mode); + } + + /// + /// Checks if a specific status report (SMS-STATUS-REPORT) indication style is supported. + /// + /// The style to check + /// true if the style is supported, false otherwise. + public bool SupportsStatusReportStyle(int style) + { + ArrayList arrayLists = this.ParseArrayAsString(this.statusReport); + return arrayLists.Contains(style); + } + + /// + /// Checks if a specific status report (SMS-STATUS-REPORT) indication style is supported. + /// + /// The style to check + /// true if the style is supported, false otherwise. + public bool SupportsStatusReportStyle(SmsStatusReportIndicationStyle style) + { + return this.SupportsStatusReportStyle(style); + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageMemoryStatus.cs b/GSMCommunication/GsmCommunication/MessageMemoryStatus.cs new file mode 100644 index 0000000..840effd --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageMemoryStatus.cs @@ -0,0 +1,79 @@ +/// +/// Contains status information of all message memories. +/// +namespace GsmComm.GsmCommunication +{ + public class MessageMemoryStatus + { + private MemoryStatus readStorage; + + private MemoryStatus writeStorage; + + private MemoryStatus receiveStorage; + + /// + /// Gets or sets the status of the current read storage. + /// + public MemoryStatus ReadStorage + { + get + { + return this.readStorage; + } + set + { + this.readStorage = value; + } + } + + /// + /// Gets or sets the status of the current receive storage. + /// + public MemoryStatus ReceiveStorage + { + get + { + return this.receiveStorage; + } + set + { + this.receiveStorage = value; + } + } + + /// + /// Gets or sets the status of the current write storage. + /// + public MemoryStatus WriteStorage + { + get + { + return this.writeStorage; + } + set + { + this.writeStorage = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public MessageMemoryStatus() + { + } + + /// + /// Initializes a new instance of the class with the specified parameters. + /// + /// Status of the current read storage + /// Status of the current write storage + /// Status of the current receive storage + public MessageMemoryStatus(MemoryStatus readStorage, MemoryStatus writeStorage, MemoryStatus receiveStorage) + { + this.readStorage = readStorage; + this.writeStorage = writeStorage; + this.receiveStorage = receiveStorage; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageReceivedEventArgs.cs b/GSMCommunication/GsmCommunication/MessageReceivedEventArgs.cs new file mode 100644 index 0000000..0e12289 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageReceivedEventArgs.cs @@ -0,0 +1,30 @@ +/// +/// Provides data for the events. +/// +namespace GsmComm.GsmCommunication +{ + public class MessageReceivedEventArgs + { + private IMessageIndicationObject indicationObject; + + /// + /// The object that indicates a new received message. + /// + public IMessageIndicationObject IndicationObject + { + get + { + return this.indicationObject; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The object that indicates a new received message. + public MessageReceivedEventArgs(IMessageIndicationObject obj) + { + this.indicationObject = obj; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageReceivedEventHandler.cs b/GSMCommunication/GsmCommunication/MessageReceivedEventHandler.cs new file mode 100644 index 0000000..0d7857f --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageReceivedEventHandler.cs @@ -0,0 +1,11 @@ +using System; + +/// +/// The method that handles the event. +/// +/// The origin of the event. +/// The arguments containing more information. +namespace GsmComm.GsmCommunication +{ + public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e); +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MessageStorageInfo.cs b/GSMCommunication/GsmCommunication/MessageStorageInfo.cs new file mode 100644 index 0000000..5711932 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MessageStorageInfo.cs @@ -0,0 +1,19 @@ +using System; + +/// +/// Provides a structure that contains details about supported message storages. +/// +namespace GsmComm.GsmCommunication +{ + public struct MessageStorageInfo + { + /// Specifies the storages that can be used for reading and deleting. + public string[] ReadStorages; + + /// Speicifies the storages that can be used for writing and sending. + public string[] WriteStorages; + + /// Specifies the storages that can be used as the preferred receive storage. + public string[] ReceiveStorages; + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/MoreMessagesMode.cs b/GSMCommunication/GsmCommunication/MoreMessagesMode.cs new file mode 100644 index 0000000..d89e352 --- /dev/null +++ b/GSMCommunication/GsmCommunication/MoreMessagesMode.cs @@ -0,0 +1,23 @@ +/// +/// Contains the possible modes for the AT+CMMS command to set the high-speed SMS sending behaviour. +/// +namespace GsmComm.GsmCommunication +{ + public enum MoreMessagesMode + { + /// The function is disabled, the SMS link is not kept open. + Disabled, + /// + /// Keep enabled until the time between the response of the latest message send command (+CMGS, +CMSS, etc.) + /// and the next send command exceeds 1-5 seconds (the exact value is up to ME implementation), then ME shall + /// close the link and TA switches the mode automatically back to disabled (0). + /// + Temporary, + /// + /// Enables (if the time between the response of the latest message send command and the next send command + /// exceeds 1-5 seconds (the exact value is up to ME implementation), ME shall close the link but TA shall + /// not switch automatically back to disabled (0)). + /// + Permanent + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/OperatorFormat.cs b/GSMCommunication/GsmCommunication/OperatorFormat.cs new file mode 100644 index 0000000..1f95fba --- /dev/null +++ b/GSMCommunication/GsmCommunication/OperatorFormat.cs @@ -0,0 +1,18 @@ +/// +/// Contains the possible formats in which a network operator can be returned. +/// +namespace GsmComm.GsmCommunication +{ + public enum OperatorFormat + { + /// Long format, alphanumeric + LongFormatAlphanumeric, + /// Short format, alphanumeric + ShortFormatAlphanumeric, + /// + /// Numeric format, GSM Location Area Identification number (BCD encoded, 3 digits country code, + /// 2 digits network code) + /// + Numeric + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/OperatorInfo.cs b/GSMCommunication/GsmCommunication/OperatorInfo.cs new file mode 100644 index 0000000..287f096 --- /dev/null +++ b/GSMCommunication/GsmCommunication/OperatorInfo.cs @@ -0,0 +1,80 @@ +using System; + +/// +/// Contains information about a GSM network operator. +/// +namespace GsmComm.GsmCommunication +{ + public class OperatorInfo + { + private OperatorFormat format; + + private string theOperator; + + private string accessTechnology; + + /// + /// Gets the access technology registered to. + /// + /// This is optional, as it is only useful for terminals capable to register to more than + /// one access technology. + public string AccessTechnology + { + get + { + return this.accessTechnology; + } + } + + /// + /// Gets the format in which is specified in. + /// + public OperatorFormat Format + { + get + { + return this.format; + } + } + + /// + /// Gets the operator in the format specified by . + /// + public string TheOperator + { + get + { + return this.theOperator; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The format in which theOperator is specified in. See + /// for a list of possible values. + /// + /// The operator in the format specified by format + public OperatorInfo(OperatorFormat format, string theOperator) + { + this.format = format; + this.theOperator = theOperator; + this.accessTechnology = string.Empty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The format in which theOperator is specified in. See + /// for a list of possible values. + /// + /// The operator in the format specified by format + /// The access technology registered to. + public OperatorInfo(OperatorFormat format, string theOperator, string accessTechnology) + { + this.format = format; + this.theOperator = theOperator; + this.accessTechnology = accessTechnology; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/OperatorInfo2.cs b/GSMCommunication/GsmCommunication/OperatorInfo2.cs new file mode 100644 index 0000000..b6b291c --- /dev/null +++ b/GSMCommunication/GsmCommunication/OperatorInfo2.cs @@ -0,0 +1,121 @@ +using System; + +/// +/// Contains information about a GSM network operator. +/// +namespace GsmComm.GsmCommunication +{ + public class OperatorInfo2 + { + private OperatorStatus stat; + + private string longAlpha; + + private string shortAlpha; + + private string numeric; + + private string act; + + /// + /// Gets the access technology the operator uses. + /// + /// This is optional, as it is only useful for terminals capable to register to more than + /// one access technology. + public string AccessTechnology + { + get + { + return this.act; + } + } + + /// + /// Gets the operator name in long alphanumeric format. + /// + /// If the phone does not support this format, the string will be empty. + public string LongAlphanumeric + { + get + { + return this.longAlpha; + } + } + + /// + /// Gets the operator in numeric format. + /// + /// If the phone does not support this format, the string will be empty. + public string Numeric + { + get + { + return this.numeric; + } + } + + /// + /// Gets the operator name in short alphanumic format. + /// + /// If the phone does not support this format, the string will be empty. + public string ShortAlphanumeric + { + get + { + return this.shortAlpha; + } + } + + /// + /// Gets the availability of the operator. + /// + public OperatorStatus Status + { + get + { + return this.stat; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The operator availability + /// The operator name in long alphanumeric format + /// The operator name in short alphanumeric format + /// The operator in numeric format + /// If the phone does not support one of the formats longAlphanumeric, + /// shortAlphanumeric, numeric, the curresponding string is left empty. + public OperatorInfo2(OperatorStatus status, string longAlphanumeric, string shortAlphanumeric, string numeric) + { + this.stat = status; + this.longAlpha = longAlphanumeric; + this.shortAlpha = shortAlphanumeric; + this.numeric = numeric; + this.act = string.Empty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The operator availability + /// The operator name in long alphanumeric format + /// The operator name in short alphanumeric format + /// The operator in numeric format + /// The access technology the operator uses. + /// + /// If the phone does not support one of the formats longAlphanumeric, + /// shortAlphanumeric, numeric, the curresponding string is left empty. + /// The accessTechnology is optional, as it is only useful for terminals capable + /// to register to more than one access technology. + /// + public OperatorInfo2(OperatorStatus status, string longAlphanumeric, string shortAlphanumeric, string numeric, string accessTechnology) + { + this.stat = status; + this.longAlpha = longAlphanumeric; + this.shortAlpha = shortAlphanumeric; + this.numeric = numeric; + this.act = accessTechnology; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/OperatorSelectionMode.cs b/GSMCommunication/GsmCommunication/OperatorSelectionMode.cs new file mode 100644 index 0000000..54fd6c1 --- /dev/null +++ b/GSMCommunication/GsmCommunication/OperatorSelectionMode.cs @@ -0,0 +1,25 @@ +/// +/// Contains the possible values for the operator selection mode. +/// +namespace GsmComm.GsmCommunication +{ + public enum OperatorSelectionMode + { + /// + /// The phone selects the operator automatically. + /// + Automatic = 0, + /// + /// A specific operator is selected. The phone does not attempt to select the operator automatically. + /// + Manual = 1, + /// + /// The phone is not registered to the network. + /// + Deregistered = 2, + /// + /// If manual selection fails, automatic mode is entered. + /// + ManualAutomatic = 4 + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/OperatorStatus.cs b/GSMCommunication/GsmCommunication/OperatorStatus.cs new file mode 100644 index 0000000..c70f816 --- /dev/null +++ b/GSMCommunication/GsmCommunication/OperatorStatus.cs @@ -0,0 +1,17 @@ +/// +/// Conatins the operator availability values. +/// +namespace GsmComm.GsmCommunication +{ + public enum OperatorStatus + { + /// The operator status is unknown. + Unknown, + /// The operator is available for selection. + Available, + /// Denotes that this is the currently selected operator. + Current, + /// The phone must not connect to this operator. + Forbidden + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/PhoneMessageStatus.cs b/GSMCommunication/GsmCommunication/PhoneMessageStatus.cs new file mode 100644 index 0000000..12ae1a4 --- /dev/null +++ b/GSMCommunication/GsmCommunication/PhoneMessageStatus.cs @@ -0,0 +1,19 @@ +/// +/// The message status to request from the phone or the actual type. +/// +namespace GsmComm.GsmCommunication +{ + public enum PhoneMessageStatus + { + /// The message was received, but not yet read. + ReceivedUnread, + /// The message was received and has been read. + ReceivedRead, + /// The message was stored, but had not been sent yet. + StoredUnsent, + /// The message was stored and sent. + StoredSent, + /// Specifies all status. + All + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/PhoneNumberService.cs b/GSMCommunication/GsmCommunication/PhoneNumberService.cs new file mode 100644 index 0000000..8c4dc85 --- /dev/null +++ b/GSMCommunication/GsmCommunication/PhoneNumberService.cs @@ -0,0 +1,21 @@ +/// +/// Contains services related to a subscriber phone number. +/// +namespace GsmComm.GsmCommunication +{ + public enum PhoneNumberService + { + /// Asynchronous modem + AsynchronousModem, + /// Synchronous modem + SynchronousModem, + /// PAD Access (asynchronous) + PadAccess, + /// Packet Access (synchronous) + PacketAccess, + /// Voice + Voice, + /// Fax + Fax + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/PhoneStorageType.cs b/GSMCommunication/GsmCommunication/PhoneStorageType.cs new file mode 100644 index 0000000..2a05fb4 --- /dev/null +++ b/GSMCommunication/GsmCommunication/PhoneStorageType.cs @@ -0,0 +1,20 @@ +using System; + +/// +/// Lists common storage types. +/// +namespace GsmComm.GsmCommunication +{ + public class PhoneStorageType + { + /// SIM storage + public const string Sim = "SM"; + + /// Phone storage + public const string Phone = "ME"; + + public PhoneStorageType() + { + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/PhonebookEntry.cs b/GSMCommunication/GsmCommunication/PhonebookEntry.cs new file mode 100644 index 0000000..b1059dc --- /dev/null +++ b/GSMCommunication/GsmCommunication/PhonebookEntry.cs @@ -0,0 +1,119 @@ +using System; +using System.Xml.Serialization; + +/// +/// Represents a phonebook entry. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + [XmlInclude(typeof(PhonebookEntryWithStorage))] + public class PhonebookEntry + { + private int index; + + private string number; + + private int type; + + private string text; + + /// + /// The index where the entry is saved in the phone. + /// + [XmlAttribute] + public int Index + { + get + { + return this.index; + } + set + { + this.index = value; + } + } + + /// + /// The phone number. + /// + [XmlAttribute] + public string Number + { + get + { + return this.number; + } + set + { + this.number = value; + } + } + + /// + /// The text (name) associated with the . + /// + [XmlAttribute] + public string Text + { + get + { + return this.text; + } + set + { + this.text = value; + } + } + + /// + /// The 's address type. + /// + [XmlAttribute] + public int Type + { + get + { + return this.type; + } + set + { + this.type = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public PhonebookEntry() + { + } + + /// + /// Initializes a new instance of the class using the specified values. + /// + /// The index where the entry is saved in the phone. + /// The phone number. + /// The 's address type. + /// The text (name) associated with the . + public PhonebookEntry(int index, string number, int type, string text) + { + this.index = index; + this.number = number; + this.type = type; + this.text = text; + } + + /// + /// Initializes a new instance of the class to copy an existing . + /// + /// The entry to copy. + public PhonebookEntry(PhonebookEntry entry) + { + this.index = entry.Index; + this.number = entry.Number; + this.type = entry.type; + this.text = entry.Text; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/PhonebookEntryWithStorage.cs b/GSMCommunication/GsmCommunication/PhonebookEntryWithStorage.cs new file mode 100644 index 0000000..b11bdb2 --- /dev/null +++ b/GSMCommunication/GsmCommunication/PhonebookEntryWithStorage.cs @@ -0,0 +1,47 @@ +using System; +using System.Xml.Serialization; + +/// +/// Represents a extended by the storage value. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class PhonebookEntryWithStorage : PhonebookEntry + { + private string storage; + + /// + /// The storage the entry was read from. + /// + [XmlAttribute] + public string Storage + { + get + { + return this.storage; + } + set + { + this.storage = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public PhonebookEntryWithStorage() + { + } + + /// + /// Initializes a new instance of the class using the specified values. + /// + /// The phonebook entry + /// The storage the entry was read from. + public PhonebookEntryWithStorage(PhonebookEntry entry, string storage) : base(entry) + { + this.storage = storage; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/PinStatus.cs b/GSMCommunication/GsmCommunication/PinStatus.cs new file mode 100644 index 0000000..0660784 --- /dev/null +++ b/GSMCommunication/GsmCommunication/PinStatus.cs @@ -0,0 +1,51 @@ +/// +/// Lists the possible PIN states of the phone. +/// +namespace GsmComm.GsmCommunication +{ + public enum PinStatus + { + /// Phone does not wait for any password + Ready, + /// Phone is waiting for SIM PIN to be given + SimPin, + /// Phone is waiting for SIM PUK to be given + SimPuk, + /// Phone is waiting for phone to SIM card password to be given + PhoneToSimPin, + /// Phone is waiting for phone-to-very first SIM card password to be given + PhoneToFirstSimPin, + /// Phone is waiting for phone-to-very first SIM card unblocking password to be given + PhoneToFirstSimPuk, + /// + /// Phone is waiting for SIM PIN2 to be given (this status should be expected to be returned + /// by phones only when the last executed command resulted in PIN2 authentication failure (i.e. device + /// error 17); if PIN2 is not entered right after the failure, the phone should be expected not to block + /// its operation) + /// + SimPin2, + /// + /// Phone is waiting for SIM PUK2 to be given (this status should be expected to be returned + /// by phones only when the last executed command resulted in PUK2 authentication failure (i.e. device + /// error 18); if PUK2 is not entered right after the failure, the phone should be expected not to block + /// its operation) + /// + SimPuk2, + /// Phone is waiting for network personalization password to be given + PhoneToNetworkPin, + /// Phone is waiting for network personalization unblocking password to be given + PhoneToNetworkPuk, + /// Phone is waiting for network subset personalization password to be given + PhoneToNetworkSubsetPin, + /// Phone is waiting for network subset personalization unblocking password to be given + PhoneToNetworkSubsetPuk, + /// Phone is waiting for service provider personalization password to be given + PhoneToServiceProviderPin, + /// Phone is waiting for service provider personalization unblocking password to be given + PhoneToServiceProviderPuk, + /// Phone is waiting for corporate personalization password to be given + PhoneToCorporatePin, + /// Phone is waiting for corporate personalization unblocking password to be given + PhoneToCorporatePuk + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/ProgressEventArgs.cs b/GSMCommunication/GsmCommunication/ProgressEventArgs.cs new file mode 100644 index 0000000..73656a6 --- /dev/null +++ b/GSMCommunication/GsmCommunication/ProgressEventArgs.cs @@ -0,0 +1,32 @@ +using System; + +/// +/// Provides data for the and events. +/// +namespace GsmComm.GsmCommunication +{ + public class ProgressEventArgs : EventArgs + { + private int progress; + + /// + /// Get the current progress value. + /// + public int Progress + { + get + { + return this.progress; + } + } + + /// + /// Initializes a new instance of the event args. + /// + /// The current progress value. + public ProgressEventArgs(int progress) + { + this.progress = progress; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/ProgressEventHandler.cs b/GSMCommunication/GsmCommunication/ProgressEventHandler.cs new file mode 100644 index 0000000..1e44c83 --- /dev/null +++ b/GSMCommunication/GsmCommunication/ProgressEventHandler.cs @@ -0,0 +1,11 @@ +using System; + +/// +/// The method that handles the and the events. +/// +/// The origin of the event. +/// The arguments containing more information. +namespace GsmComm.GsmCommunication +{ + public delegate void ProgressEventHandler(object sender, ProgressEventArgs e); +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/SerialPortFixer.cs b/GSMCommunication/GsmCommunication/SerialPortFixer.cs new file mode 100644 index 0000000..b8316d9 --- /dev/null +++ b/GSMCommunication/GsmCommunication/SerialPortFixer.cs @@ -0,0 +1,213 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace GsmComm.GsmCommunication +{ + internal class SerialPortFixer : IDisposable + { + private const int DcbFlagAbortOnError = 14; + + private const int CommStateRetries = 10; + + private SafeFileHandle m_Handle; + + private SerialPortFixer(string portName) + { + if (portName == null || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException("Invalid Serial Port", "portName"); + } + else + { + SafeFileHandle safeFileHandle = SerialPortFixer.CreateFile(string.Concat("\\\\.\\", portName), -1073741824, 0, IntPtr.Zero, 3, 1073741824, IntPtr.Zero); + if (safeFileHandle.IsInvalid) + { + SerialPortFixer.WinIoError(); + } + try + { + int fileType = SerialPortFixer.GetFileType(safeFileHandle); + if (fileType == 2 || fileType == 0) + { + this.m_Handle = safeFileHandle; + this.InitializeDcb(); + } + else + { + throw new ArgumentException("Invalid Serial Port", "portName"); + } + } + catch + { + safeFileHandle.Close(); + this.m_Handle = null; + throw; + } + return; + } + } + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + private static extern bool ClearCommError(SafeFileHandle hFile, ref int lpErrors, ref SerialPortFixer.Comstat lpStat); + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + private static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); + + public void Dispose() + { + if (this.m_Handle != null) + { + this.m_Handle.Close(); + this.m_Handle = null; + } + } + + public static void Execute(string portName) + { + using (SerialPortFixer serialPortFixer = new SerialPortFixer(portName)) + { + } + } + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + private static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr arguments); + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + private static extern bool GetCommState(SafeFileHandle hFile, ref SerialPortFixer.Dcb lpDcb); + + private void GetCommStateNative(ref SerialPortFixer.Dcb lpDcb) + { + int num = 0; + SerialPortFixer.Comstat comstat = new SerialPortFixer.Comstat(); + int num1 = 0; + while (num1 < 10) + { + if (!SerialPortFixer.ClearCommError(this.m_Handle, ref num, ref comstat)) + { + SerialPortFixer.WinIoError(); + } + if (!SerialPortFixer.GetCommState(this.m_Handle, ref lpDcb)) + { + if (num1 == 9) + { + SerialPortFixer.WinIoError(); + } + num1++; + } + else + { + return; + } + } + } + + [DllImport("kernel32.dll", CharSet=CharSet.None)] + private static extern int GetFileType(SafeFileHandle hFile); + + private static string GetMessage(int errorCode) + { + StringBuilder stringBuilder = new StringBuilder(512); + if (SerialPortFixer.FormatMessage(12800, new HandleRef(null, IntPtr.Zero), errorCode, 0, stringBuilder, stringBuilder.Capacity, IntPtr.Zero) == 0) + { + return "Unknown Error"; + } + else + { + return stringBuilder.ToString(); + } + } + + private void InitializeDcb() + { + SerialPortFixer.Dcb flags = new SerialPortFixer.Dcb(); + this.GetCommStateNative(ref flags); + flags.Flags = flags.Flags & -16385; + this.SetCommStateNative(ref flags); + } + + private static int MakeHrFromErrorCode(int errorCode) + { + return -2147024896 | errorCode; + } + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + private static extern bool SetCommState(SafeFileHandle hFile, ref SerialPortFixer.Dcb lpDcb); + + private void SetCommStateNative(ref SerialPortFixer.Dcb lpDcb) + { + int num = 0; + SerialPortFixer.Comstat comstat = new SerialPortFixer.Comstat(); + int num1 = 0; + while (num1 < 10) + { + if (!SerialPortFixer.ClearCommError(this.m_Handle, ref num, ref comstat)) + { + SerialPortFixer.WinIoError(); + } + if (!SerialPortFixer.SetCommState(this.m_Handle, ref lpDcb)) + { + if (num1 == 9) + { + SerialPortFixer.WinIoError(); + } + num1++; + } + else + { + return; + } + } + } + + private static void WinIoError() + { + int lastWin32Error = Marshal.GetLastWin32Error(); + throw new IOException(SerialPortFixer.GetMessage(lastWin32Error), SerialPortFixer.MakeHrFromErrorCode(lastWin32Error)); + } + + private struct Comstat + { + public readonly uint Flags; + + public readonly uint cbInQue; + + public readonly uint cbOutQue; + } + + private struct Dcb + { + public readonly uint DCBlength; + + public readonly uint BaudRate; + + public uint Flags; + + public readonly ushort wReserved; + + public readonly ushort XonLim; + + public readonly ushort XoffLim; + + public readonly byte ByteSize; + + public readonly byte Parity; + + public readonly byte StopBits; + + public readonly byte XonChar; + + public readonly byte XoffChar; + + public readonly byte ErrorChar; + + public readonly byte EofChar; + + public readonly byte EvtChar; + + public readonly ushort wReserved1; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/ShortMessage.cs b/GSMCommunication/GsmCommunication/ShortMessage.cs new file mode 100644 index 0000000..bc5f431 --- /dev/null +++ b/GSMCommunication/GsmCommunication/ShortMessage.cs @@ -0,0 +1,90 @@ +using System; +using System.Xml.Serialization; + +/// +/// Represents a short message in undecoded PDU format. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + [XmlInclude(typeof(ShortMessageFromPhone))] + public class ShortMessage : IMessageIndicationObject + { + private string alpha; + + private int length; + + private string data; + + /// + /// The alphabet in which the message is encoded. + /// + [XmlAttribute] + public string Alpha + { + get + { + return this.alpha; + } + set + { + this.alpha = value; + } + } + + /// + /// The actual message. + /// + [XmlElement] + public string Data + { + get + { + return this.data; + } + set + { + this.data = value; + } + } + + /// + /// The length of the message. In PDU format, this is the actual length without the SMSC header. + /// + [XmlAttribute] + public int Length + { + get + { + return this.length; + } + set + { + this.length = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public ShortMessage() + { + this.alpha = string.Empty; + this.length = 0; + this.data = string.Empty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The alphabet in which the message is encoded. + /// The length of the data. + /// The message. + public ShortMessage(string alpha, int length, string data) + { + this.Alpha = alpha; + this.Length = length; + this.Data = data; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/ShortMessageFromPhone.cs b/GSMCommunication/GsmCommunication/ShortMessageFromPhone.cs new file mode 100644 index 0000000..e94bb1d --- /dev/null +++ b/GSMCommunication/GsmCommunication/ShortMessageFromPhone.cs @@ -0,0 +1,73 @@ +using System; +using System.Xml.Serialization; + +/// +/// Represents a short message read from the phone in undecoded PDU format. +/// +namespace GsmComm.GsmCommunication +{ + [Serializable] + public class ShortMessageFromPhone : ShortMessage + { + private int index; + + private int status; + + /// + /// The index of the message. + /// + [XmlAttribute] + public int Index + { + get + { + return this.index; + } + set + { + this.index = value; + } + } + + /// + /// The message status (e.g. read, unread, etc.) + /// + [XmlAttribute] + public int Status + { + get + { + return this.status; + } + set + { + this.status = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public ShortMessageFromPhone() + { + this.index = 0; + this.status = 0; + } + + /// + /// Initializes a new instance of the class. + /// + /// The index where the message is saved in the device in the currently active storage. + /// The message status (e.g. read or unread) + /// The alphabet in which the message is encoded. + /// The length of the data. + /// The actual message. + /// The object contains all data returned by the phone. + /// + public ShortMessageFromPhone(int index, int status, string alpha, int length, string data) : base(alpha, length, data) + { + this.Index = index; + this.Status = status; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/SignalQualityInfo.cs b/GSMCommunication/GsmCommunication/SignalQualityInfo.cs new file mode 100644 index 0000000..612e8e5 --- /dev/null +++ b/GSMCommunication/GsmCommunication/SignalQualityInfo.cs @@ -0,0 +1,51 @@ +using System; + +/// +/// Contains the signal strength as calculcated by the ME. +/// +namespace GsmComm.GsmCommunication +{ + public class SignalQualityInfo + { + private int signalStrength; + + private int bitErrorRate; + + /// + /// Gets the bit error rate. + /// + /// Usually 99 is used if the bit error rate is not known. + public int BitErrorRate + { + get + { + return this.bitErrorRate; + } + } + + /// + /// Gets the signal strength. + /// + /// Usual value is an RSSI value in the range of 0 (no signal) to 31 (best signal), + /// 99 if not known. + public int SignalStrength + { + get + { + return this.signalStrength; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The signal strength, usual as an RSSI value in the range of 0 (no signal) + /// to 31 (best signal), 99 if not known. + /// The bit error rate, 99 if not known. + public SignalQualityInfo(int signalStrength, int bitErrorRate) + { + this.signalStrength = signalStrength; + this.bitErrorRate = bitErrorRate; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/SmsDeliverIndicationStyle.cs b/GSMCommunication/GsmCommunication/SmsDeliverIndicationStyle.cs new file mode 100644 index 0000000..034bf21 --- /dev/null +++ b/GSMCommunication/GsmCommunication/SmsDeliverIndicationStyle.cs @@ -0,0 +1,30 @@ +/// +/// Specifies the possible indication styles for new SMS-DELIVER messages. +/// +namespace GsmComm.GsmCommunication +{ + public enum SmsDeliverIndicationStyle + { + /// + /// No SMS-DELIVER indications are routed to the TE. + /// + Disabled, + /// + /// If SMS-DELIVER is stored into ME/TA, indication of the memory location is routed to the TE. + /// + RouteMemoryLocation, + /// + /// SMS-DELIVERs (except class 2 messages and messages in the message waiting indication + /// group (store message)) are routed directly to the TE. Depending on the currently selected message + /// format, this is done in either PDU or text mode. + /// Class 2 messages and messages in the message waiting indication group (store message) result in + /// the same indication as with . + /// + RouteMessage, + /// + /// Class 3 SMS-DELIVERs are routed directly to TE with the same format as with . + /// Messages of other data coding schemes result in indication as with . + /// + RouteSpecial + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/SmsStatusReportIndicationStyle.cs b/GSMCommunication/GsmCommunication/SmsStatusReportIndicationStyle.cs new file mode 100644 index 0000000..66a01e1 --- /dev/null +++ b/GSMCommunication/GsmCommunication/SmsStatusReportIndicationStyle.cs @@ -0,0 +1,22 @@ +/// +/// Specifies the possible indication settings for new status report messages. +/// +namespace GsmComm.GsmCommunication +{ + public enum SmsStatusReportIndicationStyle + { + /// + /// No SMS-STATUS-REPORTs are routed to the TE. + /// + Disabled, + /// + /// SMS-STATUS-REPORTs are routed to the TE using unsolicited result code. Depending on the currently + /// selected message format, this is done in either PDU or text mode. + /// + RouteMessage, + /// + /// If SMS-STATUS-REPORT is stored into ME/TA, indication of the memory location is routed to the TE. + /// + RouteMemoryLocation + } +} \ No newline at end of file diff --git a/GSMCommunication/GsmCommunication/SubscriberInfo.cs b/GSMCommunication/GsmCommunication/SubscriberInfo.cs new file mode 100644 index 0000000..6db8b07 --- /dev/null +++ b/GSMCommunication/GsmCommunication/SubscriberInfo.cs @@ -0,0 +1,133 @@ +using System; + +/// +/// Contains network subscriber info retrieved from the phone. +/// +namespace GsmComm.GsmCommunication +{ + public class SubscriberInfo + { + private string alpha; + + private string number; + + private int type; + + private int speed; + + private int service; + + private int itc; + + /// + /// Gets an optional alphanumeric string associated with ; + /// used character set is the one selected with . + /// + /// If the string is not defined, it is empty. + public string Alpha + { + get + { + return this.alpha; + } + } + + /// + /// Gets a value for the information transfer capability. + /// + /// Valid values are zero or greater, -1 means this info is not available. + public int Itc + { + get + { + return this.itc; + } + } + + /// + /// Gets the phone number of format specified by . + /// + public string Number + { + get + { + return this.number; + } + } + + /// + /// Gets the service related to the phone number. + /// + /// Valid values are zero or greater, -1 means this info is not available. + /// Some defined values can be found in the enumeration. + /// + public int Service + { + get + { + return this.service; + } + } + + /// + /// Gets a value for the speed for data calls. + /// + /// Valid values are zero or greater, -1 means this info is not available. + public int Speed + { + get + { + return this.speed; + } + } + + /// + /// Gets the type of Address in integer format. + /// + public int Type + { + get + { + return this.type; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// Phone number of format specified by type. + /// Type of address in integer format. + public SubscriberInfo(string number, int type) + { + this.alpha = string.Empty; + this.number = number; + this.type = type; + this.speed = -1; + this.service = -1; + this.itc = -1; + } + + /// + /// Initializes a new instance of the class. + /// + /// An optional alphanumeric string associated with number + /// Phone number of format specified by type + /// Type of address in integer format + /// A value for the speed of data calls + /// The service related to the phone number + /// A value for the information transfer capability + /// + /// Valid values for speed, service and itc are zero or greater, + /// set values to -1 where the information is not available. + /// + public SubscriberInfo(string alpha, string number, int type, int speed, int service, int itc) + { + this.alpha = alpha; + this.number = number; + this.type = type; + this.speed = speed; + this.service = service; + this.itc = itc; + } + } +} \ No newline at end of file diff --git a/GSMCommunication/Properties/AssemblyInfo.cs b/GSMCommunication/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1986f90 --- /dev/null +++ b/GSMCommunication/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyCompany("Stefan Mayr")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCopyright("Copyright © 2004-2011 Stefan Mayr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyFileVersion("1.21.0.0")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyTitle("GSM Communication")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyVersion("1.21.0.0")] +[assembly: CompilationRelaxations(8)] +[assembly: ComVisible(false)] +[assembly: Guid("eec0C65f-a333-42d5-82b9-163a57814507")] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows=true)] diff --git a/PDUConverter/GsmComm.PduConverter/AddressType.cs b/PDUConverter/GsmComm.PduConverter/AddressType.cs new file mode 100644 index 0000000..1a8067c --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/AddressType.cs @@ -0,0 +1,155 @@ +using System; + +/// +/// Indicates the format of a phone number. +/// +/// +/// The most common value of this octet is 91 hex (10010001 bin), which indicates international format. +/// A phone number in international format looks like 46708251358 (where the country code is 46). +/// In the national (or unknown) format the same phone number would look like 0708251358. The international +/// format is the most generic, and it has to be accepted also when the message is destined to a recipient +/// in the same country as the MSC or as the SGSN. +/// +namespace GsmComm.PduConverter +{ + public class AddressType + { + /// + /// Unknown type of number and numbering plan. + /// + public const byte Unknown = 0; + + /// + /// Unknown type of number, telephone numbering plan. + /// + public const byte UnknownPhone = 129; + + /// + /// International number, telephone numbering plan. + /// + public const byte InternationalPhone = 145; + + private bool bit7; + + private byte ton; + + private byte npi; + + /// + /// The Numbering Plan Identification. + /// + /// + /// The Numbering-plan-identification applies for Type-of-number = 000, 001 and 010. + /// For Type-of-number = 101 bits 3,2,1,0 are reserved and shall be transmitted as 0000. + /// Note that for addressing any of the entities SC, MSC, SGSN or MS, Numbering-plan-identification = 0001 + /// will always be used. However, for addressing the SME, any specified Numbering-plan-identification + /// value may be used. + /// + public byte Npi + { + get + { + return this.npi; + } + set + { + this.npi = value; + } + } + + /// + /// The Type of number. + /// + public byte Ton + { + get + { + return this.ton; + } + set + { + this.ton = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public AddressType() + { + this.bit7 = true; + this.ton = 0; + this.npi = 0; + } + + /// + /// Initializes a new instance of the class using the given value. + /// + /// The Type-of-Address octet to initialize the object with. + public AddressType(byte toa) + { + this.bit7 = (toa & 128) > 0; + this.ton = (byte)(toa >> 4 & 7); + this.npi = (byte)(toa & 15); + } + + public static implicit operator AddressType(byte toa) + { + return new AddressType(toa); + } + + public static implicit operator Byte(AddressType a) + { + return a.ToByte(); + } + + /// + /// Returns the byte equivalent of this instance. + /// + /// The byte value. + public byte ToByte() + { + int num; + if (this.bit7) + { + num = 128; + } + else + { + num = 0; + } + byte num1 = (byte)(num | this.ton << 4 | this.npi); + return num1; + } + + /// + /// Indicates the Numbering Plan Identification (NPI) of the phone number. + /// + public enum NumberingPlan : byte + { + Unknown, + Telephone, + Data, + Telex, + National, + Private, + Ermes, + Reserved + } + + /// + /// Indicates the type of the phone number (TON). + /// + public enum TypeOfNumber : byte + { + Unknown, + International, + National, + NetworkSpecific, + Subscriber, + Alphanumeric, + Abbreviated, + Reserved + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/BcdWorker.cs b/PDUConverter/GsmComm.PduConverter/BcdWorker.cs new file mode 100644 index 0000000..7c62d56 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/BcdWorker.cs @@ -0,0 +1,151 @@ +using System; + +/// +/// A class for working with BCD encoded data strings. +/// +namespace GsmComm.PduConverter +{ + public class BcdWorker + { + public BcdWorker() + { + } + + /// + /// Counts the number of bytes in a BCD encoded string. + /// + /// The string containing the BCD data. + /// The byte count. + public static int CountBytes(string s) + { + return s.Length / 2; + } + + /// + /// Swaps the semi-octets of a BCD encoded string and checks the length. + /// + /// The string to decode. Must be of even length. + /// The converted value. + /// String length is not even. + /// 21436587 becomes 12345678. + public static string DecodeSemiOctets(string data) + { + if (data.Length % 2 == 0) + { + string empty = string.Empty; + for (int i = 0; i < data.Length; i = i + 2) + { + empty = string.Concat(empty, data.Substring(i + 1, 1), data.Substring(i, 1)); + } + return empty; + } + else + { + throw new ArgumentException("String length must be even."); + } + } + + /// + /// Swaps the semi-octets of a BCD encoded string. + /// + /// The string to convert. + /// + /// If the string is not of even length, it is padded with a + /// hexadecimal "F" before converting. + /// This method does not verify the actual contents of the string. + /// + /// The converted value. + /// + /// A string containing "12345678" will become "21436587". + /// A string containing "1234567" will become "214365F7". + /// + public static string EncodeSemiOctets(string data) + { + if (data.Length % 2 != 0) + { + data = string.Concat(data, "F"); + } + string empty = string.Empty; + for (int i = 0; i < data.Length; i = i + 2) + { + empty = string.Concat(empty, data.Substring(i + 1, 1), data.Substring(i, 1)); + } + return empty; + } + + /// + /// Swaps the semi-octets of a BCD encoded string. + /// + /// The string to convert. + /// The width to pad the string to before converting. + /// Padding character is hexadecimal "F". + /// + /// This method does not verify the actual contents of the string. + /// + /// The converted value. + /// totalWidth is not even. + public static string EncodeSemiOctets(string data, int totalWidth) + { + if (totalWidth % 2 == 0) + { + return BcdWorker.EncodeSemiOctets(data.PadRight(totalWidth, 'F')); + } + else + { + throw new ArgumentException("totalWidth must be even.", "totalWidth"); + } + } + + /// + /// Gets a single byte out of a BCD encoded string. + /// + /// The string containing the BCD data. + /// The position in the string to start. + /// The byte at the specified position. + /// No range checking is performed. + public static byte GetByte(string s, int index) + { + string str = s.Substring(index * 2, 2); + return Calc.HexToInt(str)[0]; + } + + /// + /// Gets multiple bytes out of a BCD encoded string. + /// + /// The string containing the BCD data. + /// The position in the string to start. + /// The number of bytes to read. + /// The bytes within the specified range. + /// No range checking is performed. + public static byte[] GetBytes(string s, int index, int length) + { + string str = s.Substring(index * 2, length * 2); + return Calc.HexToInt(str); + } + + /// + /// Gets multiple bytes as string out of a BCD encoded string. + /// + /// The string containing the BCD data. + /// The position in the string to start. + /// The number of bytes to read. + /// The bytes within the specified range. + /// No range checking is performed. + public static string GetBytesString(string s, int index, int length) + { + return s.Substring(index * 2, length * 2); + } + + /// + /// Gets a single byte as string out of a BCD encoded string. + /// + /// The string containing the BCD data. + /// The byte at the specified position. + /// The byte at the specified position. + /// No range checking is performed. + public static string GetByteString(string s, int index) + { + return s.Substring(index * 2, 2); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/Calc.cs b/PDUConverter/GsmComm.PduConverter/Calc.cs new file mode 100644 index 0000000..53e5994 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/Calc.cs @@ -0,0 +1,173 @@ +using System; + +/// +/// Performs various numerical conversions and calculations. +/// +namespace GsmComm.PduConverter +{ + public class Calc + { + private static char[] hexDigits; + + static Calc() + { + char[] chrArray = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + Calc.hexDigits = chrArray; + } + + public Calc() + { + } + + /// + /// Converts a bit string into a byte. + /// + /// The string to convert. + /// The converted value. + public static byte BinToInt(string s) + { + return Convert.ToByte(s, 2); + } + + /// + /// Converts a BCD encoded string (hexadecimal) into its byte representation. + /// + /// The string to convert. + /// + /// The length of the string should be even. This is not checked + /// here to be able to process truncated strings. + /// + /// The converted value. + /// + /// A string containing "41" will become {0x41}, which equals + /// the character 'A'. + /// A string containing "414242" will become {0x41, 0x42, 0x43} + /// which equals the string "ABC". + /// + public static byte[] HexToInt(string s) + { + byte[] num = new byte[s.Length / 2]; + for (int i = 0; i < s.Length / 2; i++) + { + string str = s.Substring(i * 2, 2); + num[i] = Convert.ToByte(str, 16); + } + return num; + } + + /// + /// Converts a byte into a bit string. + /// + /// The byte to convert. + /// + /// The final length the string should have. If the resulting string is + /// shorter than this value, it is padded with leading zeroes. + /// + /// The converted value. + public static string IntToBin(byte b, byte size) + { + return Convert.ToString(b, 2).PadLeft(size, '0'); + } + + /// + /// Converts a byte array into its hexadecimal representation (BCD encoding). + /// + /// The byte array to convert. + /// The converted value. + public static string IntToHex(byte[] bytes) + { + char[] chrArray = new char[(int)bytes.Length * 2]; + for (int i = 0; i < (int)bytes.Length; i++) + { + int num = bytes[i]; + chrArray[i * 2] = Calc.hexDigits[num >> 4]; + chrArray[i * 2 + 1] = Calc.hexDigits[num & 15]; + } + return new string(chrArray); + } + + /// + /// Converts a byte array into its hexadecimal representation (BCD encoding). + /// + /// The byte array to convert. + /// The starting index of the byte array to convert. + /// The number of bytes to convert. + /// The converted value. + public static string IntToHex(byte[] bytes, int index, int count) + { + char[] chrArray = new char[count * 2]; + for (int i = 0; i < count; i++) + { + int num = bytes[index + i]; + chrArray[i * 2] = Calc.hexDigits[num >> 4]; + chrArray[i * 2 + 1] = Calc.hexDigits[num & 15]; + } + return new string(chrArray); + } + + /// + /// Converts a byte into its BCD (hexadecimal) representation. + /// + /// The byte to convert. + /// The converted value. + public static string IntToHex(byte b) + { + return string.Concat(Calc.hexDigits[b >> 4].ToString(), Calc.hexDigits[b & 15].ToString()); + } + + /// + /// Determines if a string is a hexadecimal character. + /// + /// The character to check. + /// true if the character is a hex char, false otherwise. + public static bool IsHexDigit(char c) + { + char upper = char.ToUpper(c); + char[] chrArray = Calc.hexDigits; + int num = 0; + while (num < (int)chrArray.Length) + { + char chr = chrArray[num]; + if (upper != chr) + { + num++; + } + else + { + bool flag = true; + return flag; + } + } + return false; + } + + /// + /// Determines if a string consists only of hexadecimal characters. + /// + /// The string to check. + /// true if the string is a hex string, false otherwise. + public static bool IsHexString(string s) + { + if (s.Length != 0) + { + int num = 0; + while (num < s.Length) + { + if (Calc.IsHexDigit(s[num])) + { + num++; + } + else + { + return false; + } + } + return true; + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/DataCodingScheme.cs b/PDUConverter/GsmComm.PduConverter/DataCodingScheme.cs new file mode 100644 index 0000000..4ab29a8 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/DataCodingScheme.cs @@ -0,0 +1,268 @@ +using System; + +/// +/// Indicates how the user data is encoded, this class represents the TP-DCS field. +/// +/// +/// The TP-Data-Coding-Scheme field, defined in GSM 03.40, indicates the data coding scheme of the TP-UD field, +/// and may indicate a message class. Any reserved codings shall be assumed to be the GSM default alphabet +/// (the same as codepoint 00000000) by a receiving entity. The octet is used according to a coding group +/// which is indicated in bits 7..4 +/// +namespace GsmComm.PduConverter +{ + public abstract class DataCodingScheme + { + private const byte classValid = 16; + + /// + /// Specifies no message class and 7-bit default alphabet. + /// + public const byte NoClass_7Bit = 0; + + /// + /// Specifies message class 0 (immediate display) and 7-bit default alphabet. + /// + public const byte Class0_7Bit = 16; + + /// + /// Specifies message class 1 (ME specific) and 7-bit default alphabet. + /// + public const byte Class1_7Bit = 17; + + /// + /// Specifies message class 2 (SIM specific) and 7-bit default alphabet. + /// + public const byte Class2_7Bit = 18; + + /// + /// Specifies message class 3 (TE specific) and 7-bit default alphabet. + /// + public const byte Class3_7Bit = 19; + + /// + /// Specifies no message class and 8-bit data. + /// + public const byte NoClass_8Bit = 4; + + /// + /// Specifies message class 0 (immediate display) and 8-bit data. + /// + public const byte Class0_8Bit = 20; + + /// + /// Specifies message class 1 (ME specific) and 8-bit data. + /// + public const byte Class1_8Bit = 21; + + /// + /// Specifies message class 2 (SIM specific) and 8-bit data. + /// + public const byte Class2_8Bit = 22; + + /// + /// Specifies message class 3 (TE specific) and 8-bit data. + /// + public const byte Class3_8Bit = 23; + + /// + /// Specifies no message class and UCS2 (16-bit) alphabet. + /// + public const byte NoClass_16Bit = 8; + + /// + /// Specifies message class 0 (immediate display) and UCS2 (16-bit) alphabet. + /// + public const byte Class0_16Bit = 24; + + /// + /// Specifies message class 1 (ME specific) and UCS2 (16-bit) alphabet. + /// + public const byte Class1_16Bit = 25; + + /// + /// Specifies message class 2 (SIM specific) and UCS2 (16-bit) alphabet. + /// + public const byte Class2_16Bit = 26; + + /// + /// Specifies message class 3 (TE specific) and UCS2 (16-bit) alphabet. + /// + public const byte Class3_16Bit = 27; + + /// Offset for bit 7 value. + protected const byte bit7offset = 128; + + /// Offset for bit 6 value. + protected const byte bit6offset = 64; + + /// Offset for bit 5 value. + protected const byte bit5offset = 32; + + /// Offset for bit 4 value. + protected const byte bit4offset = 16; + + /// Offset for bit 3 value. + protected const byte bit3offset = 8; + + /// Offset for bit 2 value. + protected const byte bit2offset = 4; + + /// Offset for bit 1 value. + protected const byte bit1offset = 2; + + /// Offset for bit 0 value. + protected const byte bit0offset = 1; + + private byte codingGroup; + + /// + /// Gets the alphabet being used. + /// + public abstract byte Alphabet + { + get; + } + + /// + /// Gets the coding group, that tells about the further contents of the data coding scheme. + /// + public byte CodingGroup + { + get + { + return this.codingGroup; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + protected DataCodingScheme(byte dcs) + { + this.codingGroup = (byte)(dcs >> 4 & 15); + } + + /// + /// Decodes the given DCS byte. + /// + /// The DCS octet to decode. + /// An object of type or one of its derived classes. + public static DataCodingScheme Decode(byte dcs) + { + DataCodingScheme messageWaitingDiscard; + byte num = (byte)(dcs >> 4 & 15); + if ((dcs & 64) != 0 || (dcs & 128) != 0) + { + byte num1 = num; + switch (num1) + { + case 12: + { + messageWaitingDiscard = new MessageWaitingDiscard(dcs); + break; + } + case 13: + { + messageWaitingDiscard = new MessageWaitingStore(dcs); + break; + } + case 14: + { + messageWaitingDiscard = new MessageWaitingStoreUcs2(dcs); + break; + } + case 15: + { + messageWaitingDiscard = new MessageCoding(dcs); + break; + } + default: + { + messageWaitingDiscard = new ReservedCodingGroup(dcs); + break; + } + } + } + else + { + messageWaitingDiscard = new GeneralDataCoding(dcs); + } + return messageWaitingDiscard; + } + + /// + /// Lists the available alphabets within a general data coding indication DCS. + /// + public enum Alphabets : byte + { + DefaultAlphabet, + EightBit, + Ucs2, + Reserved + } + + /// + /// Data coding/message class. Members can be combined. + /// + /// At least a "Group" member must be specified. + [Flags] + public enum DataCoding + { + Alpha7BitDefault = 0, + Class0 = 0, + Class1 = 1, + Class2 = 2, + Class3 = 3, + Alpha8Bit = 4, + Group_DataCoding = 240 + } + + /// + /// General data coding indication. Members can be combined. + /// + [Flags] + public enum GeneralCoding : byte + { + Uncompressed, + Alpha7BitDefault, + NoClass, + Alpha8Bit, + Alpha16Bit, + AlphaReserved, + Class0, + Class1, + Class2, + Class3, + Compressed + } + + /// + /// Lists the available message codings within a data coding/message class DCS. + /// + public enum MessageCodings : byte + { + DefaultAlphabet, + EightBit + } + + /// + /// Message waiting indication. Members can be combined. + /// + /// At least a "Group" member must be specified. + [Flags] + public enum MessageWaiting : byte + { + SetIndicationInactive, + VoicemailMsgWaiting, + FaxMsgWaiting, + EMailMsgWaiting, + OtherMsgWaiting, + SetIndicationActive, + Group_Discard_7BitDefault, + Group_Store_7BitDefault, + Group_Store_16Bit + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/GeneralDataCoding.cs b/PDUConverter/GsmComm.PduConverter/GeneralDataCoding.cs new file mode 100644 index 0000000..aff04c9 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/GeneralDataCoding.cs @@ -0,0 +1,75 @@ +using System; + +/// +/// General Data Coding indication +/// +namespace GsmComm.PduConverter +{ + public class GeneralDataCoding : DataCodingScheme + { + private bool compressed; + + private bool classSpecified; + + private byte alphabet; + + private byte messageClass; + + /// + /// Gets the alphabet being used. + /// + public override byte Alphabet + { + get + { + return this.alphabet; + } + } + + /// + /// Determines if the property has a message class meaning. If not, + /// the property contains a reserved value and has no message class meaning. + /// + public bool ClassSpecified + { + get + { + return this.classSpecified; + } + } + + /// + /// Gets whether the text is compressed. + /// + public bool Compressed + { + get + { + return this.compressed; + } + } + + /// + /// Gets the message class. + /// + public byte MessageClass + { + get + { + return this.messageClass; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public GeneralDataCoding(byte dcs) : base(dcs) + { + this.compressed = (dcs & 32) > 0; + this.classSpecified = (dcs & 16) > 0; + this.alphabet = (byte)(dcs >> 2 & 3); + this.messageClass = (byte)(dcs & 3); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/ITimestamp.cs b/PDUConverter/GsmComm.PduConverter/ITimestamp.cs new file mode 100644 index 0000000..480c13c --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/ITimestamp.cs @@ -0,0 +1,15 @@ +/// +/// Represents a common interface for messages to return their relevant +/// timestamp. +/// +namespace GsmComm.PduConverter +{ + public interface ITimestamp + { + /// + /// Returns the relevant timestamp. + /// + /// A structure representing the relevant message timestamp. + SmsTimestamp GetTimestamp(); + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/IncomingMessageFlags.cs b/PDUConverter/GsmComm.PduConverter/IncomingMessageFlags.cs new file mode 100644 index 0000000..f50cf76 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/IncomingMessageFlags.cs @@ -0,0 +1,49 @@ +using System; + +/// +/// The base class for the message flags of incoming messages. +/// +namespace GsmComm.PduConverter +{ + public abstract class IncomingMessageFlags + { + /// + /// Gets the message type. + /// + public abstract IncomingMessageType MessageType + { + get; + } + + protected IncomingMessageFlags() + { + } + + /// + /// In derived classes, converts the specified value into a new instance of the class. + /// + /// A value. + protected abstract void FromByte(byte b); + + public static implicit operator Byte(IncomingMessageFlags flags) + { + return flags.ToByte(); + } + + /// + /// In derived classes, returns the byte equivalent of this instance. + /// + /// The byte value. + public abstract byte ToByte(); + + /// + /// Returns the string equivalent of this instance. + /// + /// The string. + public override string ToString() + { + byte num = this.ToByte(); + return num.ToString(); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/IncomingMessageType.cs b/PDUConverter/GsmComm.PduConverter/IncomingMessageType.cs new file mode 100644 index 0000000..6d18471 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/IncomingMessageType.cs @@ -0,0 +1,15 @@ +/// +/// Specifies the type of the incoming message. +/// +namespace GsmComm.PduConverter +{ + public enum IncomingMessageType + { + /// Specifies that the message is an SMS-DELIVER. + SmsDeliver, + /// Specifies that the message is an SMS-STATUS-REPORT. + SmsStatusReport, + /// Specifies that the message is an SMS-SUBMIT-REPORT. + SmsSubmitReport + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/IncomingSmsPdu.cs b/PDUConverter/GsmComm.PduConverter/IncomingSmsPdu.cs new file mode 100644 index 0000000..c341a8b --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/IncomingSmsPdu.cs @@ -0,0 +1,131 @@ +using System; + +/// +/// Represents an incoming SMS PDU. +/// +namespace GsmComm.PduConverter +{ + public abstract class IncomingSmsPdu : SmsPdu + { + private const byte TP_MTI_SMS_Deliver = 0; + + private const byte TP_MTI_SMS_Submit_Report = 1; + + private const byte TP_MTI_SMS_Status_Report = 2; + + /// + /// The flags for this message. + /// + protected IncomingMessageFlags messageFlags; + + /// + /// Gets the message type. + /// + public IncomingMessageType MessageType + { + get + { + return this.messageFlags.MessageType; + } + } + + protected IncomingSmsPdu() + { + } + + /// + /// Decodes an incoming SMS PDU stream. + /// + /// The PDU string to decode. + /// Specify true if the PDU data contains an SMSC header, otherwise false. + /// The size of the PDU data in bytes, not counting the SMSC header. Set to -1 if unknown. + /// An object representing the decoded message. + public static IncomingSmsPdu Decode(string pdu, bool includesSmscData, int actualLength) + { + if (pdu != string.Empty) + { + int num = 0; + if (includesSmscData) + { + int num1 = num; + num = num1 + 1; + byte num2 = BcdWorker.GetByte(pdu, num1); + if (num2 > 0) + { + num = num + num2; + } + } + int num3 = num; + IncomingMessageType messageType = IncomingSmsPdu.GetMessageType(BcdWorker.GetByte(pdu, num3)); + IncomingMessageType incomingMessageType = messageType; + switch (incomingMessageType) + { + case IncomingMessageType.SmsDeliver: + { + return new SmsDeliverPdu(pdu, includesSmscData, actualLength); + } + case IncomingMessageType.SmsStatusReport: + { + return new SmsStatusReportPdu(pdu, includesSmscData, actualLength); + } + } + throw new NotSupportedException(string.Concat("Message type ", messageType.ToString(), " recognized, but not supported by the SMS decoder.")); + } + else + { + throw new ArgumentException("pdu must not be an empty string."); + } + } + + /// + /// Decodes an incoming SMS PDU stream. + /// + /// The PDU string to decode. + /// Specify true if the PDU data contains an SMSC header, otherwise false. + /// An object representing the decoded message. + /// Use this overload only if you do not know the size of the PDU data. + public static IncomingSmsPdu Decode(string pdu, bool includesSmscData) + { + return IncomingSmsPdu.Decode(pdu, includesSmscData, -1); + } + + private static IncomingMessageType GetMessageType(byte flags) + { + IncomingMessageType incomingMessageType; + int num; + if ((flags & 2) > 0) + { + num = 1; + } + else + { + num = 0; + } + byte num1 = (byte)(num * 2 + (flags & 1)); + byte num2 = num1; + if (num2 == 0) + { + incomingMessageType = IncomingMessageType.SmsDeliver; + } + else if (num2 == 1) + { + incomingMessageType = IncomingMessageType.SmsSubmitReport; + } + else if (num2 == 2) + { + incomingMessageType = IncomingMessageType.SmsStatusReport; + } + else + { + string[] str = new string[5]; + str[0] = "Unknown message type "; + str[1] = num1.ToString(); + str[2] = " (flags="; + str[3] = flags.ToString(); + str[4] = ")"; + throw new ArgumentException(string.Concat(str), "flags"); + } + return incomingMessageType; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/KnownMessageStatus.cs b/PDUConverter/GsmComm.PduConverter/KnownMessageStatus.cs new file mode 100644 index 0000000..23595eb --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/KnownMessageStatus.cs @@ -0,0 +1,63 @@ +using System; + +/// +/// This enumarator represents the known status codes of a TP-ST octet. +/// Reserved and SC specific values are not part of this list. +/// +namespace GsmComm.PduConverter +{ + public enum KnownMessageStatus : byte + { + /// Short message received by the SME. + OK_Received, + /// Short message forwarded by the SC to the SME but the SC is unable to confirm delivery. + OK_NotConfirmed, + /// Short message replaced by the SC. + OK_Replaced, + /// Congestion. + Temp_Congestion, + /// SME busy. + Temp_SmeBusy, + /// No response from SME. + Temp_NoResponseFromSme, + /// Service Rejected. + Temp_ServiceRejected, + /// Quality of service not available. + Temp_QosNotAvailable, + /// Error in SME. + Temp_ErrorInSme, + /// Remote procedure error. + Perm_RemoteProcedureError, + /// Incompatible destination. + Perm_IncompatibleDestination, + /// Connection rejected by SME. + Perm_ConnectionRejectedBySme, + /// Not obtainable. + Perm_NotObtainable, + /// Quality of service not available. + Perm_QosNotAvailable, + /// No interworking available. + Perm_NoInterworkingAvailable, + /// SM Validity Period expired. + Perm_SMValidityPeriodExpired, + /// SM Deleted by originating SME. + Perm_SMDeletedByOriginatingSme, + /// SM Deleted by SC Administration. + Perm_SMDeletedBySCAdministration, + /// SM does not exist (The SM may have previously existed in the SC but the + /// SC no longer has knowledge of it or the SM may never have previously existed in the SC). + Perm_SMDoesNotExist, + /// Congestion. + Ntemp_Congestion, + /// SME busy. + Ntemp_SmeBusy, + /// No response from SME. + Ntemp_NoResponseFromSme, + /// Service rejected. + Ntemp_ServiceRejected, + /// Quality of service not available. + Ntemp_QosNotAvailable, + /// Error in SME. + Ntemp_ErrorInSme + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/MessageCoding.cs b/PDUConverter/GsmComm.PduConverter/MessageCoding.cs new file mode 100644 index 0000000..39a4b7c --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/MessageCoding.cs @@ -0,0 +1,70 @@ +using System; + +/// +/// Data coding/Message class +/// +namespace GsmComm.PduConverter +{ + public class MessageCoding : DataCodingScheme + { + private bool bit3; + + private byte dataCoding; + + private byte messageClass; + + /// + /// Gets the alphabet being used. + /// + public override byte Alphabet + { + get + { + return this.dataCoding; + } + } + + /// + /// Gets the data coding. + /// + public byte DataCoding + { + get + { + return this.dataCoding; + } + } + + /// + /// Gets the message class. + /// + public byte MessageClass + { + get + { + return this.messageClass; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public MessageCoding(byte dcs) : base(dcs) + { + object obj; + this.bit3 = (dcs & 8) > 0; + MessageCoding messageCoding = this; + if ((dcs & 4) > 0) + { + obj = 1; + } + else + { + obj = null; + } + messageCoding.dataCoding = (byte)obj; + this.messageClass = (byte)(dcs & 3); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/MessageStatus.cs b/PDUConverter/GsmComm.PduConverter/MessageStatus.cs new file mode 100644 index 0000000..a83b6a7 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/MessageStatus.cs @@ -0,0 +1,141 @@ +using System; + +/// +/// TP-ST / TP-Status. +/// +namespace GsmComm.PduConverter +{ + public struct MessageStatus + { + private byte status; + + /// + /// Gets the status category. + /// + /// + /// If the valus does not fall into one of the predefined categories, + /// is returned. + /// + public StatusCategory Category + { + get + { + if (this.status < 0 || this.status > 31) + { + if (this.status < 32 || this.status > 47) + { + if (this.status < 64 || this.status > 95) + { + if (this.status < 96 || this.status > 127) + { + return StatusCategory.Reserved; + } + else + { + return StatusCategory.TemporaryErrorNoRetry; + } + } + else + { + return StatusCategory.PermanentError; + } + } + else + { + return StatusCategory.TemporaryErrorWithRetry; + } + } + else + { + return StatusCategory.Success; + } + } + } + + /// + /// Initializes a new instance of the . + /// + /// The status code. + public MessageStatus(byte status) + { + this.status = status; + } + + /// + /// Initializes a new instance of the . + /// + /// One of the values. + public MessageStatus(KnownMessageStatus status) + { + this.status = (byte)status; + } + + /// + /// Retrieves the known status of the current message status. + /// + /// Ae representing the message status. + /// Check first with before calling this method. + /// Message status is not a known message status. + public KnownMessageStatus GetKnownStatus() + { + if (!this.IsKnownStatus()) + { + throw new ArgumentException(string.Concat(this.status.ToString(), " is not a known message status.")); + } + else + { + return (KnownMessageStatus)Enum.Parse(typeof(KnownMessageStatus), this.status.ToString()); + } + } + + /// + /// Checks if the message status exists within the known status list. + /// + /// true if the status is a known status, otherwise false. + public bool IsKnownStatus() + { + return Enum.IsDefined(typeof(KnownMessageStatus), this.status); + } + + public static implicit operator Byte(MessageStatus s) + { + return s.ToByte(); + } + + public static implicit operator MessageStatus(byte b) + { + return new MessageStatus(b); + } + + public static implicit operator MessageStatus(KnownMessageStatus s) + { + return new MessageStatus(s); + } + + /// + /// Returns the byte representation of the status. + /// + /// A value representing the object's value. + public byte ToByte() + { + return this.status; + } + + /// + /// Returns the string representation of the status. + /// + /// The string representation of the known status if it is a known status, + /// the numerical status value otherwise. + public override string ToString() + { + if (!this.IsKnownStatus()) + { + return this.status.ToString(); + } + else + { + return this.GetKnownStatus().ToString(); + } + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/MessageWaitingDiscard.cs b/PDUConverter/GsmComm.PduConverter/MessageWaitingDiscard.cs new file mode 100644 index 0000000..8281c2a --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/MessageWaitingDiscard.cs @@ -0,0 +1,29 @@ +using System; + +/// +/// Message Waiting Indication Group: Discard Message +/// +namespace GsmComm.PduConverter +{ + public class MessageWaitingDiscard : MessageWaitingIndication + { + /// + /// Gets the alphabet being used. + /// + public override byte Alphabet + { + get + { + return 0; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public MessageWaitingDiscard(byte dcs) : base(dcs) + { + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/MessageWaitingIndication.cs b/PDUConverter/GsmComm.PduConverter/MessageWaitingIndication.cs new file mode 100644 index 0000000..30cef91 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/MessageWaitingIndication.cs @@ -0,0 +1,50 @@ +using System; + +/// +/// Message waiting indication. This class is abstract. +/// +namespace GsmComm.PduConverter +{ + public abstract class MessageWaitingIndication : DataCodingScheme + { + private bool indicationActive; + + private bool bit2; + + private byte indicationType; + + /// + /// Gets if the indication should be set active. + /// + /// If true, the indication should be set active, if false, the indication should be set inactive. + public bool IndicationActive + { + get + { + return this.indicationActive; + } + } + + /// + /// Gets the indication type, how the indication should be shown. + /// + public byte IndicationType + { + get + { + return this.indicationType; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public MessageWaitingIndication(byte dcs) : base(dcs) + { + this.indicationType = (byte)(dcs & 3); + this.bit2 = (dcs & 4) > 0; + this.indicationActive = (dcs & 8) > 0; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/MessageWaitingStore.cs b/PDUConverter/GsmComm.PduConverter/MessageWaitingStore.cs new file mode 100644 index 0000000..5d372ec --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/MessageWaitingStore.cs @@ -0,0 +1,29 @@ +using System; + +/// +/// Message Waiting Indication Group: Store Message +/// +namespace GsmComm.PduConverter +{ + public class MessageWaitingStore : MessageWaitingIndication + { + /// + /// Gets the alphabet being used. + /// + public override byte Alphabet + { + get + { + return 0; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public MessageWaitingStore(byte dcs) : base(dcs) + { + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/MessageWaitingStoreUcs2.cs b/PDUConverter/GsmComm.PduConverter/MessageWaitingStoreUcs2.cs new file mode 100644 index 0000000..7494a86 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/MessageWaitingStoreUcs2.cs @@ -0,0 +1,29 @@ +using System; + +/// +/// Message Waiting Indication Group: Store Message (UCS2) +/// +namespace GsmComm.PduConverter +{ + public class MessageWaitingStoreUcs2 : MessageWaitingIndication + { + /// + /// Gets the alphabet being used. + /// + public override byte Alphabet + { + get + { + return 2; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public MessageWaitingStoreUcs2(byte dcs) : base(dcs) + { + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/OutgoingMessageFlags.cs b/PDUConverter/GsmComm.PduConverter/OutgoingMessageFlags.cs new file mode 100644 index 0000000..2017877 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/OutgoingMessageFlags.cs @@ -0,0 +1,49 @@ +using System; + +/// +/// The base class for the message flags of outgoing messages. +/// +namespace GsmComm.PduConverter +{ + public abstract class OutgoingMessageFlags + { + /// + /// Gets the message type. + /// + public abstract OutgoingMessageType MessageType + { + get; + } + + protected OutgoingMessageFlags() + { + } + + /// + /// In derived classes, converts the specified value into a new instance of the class. + /// + /// A value. + protected abstract void FromByte(byte b); + + public static implicit operator Byte(OutgoingMessageFlags flags) + { + return flags.ToByte(); + } + + /// + /// In derived classes, returns the byte equivalent of this instance. + /// + /// The byte value. + public abstract byte ToByte(); + + /// + /// Returns the string equivalent of this instance. + /// + /// The string. + public override string ToString() + { + byte num = this.ToByte(); + return num.ToString(); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/OutgoingMessageType.cs b/PDUConverter/GsmComm.PduConverter/OutgoingMessageType.cs new file mode 100644 index 0000000..7ab3bc0 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/OutgoingMessageType.cs @@ -0,0 +1,15 @@ +/// +/// Specifies the type of the outgoing message. +/// +namespace GsmComm.PduConverter +{ + public enum OutgoingMessageType + { + /// Specifies that the message is an SMS-SUBMIT. + SmsSubmit, + /// Specifies that the message is an SMS-COMMAND. + SmsCommand, + /// Specifies that the message is an SMS-DELIVER-REPORT. + SmsDeliverReport + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/OutgoingSmsPdu.cs b/PDUConverter/GsmComm.PduConverter/OutgoingSmsPdu.cs new file mode 100644 index 0000000..a268141 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/OutgoingSmsPdu.cs @@ -0,0 +1,155 @@ +using System; + +/// +/// Represents an outgoing SMS PDU. +/// +namespace GsmComm.PduConverter +{ + public abstract class OutgoingSmsPdu : SmsPdu + { + private const byte TP_MTI_SMS_Deliver_Report = 0; + + private const byte TP_MTI_SMS_Submit = 1; + + private const byte TP_MTI_SMS_Command = 2; + + /// + /// The flags for this message. + /// + protected OutgoingMessageFlags messageFlags; + + /// + /// The message reference. + /// + protected byte messageReference; + + /// + /// Gets or sets the message reference. + /// + /// Represents the TP-Message-Reference octet of the PDU. + /// Normally there is no need to change this property because + /// the reference is set by the sending device.. + /// + public byte MessageReference + { + get + { + return this.messageReference; + } + set + { + this.messageReference = value; + } + } + + /// + /// Gets the message type. + /// + public OutgoingMessageType MessageType + { + get + { + return this.messageFlags.MessageType; + } + } + + /// + /// Initializes a new instance. + /// + protected OutgoingSmsPdu() + { + this.messageReference = 0; + } + + /// + /// Decodes an outgoing SMS PDU stream. + /// + /// The PDU string to decode + /// Specify true if the PDU data contains an SMSC header, otherwise false. + /// The length of the PDU in bytes, not including the SMSC header. + /// An object representing the decoded message. + public static OutgoingSmsPdu Decode(string pdu, bool includesSmscData, int actualLength) + { + if (pdu != string.Empty) + { + int num = 0; + if (includesSmscData) + { + int num1 = num; + num = num1 + 1; + byte num2 = BcdWorker.GetByte(pdu, num1); + if (num2 > 0) + { + num = num + num2; + } + } + int num3 = num; + OutgoingMessageType messageType = OutgoingSmsPdu.GetMessageType(BcdWorker.GetByte(pdu, num3)); + OutgoingMessageType outgoingMessageType = messageType; + if (outgoingMessageType != OutgoingMessageType.SmsSubmit) + { + throw new NotSupportedException(string.Concat("Message type ", messageType.ToString(), " recognized, but not supported by the SMS decoder.")); + } + else + { + return new SmsSubmitPdu(pdu, includesSmscData, actualLength); + } + } + else + { + throw new ArgumentException("pdu must not be an empty string."); + } + } + + /// + /// Decodes an outgoing SMS PDU stream. + /// + /// The PDU string to decode. + /// Specify true if the PDU data contains an SMSC header, otherwise false. + /// An object representing the decoded message. + /// Use this method when the actual length of the message is not known. + public static OutgoingSmsPdu Decode(string pdu, bool includesSmscData) + { + return OutgoingSmsPdu.Decode(pdu, includesSmscData, -1); + } + + private static OutgoingMessageType GetMessageType(byte flags) + { + OutgoingMessageType outgoingMessageType; + int num; + if ((flags & 2) > 0) + { + num = 1; + } + else + { + num = 0; + } + byte num1 = (byte)(num * 2 + (flags & 1)); + byte num2 = num1; + if (num2 == 0) + { + outgoingMessageType = OutgoingMessageType.SmsDeliverReport; + } + else if (num2 == 1) + { + outgoingMessageType = OutgoingMessageType.SmsSubmit; + } + else if (num2 == 2) + { + outgoingMessageType = OutgoingMessageType.SmsCommand; + } + else + { + string[] str = new string[5]; + str[0] = "Unknown message type "; + str[1] = num1.ToString(); + str[2] = " (flags="; + str[3] = flags.ToString(); + str[4] = ")"; + throw new ArgumentException(string.Concat(str), "flags"); + } + return outgoingMessageType; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/ParameterIndicator.cs b/PDUConverter/GsmComm.PduConverter/ParameterIndicator.cs new file mode 100644 index 0000000..e98f069 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/ParameterIndicator.cs @@ -0,0 +1,235 @@ +using System; + +/// +/// TP-PI / TP-Parameter-Indicator. Represents particular optional parameter +/// presence in the fields which follow. +/// +namespace GsmComm.PduConverter +{ + public class ParameterIndicator + { + private const byte bit0 = 1; + + private const byte bit1 = 2; + + private const byte bit2 = 4; + + private const byte bit3 = 8; + + private const byte bit4 = 16; + + private const byte bit5 = 32; + + private const byte bit6 = 64; + + private const byte bit7 = 128; + + private bool tp_pid; + + private bool tp_dcs; + + private bool tp_udl; + + private bool reserved_bit3; + + private bool reserved_bit4; + + private bool reserved_bit5; + + private bool reserved_bit6; + + private bool extension; + + /// + /// When set to true, will indicate that another TP-PI octet follows immediately afterwards. + /// + public bool Extension + { + get + { + return this.extension; + } + set + { + this.extension = value; + } + } + + /// + /// Reserved. If set to true, the receiving entity should ignore + /// this setting. + /// + public bool Reserved_Bit3 + { + get + { + return this.reserved_bit3; + } + set + { + this.reserved_bit3 = value; + } + } + + /// + /// Reserved. If set to true, the receiving entity should ignore + /// this setting. + /// + public bool Reserved_Bit4 + { + get + { + return this.reserved_bit4; + } + set + { + this.reserved_bit4 = value; + } + } + + /// + /// Reserved. If set to true, the receiving entity should ignore + /// this setting. + /// + public bool Reserved_Bit5 + { + get + { + return this.reserved_bit5; + } + set + { + this.reserved_bit5 = value; + } + } + + /// + /// Reserved. If set to true, the receiving entity should ignore + /// this setting. + /// + public bool Reserved_Bit6 + { + get + { + return this.reserved_bit6; + } + set + { + this.reserved_bit6 = value; + } + } + + /// + /// When true, a TP-DCS field is present. + /// + public bool TP_DCS + { + get + { + return this.tp_dcs; + } + set + { + this.tp_dcs = value; + } + } + + /// + /// When true, a TP-PID field is present. + /// + public bool TP_PID + { + get + { + return this.tp_pid; + } + set + { + this.tp_pid = value; + } + } + + /// + /// When false, neither TP-UDL nor TP-UD field can be present. + /// + public bool TP_UDL + { + get + { + return this.tp_udl; + } + set + { + this.tp_udl = value; + } + } + + /// + /// Initializes a new instance of the . + /// + /// The value to initialize the object with. + public ParameterIndicator(byte value) + { + this.tp_pid = (value & 1) > 0; + this.tp_dcs = (value & 2) > 0; + this.tp_udl = (value & 4) > 0; + this.reserved_bit3 = (value & 8) > 0; + this.reserved_bit4 = (value & 16) > 0; + this.reserved_bit5 = (value & 32) > 0; + this.reserved_bit6 = (value & 64) > 0; + this.extension = (value & 128) > 0; + } + + public static implicit operator Byte(ParameterIndicator pi) + { + return pi.ToByte(); + } + + public static implicit operator ParameterIndicator(byte b) + { + return new ParameterIndicator(b); + } + + /// + /// Returns the byte equivalent of this instance. + /// + /// The byte value. + public byte ToByte() + { + byte num = 0; + if (this.tp_pid) + { + num = (byte)(num | 1); + } + if (this.tp_dcs) + { + num = (byte)(num | 2); + } + if (this.tp_udl) + { + num = (byte)(num | 4); + } + if (this.reserved_bit3) + { + num = (byte)(num | 8); + } + if (this.reserved_bit4) + { + num = (byte)(num | 16); + } + if (this.reserved_bit5) + { + num = (byte)(num | 32); + } + if (this.reserved_bit6) + { + num = (byte)(num | 64); + } + if (this.extension) + { + num = (byte)(num | 128); + } + return num; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/PduParts.cs b/PDUConverter/GsmComm.PduConverter/PduParts.cs new file mode 100644 index 0000000..fdea4d0 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/PduParts.cs @@ -0,0 +1,264 @@ +using System; +using System.Text; + +/// +/// Implements decoding routines for SMS PDU parts that are not implemented as separate objects. +/// +namespace GsmComm.PduConverter +{ + public static class PduParts + { + /// + /// Decodes the text from 7-Bit user data. + /// + /// The user data to decode. Must contain an encoded GSM 7-Bit default text packed into octets. + /// The decoded user data. + public static string Decode7BitText(byte[] userData) + { + string septetsStr = TextDataConverter.OctetsToSeptetsStr(userData); + return TextDataConverter.SevenBitToString(septetsStr, true); + } + + /// + /// Decodes an address out of a PDU string. + /// + /// The PDU string to use. + /// The index where to start in the string. + /// The address (phone number) read. + /// The address type of the read address. + public static void DecodeGeneralAddress(string pdu, ref int index, out string address, out byte addressType) + { + int num; + int num1 = index; + int num2 = num1; + index = num1 + 1; + byte num3 = BcdWorker.GetByte(pdu, num2); + int num4 = index; + int num5 = num4; + index = num4 + 1; + addressType = BcdWorker.GetByte(pdu, num5); + if (num3 <= 0) + { + address = string.Empty; + return; + } + else + { + bool flag = false; + if (num3 % 2 != 0) + { + num = num3 + 1; + } + else + { + num = (int)num3; + } + int length = num / 2; + if (index * 2 + length * 2 > pdu.Length - length * 2) + { + length = (pdu.Length - index * 2) / 2; + flag = true; + } + AddressType addressType1 = new AddressType(addressType); + if (addressType1.Ton != 5) + { + string bytesString = BcdWorker.GetBytesString(pdu, index, length); + index = index + length; + if (flag) + { + address = BcdWorker.DecodeSemiOctets(bytesString).Substring(0, length * 2); + return; + } + else + { + address = BcdWorker.DecodeSemiOctets(bytesString).Substring(0, num3); + return; + } + } + else + { + byte[] bytes = BcdWorker.GetBytes(pdu, index, length); + index = index + length; + address = PduParts.Decode7BitText(bytes); + return; + } + } + } + + /// + /// Decodes an SMSC address out of a PDU string. + /// + /// The PDU string to use. + /// The index where to start in the string. + /// The address (phone number) read. + /// The address type of the read address. + public static void DecodeSmscAddress(string pdu, ref int index, out string address, out byte addressType) + { + int num = index; + int num1 = num; + index = num + 1; + byte num2 = BcdWorker.GetByte(pdu, num1); + if (num2 <= 0) + { + addressType = 0; + address = string.Empty; + return; + } + else + { + int num3 = index; + int num4 = num3; + index = num3 + 1; + byte num5 = BcdWorker.GetByte(pdu, num4); + int num6 = num2 - 1; + string bytesString = BcdWorker.GetBytesString(pdu, index, num6); + index = index + num6; + string str = BcdWorker.DecodeSemiOctets(bytesString); + if (str.EndsWith("F") || str.EndsWith("f")) + { + str = str.Substring(0, str.Length - 1); + } + addressType = num5; + address = str; + return; + } + } + + /// + /// Decodes text from user data in the specified data coding scheme. + /// + /// The user data to decode. Must contain text according to the specified data coding scheme. + /// The data coding scheme specified in the PDU. + /// The decoded user data. + public static string DecodeText(byte[] userData, byte dataCodingScheme) + { + string str; + byte alphabet = DataCodingScheme.Decode(dataCodingScheme).Alphabet; + byte num = alphabet; + switch (num) + { + case 0: + { + str = PduParts.Decode7BitText(userData); + break; + } + case 1: + { + Label0: + str = PduParts.Decode7BitText(userData); + break; + } + case 2: + { + str = PduParts.DecodeUcs2Text(userData); + break; + } + default: + { + goto Label0; + } + } + return str; + } + + /// + /// Decodes the text from UCS2 (16-Bit) user data. + /// + /// The user data to decode. Must contain an encoded UCS2 text. + /// The decoded user data. + public static string DecodeUcs2Text(byte[] userData) + { + Encoding bigEndianUnicode = Encoding.BigEndianUnicode; + return bigEndianUnicode.GetString(userData); + } + + /// + /// Gets the user data out of the string. + /// + /// The PDU string to use. + /// The index where to start in the string. + /// The coding that was used to encode the data. Required to determine the proper data length. + /// Receives the user data length in bytes. + /// Received the user data. + /// + /// If there's no data, userDataLength will be set to 0 and userData to null. + /// The decoded data might require further processing, for example 7-bit data (septets) packed + /// into octets, that must be converted back to septets before the data can be used. + /// Processing will stop at the first character that is not hex encountered or if the + /// string ends too early. It will not change the userDataLength read from the string. + /// + public static void DecodeUserData(string pdu, ref int index, byte dcs, out byte userDataLength, out byte[] userData) + { + int num = index; + int num1 = num; + index = num + 1; + byte num2 = BcdWorker.GetByte(pdu, num1); + if (num2 <= 0) + { + userDataLength = 0; + userData = new byte[0]; + return; + } + else + { + int remainingUserDataBytes = PduParts.GetRemainingUserDataBytes(num2, dcs); + int num3 = BcdWorker.CountBytes(pdu) - index; + if (num3 < remainingUserDataBytes) + { + remainingUserDataBytes = num3; + } + string bytesString = BcdWorker.GetBytesString(pdu, index, remainingUserDataBytes); + index = index + remainingUserDataBytes; + string empty = string.Empty; + for (int i = 0; i < bytesString.Length / 2; i++) + { + string byteString = BcdWorker.GetByteString(bytesString, i); + if (!Calc.IsHexString(byteString)) + { + break; + } + empty = string.Concat(empty, byteString); + } + userDataLength = num2; + userData = Calc.HexToInt(empty); + return; + } + } + + /// + /// Calculates the number of bytes that must be present in the user data portion of the PDU. + /// + /// The user data length specified in the PDU. + /// The data coding scheme specified in the PDU. + /// The number of bytes (octets) that must be present, or, if decoding the user data, the number + /// of remaining bytes that must be read. + /// The dataLength and dataCodingScheme parameters are used to + /// calculate the number of bytes that must be present in the user data. + internal static int GetRemainingUserDataBytes(byte dataLength, byte dataCodingScheme) + { + int num; + DataCodingScheme dataCodingScheme1 = DataCodingScheme.Decode(dataCodingScheme); + byte alphabet = dataCodingScheme1.Alphabet; + switch (alphabet) + { + case 0: + { + num = (int)Math.Ceiling((double)dataLength * 7 / 8); + break; + } + case 1: + case 2: + { + num = dataLength; + break; + } + default: + { + num = (int)Math.Ceiling((double)dataLength * 7 / 8); + break; + } + } + return num; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/ProtocolID.cs b/PDUConverter/GsmComm.PduConverter/ProtocolID.cs new file mode 100644 index 0000000..7301932 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/ProtocolID.cs @@ -0,0 +1,213 @@ +using System; + +/// +/// This class and its contained classes contain all possible values for the +/// Protocol Identifier. +/// +/// The members represent the TP-PID octet in the PDU. +namespace GsmComm.PduConverter +{ + public class ProtocolID + { + private const byte niwOffset = 0; + + private const byte iwOffset = 32; + + private const byte nuOffset = 64; + + private const byte resOffset = 128; + + private const byte scOffset = 192; + + private ProtocolID() + { + } + + /// + /// Extracts the "Reserved" part of the PID. + /// + /// The PID containing the value. + /// The value of the "Reserved" part. + /// Value is not from the "Reserved" range. + public static byte GetReservedValue(byte pid) + { + if (ProtocolID.IsReserved(pid)) + { + return (byte)(pid - 128); + } + else + { + throw new ArgumentException("Value is not from the \"Reserved\" range.", "pid"); + } + } + + /// + /// Gets the "SC Specific Use" part of the ProtocolID. + /// + /// The PID byte to decode. + /// The "SC Specific Use" value. + /// Value in pid is not from the "SC specific" range. + public static byte GetSCSpecificUseValue(byte pid) + { + if (ProtocolID.IsSCSpecificUse(pid)) + { + return (byte)(pid - 192); + } + else + { + throw new ArgumentException("Value is not from the \"SC specific\" range.", "pid"); + } + } + + /// + /// Determines if the specified value is from the "Reserved" part. + /// + /// The value to check. + /// true if the value is from the reserved part, false otherwise. + public static bool IsReserved(byte pid) + { + if (pid < 128) + { + return false; + } + else + { + return pid - 128 <= 63; + } + } + + /// + /// Determines if the specified PID is from the "SC Specific Use" part. + /// + /// The value to check. + /// true if the value is for SC specific use, false otherwise. + public static bool IsSCSpecificUse(byte pid) + { + if (pid < 192) + { + return false; + } + else + { + return pid - 192 <= 63; + } + } + + /// + /// Allows the "Reserved" part of the ProtocolID to be used. + /// + /// The value for this part. + /// Value is greater than 0x3F (63). + /// The encoded protocol ID. + public static byte Reserved(byte value) + { + if (value <= 63) + { + return (byte)(128 + value); + } + else + { + throw new ArgumentException("Value must not be greater than 0x3F (63)."); + } + } + + /// + /// Allows the "SC Specific Use" part of the ProtocolID to be used. + /// + /// The value for this part. + /// Value is greater than 0x3F (63). + /// The encoded Protocol ID. + public static byte SCSpecificUse(byte value) + { + if (value <= 63) + { + return (byte)(192 + value); + } + else + { + throw new ArgumentException("Value must not be greater than 0x3F (63)."); + } + } + + /// + /// Telematic interworking. + /// + /// + /// If an interworking protocol is specified in an SMS-SUBMIT PDU, + /// it indicates that the SME is a telematic device of the specified type, + /// and requests the SC to convert the SM into a form suited for that + /// device type. If the destination network is ISDN, the SC must also + /// select the proper service indicators for connecting to a device of + /// that type. + /// If an interworking protocol is specified in an SMS-DELIVER PDU, + /// it indicates that the SME is a telematic device of the specified type. + /// + /// + public enum Interworking : byte + { + Implicit, + Telex, + Group3Telefax, + Group4Telefax, + VoiceTelephone, + Ermes, + PagingSystem, + VideoTex, + Teletex, + TeletexPSPDN, + TeletexCSPDN, + TeletexPSTN, + TeletexISDN, + Uci, + Reserved0E, + Reserved0F, + MessageHandler, + X400BasedHandler, + InternetEMail, + Reserved13, + Reserved14, + Reserved15, + Reserved16, + Reserved17, + SCSpecific1, + SCSpecific2, + SCSpecific3, + SCSpecific4, + SCSpecific5, + SCSpecific6, + SCSpecific7, + GsmMobileStation + } + + /// + /// For network use + /// + /// + /// Details are written in the remarks section of the types. + /// + public enum NetworkUse : byte + { + ShortMessageType0, + ReplaceShortMessageType1, + ReplaceShortMessageType2, + ReplaceShortMessageType3, + ReplaceShortMessageType4, + ReplaceShortMessageType5, + ReplaceShortMessageType6, + ReplaceShortMessageType7, + ReturnCallMessage, + MEDataDownload, + MEDepersonalization, + SIMDataDownload + } + + /// + /// For the straightforward case of simple MS-to-SC short message + /// transfer. No interworking is performed. + /// + public enum NoInterworking : byte + { + SmeToSmeProtocol + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/RelativeValidityPeriod.cs b/PDUConverter/GsmComm.PduConverter/RelativeValidityPeriod.cs new file mode 100644 index 0000000..8b2a575 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/RelativeValidityPeriod.cs @@ -0,0 +1,174 @@ +using System; +using System.Text; + +/// +/// The relative validity period gives the length of the validity period +/// counted from when the SMS-SUBMIT is received by the SC. +/// +namespace GsmComm.PduConverter +{ + public class RelativeValidityPeriod : ValidityPeriod + { + private byte @value; + + /// + /// Initializes a new instance of the . + /// + /// The byte value of the validity period. Use this + /// if you have already calculated the validity yourself. + public RelativeValidityPeriod(byte value) + { + this.@value = value; + } + + /// + /// Initializes a new instance of the . + /// + /// The validity period. + /// + /// There are some rules to note: + /// + /// + /// The smallest validity period is 5 minutes, 63 weeks the largest. + /// + /// + /// Periods between 5 minutes and 12 hours can be specified in 5 minute steps. + /// + /// + /// Periods between 12h30min and 24 hours can be specified in 30 minute steps. + /// + /// + /// Periods between two days and 30 days can be specified in 1 day steps. + /// + /// + /// Periods between 5 weeks and 63 weeks can be specified in 1 week (=7 days) steps. + /// + /// + /// + /// Validity timespan is invalid. + public RelativeValidityPeriod(TimeSpan period) + { + byte num = 0; + while (num <= 255) + { + TimeSpan timeSpan = RelativeValidityPeriod.ToTimeSpan(num); + if (timeSpan.CompareTo(period) != 0) + { + num = (byte)(num + 1); + } + else + { + this.@value = num; + return; + } + } + throw new ArgumentException("Invalid validity timespan."); + } + + private void AppendIfNonzero(StringBuilder str, int val, string suffix) + { + if (val > 0) + { + if (str.Length != 0) + { + str.Append(" "); + } + str.Append(val.ToString()); + str.Append(suffix); + } + } + + public static explicit operator RelativeValidityPeriod(TimeSpan ts) + { + return new RelativeValidityPeriod(ts); + } + + public static implicit operator TimeSpan(RelativeValidityPeriod v) + { + return v.ToTimeSpan(); + } + + public static implicit operator Byte(RelativeValidityPeriod v) + { + return v.ToByte(); + } + + public static implicit operator RelativeValidityPeriod(byte b) + { + return new RelativeValidityPeriod(b); + } + + /// + /// Returns the byte equivalent of this instance. + /// + /// The byte value. + public byte ToByte() + { + return this.@value; + } + + /// + /// Returns the string equivalent of this instance. + /// + public override string ToString() + { + TimeSpan timeSpan = this.ToTimeSpan(); + if (timeSpan.TotalHours != 24) + { + StringBuilder stringBuilder = new StringBuilder(); + this.AppendIfNonzero(stringBuilder, timeSpan.Days, "d"); + this.AppendIfNonzero(stringBuilder, timeSpan.Hours, "h"); + this.AppendIfNonzero(stringBuilder, timeSpan.Minutes, "m"); + this.AppendIfNonzero(stringBuilder, timeSpan.Seconds, "s"); + this.AppendIfNonzero(stringBuilder, timeSpan.Milliseconds, "ms"); + return stringBuilder.ToString(); + } + else + { + return "24h"; + } + } + + /// + /// Returns the TimeSpan equivalent of this instance. + /// + /// The TimeSpan value. + public TimeSpan ToTimeSpan() + { + return RelativeValidityPeriod.ToTimeSpan(this.@value); + } + + private static TimeSpan ToTimeSpan(byte value) + { + if (value < 0 || value > 143) + { + if (value < 144 || value > 167) + { + if (value < 168 || value > 196) + { + if (value < 197 || value > 255) + { + return TimeSpan.Zero; + } + else + { + return new TimeSpan((value - 192) * 7, 0, 0, 0); + } + } + else + { + return new TimeSpan(value - 166, 0, 0, 0); + } + } + else + { + return new TimeSpan(12, (value - 143) * 30, 0); + } + } + else + { + return new TimeSpan(0, (value + 1) * 5, 0); + } + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/ReservedCodingGroup.cs b/PDUConverter/GsmComm.PduConverter/ReservedCodingGroup.cs new file mode 100644 index 0000000..b47f2f9 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/ReservedCodingGroup.cs @@ -0,0 +1,32 @@ +using System; + +/// +/// Reserved coding +/// +namespace GsmComm.PduConverter +{ + public class ReservedCodingGroup : DataCodingScheme + { + private byte dcs; + + /// + /// Gets the alphabet being used. + /// + public override byte Alphabet + { + get + { + return 3; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The DCS byte to decode. + public ReservedCodingGroup(byte dcs) : base(dcs) + { + this.dcs = dcs; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatInfoComparer.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatInfoComparer.cs new file mode 100644 index 0000000..4b66210 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatInfoComparer.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; + +/// +/// Implements a method to compare objects. +/// +/// This comparer is provided for performing sort order comparisons. It does not perform exact equality comparisons. +namespace GsmComm.PduConverter.SmartMessaging +{ + public class ConcatInfoComparer : IComparer + { + public ConcatInfoComparer() + { + } + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// + /// + /// Value + /// Condition + /// + /// + /// Less than zero + /// x is less than y. + /// + /// + /// Zero + /// x equals y. + /// + /// + /// Greater than zero + /// x is greater than y. + /// + /// + /// + /// + /// This method provides a sort order comparison for type . + /// Comparing null with any reference type is allowed and does not generate an exception. A null reference + /// is considered to be less than any reference that is not null. + /// + public int Compare(IConcatenationInfo x, IConcatenationInfo y) + { + int num; + if (x != null || y != null) + { + if (x != null || y == null) + { + if (x == null || y != null) + { + if (x.ReferenceNumber != y.ReferenceNumber) + { + ushort referenceNumber = x.ReferenceNumber; + num = referenceNumber.CompareTo(y.ReferenceNumber); + } + else + { + byte currentNumber = x.CurrentNumber; + num = currentNumber.CompareTo(y.CurrentNumber); + } + } + else + { + num = 1; + } + } + else + { + num = -1; + } + } + else + { + num = 0; + } + return num; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatMessageElement16.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatMessageElement16.cs new file mode 100644 index 0000000..c9670e1 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatMessageElement16.cs @@ -0,0 +1,157 @@ +using System; + +/// +/// Implements a Concatenated Short Message Information Element (16-bit reference number) +/// +/// This element is used to indiate that a message is split into +/// multiple parts. +namespace GsmComm.PduConverter.SmartMessaging +{ + public class ConcatMessageElement16 : InformationElement, IConcatenationInfo + { + /// + /// The Information Element Identifier (IEI). + /// + public const byte Identifier = 8; + + private ushort referenceNumber; + + private byte totalMessages; + + private byte currentNumber; + + /// + /// Gets the current message number. + /// + public byte CurrentNumber + { + get + { + return this.currentNumber; + } + } + + /// + /// Gets the current message number. + /// + byte GsmComm.PduConverter.SmartMessaging.IConcatenationInfo.CurrentNumber + { + get + { + return this.currentNumber; + } + } + + /// + /// Gets the message reference number. + /// + ushort GsmComm.PduConverter.SmartMessaging.IConcatenationInfo.ReferenceNumber + { + get + { + return this.referenceNumber; + } + } + + /// + /// Gets the total number of parts of the message. + /// + byte GsmComm.PduConverter.SmartMessaging.IConcatenationInfo.TotalMessages + { + get + { + return this.totalMessages; + } + } + + /// + /// Gets the message reference number. + /// + public ushort ReferenceNumber + { + get + { + return this.referenceNumber; + } + } + + /// + /// Gets the total number of parts of the message. + /// + public byte TotalMessages + { + get + { + return this.totalMessages; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The message's reference number, must + /// be the same in all parts of the same message. + /// The total number of parts of the message. + /// The current message number. + public ConcatMessageElement16(ushort referenceNumber, byte totalMessages, byte currentNumber) + { + this.referenceNumber = referenceNumber; + this.totalMessages = totalMessages; + this.currentNumber = currentNumber; + } + + /// + /// Initializes a new instance of the class. + /// + /// The information element as a byte array. + public ConcatMessageElement16(byte[] element) + { + if (element != null) + { + if (element[0] == 8) + { + byte num = element[1]; + if (num >= 4) + { + byte[] numArray = new byte[2]; + numArray[0] = element[3]; + numArray[1] = element[2]; + this.referenceNumber = BitConverter.ToUInt16(numArray, 0); + this.totalMessages = element[4]; + this.currentNumber = element[5]; + return; + } + else + { + throw new FormatException("Information element data must be 4 bytes long."); + } + } + else + { + throw new ArgumentException("Element is not a Concatenated Short Message Information Element (16-bit reference number).", "element"); + } + } + else + { + throw new ArgumentNullException("element"); + } + } + + /// + /// Returns the byte array equivalent of this instance. + /// + /// The byte array. + public override byte[] ToByteArray() + { + byte[] bytes = BitConverter.GetBytes(this.referenceNumber); + byte[] numArray = new byte[6]; + numArray[0] = 8; + numArray[1] = 4; + numArray[2] = bytes[1]; + numArray[3] = bytes[0]; + numArray[4] = this.totalMessages; + numArray[5] = this.currentNumber; + return numArray; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatMessageElement8.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatMessageElement8.cs new file mode 100644 index 0000000..3db0431 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/ConcatMessageElement8.cs @@ -0,0 +1,151 @@ +using System; + +/// +/// Implements a Concatenated Short Message Information Element (8-bit reference number) +/// +/// This element is used to indiate that a message is split into +/// multiple parts. +namespace GsmComm.PduConverter.SmartMessaging +{ + public class ConcatMessageElement8 : InformationElement, IConcatenationInfo + { + /// + /// The Information Element Identifier (IEI). + /// + public const byte Identifier = 0; + + private byte referenceNumber; + + private byte totalMessages; + + private byte currentNumber; + + /// + /// Gets the current message number. + /// + public byte CurrentNumber + { + get + { + return this.currentNumber; + } + } + + /// + /// Gets the current message number. + /// + byte GsmComm.PduConverter.SmartMessaging.IConcatenationInfo.CurrentNumber + { + get + { + return this.currentNumber; + } + } + + /// + /// Gets the message reference number. + /// + ushort GsmComm.PduConverter.SmartMessaging.IConcatenationInfo.ReferenceNumber + { + get + { + return this.referenceNumber; + } + } + + /// + /// Gets the total number of parts of the message. + /// + byte GsmComm.PduConverter.SmartMessaging.IConcatenationInfo.TotalMessages + { + get + { + return this.totalMessages; + } + } + + /// + /// Gets the message reference number. + /// + public byte ReferenceNumber + { + get + { + return this.referenceNumber; + } + } + + /// + /// Gets the total number of parts of the message. + /// + public byte TotalMessages + { + get + { + return this.totalMessages; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The message's reference number, must + /// be the same in all parts of the same message. + /// The total number of parts of the message. + /// The current message number. + public ConcatMessageElement8(byte referenceNumber, byte totalMessages, byte currentNumber) + { + this.referenceNumber = referenceNumber; + this.totalMessages = totalMessages; + this.currentNumber = currentNumber; + } + + /// + /// Initializes a new instance of the class. + /// + /// The information element as a byte array. + public ConcatMessageElement8(byte[] element) + { + if (element != null) + { + if (element[0] == 0) + { + byte num = element[1]; + if (num >= 3) + { + this.referenceNumber = element[2]; + this.totalMessages = element[3]; + this.currentNumber = element[4]; + return; + } + else + { + throw new FormatException("Information element data must be 3 bytes long."); + } + } + else + { + throw new ArgumentException("Element is not a Concatenated Short Message Information Element (8-bit reference number).", "element"); + } + } + else + { + throw new ArgumentNullException("element"); + } + } + + /// + /// Returns the byte array equivalent of this instance. + /// + /// The byte array. + public override byte[] ToByteArray() + { + byte[] numArray = new byte[5]; + numArray[1] = 3; + numArray[2] = this.referenceNumber; + numArray[3] = this.totalMessages; + numArray[4] = this.currentNumber; + return numArray; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/IConcatenationInfo.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/IConcatenationInfo.cs new file mode 100644 index 0000000..fdead30 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/IConcatenationInfo.cs @@ -0,0 +1,35 @@ +using System; + +/// +/// A common interface for all information elements containing concatenation information. +/// +namespace GsmComm.PduConverter.SmartMessaging +{ + public interface IConcatenationInfo + { + /// + /// Gets the current message number. + /// + byte CurrentNumber + { + get; + } + + /// + /// Gets the message reference number. + /// + ushort ReferenceNumber + { + get; + } + + /// + /// Gets the total number of parts of the message. + /// + byte TotalMessages + { + get; + } + + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/InformationElement.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/InformationElement.cs new file mode 100644 index 0000000..10aa5f8 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/InformationElement.cs @@ -0,0 +1,33 @@ +using GsmComm.PduConverter; +using System; + +/// +/// Implements the base for an information element. +/// +namespace GsmComm.PduConverter.SmartMessaging +{ + public abstract class InformationElement + { + /// + /// Initializes a new instance of the class. + /// + protected InformationElement() + { + } + + /// + /// In the derived classes, returns the byte array equivalent of this instance. + /// + /// The byte array. + public abstract byte[] ToByteArray(); + + /// + /// Returns the string equivalent of this instance, which is a hexadecimal representation of the element. + /// + /// The string. + public override string ToString() + { + return Calc.IntToHex(this.ToByteArray()); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/OtaBitmap.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/OtaBitmap.cs new file mode 100644 index 0000000..9178712 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/OtaBitmap.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections; +using System.Drawing; + +/// +/// Represents an OTA (over-the-air) bitmap. +/// +namespace GsmComm.PduConverter.SmartMessaging +{ + public class OtaBitmap + { + private byte[] bitmap; + + private byte infoField; + + private byte width; + + private byte height; + + private byte grayscales; + + private int dataStart; + + private int dataLen; + + /// + /// Gets the actual bitmap data. + /// + public byte[] Data + { + get + { + byte[] numArray = new byte[this.dataLen]; + Array.Copy(this.bitmap, this.dataStart, numArray, 0, this.dataLen); + return numArray; + } + } + + /// + /// Gets the bitmap's height. + /// + public byte Height + { + get + { + return this.height; + } + } + + /// + /// Gets the bitmap's InfoField. + /// + public byte InfoField + { + get + { + return this.infoField; + } + } + + /// + /// Gets the bitmap's number of grayscales. + /// + public byte NumGrayscales + { + get + { + return this.grayscales; + } + } + + /// + /// Gets the bitmap's width. + /// + public byte Width + { + get + { + return this.width; + } + } + + /// + /// Creates a new OTA bitmap from an existing object. + /// + /// The to create the OTA bitmap from. + public OtaBitmap(Bitmap bitmap) : this(OtaBitmap.BitmapToOtaBitmap(bitmap)) + { + } + + /// + /// Creates a new OTA bitmap from an existing byte array. + /// + /// The byte array containing the OTA bitmap. + /// otaBitmap is null. + public OtaBitmap(byte[] otaBitmap) + { + if (otaBitmap != null) + { + int num = 0; + int num1 = num; + num = num1 + 1; + this.infoField = otaBitmap[num1]; + int num2 = num; + num = num2 + 1; + this.width = otaBitmap[num2]; + int num3 = num; + num = num3 + 1; + this.height = otaBitmap[num3]; + int num4 = num; + num = num4 + 1; + this.grayscales = otaBitmap[num4]; + this.dataStart = num; + this.dataLen = (int)otaBitmap.Length - num; + this.bitmap = new byte[(int)otaBitmap.Length]; + otaBitmap.CopyTo(this.bitmap, 0); + return; + } + else + { + throw new ArgumentException("otaBitmap"); + } + } + + /// + /// Converts a into an OTA (over-the-air) bitmap. + /// + /// The to convert. The maximum allowed + /// size is 255x255 pixels, minimum is 1x1. The bitmap can be any + /// pixel format, but only the black pixels are converted. + /// Can be null to get an empty header. + /// The converted image. If bitmap is null, an empty OTA bitmap + /// header and no data is returned. + /// bitmap is greater than 255x255 pixels. + private static byte[] BitmapToOtaBitmap(Bitmap bitmap) + { + byte[] numArray = null; + byte[] numArray1 = null; + if (bitmap == null) + { + numArray1 = new byte[0]; + byte[] numArray2 = new byte[4]; + numArray2[3] = 1; + numArray = numArray2; + } + else + { + if (bitmap.Height < 1 || bitmap.Width < 1 || bitmap.Height > 255 || bitmap.Width > 255) + { + throw new ArgumentException("Invalid bitmap dimensions. Maximum size is 255x255, minimum size is 1x1 pixels."); + } + else + { + int num = 7; + byte num1 = 0; + ArrayList arrayLists = new ArrayList(); + for (int i = 0; i < bitmap.Height; i++) + { + for (int j = 0; j < bitmap.Width; j++) + { + byte num2 = (byte)Math.Pow(2, (double)num); + Color pixel = bitmap.GetPixel(j, i); + Color black = Color.Black; + if (pixel.ToArgb() == black.ToArgb()) + { + num1 = (byte)(num1 | num2); + } + if (num != 0) + { + num--; + } + else + { + arrayLists.Add(num1); + num1 = 0; + num = 7; + } + } + } + if (num < 7) + { + arrayLists.Add(num1); + } + numArray1 = new byte[arrayLists.Count]; + arrayLists.CopyTo(numArray1); + byte[] width = new byte[4]; + width[1] = (byte)bitmap.Width; + width[2] = (byte)bitmap.Height; + width[3] = 1; + numArray = width; + } + } + byte[] numArray3 = new byte[(int)numArray.Length + (int)numArray1.Length]; + numArray.CopyTo(numArray3, 0); + numArray1.CopyTo(numArray3, (int)numArray.Length); + return numArray3; + } + + public static explicit operator Bitmap(OtaBitmap b) + { + return b.ToBitmap(); + } + + public static explicit operator OtaBitmap(Bitmap b) + { + return new OtaBitmap(b); + } + + public static explicit operator OtaBitmap(byte[] b) + { + return new OtaBitmap(b); + } + + public static implicit operator Byte[](OtaBitmap b) + { + return b.ToByteArray(); + } + + /// + /// Converts an OTA bitmap into a . + /// + /// The OTA bitmap to convert. Can be null. + /// The converted image. If otaBitmap is null, null is returned. + /// null is also returned, if the height or width of the OTA bitmap is 0. + /// + /// The grayscales attribute of the bitmap is ignored, always a monochrome bitmap is created. + /// + private static Bitmap OtaBitmapToBitmap(byte[] otaBitmap) + { + Color black; + if (otaBitmap != null) + { + int num = 0; + int num1 = num; + num = num1 + 1; + int num2 = num; + num = num2 + 1; + byte num3 = otaBitmap[num2]; + int num4 = num; + num = num4 + 1; + byte num5 = otaBitmap[num4]; + int num6 = num; + num = num6 + 1; + if (num3 == 0 || num5 == 0) + { + return null; + } + else + { + Bitmap bitmap = new Bitmap(num3, num5); + int num7 = 0; + byte num8 = 0; + for (int i = 0; i < num5; i++) + { + for (int j = 0; j < num3; j++) + { + if (num7 != 0) + { + num7--; + } + else + { + int num9 = num; + num = num9 + 1; + num8 = otaBitmap[num9]; + num7 = 7; + } + byte num10 = (byte)Math.Pow(2, (double)num7); + Bitmap bitmap1 = bitmap; + int num11 = j; + int num12 = i; + if ((num8 & num10) > 0) + { + black = Color.Black; + } + else + { + black = Color.White; + } + bitmap1.SetPixel(num11, num12, black); + } + } + return bitmap; + } + } + else + { + return null; + } + } + + /// + /// Returns the equivalent of this instance. + /// + /// The . + public Bitmap ToBitmap() + { + return OtaBitmap.OtaBitmapToBitmap(this.bitmap); + } + + /// + /// Returns the byte array equivalent of this instance. + /// + /// The byte array. + public byte[] ToByteArray() + { + byte[] numArray = new byte[(int)this.bitmap.Length]; + this.bitmap.CopyTo(numArray, 0); + return numArray; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/PortAddressElement16.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/PortAddressElement16.cs new file mode 100644 index 0000000..85ea0f5 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/PortAddressElement16.cs @@ -0,0 +1,111 @@ +using System; + +/// +/// Implements an Application Port Addressing Information Element (16 bit address). +/// +/// This element is used to indiate from which port a message +/// originated and to which port it should be directed to. +namespace GsmComm.PduConverter.SmartMessaging +{ + public class PortAddressElement16 : InformationElement + { + /// + /// The Information Element Identifier (IEI). + /// + public const byte Identifier = 5; + + private ushort destinationPort; + + private ushort originatorPort; + + /// + /// Gets the destination port. + /// + public ushort DestinationPort + { + get + { + return this.destinationPort; + } + } + + /// + /// Gets the originator port. + /// + public ushort OriginatorPort + { + get + { + return this.originatorPort; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The destination port, e.g. 0x1582. + /// The source port, e.g. 0x00. + public PortAddressElement16(ushort destinationPort, ushort originatorPort) + { + this.destinationPort = destinationPort; + this.originatorPort = originatorPort; + } + + /// + /// Initializes a new instance of the class. + /// + /// The information element as a byte array. + public PortAddressElement16(byte[] element) + { + if (element != null) + { + if (element[0] == 5) + { + byte num = element[1]; + if (num >= 4) + { + byte[] numArray = new byte[2]; + numArray[0] = element[3]; + numArray[1] = element[2]; + this.destinationPort = BitConverter.ToUInt16(numArray, 0); + byte[] numArray1 = new byte[2]; + numArray1[0] = element[5]; + numArray1[1] = element[4]; + this.originatorPort = BitConverter.ToUInt16(numArray1, 0); + return; + } + else + { + throw new FormatException("Information element data must be 4 bytes long."); + } + } + else + { + throw new ArgumentException("Element is not an Application Port Addressing Information Element (16 bit address).", "element"); + } + } + else + { + throw new ArgumentNullException("element"); + } + } + + /// + /// Returns the byte array equivalent of this instance. + /// + /// The byte array. + public override byte[] ToByteArray() + { + byte[] bytes = BitConverter.GetBytes(this.destinationPort); + byte[] numArray = BitConverter.GetBytes(this.originatorPort); + byte[] numArray1 = new byte[6]; + numArray1[0] = 5; + numArray1[1] = 4; + numArray1[2] = bytes[1]; + numArray1[3] = bytes[0]; + numArray1[4] = numArray[1]; + numArray1[5] = numArray[0]; + return numArray1; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/SmartMessageDecoder.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/SmartMessageDecoder.cs new file mode 100644 index 0000000..1d79869 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/SmartMessageDecoder.cs @@ -0,0 +1,470 @@ +using GsmComm.PduConverter; +using System; +using System.Collections.Generic; +using System.Text; + +/// +/// Decodes messages based on Nokia's Smart Messaging specification and related messages. +/// +/// +/// This methods in this class can be used to find and combine concatenated (long, multi-part) SMS messages. +/// To determine, whether a message is part of a concatenated message at all, use +/// When you have identified that a message is a part of a concatenated message, you need to find the other message parts +/// belonging to the same message, can be used for this. +/// +/// After all parts of the concatenated message have been identified, the parts need to be combined. This is done +/// using and . The difference between these two +/// methods is, that the first one returns the combined data in binary format, whereas the latter returns the result +/// as text. +/// To verify that all message parts are present before attempting to combine them, +/// can be used. Calling this method is optional, but prevents exceptions when combining without all parts present. +/// The above methods all accept instances to abstract some of the work that has to be done +/// to find and combine concatenated SMS messages. However, it is also possible to retrieve the underlying data that +/// is used for these operations (and also possible for other operations as well) using the methods +/// and . +/// +namespace GsmComm.PduConverter.SmartMessaging +{ + public class SmartMessageDecoder + { + public SmartMessageDecoder() + { + } + + /// + /// Determines whether all parts of a concatenated message are present. + /// + /// The parts that make up the concatenated message. + /// true if all parts are present, false otherwise. + /// parts is null. + /// + /// The reference numbers differ between the message parts. + /// -or- + /// The number of total messages differs between the message parts. + /// -or- + /// A non-concatenated message part is present at an invalid position. + /// + public static bool AreAllConcatPartsPresent(IList parts) + { + bool flag = false; + SmartMessageDecoder.GetConcatUserData(parts, false, true, true, out flag); + return flag; + } + + /// + /// Determines whether two messages are part of the same concatenated message. + /// + /// The first message to compare. + /// The second message to compare. + /// true if both messages appear to belong to the same concatenated message, false otherwise. + /// + /// This comparison is supported for and objects. + /// For all other objects, this comparison always returns false. + /// For objects, the , + /// and properties are compared. + /// For objects, the , + /// and properties are compared. + /// + /// pdu1 or pdu2 is null. + public static bool ArePartOfSameMessage(SmsPdu pdu1, SmsPdu pdu2) + { + if (pdu1 != null) + { + if (pdu2 != null) + { + bool flag = false; + if (pdu1 as SmsDeliverPdu == null || pdu2 as SmsDeliverPdu == null) + { + if (pdu1 is SmsSubmitPdu && pdu2 is SmsSubmitPdu) + { + SmsSubmitPdu smsSubmitPdu = (SmsSubmitPdu)pdu1; + SmsSubmitPdu smsSubmitPdu1 = (SmsSubmitPdu)pdu2; + if (smsSubmitPdu.DestinationAddress == smsSubmitPdu1.DestinationAddress && smsSubmitPdu.DestinationAddressType == smsSubmitPdu1.DestinationAddressType) + { + flag = SmartMessageDecoder.HaveSameReferenceNumber(pdu1, pdu2); + } + } + } + else + { + SmsDeliverPdu smsDeliverPdu = (SmsDeliverPdu)pdu1; + SmsDeliverPdu smsDeliverPdu1 = (SmsDeliverPdu)pdu2; + if (smsDeliverPdu.OriginatingAddress == smsDeliverPdu1.OriginatingAddress && smsDeliverPdu.OriginatingAddressType == smsDeliverPdu1.OriginatingAddressType) + { + flag = SmartMessageDecoder.HaveSameReferenceNumber(pdu1, pdu2); + } + } + return flag; + } + else + { + throw new ArgumentNullException("pdu2"); + } + } + else + { + throw new ArgumentNullException("pdu1"); + } + } + + /// + /// Combines the parts of a concatenated message into a single message. + /// + /// The parts that make up the concatenated message. + /// A byte array containing the combined user data of all parts without any headers. + /// + /// All parts must be available, but can be in any order. + /// The user data is returned in its binary format. If you want the user data to be + /// returned as text, use instead. + /// If the first part is a non-concatenated message, its user data is returned back, and no more parts are processed + /// afterwards. + /// + /// parts is null. + /// + /// Not all parts of the message are available. + /// -or- + /// The reference numbers differ between the message parts. + /// -or- + /// The number of total messages differs between the message parts. + /// -or- + /// A non-concatenated message part is present at an invalid position. + /// + /// + public static byte[] CombineConcatMessage(IList parts) + { + bool flag = false; + List concatUserData = SmartMessageDecoder.GetConcatUserData(parts, false, false, false, out flag); + List nums = new List(); + foreach (byte[] concatUserDatum in concatUserData) + { + nums.AddRange(concatUserDatum); + } + return nums.ToArray(); + } + + /// + /// Combines the parts of a concatenated message into a single message text. + /// + /// The parts that make up the concatenated message. + /// A string containing the combined message text of all parts. + /// + /// All parts must be available, but can be in any order. + /// The user data is converted into text according to the data coding scheme specified in the message. + /// If you want the user data to be returned in its binary format, use instead. + /// If the first part is a non-concatenated message, its user data is returned back as text, and no more parts are processed + /// afterwards. + /// + /// parts is null. + /// + /// Not all parts of the message are available. + /// -or- + /// The reference numbers differ between the message parts. + /// -or- + /// The number of total messages differs between the message parts. + /// -or- + /// A non-concatenated message part is present at an invalid position. + /// + /// + public static string CombineConcatMessageText(IList parts) + { + bool flag = false; + List concatUserData = SmartMessageDecoder.GetConcatUserData(parts, true, false, false, out flag); + StringBuilder stringBuilder = new StringBuilder(); + foreach (string concatUserDatum in concatUserData) + { + stringBuilder.Append(concatUserDatum); + } + return stringBuilder.ToString(); + } + + /// + /// Decodes a user data header into information elements. + /// + /// The user data header to be decoded. + /// The elements found as an array of objects. + /// + /// Known information elements are decoded into their respective objects, while unknown + /// information elements are stored in generic objects. + /// The list of known information elements consists of elements used within Smart Messaging, + /// and does not aim to be a complete set of all existing elements. + /// The currently recognized elements are: + /// + /// + /// + /// + /// + /// + public static InformationElement[] DecodeUserDataHeader(byte[] userDataHeader) + { + InformationElement unknownInformationElement; + List informationElements = new List(); + byte num = 0; + byte num1 = userDataHeader[0]; + if (num1 > 0) + { + num = (byte)(num + 1); + do + { + byte num2 = num; + num = (byte)(num2 + 1); + byte num3 = userDataHeader[num2]; + byte num4 = num; + num = (byte)(num4 + 1); + byte num5 = userDataHeader[num4]; + byte[] numArray = new byte[num5 + 2]; + Array.Copy(userDataHeader, num - 2, numArray, 0, num5 + 2); + num = (byte)(num + num5); + if (num3 != 0) + { + if (num3 != 8) + { + if (num3 != 5) + { + unknownInformationElement = new UnknownInformationElement(numArray); + } + else + { + unknownInformationElement = new PortAddressElement16(numArray); + } + } + else + { + unknownInformationElement = new ConcatMessageElement16(numArray); + } + } + else + { + unknownInformationElement = new ConcatMessageElement8(numArray); + } + informationElements.Add(unknownInformationElement); + } + while (num < num1); + } + return informationElements.ToArray(); + } + + /// + /// Gets the concatenation information of a message. + /// + /// The message to get the information of. + /// An object implementing , if + /// the message is a part of a concatenated message, null otherwise. + /// + /// The returned information can be used to discover the parts of a concatenated message + /// or recombine the parts back into one message. + /// + public static IConcatenationInfo GetConcatenationInfo(SmsPdu pdu) + { + if (pdu != null) + { + IConcatenationInfo concatenationInfo = null; + if (pdu.UserDataHeaderPresent) + { + byte[] userDataHeader = pdu.GetUserDataHeader(); + InformationElement[] informationElementArray = SmartMessageDecoder.DecodeUserDataHeader(userDataHeader); + InformationElement[] informationElementArray1 = informationElementArray; + int num = 0; + while (num < (int)informationElementArray1.Length) + { + InformationElement informationElement = informationElementArray1[num]; + if (informationElement as IConcatenationInfo == null) + { + num++; + } + else + { + concatenationInfo = (IConcatenationInfo)informationElement; + break; + } + } + } + return concatenationInfo; + } + else + { + throw new ArgumentNullException("pdu"); + } + } + + /// + /// Retrieves the user data of all parts of a concatenated message. + /// + /// The parts that make up the concatenated message. + /// If true, formats the returned user data as text. If false, returns the user data + /// in its binary form. + /// Specifies whether missing parts are allowed. If true, null is returned + /// in the resulting list in place of every missing part. If false, an exception is raised when a part is + /// missing. + /// If set to true, does not fill the returned list with data. If set to false, the data is returned + /// normally. Use this in conjunction with allowMissingParts set to true to verify whether all message parts are present. + /// Is set to true if all message parts are available, false otherwise. Use this in conjunction + /// with allowMissingParts set to true to verify whether all message parts are present. + /// A list of objects containing the user data of every part without any headers. + /// The outputAsText parameter determines the actual data type that is returned. If outputAsText is true, the return type is a + /// list of byte arrays, if false a list of strings is returned. + /// + /// + /// The parts can be in any order. + /// If the first part is a non-concatenated message, its user data is returned back, and no more parts are processed + /// afterwards. + /// + /// parts is null. + /// + /// Not all parts of the message are available and allowMissingParts is false. + /// -or- + /// The reference numbers differ between the message parts. + /// -or- + /// The number of total messages differs between the message parts. + /// -or- + /// A non-concatenated message part is present at an invalid position. + /// + private static List GetConcatUserData(IList parts, bool outputAsText, bool allowMissingParts, bool noOutput, out bool allPartsAvailable) + { + if (parts != null) + { + allPartsAvailable = true; + List objs = new List(); + if (parts.Count > 0) + { + SmsPdu item = parts[0]; + if (!SmartMessageDecoder.IsPartOfConcatMessage(item)) + { + if (!noOutput) + { + if (!outputAsText) + { + objs.Add(item.UserData); + } + else + { + objs.Add(item.UserDataText); + } + } + } + else + { + SortedList concatenationInfos = SmartMessageDecoder.SortConcatMessageParts(parts); + int totalMessages = concatenationInfos.Keys[0].TotalMessages; + int num = 0; + for (int i = 1; i <= totalMessages; i++) + { + bool flag = false; + if (num < concatenationInfos.Count) + { + IConcatenationInfo concatenationInfo = concatenationInfos.Keys[num]; + SmsPdu smsPdu = concatenationInfos.Values[num]; + if (i == concatenationInfo.CurrentNumber) + { + if (!noOutput) + { + if (!outputAsText) + { + objs.Add(smsPdu.GetUserDataWithoutHeader()); + } + else + { + objs.Add(smsPdu.GetUserDataTextWithoutHeader()); + } + } + num++; + } + else + { + flag = true; + } + } + else + { + flag = true; + } + if (flag) + { + allPartsAvailable = false; + if (!allowMissingParts) + { + throw new ArgumentException(string.Concat("Not all parts of the message are available. Part #", i, " is missing."), "parts"); + } + else + { + if (!noOutput) + { + objs.Add(null); + } + } + } + } + } + } + return objs; + } + else + { + throw new ArgumentNullException("parts"); + } + } + + private static bool HaveSameReferenceNumber(SmsPdu pdu1, SmsPdu pdu2) + { + bool flag = false; + if (SmartMessageDecoder.IsPartOfConcatMessage(pdu1) && SmartMessageDecoder.IsPartOfConcatMessage(pdu2)) + { + IConcatenationInfo concatenationInfo = SmartMessageDecoder.GetConcatenationInfo(pdu1); + IConcatenationInfo concatenationInfo1 = SmartMessageDecoder.GetConcatenationInfo(pdu2); + if (concatenationInfo.ReferenceNumber == concatenationInfo1.ReferenceNumber) + { + flag = true; + } + } + return flag; + } + + /// + /// Determines whether a message is part of a concatenated message. + /// + /// The message. + /// true if the message is part of a concatenated message, false otherwise. + public static bool IsPartOfConcatMessage(SmsPdu pdu) + { + return SmartMessageDecoder.GetConcatenationInfo(pdu) != null; + } + + private static SortedList SortConcatMessageParts(IList parts) + { + IConcatenationInfo concatenationInfo; + IConcatenationInfo concatenationInfo1 = null; + SortedList concatenationInfos = new SortedList(new ConcatInfoComparer()); + foreach (SmsPdu part in parts) + { + if (concatenationInfo1 != null) + { + if (!SmartMessageDecoder.IsPartOfConcatMessage(part)) + { + throw new ArgumentException("A non-concatenated message part is present at an invalid position.", "parts"); + } + else + { + concatenationInfo = SmartMessageDecoder.GetConcatenationInfo(part); + if (concatenationInfo1.ReferenceNumber == concatenationInfo.ReferenceNumber) + { + if (concatenationInfo1.TotalMessages != concatenationInfo.TotalMessages) + { + throw new ArgumentException("The number of total messages differs between the message parts.", "parts"); + } + } + else + { + throw new ArgumentException("The reference numbers differ between the message parts.", "parts"); + } + } + } + else + { + concatenationInfo = SmartMessageDecoder.GetConcatenationInfo(part); + } + concatenationInfos.Add(concatenationInfo, part); + concatenationInfo1 = concatenationInfo; + } + return concatenationInfos; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/SmartMessageFactory.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/SmartMessageFactory.cs new file mode 100644 index 0000000..7e97f4b --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/SmartMessageFactory.cs @@ -0,0 +1,455 @@ +using GsmComm.PduConverter; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; + +/// +/// Creates messages based on Nokia's Smart Messaging specification and related messages. +/// +namespace GsmComm.PduConverter.SmartMessaging +{ + public class SmartMessageFactory + { + private static ushort refNumber; + + /// + /// Represents an empty operator logo that can be used to remove an operator logo. + /// It depends on the phone model whether it works. + /// + public readonly static byte[] EmptyOperatorLogo; + + static SmartMessageFactory() + { + SmartMessageFactory.refNumber = 1; + SmartMessageFactory.EmptyOperatorLogo = SmartMessageFactory.CreateOperatorLogo(new OtaBitmap(null), "000", "00"); + } + + public SmartMessageFactory() + { + } + + /// + /// Calculates the next reference number. + /// + /// Returns the current number and then increments it by one. + /// If the new number would exceed 65535, it it reset to 1. + /// The next reference number. + protected static ushort CalcNextRefNumber() + { + ushort num; + lock (typeof(SmartMessageFactory)) + { + num = SmartMessageFactory.refNumber; + if (SmartMessageFactory.refNumber != 65535) + { + SmartMessageFactory.refNumber = (ushort)(SmartMessageFactory.refNumber + 1); + } + else + { + SmartMessageFactory.refNumber = 1; + } + } + return num; + } + + /// + /// Creates a concatenated text message. + /// + /// The message text. + /// The message's destination address. + /// A set of objects that represent the message. + /// + /// A concatenated message makes it possible to exceed the maximum length of a normal message, + /// created by splitting the message data into multiple parts. + /// Concatenated messages are also known as long or multi-part messages. + /// The userDataText is converted to the GSM 7-bit default alphabet automatically. + /// If no concatenation is necessary, a single, non-concatenated object is created. + /// + /// userDataText is so long that it would create more than 255 message parts. + public static SmsSubmitPdu[] CreateConcatTextMessage(string userDataText, string destinationAddress) + { + return SmartMessageFactory.CreateConcatTextMessage(userDataText, false, destinationAddress); + } + + /// + /// Creates a concatenated text message. + /// + /// The message text. + /// Specifies if the userDataText is to be encoded as Unicode. If not, the GSM 7-bit default alphabet is used. + /// The message's destination address. + /// A set of objects that represent the message. + /// + /// A concatenated message makes it possible to exceed the maximum length of a normal message, + /// created by splitting the message data into multiple parts. + /// Concatenated messages are also known as long or multi-part messages. + /// If no concatenation is necessary, a single, non-concatenated object is created. + /// + /// userDataText is so long that it would create more than 255 message parts. + public static SmsSubmitPdu[] CreateConcatTextMessage(string userDataText, bool unicode, string destinationAddress) + { + string str; + int length = 0; + int num; + byte[] bytes; + SmsSubmitPdu smsSubmitPdu; + int num1; + byte num2; + if (unicode) + { + num1 = 70; + } + else + { + num1 = 160; + } + int num3 = num1; + if (unicode) + { + str = userDataText; + } + else + { + str = TextDataConverter.StringTo7Bit(userDataText); + } + if (str.Length <= num3) + { + if (unicode) + { + smsSubmitPdu = new SmsSubmitPdu(userDataText, destinationAddress, 8); + } + else + { + smsSubmitPdu = new SmsSubmitPdu(userDataText, destinationAddress); + } + SmsSubmitPdu[] smsSubmitPduArray = new SmsSubmitPdu[1]; + smsSubmitPduArray[0] = smsSubmitPdu; + return smsSubmitPduArray; + } + else + { + ConcatMessageElement16 concatMessageElement16 = new ConcatMessageElement16(0, 0, 0); + byte length1 = (byte)((int)SmartMessageFactory.CreateUserDataHeader(concatMessageElement16).Length); + byte num4 = (byte)((double)length1 / 7 * 8); + if (unicode) + { + num2 = length1; + } + else + { + num2 = num4; + } + byte num5 = num2; + StringCollection stringCollections = new StringCollection(); + for (int i = 0; i < str.Length; i = i + length) + { + if (!unicode) + { + if (str.Length - i < num3 - num5) + { + length = str.Length - i; + } + else + { + length = num3 - num5; + } + } + else + { + if (str.Length - i < (num3 * 2 - num5) / 2) + { + length = str.Length - i; + } + else + { + length = (num3 * 2 - num5) / 2; + } + } + string str1 = str.Substring(i, length); + stringCollections.Add(str1); + } + if (stringCollections.Count <= 255) + { + SmsSubmitPdu[] smsSubmitPduArray1 = new SmsSubmitPdu[stringCollections.Count]; + ushort num6 = SmartMessageFactory.CalcNextRefNumber(); + byte num7 = 0; + for (int j = 0; j < stringCollections.Count; j++) + { + num7 = (byte)(num7 + 1); + ConcatMessageElement16 concatMessageElement161 = new ConcatMessageElement16(num6, (byte)stringCollections.Count, num7); + byte[] numArray = SmartMessageFactory.CreateUserDataHeader(concatMessageElement161); + if (unicode) + { + Encoding bigEndianUnicode = Encoding.BigEndianUnicode; + bytes = bigEndianUnicode.GetBytes(stringCollections[j]); + num = (int)bytes.Length; + } + else + { + bytes = TextDataConverter.SeptetsToOctetsInt(stringCollections[j]); + num = stringCollections[j].Length; + } + SmsSubmitPdu smsSubmitPdu1 = new SmsSubmitPdu(); + smsSubmitPdu1.DestinationAddress = destinationAddress; + if (unicode) + { + smsSubmitPdu1.DataCodingScheme = 8; + } + smsSubmitPdu1.SetUserData(bytes, (byte)num); + smsSubmitPdu1.AddUserDataHeader(numArray); + smsSubmitPduArray1[j] = smsSubmitPdu1; + } + return smsSubmitPduArray1; + } + else + { + throw new ArgumentException("A concatenated message must not have more than 255 parts.", "userDataText"); + } + } + } + + /// + /// Creates an operator logo. + /// + /// The OTA bitmap to use as the logo. Maximum size is 72x14 pixels. + /// MCC. The operator's country code. Must be 3 digits long. + /// MNC. The operator's network code. Must be 2 digits long. + /// A byte array containing the generated operator logo. + /// otaBitmap is null. + /// mobileCountryCode is not 3 digits long. -or- + /// mobileNetworkCode is not 2 digits long. + /// -or- The bitmap is larger than 72x14 pixels. + /// + public static byte[] CreateOperatorLogo(OtaBitmap otaBitmap, string mobileCountryCode, string mobileNetworkCode) + { + if (otaBitmap != null) + { + if (otaBitmap.Width > 72 || otaBitmap.Height > 14) + { + string[] str = new string[5]; + str[0] = "Bitmaps used as operator logos must not be larger than "; + int num = 72; + str[1] = num.ToString(); + str[2] = "x"; + int num1 = 14; + str[3] = num1.ToString(); + str[4] = " pixels."; + throw new ArgumentException(string.Concat(str)); + } + else + { + if (mobileCountryCode.Length == 3) + { + if (mobileNetworkCode.Length == 2) + { + byte num2 = 48; + byte[] numArray = Calc.HexToInt(BcdWorker.EncodeSemiOctets(mobileCountryCode.PadRight(4, 'F'))); + byte[] numArray1 = Calc.HexToInt(BcdWorker.EncodeSemiOctets(mobileNetworkCode.PadRight(2, 'F'))); + int length = 1 + (int)numArray.Length + (int)numArray1.Length + 1; + int length1 = 0; + byte[] numArray2 = new byte[length]; + int num3 = length1; + length1 = num3 + 1; + numArray2[num3] = num2; + numArray.CopyTo(numArray2, length1); + length1 = length1 + (int)numArray.Length; + numArray1.CopyTo(numArray2, length1); + length1 = length1 + (int)numArray1.Length; + int num4 = length1; + length1 = num4 + 1; + numArray2[num4] = 10; + byte[] byteArray = otaBitmap.ToByteArray(); + byte[] numArray3 = new byte[(int)numArray2.Length + (int)byteArray.Length]; + numArray2.CopyTo(numArray3, 0); + byteArray.CopyTo(numArray3, length1); + return numArray3; + } + else + { + throw new ArgumentException("mobileNetworkCode must be 2 digits long.", "mobileNetworkCode"); + } + } + else + { + throw new ArgumentException("mobileCountryCode must be 3 digits long.", "mobileCountryCode"); + } + } + } + else + { + throw new ArgumentNullException("otaBitmap"); + } + } + + /// + /// Creates an operator logo message. + /// + /// The operator logo. Use to create one. + /// The message's destination address. + /// A set of objects that represent the message. + /// operatorLogo is null. + /// operatorLogo is an empty array. + /// -or- destinationAddress is an empty string. + /// -or- operatorLogo is so big that it would create more than 255 message parts. + public static SmsSubmitPdu[] CreateOperatorLogoMessage(byte[] operatorLogo, string destinationAddress) + { + int length; + if (operatorLogo != null) + { + if ((int)operatorLogo.Length != 0) + { + PortAddressElement16 portAddressElement16 = new PortAddressElement16(5506, 0); + byte num = (byte)((int)portAddressElement16.ToByteArray().Length); + int num1 = num + 1; + bool flag = (int)operatorLogo.Length > 140 - num1; + if (flag) + { + ConcatMessageElement8 concatMessageElement8 = new ConcatMessageElement8(0, 0, 0); + num = (byte)((int)portAddressElement16.ToByteArray().Length + (int)concatMessageElement8.ToByteArray().Length); + num1 = num + 1; + } + ArrayList arrayLists = new ArrayList(); + int num2 = 0; + byte num3 = (byte)Math.Ceiling((double)((int)operatorLogo.Length) / (double)(140 - num1)); + if (arrayLists.Count <= 255) + { + ushort num4 = 1; + if (flag) + { + num4 = SmartMessageFactory.CalcNextRefNumber(); + } + for (byte i = 1; i <= num3; i = (byte)(i + 1)) + { + if ((int)operatorLogo.Length - num2 < 140 - num1) + { + length = (int)operatorLogo.Length - num2; + } + else + { + length = 140 - num1; + } + byte[] numArray = new byte[num1]; + int length1 = 0; + int num5 = length1; + length1 = num5 + 1; + numArray[num5] = num; + portAddressElement16.ToByteArray().CopyTo(numArray, length1); + length1 = length1 + (int)portAddressElement16.ToByteArray().Length; + if (flag) + { + ConcatMessageElement8 concatMessageElement81 = new ConcatMessageElement8((byte)(num4 % 256), num3, i); + concatMessageElement81.ToByteArray().CopyTo(numArray, length1); + length1 = length1 + (int)concatMessageElement81.ToByteArray().Length; + } + byte[] numArray1 = new byte[(int)numArray.Length + length]; + numArray.CopyTo(numArray1, 0); + Array.Copy(operatorLogo, num2, numArray1, length1, length); + SmsSubmitPdu smsSubmitPdu = new SmsSubmitPdu(); + smsSubmitPdu.MessageFlags.UserDataHeaderPresent = true; + smsSubmitPdu.DataCodingScheme = 21; + smsSubmitPdu.DestinationAddress = destinationAddress; + smsSubmitPdu.SetUserData(numArray1, (byte)((int)numArray1.Length)); + arrayLists.Add(smsSubmitPdu); + num2 = num2 + length; + } + SmsSubmitPdu[] smsSubmitPduArray = new SmsSubmitPdu[arrayLists.Count]; + arrayLists.CopyTo(smsSubmitPduArray, 0); + return smsSubmitPduArray; + } + else + { + throw new ArgumentException("A concatenated message must not have more than 255 parts.", "operatorLogo"); + } + } + else + { + throw new ArgumentException("operatorLogo must not be an empty array.", "operatorLogo"); + } + } + else + { + throw new ArgumentNullException("operatorLogo"); + } + } + + /// + /// Creates a user data header with port addressing information. + /// + /// The message's destination port. + /// A byte array containing the user data header. + public static byte[] CreatePortAddressHeader(ushort destinationPort) + { + PortAddressElement16 portAddressElement16 = new PortAddressElement16(destinationPort, 0); + return SmartMessageFactory.CreateUserDataHeader(portAddressElement16); + } + + /// + /// Creates a user data header out of information elements. + /// + /// The instance to be stored in the header. + /// A byte array containing the user data header. + /// element is null. + /// Element is too large, size exceeds 255 bytes. + public static byte[] CreateUserDataHeader(InformationElement element) + { + if (element != null) + { + byte[] byteArray = element.ToByteArray(); + if ((int)byteArray.Length <= 255) + { + byte[] length = new byte[(int)byteArray.Length + 1]; + length[0] = (byte)((int)byteArray.Length); + byteArray.CopyTo(length, 1); + return length; + } + else + { + throw new ArgumentException("Element is to large, size exceeds 255 bytes."); + } + } + else + { + throw new ArgumentNullException("element"); + } + } + + /// + /// Creates a user data header out of information elements. + /// + /// The instances to be stored in the header. + /// A byte array containing the user data header. + /// elements is null. + /// The sum of all elements is too large, size exceeds 255 bytes. + public static byte[] CreateUserDataHeader(InformationElement[] elements) + { + if (elements != null) + { + List nums = new List(); + nums.Add(0); + int length = 0; + InformationElement[] informationElementArray = elements; + for (int i = 0; i < (int)informationElementArray.Length; i++) + { + InformationElement informationElement = informationElementArray[i]; + byte[] byteArray = informationElement.ToByteArray(); + nums.AddRange(byteArray); + length = length + (int)byteArray.Length; + } + if (length <= 255) + { + nums[0] = (byte)length; + return nums.ToArray(); + } + else + { + throw new ArgumentException("The sum of all elements is too large, size exceeds 255 bytes."); + } + } + else + { + throw new ArgumentNullException("elements"); + } + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmartMessaging/UnknownInformationElement.cs b/PDUConverter/GsmComm.PduConverter/SmartMessaging/UnknownInformationElement.cs new file mode 100644 index 0000000..bbba81c --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmartMessaging/UnknownInformationElement.cs @@ -0,0 +1,114 @@ +using System; + +/// +/// Implements an unknown information element. +/// +namespace GsmComm.PduConverter.SmartMessaging +{ + public class UnknownInformationElement : InformationElement + { + private byte identifier; + + private byte[] data; + + /// + /// Gets the information element data. + /// + public byte[] Data + { + get + { + return this.data; + } + } + + /// + /// Gets the information element identifier. + /// + public byte Identifier + { + get + { + return this.identifier; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The information element identifier. + /// The information element data. + /// data is null. + /// data is larger than 255 bytes. + public UnknownInformationElement(byte identifier, byte[] data) + { + if (data != null) + { + if ((int)data.Length <= 255) + { + this.identifier = identifier; + this.data = data; + return; + } + else + { + throw new ArgumentException("Data must be between 0 and 255 bytes long.", "data"); + } + } + else + { + throw new ArgumentNullException("data"); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The information element as a byte array. + /// element is null. + /// Information element is shorter than 2 bytes. + /// Available number of data bytes is less than specified in data length. + public UnknownInformationElement(byte[] element) + { + if (element != null) + { + if ((int)element.Length >= 2) + { + this.identifier = element[0]; + byte num = element[1]; + if ((int)element.Length >= num + 2) + { + this.data = new byte[num]; + Array.Copy(element, 2, this.data, 0, num); + return; + } + else + { + throw new FormatException("Available number of data bytes is less then specified in data length."); + } + } + else + { + throw new ArgumentException("Information element must at least be 2 bytes long.", "element"); + } + } + else + { + throw new ArgumentNullException("element"); + } + } + + /// + /// Returns the byte array equivalent of this instance. + /// + /// The byte array. + public override byte[] ToByteArray() + { + byte[] length = new byte[2 + (int)this.data.Length]; + length[0] = this.identifier; + length[1] = (byte)((int)this.data.Length); + this.data.CopyTo(length, 2); + return length; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsDeliverMessageFlags.cs b/PDUConverter/GsmComm.PduConverter/SmsDeliverMessageFlags.cs new file mode 100644 index 0000000..d5d7f57 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsDeliverMessageFlags.cs @@ -0,0 +1,173 @@ +using System; + +/// +/// Represents the the first octet of an SMS-DELIVER PDU. +/// +namespace GsmComm.PduConverter +{ + public class SmsDeliverMessageFlags : IncomingMessageFlags + { + private const byte TP_MTI_SMS_Deliver = 0; + + private const byte TP_MMS = 4; + + private const byte TP_SRI = 32; + + private const byte TP_UDHI = 64; + + private const byte TP_RP = 128; + + private bool moreMessages; + + private bool statusReportRequested; + + private bool userDataHeaderPresent; + + private bool replyPathExists; + + /// + /// Gets the type of the message. + /// + /// Always returns . + public override IncomingMessageType MessageType + { + get + { + return IncomingMessageType.SmsDeliver; + } + } + + /// + /// Gets or sets if there are more messages to send. + /// + public bool MoreMessages + { + get + { + return this.moreMessages; + } + set + { + this.moreMessages = value; + } + } + + /// + /// Gets or sets if a reply path exists. + /// + public bool ReplyPathExists + { + get + { + return this.replyPathExists; + } + set + { + this.replyPathExists = value; + } + } + + /// + /// Gets or sets if a status report was be requested. + /// + public bool StatusReportRequested + { + get + { + return this.statusReportRequested; + } + set + { + this.statusReportRequested = value; + } + } + + /// + /// Gets or sets if a user data header is present. + /// + public bool UserDataHeaderPresent + { + get + { + return this.userDataHeaderPresent; + } + set + { + this.userDataHeaderPresent = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SmsDeliverMessageFlags() + { + this.moreMessages = false; + this.statusReportRequested = false; + this.userDataHeaderPresent = false; + this.replyPathExists = false; + } + + /// + /// Initializes a new instance of the SmsDeliverMessageFlags class with a + /// predefined data byte. + /// + /// The message flags as a byte value. + public SmsDeliverMessageFlags(byte flags) + { + this.FromByte(flags); + } + + /// + /// Fills the object with values from the data byte. + /// + /// The byte value. + protected override void FromByte(byte b) + { + IncomingMessageType incomingMessageType = IncomingMessageType.SmsDeliver; + if (0 > 0) + { + incomingMessageType = IncomingMessageType.SmsDeliver; + } + if (incomingMessageType == IncomingMessageType.SmsDeliver) + { + this.moreMessages = (b & 4) == 0; + this.StatusReportRequested = (b & 32) > 0; + this.userDataHeaderPresent = (b & 64) > 0; + this.replyPathExists = (b & 128) > 0; + return; + } + else + { + throw new ArgumentException("Not an SMS-DELIVER message."); + } + } + + /// + /// Returns the byte equivalent of this instance. + /// + /// The byte value. + public override byte ToByte() + { + byte num = 0; + num = (byte)num; + if (!this.moreMessages) + { + num = (byte)(num | 4); + } + if (this.StatusReportRequested) + { + num = (byte)(num | 32); + } + if (this.userDataHeaderPresent) + { + num = (byte)(num | 64); + } + if (this.replyPathExists) + { + num = (byte)(num | 128); + } + return num; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsDeliverPdu.cs b/PDUConverter/GsmComm.PduConverter/SmsDeliverPdu.cs new file mode 100644 index 0000000..91e3936 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsDeliverPdu.cs @@ -0,0 +1,253 @@ +using System; + +/// +/// Represents an SMS-DELIVER PDU, a received short message. +/// +namespace GsmComm.PduConverter +{ + public class SmsDeliverPdu : IncomingSmsPdu + { + private byte originatingAddressType; + + private string originatingAddress; + + private SmsTimestamp scTimestamp; + + /// + /// Gets the message flags. + /// + /// The property is being set + /// and the value is null. + public SmsDeliverMessageFlags MessageFlags + { + get + { + return (SmsDeliverMessageFlags)this.messageFlags; + } + } + + /// + /// Gets or sets if there are more messages to send. + /// + public bool MoreMessages + { + get + { + return this.MessageFlags.MoreMessages; + } + set + { + this.MessageFlags.MoreMessages = value; + } + } + + /// + /// Gets or sets the originating address. + /// + /// + /// When setting the property, also the property + /// will be set, attempting to autodetect the address type. + /// When getting the property, the address may be extended with address-type + /// specific prefixes or other chraracters. + /// + public string OriginatingAddress + { + get + { + return base.CreateAddressOfType(this.originatingAddress, this.originatingAddressType); + } + set + { + byte num = 0; + string str = null; + base.FindTypeOfAddress(value, out num, out str); + this.originatingAddress = str; + this.originatingAddressType = num; + } + } + + /// + /// Gets or sets the type of the originating address. + /// + /// + /// Represents the Type-of-Address octets for the originating address of the PDU. + /// + public byte OriginatingAddressType + { + get + { + return this.originatingAddressType; + } + } + + /// + /// Gets or sets if a reply path exists. + /// + public bool ReplyPathExists + { + get + { + return this.MessageFlags.ReplyPathExists; + } + set + { + this.MessageFlags.ReplyPathExists = value; + } + } + + /// + /// Gets or sets the timestamp the message was received by the SC. + /// + public SmsTimestamp SCTimestamp + { + get + { + return this.scTimestamp; + } + set + { + this.scTimestamp = value; + } + } + + /// + /// Gets or sets if a status report was be requested. + /// + public bool StatusReportRequested + { + get + { + return this.MessageFlags.StatusReportRequested; + } + set + { + this.MessageFlags.StatusReportRequested = value; + } + } + + /// + /// Gets or sets if a user data header is present. + /// + public override bool UserDataHeaderPresent + { + get + { + return this.MessageFlags.UserDataHeaderPresent; + } + set + { + this.MessageFlags.UserDataHeaderPresent = value; + } + } + + /// + /// Initializes a new instance using default values. + /// + public SmsDeliverPdu() + { + this.messageFlags = new SmsDeliverMessageFlags(); + this.originatingAddress = string.Empty; + this.originatingAddressType = 0; + this.scTimestamp = SmsTimestamp.None; + } + + /// + /// Initializes a new instance of the class + /// using the specified PDU string. + /// + /// The PDU string to convert. + /// Specifies if the string contains + /// SMSC data octets at the beginning. + /// Specifies the actual PDU length, that is the length in bytes without + /// the SMSC header. Set to -1 if unknown. + /// + /// This constructor assumes that the string contains an SMS-DELIVER + /// PDU data stream as specified by GSM 07.05. + /// + public SmsDeliverPdu(string pdu, bool includesSmscData, int actualLength) + { + string str = null; + byte num = 0; + byte num1 = 0; + byte[] numArray = null; + if (pdu != string.Empty) + { + bool flag = actualLength >= 0; + int num2 = actualLength; + if (!flag || num2 > 0) + { + int num3 = 0; + if (includesSmscData) + { + PduParts.DecodeSmscAddress(pdu, ref num3, out str, out num); + base.SetSmscAddress(str, num); + } + int num4 = num3; + num3 = num4 + 1; + this.messageFlags = new SmsDeliverMessageFlags(BcdWorker.GetByte(pdu, num4)); + if (flag) + { + num2--; + if (num2 <= 0) + { + base.ConstructLength = num3 * 2; + return; + } + } + PduParts.DecodeGeneralAddress(pdu, ref num3, out this.originatingAddress, out this.originatingAddressType); + if (num3 * 2 < pdu.Length) + { + int num5 = num3; + num3 = num5 + 1; + base.ProtocolID = BcdWorker.GetByte(pdu, num5); + int num6 = num3; + num3 = num6 + 1; + base.DataCodingScheme = BcdWorker.GetByte(pdu, num6); + this.scTimestamp = new SmsTimestamp(pdu, ref num3); + PduParts.DecodeUserData(pdu, ref num3, base.DataCodingScheme, out num1, out numArray); + base.SetUserData(numArray, num1); + base.ConstructLength = num3 * 2; + return; + } + else + { + this.scTimestamp = SmsTimestamp.None; + base.ProtocolID = 145; + base.DataCodingScheme = 137; + base.ConstructLength = num3 * 2; + return; + } + } + else + { + return; + } + } + else + { + throw new ArgumentException("pdu must not be an empty string."); + } + } + + /// + /// Returns the relevant timestamp for the message. + /// + /// An containing the SMSC timestamp, + /// the time the message was received by the service center. + public override SmsTimestamp GetTimestamp() + { + return this.scTimestamp; + } + + /// + /// Converts the value of this instance into a string. + /// + /// If true, excludes the SMSC header. + /// The encoded string. + /// Not implemented, always throws an . + public override string ToString(bool excludeSmscData) + { + throw new NotImplementedException("SmsDeliverPdu.ToString() not implemented."); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsPdu.cs b/PDUConverter/GsmComm.PduConverter/SmsPdu.cs new file mode 100644 index 0000000..582bee2 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsPdu.cs @@ -0,0 +1,628 @@ +using System; +using System.Text; + +/// +/// Provides the base for an SMS PDU. +/// +namespace GsmComm.PduConverter +{ + public abstract class SmsPdu : ITimestamp + { + private const int maxSeptets = 160; + + private const byte maxOctets = 140; + + /// + /// Gets the maximum message text length in septets. + /// + public const int MaxTextLength = 160; + + /// + /// Gets the maximum Unicode message text length in characters. + /// + public const int MaxUnicodeTextLength = 70; + + private byte smscTOA; + + private string smscAddress; + + private byte PID; + + private byte DCS; + + private byte userDataLength; + + private byte[] userData; + + private int constructLength; + + /// + /// Gets the length of the actual PDU data part in bytes. That is, + /// without the SMSC header. + /// + public int ActualLength + { + get + { + return this.ToString(true).Length / 2; + } + } + + /// + /// Gets the number of characters that have been actually been used for decoding upon construction. + /// + public int ConstructLength + { + get + { + return this.constructLength; + } + protected set + { + this.constructLength = value; + } + } + + /// + /// Gets or sets the data coding scheme. + /// + /// Represents the TP-DCS octet of the PDU. + /// The data coding scheme specifies how the data is coded + /// and may also specify a message class. + public byte DataCodingScheme + { + get + { + return this.DCS; + } + set + { + this.DCS = value; + } + } + + /// + /// Gets or sets the protocol identifier. + /// + /// Represents the TP-PID octet of the PDU. + public byte ProtocolID + { + get + { + return this.PID; + } + set + { + this.PID = value; + } + } + + /// + /// Gets or sets the SMSC address. + /// + /// + /// When setting the property: Also the property will be set, + /// attempting to autodetect the address type. + /// When getting the property: The address may be extended with address-type + /// specific prefixes or other chraracters. + /// + public string SmscAddress + { + get + { + return this.CreateAddressOfType(this.smscAddress, this.smscTOA); + } + set + { + byte num = 0; + string str = null; + this.FindTypeOfAddress(value, out num, out str); + this.smscAddress = str; + this.smscTOA = num; + } + } + + /// + /// Gets the type of the SMSC address. + /// + /// + /// Represents the Type-of-Address octets for the SMSC address of the PDU. + /// + public byte SmscAddressType + { + get + { + return this.smscTOA; + } + } + + /// + /// Gets the total length of the PDU string in bytes. + /// + public int TotalLength + { + get + { + return this.ToString().Length / 2; + } + } + + /// + /// Gets the user data. + /// + /// + /// Represents the TP-User-Data octet of the PDU. + /// + public byte[] UserData + { + get + { + return this.userData; + } + } + + /// + /// Gets or sets if a user data header is present. + /// + public abstract bool UserDataHeaderPresent + { + get; + set; + } + + /// + /// Gets the user data length. + /// + /// + /// Represents the TP-User-Data-Length octet of the PDU. + /// The does not necessarily match the number + /// of bytes in the because it may be further encoded. + /// + public byte UserDataLength + { + get + { + return this.userDataLength; + } + } + + /// + /// Gets or sets the user data text (i.e. the message text). + /// + /// + /// This property supports automatic encoding and decoding of text from and to the GSM 7-bit default + /// alphabet and the UCS2 charset. For this to work properly, the + /// property must be set correctly before setting the UserDataText. + /// For all other data with other alphabets or special data codings, the property + /// must be used to get or set the data. + /// Setting this property also sets the property accordingly. + /// + public string UserDataText + { + get + { + return PduParts.DecodeText(this.userData, this.DCS); + } + set + { + DataCodingScheme dataCodingScheme = GsmComm.PduConverter.DataCodingScheme.Decode(this.DCS); + if (dataCodingScheme.Alphabet != 2) + { + this.Encode7BitText(value); + return; + } + else + { + this.EncodeUcs2Text(value); + return; + } + } + } + + /// + /// Initializes a new instance using default values. + /// + protected SmsPdu() + { + this.SmscAddress = string.Empty; + this.PID = 0; + this.DCS = 0; + this.userDataLength = 0; + this.userData = null; + } + + /// + /// Adds a user data header to an existing user data. + /// + /// The user data header to add. + /// There is already a user data header present in this message. + /// The resulting total of user data header and existing user data exceeds the allowed + /// maximum data length. + /// + /// The user data must already be set before adding a user data header. + /// Adding a user data header reduces the available space for the remaining user data. If the resulting total of + /// user data header and existing user data exceeds allowed maximum data length, an exception is raised. + /// + public void AddUserDataHeader(byte[] header) + { + int num; + if (this.UserDataHeaderPresent) + { + throw new InvalidOperationException("There is already a user data header present in this message."); + } + else + { + int length = (int)header.Length; + byte num1 = (byte)((double)length * 8 / 7); + DataCodingScheme dataCodingScheme = GsmComm.PduConverter.DataCodingScheme.Decode(this.DCS); + if (dataCodingScheme.Alphabet != 0) + { + if (dataCodingScheme.Alphabet == 1 || dataCodingScheme.Alphabet == 2) + { + num = length + this.userDataLength; + } + else + { + num = num1 + this.userDataLength; + } + } + else + { + num = num1 + this.userDataLength; + } + byte[] numArray = new byte[(int)header.Length + (int)this.userData.Length]; + header.CopyTo(numArray, 0); + this.userData.CopyTo(numArray, (int)header.Length); + this.SetUserData(numArray, (byte)num); + this.UserDataHeaderPresent = true; + return; + } + } + + /// + /// Modifies an address to make it look like the specified address type. + /// + /// The address (phone number). + /// The address type. + /// The modified address. + /// If the address can't be modified, the original string is returned. + protected string CreateAddressOfType(string address, byte type) + { + if (!(address != string.Empty) || type != 145 || address.StartsWith("+")) + { + return address; + } + else + { + return string.Concat("+", address); + } + } + + /// + /// Decodes the text from 7-Bit user data in this instance. + /// + /// The decoded . + /// This method assumes that the property contains an encoded + /// GSM 7-Bit default text packed into octets. If contains something different, + /// the results are not defined. + protected string Decode7BitText() + { + return PduParts.Decode7BitText(this.userData); + } + + /// + /// Decodes the text from UCS2 (16-Bit) user data in this instance. + /// + /// The decoded . + /// This method assumes that the property contains an encoded + /// UCS2 text. If contains something different, the results are not defined. + /// + protected string DecodeUcs2Text() + { + return PduParts.DecodeUcs2Text(this.userData); + } + + /// + /// Encodes the specified text as 7-Bit user data in this instance. + /// + /// The text to encode. + /// The text is converted to the GSM 7-Bit default alphabet first, then it is packed into octets. + /// The final result is saved in the properties and . + /// + /// Text is too long. + protected void Encode7BitText(string text) + { + string str = TextDataConverter.StringTo7Bit(text); + int length = str.Length; + if (length <= 160) + { + this.SetUserData(TextDataConverter.SeptetsToOctetsInt(str), (byte)length); + return; + } + else + { + string[] strArrays = new string[5]; + strArrays[0] = "Text is too long. A maximum of "; + int num = 160; + strArrays[1] = num.ToString(); + strArrays[2] = " resulting septets is allowed. The current input results in "; + strArrays[3] = length.ToString(); + strArrays[4] = " septets."; + throw new ArgumentException(string.Concat(strArrays)); + } + } + + /// + /// Encodes the specified text as UCS2 (16-Bit) user data in this instance. + /// + /// The text to encode. + /// The text is converted to the UCS2 character set. The result is saved in the properties + /// and . + protected void EncodeUcs2Text(string text) + { + Encoding bigEndianUnicode = Encoding.BigEndianUnicode; + byte[] bytes = bigEndianUnicode.GetBytes(text); + int length = (int)bytes.Length; + if (length <= 140) + { + this.SetUserData(bytes, (byte)length); + return; + } + else + { + string[] str = new string[5]; + str[0] = "Text is too long. A maximum of "; + byte num = 140; + str[1] = num.ToString(); + str[2] = " resulting octets is allowed. The current input results in "; + str[3] = length.ToString(); + str[4] = " octets."; + throw new ArgumentException(string.Concat(str)); + } + } + + /// + /// Determines the address type. + /// + /// The address (phone number). + /// The detected address type. + /// The modified address that can be directly used for communication. + /// If you use address "+4812345678" the resulting type will be 0x91 and useThisAddress + /// will be "4812345678". Call to recreate the original address. + protected void FindTypeOfAddress(string address, out byte type, out string useThisAddress) + { + byte num = default(byte); + if (address == string.Empty) + { + useThisAddress = address; + type = 0; + return; + } + else + { + while (address.StartsWith("+")) + { + num = 145; + address = address.Substring(1); + } + useThisAddress = address; + type = num; + return; + } + } + + /// + /// Modifies the message text so that it is safe to be sent via GSM 7-Bit default encoding. + /// + /// The message text. + /// The converted message text. + /// Replaces invalid characters in the text and truncates it to the maximum allowed length. + public static string GetSafeText(string data) + { + bool flag = false; + bool flag1 = false; + return SmsPdu.GetSafeText(data, out flag, out flag1); + } + + /// + /// Modifies the message text so that it is safe to be sent via GSM 7-Bit default encoding. + /// + /// The message text. + /// Will be set to true if the message length was corrected. + /// Will be set to true if one or more characters were replaced. + /// The converted message text. + /// Replaces invalid characters in the text and truncates it to the maximum allowed length. + public static string GetSafeText(string data, out bool lengthCorrected, out bool charsCorrected) + { + string str; + string str1; + bool flag = false; + lengthCorrected = false; + charsCorrected = false; + if (data.Length <= 160) + { + str = data; + } + else + { + str = data.Substring(0, 160); + lengthCorrected = true; + } + do + { + str1 = TextDataConverter.StringTo7Bit(str, false, out flag); + if (flag) + { + charsCorrected = true; + } + if (str1.Length <= 160) + { + continue; + } + str = data.Substring(0, str.Length - 1); + lengthCorrected = true; + } + while (str1.Length > 160); + string str2 = TextDataConverter.SevenBitToString(str1); + return str2; + } + + /// + /// Gets the SMSC address and the type as it is saved internally. + /// + /// The SMSC address. + /// The address type of address. + public void GetSmscAddress(out string address, out byte addressType) + { + address = this.smscAddress; + addressType = this.smscTOA; + } + + /// + /// Gets the length in septets of the specified text. + /// + /// The text the get the length for. + /// The text length. + public static int GetTextLength(string text) + { + return TextDataConverter.StringTo7Bit(text).Length; + } + + /// + /// In derived classes, returns the relevant timestamp for the message. + /// + /// The timestamp. + /// If the message type does not have a relevant timestamp, + /// it returns + public abstract SmsTimestamp GetTimestamp(); + + /// + /// Extracts the user data header out of the user data. + /// + /// A byte array containing the extracted header. + /// There is no user data header is present in this message. + /// Use to determine whether a user data header is present. + public byte[] GetUserDataHeader() + { + if (!this.UserDataHeaderPresent) + { + throw new InvalidOperationException("There is no user data header is present in this message."); + } + else + { + byte num = this.userData[0]; + num = (byte)(num + 1); + byte[] numArray = new byte[num]; + Array.Copy(this.userData, numArray, num); + return numArray; + } + } + + /// + /// Extracts the section of the user data that is not occupied by the user data header + /// and returns it as text. + /// + /// A string containing the extracted text. + /// There is no user data header is present in this message. + /// Use to determine whether a user data header is present. + public string GetUserDataTextWithoutHeader() + { + byte[] userDataWithoutHeader = this.GetUserDataWithoutHeader(); + return PduParts.DecodeText(userDataWithoutHeader, this.DCS); + } + + /// + /// Extracts the section of the user data that is not occupied by the user data header. + /// + /// A byte array containing the extracted data. + /// There is no user data header is present in this message. + /// Use to determine whether a user data header is present. + public byte[] GetUserDataWithoutHeader() + { + if (!this.UserDataHeaderPresent) + { + throw new InvalidOperationException("There is no user data header is present in this message."); + } + else + { + byte num = this.userData[0]; + num = (byte)(num + 1); + int length = (int)this.userData.Length - num; + byte[] numArray = new byte[length]; + Array.Copy(this.userData, num, numArray, 0, length); + return numArray; + } + } + + /// + /// Checks if the user data portion of the PDU is complete. + /// + /// true if all data is available, false otherwise. + /// This method can be used to verify that the user data has not been truncated or otherwise + /// invalidated. + public bool IsUserDataComplete() + { + int remainingUserDataBytes = PduParts.GetRemainingUserDataBytes(this.userDataLength, this.DCS); + return (int)this.userData.Length >= remainingUserDataBytes; + } + + /// + /// Sets the SMSC address and type directly without attempting to + /// autodetect the type. + /// + /// The SMSC address. + /// The address type of address. + public void SetSmscAddress(string address, byte addressType) + { + this.smscAddress = address; + this.smscTOA = addressType; + } + + /// + /// Sets the user data using raw octets. + /// + /// The user data directly as a byte array. + /// The length of the data. Note that this is not necessarily + /// the number of bytes in the array, the length depends on the data coding. + /// UserData is too long, more than 140 octets were passed + /// Assumes that raw octets are passed. Use the property + /// if you want to pass text. + public void SetUserData(byte[] data, byte dataLength) + { + if ((int)data.Length <= 140) + { + this.userData = data; + this.userDataLength = dataLength; + return; + } + else + { + string[] str = new string[5]; + str[0] = "User data is too long. A maximum of "; + byte num = 140; + str[1] = num.ToString(); + str[2] = " octets is allowed. "; + int length = (int)data.Length; + str[3] = length.ToString(); + str[4] = " octets were passed."; + throw new ArgumentException(string.Concat(str)); + } + } + + /// + /// In derived classes, converts the value of this instance into a string. + /// + /// If true, excludes the SMSC header, otherwise it is included. + /// The encoded string. + public abstract string ToString(bool excludeSmscData); + + /// + /// Converts the value of this instance into a string, including SMSC header. + /// + /// The encoded string. + public override string ToString() + { + return this.ToString(false); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsStatusReportMessageFlags.cs b/PDUConverter/GsmComm.PduConverter/SmsStatusReportMessageFlags.cs new file mode 100644 index 0000000..0293a31 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsStatusReportMessageFlags.cs @@ -0,0 +1,151 @@ +using System; + +/// +/// Represents the the first octet of an SMS-STATUS-REPORT PDU. +/// +namespace GsmComm.PduConverter +{ + public class SmsStatusReportMessageFlags : IncomingMessageFlags + { + private const byte TP_MTI_SMS_Status_Report = 2; + + private const byte TP_UDHI = 4; + + private const byte TP_MMS = 8; + + private const byte TP_SRQ = 16; + + private bool userDataHeaderPresent; + + private bool moreMessages; + + private bool qualifier; + + /// + /// Parameter describing the message type. + /// + public override IncomingMessageType MessageType + { + get + { + return IncomingMessageType.SmsStatusReport; + } + } + + /// + /// Parameter indicating whether or not there are more messages to send. + /// + public bool MoreMessages + { + get + { + return this.moreMessages; + } + set + { + this.moreMessages = value; + } + } + + /// + /// Parameter indicating whether the previously submitted TPDU was an + /// SMS-SUBMIT or an SMS-COMMAND. + /// + /// + /// false = SMS-STATUS-REPORT is the result of a SMS-SUBMIT + /// true = SMS-STATUS-REPORT is the result of a SMS-COMMAND + /// + public bool Qualifier + { + get + { + return this.qualifier; + } + set + { + this.qualifier = value; + } + } + + /// + /// Parameter indicating that the TP-UD field contains a Header. + /// + public bool UserDataHeaderPresent + { + get + { + return this.userDataHeaderPresent; + } + set + { + this.userDataHeaderPresent = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SmsStatusReportMessageFlags() + { + this.userDataHeaderPresent = false; + this.moreMessages = false; + this.qualifier = false; + } + + /// + /// Initializes a new instance of the IncomingMessageFlags class with a + /// predefined data byte. + /// + public SmsStatusReportMessageFlags(byte flags) + { + this.FromByte(flags); + } + + /// + /// Fills the object with values from the data byte. + /// + /// The byte value. + protected override void FromByte(byte b) + { + IncomingMessageType incomingMessageType = IncomingMessageType.SmsStatusReport; + if ((b & 2) > 0) + { + incomingMessageType = IncomingMessageType.SmsStatusReport; + } + if (incomingMessageType == IncomingMessageType.SmsStatusReport) + { + this.userDataHeaderPresent = (b & 4) > 0; + this.moreMessages = (b & 8) == 0; + this.qualifier = (b & 16) > 0; + return; + } + else + { + throw new ArgumentException("Not an SMS-STATUS-REPORT message."); + } + } + + /// + /// Returns the byte equivalent of this instance. + /// + /// The byte value. + public override byte ToByte() + { + byte num = 0; + num = (byte)(num | 2); + if (this.userDataHeaderPresent) + { + num = (byte)(num | 4); + } + if (!this.moreMessages) + { + num = (byte)(num | 8); + } + if (this.qualifier) + { + num = (byte)(num | 16); + } + return num; + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsStatusReportPdu.cs b/PDUConverter/GsmComm.PduConverter/SmsStatusReportPdu.cs new file mode 100644 index 0000000..f84f9d8 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsStatusReportPdu.cs @@ -0,0 +1,380 @@ +using System; + +/// +/// Represents an SMS-STATUS-REPORT PDU, a status report message. +/// +namespace GsmComm.PduConverter +{ + public class SmsStatusReportPdu : IncomingSmsPdu + { + private byte messageReference; + + private byte recipientAddressType; + + private string recipientAddress; + + private SmsTimestamp scTimestamp; + + private SmsTimestamp dischargeTime; + + private MessageStatus status; + + private ParameterIndicator parameterIndicator; + + /// + /// Gets or sets the parameter identifying the time associated with a + /// particular TP-ST outcome. + /// + public SmsTimestamp DischargeTime + { + get + { + return this.dischargeTime; + } + set + { + this.dischargeTime = value; + } + } + + /// + /// Gets the message flags. + /// + public SmsStatusReportMessageFlags MessageFlags + { + get + { + return (SmsStatusReportMessageFlags)this.messageFlags; + } + } + + /// + /// Gets or sets the parameter identifying the previously submitted + /// SMS-SUBMIT or an SMS-COMMAND. + /// + public byte MessageReference + { + get + { + return this.messageReference; + } + set + { + this.messageReference = value; + } + } + + /// + /// Parameter indicating whether or not there are more messages to send. + /// + public bool MoreMessages + { + get + { + return this.MessageFlags.MoreMessages; + } + set + { + this.MessageFlags.MoreMessages = value; + } + } + + /// + /// Gets or sets the parameter indicating the presence of any of the + /// optional parameters which follow. + /// + public ParameterIndicator ParameterIndicator + { + get + { + return this.parameterIndicator; + } + set + { + this.parameterIndicator = value; + } + } + + /// + /// Parameter indicating whether the previously submitted TPDU was an + /// SMS-SUBMIT or an SMS-COMMAND. + /// + /// + /// false = SMS-STATUS-REPORT is the result of a SMS-SUBMIT + /// true = SMS-STATUS-REPORT is the result of a SMS-COMMAND + /// + public bool Qualifier + { + get + { + return this.MessageFlags.Qualifier; + } + set + { + this.MessageFlags.Qualifier = value; + } + } + + /// + /// Gets or sets the address of the recipient of the previously + /// submitted mobile originated short message. + /// + /// + /// When setting the property, also the property + /// will be set, attempting to autodetect the address type. + /// When getting the property, the address may be extended with address-type + /// specific prefixes or other chraracters. + /// + public string RecipientAddress + { + get + { + return base.CreateAddressOfType(this.recipientAddress, this.recipientAddressType); + } + set + { + byte num = 0; + string str = null; + base.FindTypeOfAddress(value, out num, out str); + this.recipientAddress = str; + this.recipientAddressType = num; + } + } + + /// + /// Gets the type of the recipient address. + /// + /// + /// Represents the Type-of-Address octets for the recipient address of the PDU. + /// + public byte RecipientAddressType + { + get + { + return this.recipientAddressType; + } + } + + /// + /// Gets or sets the parameter identifying time when the SC received + /// the previously sent SMS-SUBMIT. + /// + public SmsTimestamp SCTimestamp + { + get + { + return this.scTimestamp; + } + set + { + this.scTimestamp = value; + } + } + + /// + /// Gets or sets the parameter identifying the status of the previously + /// sent mobile originated short message. + /// + public MessageStatus Status + { + get + { + return this.status; + } + set + { + this.status = value; + } + } + + /// + /// Parameter indicating that the TP-UD field contains a Header. + /// + public override bool UserDataHeaderPresent + { + get + { + return this.MessageFlags.UserDataHeaderPresent; + } + set + { + this.MessageFlags.UserDataHeaderPresent = value; + } + } + + /// + /// Initializes a new instance using default values. + /// + public SmsStatusReportPdu() + { + this.messageFlags = new SmsStatusReportMessageFlags(); + this.messageReference = 0; + this.RecipientAddress = string.Empty; + this.scTimestamp = SmsTimestamp.None; + this.dischargeTime = SmsTimestamp.None; + this.status = 0; + this.parameterIndicator = null; + } + + /// + /// Initializes a new instance of the class + /// using the specified PDU string. + /// + /// The PDU string to convert. + /// Specifies if the string contains + /// SMSC data octets at the beginning. + /// Specifies the actual PDU length, that is the length in bytes without + /// the SMSC header. Set to -1 if unknown. + /// + /// This constructor assumes that the string contains an SMS-STATUS-REPORT + /// PDU data stream as specified + /// by GSM 07.05. + /// + public SmsStatusReportPdu(string pdu, bool includesSmscData, int actualLength) + { + string str = null; + byte num = 0; + ParameterIndicator parameterIndicator; + byte num1 = 0; + byte[] numArray = null; + if (pdu != string.Empty) + { + bool flag = actualLength >= 0; + int num2 = actualLength; + if (!flag || num2 > 0) + { + int num3 = 0; + if (includesSmscData) + { + PduParts.DecodeSmscAddress(pdu, ref num3, out str, out num); + base.SetSmscAddress(str, num); + } + int num4 = num3; + num3 = num4 + 1; + this.messageFlags = new SmsStatusReportMessageFlags(BcdWorker.GetByte(pdu, num4)); + if (flag) + { + num2--; + if (num2 <= 0) + { + base.ConstructLength = num3 * 2; + return; + } + } + int num5 = num3; + num3 = num5 + 1; + this.messageReference = BcdWorker.GetByte(pdu, num5); + PduParts.DecodeGeneralAddress(pdu, ref num3, out this.recipientAddress, out this.recipientAddressType); + if (num3 * 2 < pdu.Length) + { + this.scTimestamp = new SmsTimestamp(pdu, ref num3); + this.dischargeTime = new SmsTimestamp(pdu, ref num3); + int num6 = num3; + num3 = num6 + 1; + this.status = BcdWorker.GetByte(pdu, num6); + int num7 = BcdWorker.CountBytes(pdu); + if (num3 < num7) + { + int num8 = num3; + num3 = num8 + 1; + this.parameterIndicator = new ParameterIndicator(BcdWorker.GetByte(pdu, num8)); + if (this.parameterIndicator.Extension) + { + do + { + int num9 = BcdWorker.CountBytes(pdu); + if (num3 < num9) + { + int num10 = num3; + num3 = num10 + 1; + parameterIndicator = new ParameterIndicator(BcdWorker.GetByte(pdu, num10)); + } + else + { + base.ConstructLength = num3 * 2; + return; + } + } + while (parameterIndicator.Extension); + } + if (this.parameterIndicator.TP_PID) + { + int num11 = num3; + num3 = num11 + 1; + base.ProtocolID = BcdWorker.GetByte(pdu, num11); + } + if (this.parameterIndicator.TP_DCS) + { + int num12 = num3; + num3 = num12 + 1; + base.DataCodingScheme = BcdWorker.GetByte(pdu, num12); + } + if (this.parameterIndicator.TP_UDL) + { + PduParts.DecodeUserData(pdu, ref num3, base.DataCodingScheme, out num1, out numArray); + base.SetUserData(numArray, num1); + } + base.ConstructLength = num3 * 2; + return; + } + else + { + base.ConstructLength = num3 * 2; + return; + } + } + else + { + base.ProtocolID = 145; + base.DataCodingScheme = 137; + this.SCTimestamp = SmsTimestamp.None; + this.DischargeTime = SmsTimestamp.None; + base.ConstructLength = num3 * 2; + return; + } + } + else + { + return; + } + } + else + { + throw new ArgumentException("pdu must not be an empty string."); + } + } + + /// + /// Returns the relevant timestamp for the message. + /// + /// An containing the discharge time, the timestamp where + /// the status in this report occurred. + public override SmsTimestamp GetTimestamp() + { + return this.dischargeTime; + } + + /// + /// Sets the recipient address and type directly without attempting to + /// autodetect the type. + /// + /// The recipient address + /// The address type + public void SetRecipientAddress(string address, byte addressType) + { + this.recipientAddress = address; + this.recipientAddressType = addressType; + } + + /// + /// Converts the value of this instance into a string. + /// + /// If true, excludes the SMSC header. + /// The encoded string. + /// Not implemented, always throws an . + public override string ToString(bool excludeSmscData) + { + throw new NotImplementedException("SmsStatusReportPdu.ToString() not implemented."); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsSubmitMessageFlags.cs b/PDUConverter/GsmComm.PduConverter/SmsSubmitMessageFlags.cs new file mode 100644 index 0000000..54d0d01 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsSubmitMessageFlags.cs @@ -0,0 +1,259 @@ +using System; + +/// +/// Represents the the first octet of an SMS-SUBMIT-PDU. +/// +namespace GsmComm.PduConverter +{ + public class SmsSubmitMessageFlags : OutgoingMessageFlags + { + private const byte TP_MTI_SMS_Submit = 1; + + private const byte TP_RD = 4; + + private const byte TP_VPF_Enhanced = 8; + + private const byte TP_VPF_Relative = 16; + + private const byte TP_VPF_Absolute = 24; + + private const byte TP_SRR = 32; + + private const byte TP_UDHI = 64; + + private const byte TP_RP = 128; + + private bool rejectDuplicates; + + private ValidityPeriodFormat validityPeriodFormat; + + private bool requestStatusReport; + + private bool userDataHeaderPresent; + + private bool replyPathExists; + + /// + /// Gets the message type, always returns . + /// + public override OutgoingMessageType MessageType + { + get + { + return OutgoingMessageType.SmsSubmit; + } + } + + /// + /// Gets or sets if the SC should reject duplicate messages. + /// + public bool RejectDuplicates + { + get + { + return this.rejectDuplicates; + } + set + { + this.rejectDuplicates = value; + } + } + + /// + /// Gets or sets if a reply path exists. + /// + public bool ReplyPathExists + { + get + { + return this.replyPathExists; + } + set + { + this.replyPathExists = value; + } + } + + /// + /// Gets or sets if s status report should be requested. + /// + public bool RequestStatusReport + { + get + { + return this.requestStatusReport; + } + set + { + this.requestStatusReport = value; + } + } + + /// + /// Gets or sets if a user data header is present. + /// + public bool UserDataHeaderPresent + { + get + { + return this.userDataHeaderPresent; + } + set + { + this.userDataHeaderPresent = value; + } + } + + /// + /// Gets or sets the validity period format. + /// + public ValidityPeriodFormat ValidityPeriodFormat + { + get + { + return this.validityPeriodFormat; + } + set + { + this.validityPeriodFormat = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Default settings are: + /// + /// + /// ReplyPathExists = false + /// + /// + /// UserDataHeaderPresent = false + /// + /// + /// RequestStatusReport = false + /// + /// + /// ValidityPeriodFormat = ValidityPeriodFormat.Unspecified + /// + /// + /// RejectDuplicates = false + /// + /// + /// MessageType = OutgoingMessageType.SmsSubmit + /// + /// + /// + public SmsSubmitMessageFlags() + { + this.rejectDuplicates = false; + this.validityPeriodFormat = ValidityPeriodFormat.Unspecified; + this.requestStatusReport = false; + this.userDataHeaderPresent = false; + this.replyPathExists = false; + } + + /// + /// Initializes a new instance of the MessageFlags class with a + /// predefined data byte. + /// + public SmsSubmitMessageFlags(byte flags) + { + this.FromByte(flags); + } + + /// + /// Fills the object with values from the data byte. + /// + /// The byte value. + protected override void FromByte(byte b) + { + OutgoingMessageType outgoingMessageType = OutgoingMessageType.SmsSubmit; + if ((b & 1) > 0) + { + outgoingMessageType = OutgoingMessageType.SmsSubmit; + } + if (outgoingMessageType == OutgoingMessageType.SmsSubmit) + { + this.rejectDuplicates = (b & 4) > 0; + this.validityPeriodFormat = ValidityPeriodFormat.Unspecified; + if ((b & 24) > 0) + { + this.validityPeriodFormat = ValidityPeriodFormat.Absolute; + } + if ((b & 16) > 0) + { + this.validityPeriodFormat = ValidityPeriodFormat.Relative; + } + if ((b & 8) > 0) + { + this.validityPeriodFormat = ValidityPeriodFormat.Enhanced; + } + this.requestStatusReport = (b & 32) > 0; + this.userDataHeaderPresent = (b & 64) > 0; + this.replyPathExists = (b & 128) > 0; + return; + } + else + { + throw new ArgumentException("Not an SMS-SUBMIT message."); + } + } + + /// + /// Returns the byte equivalent of this instance. + /// + /// The byte value. + public override byte ToByte() + { + byte num = 0; + num = (byte)(num | 1); + if (this.rejectDuplicates) + { + num = (byte)(num | 4); + } + ValidityPeriodFormat validityPeriodFormat = this.validityPeriodFormat; + switch (validityPeriodFormat) + { + case ValidityPeriodFormat.Relative: + { + num = (byte)(num | 16); + break; + } + case ValidityPeriodFormat.Absolute: + { + num = (byte)(num | 24); + break; + } + case ValidityPeriodFormat.Enhanced: + { + num = (byte)(num | 8); + break; + } + } + if (this.requestStatusReport) + { + num = (byte)(num | 32); + } + if (this.userDataHeaderPresent) + { + num = (byte)(num | 64); + } + if (this.replyPathExists) + { + num = (byte)(num | 128); + } + return num; + } + + /// + /// Returns the string equivalent of this instance. + /// + public override string ToString() + { + byte num = this.ToByte(); + return num.ToString(); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsSubmitPdu.cs b/PDUConverter/GsmComm.PduConverter/SmsSubmitPdu.cs new file mode 100644 index 0000000..6d23391 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsSubmitPdu.cs @@ -0,0 +1,476 @@ +using System; +using System.Text; + +/// +/// Represents an SMS-SUBMIT PDU, an outgoing short message. +/// +namespace GsmComm.PduConverter +{ + public class SmsSubmitPdu : OutgoingSmsPdu + { + private byte destTOA; + + private string destAddress; + + private ValidityPeriod validityPeriod; + + /// + /// Gets or sets the destination address. + /// + /// + /// When setting the property also the + /// property will be set, attempting to autodetect the address type. + /// When getting the property: The address may be extended with address-type + /// specific prefixes or other chraracters. + /// + public string DestinationAddress + { + get + { + return base.CreateAddressOfType(this.destAddress, this.destTOA); + } + set + { + byte num = 0; + string str = null; + base.FindTypeOfAddress(value, out num, out str); + this.destAddress = str; + this.destTOA = num; + } + } + + /// + /// Gets the type of the destination address. + /// + /// + /// Represents the Type-of-Address octets for the destination address of the PDU. + /// + public byte DestinationAddressType + { + get + { + return this.destTOA; + } + } + + /// + /// Gets the message flags. + /// + public SmsSubmitMessageFlags MessageFlags + { + get + { + return (SmsSubmitMessageFlags)this.messageFlags; + } + } + + /// + /// Gets or sets if the SC should reject duplicate messages. + /// + public bool RejectDuplicates + { + get + { + return this.MessageFlags.RejectDuplicates; + } + set + { + this.MessageFlags.RejectDuplicates = value; + } + } + + /// + /// Gets or sets if a reply path exists. + /// + public bool ReplyPathExists + { + get + { + return this.MessageFlags.ReplyPathExists; + } + set + { + this.MessageFlags.ReplyPathExists = value; + } + } + + /// + /// Gets or sets if s status report should be requested. + /// + public bool RequestStatusReport + { + get + { + return this.MessageFlags.RequestStatusReport; + } + set + { + this.MessageFlags.RequestStatusReport = value; + } + } + + /// + /// Gets or sets if a user data header is present. + /// + public override bool UserDataHeaderPresent + { + get + { + return this.MessageFlags.UserDataHeaderPresent; + } + set + { + this.MessageFlags.UserDataHeaderPresent = value; + } + } + + /// + /// Gets or sets the validity period. + /// + /// + /// Represents the TP-Validity-Period octet of the PDU. + /// The validity period specifies the time when SM expires. If SM is't delivered + /// before that moment, it is discarded by SC. Validity-Period can be in + /// three different formats: Relative, Enhanced and Absolute. + /// + public ValidityPeriod ValidityPeriod + { + get + { + return this.validityPeriod; + } + set + { + if (value as RelativeValidityPeriod == null) + { + if (value != null) + { + throw new ArgumentException(string.Concat("Unknown or unsupported validity period format: ", value.GetType().ToString())); + } + else + { + this.MessageFlags.ValidityPeriodFormat = ValidityPeriodFormat.Unspecified; + } + } + else + { + this.MessageFlags.ValidityPeriodFormat = ValidityPeriodFormat.Relative; + } + this.validityPeriod = value; + } + } + + /// + /// Initializes a new instance using default values. + /// + public SmsSubmitPdu() + { + this.messageFlags = new SmsSubmitMessageFlags(); + this.DestinationAddress = string.Empty; + this.ValidityPeriod = new RelativeValidityPeriod(167); + } + + /// + /// Initializes a new instance of the class + /// using the specified text and destination address. + /// + /// The message text, not exceeding 160 characters. + /// The message's destination address. + public SmsSubmitPdu(string userDataText, string destinationAddress) : this() + { + base.Encode7BitText(userDataText); + this.DestinationAddress = destinationAddress; + } + + /// + /// Initializes a new instance of the class using the specified text, + /// destination address and SMSC address. + /// + /// The message text, not exceeding 160 characters. + /// The message's destination address. + /// The service center (SMSC) address. Can be an empty string. + public SmsSubmitPdu(string userDataText, string destinationAddress, string smscAddress) : this() + { + base.Encode7BitText(userDataText); + this.DestinationAddress = destinationAddress; + base.SmscAddress = smscAddress; + } + + /// + /// Initializes a new instance of the class using the specified text, + /// destination address and data coding scheme. + /// + /// The message text. + /// The message's destination address. + /// Specifies how the userDataText should be encoded. + /// The maximum length of the userDataText parameter depends on the alphabet specified with + /// the dataCodingScheme parameter.Common values for the dataCodingScheme are + /// for GSM default alphabet and + /// for UCS2 alphabet (Unicode). + public SmsSubmitPdu(string userDataText, string destinationAddress, byte dataCodingScheme) : this() + { + base.DataCodingScheme = dataCodingScheme; + base.UserDataText = userDataText; + this.DestinationAddress = destinationAddress; + } + + /// + /// Initializes a new instance of the class using the specified text, + /// destination address, SMSC address and data coding scheme. + /// + /// The message text. + /// The message's destination address. + /// The service center (SMSC) address. Can be an empty string. + /// Specifies how the userDataText should be encoded. + /// The maximum length of the userDataText parameter depends on the alphabet specified with + /// the dataCodingScheme parameter.Common values for the dataCodingScheme are + /// for GSM default alphabet and + /// for UCS2 alphabet (Unicode). + public SmsSubmitPdu(string userDataText, string destinationAddress, string smscAddress, byte dataCodingScheme) : this() + { + base.DataCodingScheme = dataCodingScheme; + base.UserDataText = userDataText; + this.DestinationAddress = destinationAddress; + base.SmscAddress = smscAddress; + } + + /// + /// Initializes a new instance of the class + /// using the specified PDU string. + /// + /// The PDU string to convert. + /// Specifies if the string contains + /// SMSC data octets at the beginning. + /// Specifies the actual PDU length, that is the length in bytes without + /// the SMSC header. Set to -1 if unknown. + /// + /// This constructor assumes that the string contains an SMS-SUBMIT + /// PDU data stream as specified + /// by GSM 07.05. + /// AbsuluteValidityPeriod and EnhancedValidityPeriod are not + /// supported and will generate a + /// when encountered. + /// + /// + /// The string contains a + /// validity and the validity period format is not relative validity. + /// + public SmsSubmitPdu(string pdu, bool includesSmscData, int actualLength) + { + byte num = 0; + byte[] numArray = null; + int num1; + if (pdu != string.Empty) + { + bool flag = actualLength >= 0; + int num2 = actualLength; + if (!flag || num2 > 0) + { + int num3 = 0; + if (!includesSmscData) + { + base.SmscAddress = string.Empty; + } + else + { + int num4 = num3; + num3 = num4 + 1; + byte num5 = BcdWorker.GetByte(pdu, num4); + if (num5 <= 0) + { + base.SmscAddress = string.Empty; + } + else + { + int num6 = num3; + num3 = num6 + 1; + byte num7 = BcdWorker.GetByte(pdu, num6); + int num8 = num5 - 1; + string bytesString = BcdWorker.GetBytesString(pdu, num3, num8); + num3 = num3 + num8; + string str = BcdWorker.DecodeSemiOctets(bytesString); + if (str.EndsWith("F") || str.EndsWith("f")) + { + str = str.Substring(0, str.Length - 1); + } + base.SetSmscAddress(str, num7); + } + } + int num9 = num3; + num3 = num9 + 1; + this.messageFlags = new SmsSubmitMessageFlags(BcdWorker.GetByte(pdu, num9)); + if (flag) + { + num2--; + if (num2 <= 0) + { + base.ConstructLength = num3 * 2; + return; + } + } + int num10 = num3; + num3 = num10 + 1; + base.MessageReference = BcdWorker.GetByte(pdu, num10); + int num11 = num3; + num3 = num11 + 1; + byte num12 = BcdWorker.GetByte(pdu, num11); + int num13 = num3; + num3 = num13 + 1; + byte num14 = BcdWorker.GetByte(pdu, num13); + if (num12 <= 0) + { + this.DestinationAddress = string.Empty; + } + else + { + if (num12 % 2 != 0) + { + num1 = num12 + 1; + } + else + { + num1 = (int)num12; + } + int num15 = num1 / 2; + string bytesString1 = BcdWorker.GetBytesString(pdu, num3, num15); + num3 = num3 + num15; + string str1 = BcdWorker.DecodeSemiOctets(bytesString1).Substring(0, num12); + this.SetDestinationAddress(str1, num14); + } + int num16 = num3; + num3 = num16 + 1; + base.ProtocolID = BcdWorker.GetByte(pdu, num16); + int num17 = num3; + num3 = num17 + 1; + base.DataCodingScheme = BcdWorker.GetByte(pdu, num17); + ValidityPeriodFormat validityPeriodFormat = this.MessageFlags.ValidityPeriodFormat; + if (validityPeriodFormat == ValidityPeriodFormat.Unspecified) + { + this.ValidityPeriod = null; + } + else if (validityPeriodFormat == ValidityPeriodFormat.Relative) + { + int num18 = num3; + num3 = num18 + 1; + this.ValidityPeriod = new RelativeValidityPeriod(BcdWorker.GetByte(pdu, num18)); + } + else if (validityPeriodFormat == ValidityPeriodFormat.Absolute) + { + throw new NotSupportedException("Absolute validity period format not supported."); + } + else if (validityPeriodFormat == ValidityPeriodFormat.Enhanced) + { + throw new NotSupportedException("Enhanced validity period format not supported."); + } + else + { + throw new NotSupportedException(string.Concat("Validity period format \"", (object)this.MessageFlags.ValidityPeriodFormat.ToString(), "\" not supported.")); + } + PduParts.DecodeUserData(pdu, ref num3, base.DataCodingScheme, out num, out numArray); + base.SetUserData(numArray, num); + base.ConstructLength = num3 * 2; + return; + } + else + { + return; + } + } + else + { + throw new ArgumentException("pdu must not be an empty string."); + } + } + + private string EncodeDestinationAddress(string address, byte addressType) + { + if (address.Length != 0) + { + byte length = (byte)address.Length; + string str = BcdWorker.EncodeSemiOctets(address); + return string.Concat(Calc.IntToHex(length), Calc.IntToHex(addressType), str); + } + else + { + return string.Concat(Calc.IntToHex(0), Calc.IntToHex(addressType)); + } + } + + private string EncodeSmscAddress(string address, byte addressType) + { + if (address.Length != 0) + { + string str = BcdWorker.EncodeSemiOctets(address); + byte length = (byte)(str.Length / 2 + 1); + return string.Concat(Calc.IntToHex(length), Calc.IntToHex(addressType), str); + } + else + { + return Calc.IntToHex(0); + } + } + + /// + /// Returns the relevant timestamp for the message. + /// + /// Always . An does not have + /// a timestamp. + public override SmsTimestamp GetTimestamp() + { + return SmsTimestamp.None; + } + + /// + /// Sets the destination address and type directly without attempting to + /// autodetect the type. + /// + /// The destination address + /// The address type + public void SetDestinationAddress(string address, byte addressType) + { + this.destAddress = address; + this.destTOA = addressType; + } + + /// + /// Converts the value of this instance into a string. + /// + /// If true, excludes the SMSC header. + /// The encoded string. + public override string ToString(bool excludeSmscData) + { + string str = null; + byte num = 0; + string empty = string.Empty; + if (this.MessageFlags.ValidityPeriodFormat != ValidityPeriodFormat.Relative) + { + if (this.MessageFlags.ValidityPeriodFormat != ValidityPeriodFormat.Unspecified) + { + throw new NotSupportedException(string.Concat("The specified validity period format \"", this.MessageFlags.ValidityPeriodFormat.ToString(), "\" is not supported.")); + } + } + else + { + empty = Calc.IntToHex((RelativeValidityPeriod)this.ValidityPeriod); + } + StringBuilder stringBuilder = new StringBuilder(); + if (!excludeSmscData) + { + base.GetSmscAddress(out str, out num); + stringBuilder.Append(this.EncodeSmscAddress(str, num)); + } + stringBuilder.Append(Calc.IntToHex(this.messageFlags)); + stringBuilder.Append(Calc.IntToHex(this.messageReference)); + stringBuilder.Append(this.EncodeDestinationAddress(this.destAddress, this.destTOA)); + stringBuilder.Append(Calc.IntToHex(base.ProtocolID)); + stringBuilder.Append(Calc.IntToHex(base.DataCodingScheme)); + stringBuilder.Append(empty); + stringBuilder.Append(Calc.IntToHex(base.UserDataLength)); + if (base.UserData != null) + { + stringBuilder.Append(Calc.IntToHex(base.UserData)); + } + return stringBuilder.ToString(); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/SmsTimestamp.cs b/PDUConverter/GsmComm.PduConverter/SmsTimestamp.cs new file mode 100644 index 0000000..1371b42 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/SmsTimestamp.cs @@ -0,0 +1,407 @@ +using System; + +/// +/// For TP-SCTS and all other timestamps that look the same. +/// +namespace GsmComm.PduConverter +{ + public struct SmsTimestamp : IComparable + { + private byte year; + + private byte month; + + private byte day; + + private byte hour; + + private byte minute; + + private byte second; + + private int timeZoneOffset; + + /// + /// The value for an invalid or not present timestamp. + /// + public static SmsTimestamp None; + + /// + /// Gets the day. + /// + public byte Day + { + get + { + return this.day; + } + } + + /// + /// Gets the hour. + /// + public byte Hour + { + get + { + return this.hour; + } + } + + /// + /// Gets the minute. + /// + public byte Minute + { + get + { + return this.minute; + } + } + + /// + /// Gets the month. + /// + public byte Month + { + get + { + return this.month; + } + } + + /// + /// Gets the second. + /// + public byte Second + { + get + { + return this.second; + } + } + + /// + /// Gets the time zone offset as an integer. + /// + /// One unit of this offset equals 15 minutes. If you don't need this actual value, + /// consider using instead. + public int TimeZoneOffset + { + get + { + return this.timeZoneOffset; + } + } + + /// + /// Gets the time zone offset as a string useful to append to the sortable date string. + /// + private string TimeZoneOffsetSortableString + { + get + { + string str; + TimeSpan timeZoneOffsetSpan = this.TimeZoneOffsetSpan; + if (timeZoneOffsetSpan.Ticks < (long)0) + { + str = "-"; + } + else + { + str = "+"; + } + string str1 = str; + int num = Math.Abs(timeZoneOffsetSpan.Hours); + string str2 = num.ToString().PadLeft(2, '0'); + int num1 = Math.Abs(timeZoneOffsetSpan.Minutes); + string str3 = num1.ToString().PadLeft(2, '0'); + return string.Concat(str1, str2, ":", str3); + } + } + + /// + /// Gets the time zone offset as a time span. + /// + public TimeSpan TimeZoneOffsetSpan + { + get + { + return TimeSpan.FromMinutes((double)(this.timeZoneOffset * 15)); + } + } + + /// + /// Gets the time zone offset as a string. + /// + public string TimeZoneOffsetString + { + get + { + string str; + TimeSpan timeZoneOffsetSpan = this.TimeZoneOffsetSpan; + if (timeZoneOffsetSpan.Ticks < (long)0) + { + str = "-"; + } + else + { + str = "+"; + } + string str1 = str; + int num = Math.Abs(timeZoneOffsetSpan.Hours); + string str2 = num.ToString().PadLeft(2, '0'); + int num1 = Math.Abs(timeZoneOffsetSpan.Minutes); + string str3 = num1.ToString().PadLeft(2, '0'); + return string.Concat(str1, str2, str3); + } + } + + /// + /// Gets the year. + /// + public byte Year + { + get + { + return this.year; + } + } + + static SmsTimestamp() + { + SmsTimestamp.None = new SmsTimestamp(new DateTime(1900, 1, 1), 0); + } + + /// + /// Decodes the timestamp out of a PDU stream. + /// + /// The string to get the timestamp from. + /// The current position in the string + public SmsTimestamp(string pdu, ref int index) + { + string bytesString = BcdWorker.GetBytesString(pdu, index, 7); + index = index + 7; + string str = BcdWorker.DecodeSemiOctets(bytesString); + this.year = byte.Parse(str.Substring(0, 2)); + this.month = byte.Parse(str.Substring(2, 2)); + this.day = byte.Parse(str.Substring(4, 2)); + this.hour = byte.Parse(str.Substring(6, 2)); + this.minute = byte.Parse(str.Substring(8, 2)); + this.second = byte.Parse(str.Substring(10, 2)); + string str1 = str.Substring(12, 2); + byte num = Calc.HexToInt(str1)[0]; + if ((num & 128) <= 0) + { + this.timeZoneOffset = int.Parse(str1); + return; + } + else + { + num = (byte)(num & 127); + this.timeZoneOffset = -num; + return; + } + } + + /// + /// Creates the timestamp using a DateTime object and an offset. + /// + /// The timestamp to initialize this object with. + /// The time zone offset for the specified timestamp. + public SmsTimestamp(DateTime timestamp, int timeZoneOffset) + { + int year = timestamp.Year; + this.year = (byte)int.Parse(year.ToString().Substring(2, 2)); + this.month = (byte)timestamp.Month; + this.day = (byte)timestamp.Day; + this.hour = (byte)timestamp.Hour; + this.minute = (byte)timestamp.Minute; + this.second = (byte)timestamp.Second; + this.timeZoneOffset = timeZoneOffset; + } + + /// + /// Compares this instance to a specified object and returns an indication of their relative values. + /// + /// An object to compare, or a null reference + /// + /// + /// less than zero = the instance is less than value. + /// zero = the instance is equal to value. + /// greater than zero = The instance is grater than value -or- value is a null reference. + /// + /// + /// value is not an . + public int CompareTo(object value) + { + if (value as SmsTimestamp == null) + { + if (value != null) + { + throw new ArgumentException("value is not an SmsTimestamp."); + } + else + { + return 1; + } + } + else + { + SmsTimestamp smsTimestamp = (SmsTimestamp)value; + int num = this.year.CompareTo(smsTimestamp.Year); + if (num == 0) + { + num = this.month.CompareTo(smsTimestamp.Month); + if (num == 0) + { + num = this.day.CompareTo(smsTimestamp.Day); + if (num == 0) + { + num = this.hour.CompareTo(smsTimestamp.Hour); + if (num == 0) + { + num = this.minute.CompareTo(smsTimestamp.Minute); + if (num == 0) + { + num = this.second.CompareTo(smsTimestamp.Second); + if (num == 0) + { + num = this.timeZoneOffset.CompareTo(smsTimestamp.TimeZoneOffset); + return num; + } + else + { + return num; + } + } + else + { + return num; + } + } + else + { + return num; + } + } + else + { + return num; + } + } + else + { + return num; + } + } + else + { + return num; + } + } + } + + /// + /// Returns the timestamp as a object. + /// + /// The converted date object. + /// + /// The object does not hold the time zone offset, + /// this information will be lost when using this method. Use the + /// TimeZoneOffset-releated properties and methods for working with the + /// offset. If you just need the string representation, consider using the , + /// or methods instead. + /// + /// + /// An saves its years only with two digits by design, + /// uses 4 digits. The following conversion is performed when converting from an + /// to a : If the year is equal or greater than 90, + /// then 1900 is added, otherwise 2000. + /// + /// + /// If there is an error during conversion, the constant + /// is returned, no exception is thrown. + /// + /// + public DateTime ToDateTime() + { + DateTime dateTime; + int num; + try + { + if (this.year >= 90) + { + num = 1900 + this.year; + } + else + { + num = 2000 + this.year; + } + dateTime = new DateTime(num, this.month, this.day, this.hour, this.minute, this.second); + } + catch (ArgumentOutOfRangeException argumentOutOfRangeException) + { + dateTime = DateTime.MinValue; + } + return dateTime; + } + + /// + /// Returns a formatted date using the sortable date/time pattern and the time zone. + /// + /// The formatted date string. + /// The format is independent of the currently active culture and + /// is always "yyyy-MM-ddTHH:mm:sszzz". It is useful for persisting the timestamp value as text. + /// If you just want to display the value, consider using the or + /// methods instead. + public string ToSortableString() + { + DateTime dateTime = this.ToDateTime(); + return string.Concat(dateTime.ToString("s"), this.TimeZoneOffsetSortableString); + } + + /// + /// Returns the formatted date using DateTime of the current culture + the time zone offset. + /// + /// The formatted date string. + /// + /// The returned string is useful for display but not for persistence because is uses + /// the currently active culture to format the string, which can cause problems when trying to parse + /// the persisted string. If you want to persist the timestamp, consider using + /// instead. + /// + public override string ToString() + { + return this.ToString(true); + } + + /// + /// Returns a formatted date string using DateTime of the current culture. + /// + /// Specify true to include the + /// time zone offset in the string, false if it should not be included. + /// The formatted date string. + /// + /// The returned string is useful for display but not for persistence because is uses + /// the currently active culture to format the string, which can cause problems when trying to parse + /// the persisted string. If you want to persist the timestamp, consider using + /// instead. + /// + public string ToString(bool includeTimeZoneOffset) + { + string str; + DateTime dateTime = this.ToDateTime(); + string str1 = dateTime.ToString(); + if (includeTimeZoneOffset) + { + str = string.Concat(" ", this.TimeZoneOffsetString); + } + else + { + str = ""; + } + return string.Concat(str1, str); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/StatusCategory.cs b/PDUConverter/GsmComm.PduConverter/StatusCategory.cs new file mode 100644 index 0000000..f3ff79d --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/StatusCategory.cs @@ -0,0 +1,19 @@ +/// +/// Contains the possible categories of a message status. +/// +namespace GsmComm.PduConverter +{ + public enum StatusCategory + { + /// Short message transaction completed. + Success, + /// Temporary error, SC still trying to transfer SM. + TemporaryErrorWithRetry, + /// Permanent error, SC is not making any more transfer attempts. + PermanentError, + /// Temporary error, SC is not making any more transfer attempts. + TemporaryErrorNoRetry, + /// Status code is out of the defined range. + Reserved + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/TextDataConverter.cs b/PDUConverter/GsmComm.PduConverter/TextDataConverter.cs new file mode 100644 index 0000000..4845c60 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/TextDataConverter.cs @@ -0,0 +1,464 @@ +using System; +using System.Collections; +using System.Text; + +/// +/// Converts between text strings and PDU-compatible formats. +/// +namespace GsmComm.PduConverter +{ + public static class TextDataConverter + { + /// + /// The GSM 7-Bit default alphabet. + /// + private static char[] sevenBitDefault; + + static TextDataConverter() + { + char[] chrArray = new char[] { '@', '£', '$', '¥', 'è', 'é', 'ù', 'ì', 'ò', 'Ç', '\n', 'Ø', 'ø', '\r', 'Å', 'å', 'Δ', '\u005F', 'Φ', 'Γ', 'Λ', 'Ω', 'Π', 'Ψ', 'Σ', 'Θ', 'Ξ', '\u001B', 'Æ', 'æ', 'ß', 'É', ' ', '!', '\"', '#', '¤', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '¡', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ñ', 'Ü', '\u00A7', '¿', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'ä', 'ö', 'ñ', 'ü', 'à' }; + TextDataConverter.sevenBitDefault = chrArray; + } + + private static byte CharToSevenBitExtension(char c) + { + char chr = c; + if (chr > '\u005E') + { + switch (chr) + { + case '{': + { + return 40; + } + case '|': + { + return 64; + } + case '}': + { + return 41; + } + case '~': + { + return 61; + } + default: + { + if (chr == '€') + { + return 101; + } + else + { + break; + } + } + } + } + else + { + if (chr == '\f') + { + return 10; + } + else + { + switch (chr) + { + case '[': + { + return 60; + } + case '\\': + { + return 47; + } + case ']': + { + return 62; + } + case '\u005E': + { + return 20; + } + } + } + } + throw new ArgumentException(string.Concat("The character '", c.ToString(), "' does not exist in the GSM 7-bit default alphabet extension table.")); + } + + /// + /// Expands an array of octets into string of septets. + /// + /// An array of 8-bit encoded data (octets), represented as a string. + /// The converted data as a string. + public static string OctetsToSeptetsStr(byte[] data) + { + string str; + if (data != null) + { + string empty = string.Empty; + string empty1 = string.Empty; + int i = 0; + for (; i < (int)data.Length; i++) + { + string bin = Calc.IntToBin(data[i], 8); + string str1 = bin.Substring(i % 7 + 1, 7 - i % 7); + if (i == 0 || i % 7 == 0) + { + if (i != 0) + { + empty = string.Concat(empty, (char)Calc.BinToInt(empty1)); + } + str = str1; + } + else + { + str = string.Concat(str1, empty1); + } + empty1 = bin.Substring(0, i % 7 + 1); + empty = string.Concat(empty, (char)Calc.BinToInt(str)); + } + if (i != 0 && i % 7 == 0 && empty1 != "0000000") + { + empty = string.Concat(empty, (char)Calc.BinToInt(empty1)); + } + return empty; + } + else + { + return string.Empty; + } + } + + /// + /// Compacts a string of septets into octets. + /// + /// 7-bit encoded data (septets), represented as a string. + /// + /// When only 7 of 8 available bits of a character are used, 1 bit is + /// wasted per character. This method compacts a string of characters + /// which consist solely of such 7-bit characters. + /// Effectively, every 8 bytes of the original string are packed into + /// 7 bytes in the resulting string. + /// + public static string SeptetsToOctetsHex(string data) + { + string empty = string.Empty; + string str = string.Empty; + for (int i = 0; i < data.Length; i++) + { + string bin = Calc.IntToBin((byte)data[i], 7); + if (i != 0 && i % 8 != 0) + { + string str1 = bin.Substring(7 - i % 8); + string str2 = string.Concat(str1, str); + empty = string.Concat(empty, Calc.IntToHex(Calc.BinToInt(str2))); + } + str = bin.Substring(0, 7 - i % 8); + if (i == data.Length - 1 && str != string.Empty) + { + empty = string.Concat(empty, Calc.IntToHex(Calc.BinToInt(str))); + } + } + return empty; + } + + /// + /// Compacts a string of septets into octets. + /// + /// 7-bit encoded data (septets), represented as a string. + /// + /// When only 7 of 8 available bits of a character are used, 1 bit is + /// wasted per character. This method compacts a string of characters + /// which consist solely of such 7-bit characters. + /// Effectively, every 8 bytes of the original string are packed into + /// 7 bytes in the resulting string. + /// + public static byte[] SeptetsToOctetsInt(string data) + { + ArrayList arrayLists = new ArrayList(); + string empty = string.Empty; + for (int i = 0; i < data.Length; i++) + { + string bin = Calc.IntToBin((byte)data[i], 7); + if (i != 0 && i % 8 != 0) + { + string str = bin.Substring(7 - i % 8); + string str1 = string.Concat(str, empty); + arrayLists.Add(Calc.BinToInt(str1)); + } + empty = bin.Substring(0, 7 - i % 8); + if (i == data.Length - 1 && empty != string.Empty) + { + arrayLists.Add(Calc.BinToInt(empty)); + } + } + byte[] numArray = new byte[arrayLists.Count]; + arrayLists.CopyTo(numArray); + return numArray; + } + + private static char SevenBitExtensionToChar(byte b) + { + byte num = b; + if (num > 41) + { + if (num == 47) + { + return '\\'; + } + else + { + if (num == 60) + { + return '['; + } + else if (num == 61) + { + return '~'; + } + else if (num == 62) + { + return ']'; + } + else if (num == 63) + { + throw new ArgumentException(string.Concat("The value ", b.ToString(), " is not part of the 7-bit default alphabet extension table.")); + } + else if (num == 64) + { + return '|'; + } + if (num == 101) + { + return '€'; + } + } + } + else + { + if (num == 10) + { + return '\f'; + } + else + { + if (num == 20) + { + return '\u005E'; + } + else + { + switch (num) + { + case 40: + { + return '{'; + } + case 41: + { + return '}'; + } + } + } + } + } + throw new ArgumentException(string.Concat("The value ", b.ToString(), " is not part of the 7-bit default alphabet extension table.")); + } + + /// + /// Converts a string consisting of characters from the GSM + /// "7-bit default alphabet" into a string of corresponding characters + /// of the ISO-8859-1 character set. + /// + /// The string to convert. + /// The converted string. + /// + /// Note that the converted string does not necessarily have the same + /// length as the original one because some characters may be escaped. + /// This method throws an exception if an invalid character + /// is encountered. + /// + public static string SevenBitToString(string s) + { + return TextDataConverter.SevenBitToString(s, true); + } + + /// + /// Converts a string consisting of characters from the GSM + /// "7-bit default alphabet" into a string of corresponding characters + /// of the ISO-8859-1 character set. + /// + /// The string to convert. + /// If true, throws an exception if + /// an invalid character is encountered. If false, replaces every + /// unknown character with a question mark (?). + /// The converted string. + /// + /// Note that the converted string does not necessarily have the same + /// length as the original one because some characters may be escaped. + /// This method throws an exception if an invalid character + /// is encountered. + /// + public static string SevenBitToString(string s, bool throwExceptions) + { + string empty = string.Empty; + bool flag = false; + for (int i = 0; i < s.Length; i++) + { + byte num = (byte)s[i]; + if (!flag) + { + if (num == 27) + { + flag = true; + } + else + { + if (num > TextDataConverter.sevenBitDefault.GetUpperBound(0)) + { + if (!throwExceptions) + { + empty = string.Concat(empty, (char)63); + } + else + { + object[] str = new object[5]; + str[0] = "Character '"; + str[1] = (char)num; + str[2] = "' at position "; + int num1 = i + 1; + str[3] = num1.ToString(); + str[4] = " is not part of the GSM 7-bit default alphabet."; + throw new ArgumentException(string.Concat(str)); + } + } + else + { + empty = string.Concat(empty, TextDataConverter.sevenBitDefault[num]); + } + } + } + else + { + try + { + empty = string.Concat(empty, TextDataConverter.SevenBitExtensionToChar(num)); + } + catch (Exception exception) + { + if (!throwExceptions) + { + empty = string.Concat(empty, (char)63); + } + else + { + throw; + } + } + flag = false; + } + } + return empty; + } + + /// + /// Converts a string consisting of characters from the ISO-8859-1 + /// character set into a string of corresponding characters of the + /// "GSM 7-bit default alphabet" character set. + /// + /// The string to convert. + /// If true, throws an exception if + /// an invalid character is encountered. If false, replaces every + /// unknown character with a question mark (?). + /// Will be set to true if invalid characters + /// were replaced. throwExceptions must be false for this to work. + /// The converted string. + /// + /// Note that the converted string does not need to have the same + /// length as the original one because some characters may be escaped. + /// + /// throwExceptions is true and invalid character is encountered in the string. + public static string StringTo7Bit(string s, bool throwExceptions, out bool charsReplaced) + { + StringBuilder stringBuilder = new StringBuilder(); + charsReplaced = false; + bool flag = false; + int i = 0; + int num = 0; + for (i = 0; i < s.Length; i++) + { + flag = false; + char chr = s.Substring(i, 1)[0]; + num = 0; + while (num <= TextDataConverter.sevenBitDefault.GetUpperBound(0)) + { + if (TextDataConverter.sevenBitDefault[num] != chr) + { + num++; + } + else + { + stringBuilder.Append((ushort)num); + flag = true; + break; + } + } + if (!flag) + { + try + { + byte sevenBitExtension = TextDataConverter.CharToSevenBitExtension(chr); + stringBuilder.Append('\u001B'); + stringBuilder.Append(sevenBitExtension); + flag = true; + } + catch (Exception exception) + { + } + } + if (!flag) + { + if (!throwExceptions) + { + stringBuilder.Append('?'); + charsReplaced = true; + } + else + { + object[] str = new object[5]; + str[0] = "The character '"; + str[1] = chr; + str[2] = "' at position "; + int num1 = i + 1; + str[3] = num1.ToString(); + str[4] = " does not exist in the GSM 7-bit default alphabet."; + throw new ArgumentException(string.Concat(str)); + } + } + } + return stringBuilder.ToString(); + } + + /// + /// Converts a string consisting of characters from the ISO-8859-1 + /// character set into a string of corresponding characters of the + /// "GSM 7-bit default alphabet" character set. + /// + /// The string to convert. + /// The converted string. + /// + /// Throws an exception when an invalid character is encountered in the string. + /// Note that the converted string does not need to have the same + /// length as the original one because some characters may be escaped. + /// + /// An invalid character is encountered in the string. + public static string StringTo7Bit(string s) + { + bool flag = false; + return TextDataConverter.StringTo7Bit(s, true, out flag); + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/ValidityPeriod.cs b/PDUConverter/GsmComm.PduConverter/ValidityPeriod.cs new file mode 100644 index 0000000..0dc23a8 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/ValidityPeriod.cs @@ -0,0 +1,17 @@ +/// +/// Implements the base for a validity Period. +/// +/// +/// Note to inheritors: Override the ToString method in derived classes to be able to +/// display the validity immediately as a string. +/// +/// +namespace GsmComm.PduConverter +{ + public abstract class ValidityPeriod + { + protected ValidityPeriod() + { + } + } +} \ No newline at end of file diff --git a/PDUConverter/GsmComm.PduConverter/ValidityPeriodFormat.cs b/PDUConverter/GsmComm.PduConverter/ValidityPeriodFormat.cs new file mode 100644 index 0000000..24df977 --- /dev/null +++ b/PDUConverter/GsmComm.PduConverter/ValidityPeriodFormat.cs @@ -0,0 +1,17 @@ +/// +/// Specifies how the validity period is formatted. +/// +namespace GsmComm.PduConverter +{ + public enum ValidityPeriodFormat + { + /// No validity is specified, the TP-VP block will not be specified. + Unspecified, + /// A relative validity is specified in the TP-VP block. + Relative, + /// An absolute validity is specified in the TP-VP block. + Absolute, + /// An enhanced validity is specified in the TP-VP block. + Enhanced + } +} \ No newline at end of file diff --git a/PDUConverter/PDUConverter.csproj b/PDUConverter/PDUConverter.csproj new file mode 100644 index 0000000..41656b0 --- /dev/null +++ b/PDUConverter/PDUConverter.csproj @@ -0,0 +1,227 @@ + + + + {5E657EFE-0A30-466D-B025-FF177E23A727} + 2 + Debug + AnyCPU + PDUConverter + Library + v4.0 + + + bin\Debug\ + true + DEBUG;TRACE + false + 4 + full + prompt + AnyCPU + + + bin\Release\ + false + TRACE + true + 4 + pdbonly + prompt + AnyCPU + + + + + + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + false + false + + + + \ No newline at end of file diff --git a/PDUConverter/Properties/AssemblyInfo.cs b/PDUConverter/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8ff53f3 --- /dev/null +++ b/PDUConverter/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyCompany("Stefan Mayr")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCopyright("Copyright © 2004-2011 Stefan Mayr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyFileVersion("1.21.0.0")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyTitle("SMS PDU Converter")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyVersion("1.21.0.0")] +[assembly: CompilationRelaxations(8)] +[assembly: ComVisible(false)] +[assembly: Guid("36c38b1b-8e9a-4828-865e-eda5d402248d")] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows=true)]