├── SharpSMS
├── DataEncoding.cs
├── MessageIndicationOperation.cs
├── Wbxml
│ ├── ServiceLoadingAction.cs
│ ├── ServiceIndicationAction.cs
│ ├── WBXMLDocument.cs
│ ├── ServiceLoading.cs
│ └── ServiceIndication.cs
├── MessageClass.cs
├── IndicationType.cs
├── ISmsMessageContent.cs
├── Wap
│ ├── Wdp.cs
│ ├── WdpBinaryMessage.cs
│ ├── WdpMessage.cs
│ ├── WapPushMessage.cs
│ └── Wsp.cs
├── Properties
│ └── AssemblyInfo.cs
├── ShortMessageType.cs
├── TelematicDevice.cs
├── TextMessage.cs
├── ProtocoldentifierBuilder.cs
├── SharpSMS.csproj
├── MessageIndication.cs
└── SMSSubmit.cs
├── SharpSMS.sln
├── Samples
├── Properties
│ └── AssemblyInfo.cs
├── Samples.csproj
├── Program.cs
├── SampleMessage.cs
└── Modem.cs
├── .gitignore
├── README.md
└── LICENSE.txt
/SharpSMS/DataEncoding.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Encodings for SMS messages
5 | ///
6 | public enum DataEncoding
7 | {
8 | ///
9 | /// Default 7bit alphabet used in GSM
10 | ///
11 | Default7bit,
12 | ///
13 | /// 8 bit encoding
14 | ///
15 | Data8bit,
16 | ///
17 | /// UCS2 16 bit encoding
18 | ///
19 | UCS2_16bit
20 | }
21 | }
--------------------------------------------------------------------------------
/SharpSMS/MessageIndicationOperation.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Specifies if phone stores or discards message indication
5 | ///
6 | public enum MessageIndicationOperation
7 | {
8 | ///
9 | /// Message Indication Operation is not used - Default
10 | ///
11 | NotSet,
12 | ///
13 | /// Store Indication Message
14 | ///
15 | Store,
16 | ///
17 | /// Discard Indication Message
18 | ///
19 | Discard
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SharpSMS/Wbxml/ServiceLoadingAction.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS.Wbxml
2 | {
3 | ///
4 | /// Values for Action attribute in Service Loading Message
5 | ///
6 | public enum ServiceLoadingAction : byte
7 | {
8 | ///
9 | /// Action not set
10 | ///
11 | NotSet,
12 | ///
13 | /// Execute low
14 | ///
15 | Execute_low,
16 | ///
17 | /// Execute high
18 | ///
19 | Execute_high,
20 | ///
21 | /// Cache
22 | ///
23 | Cache
24 | }
25 | }
--------------------------------------------------------------------------------
/SharpSMS/MessageClass.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Message class indicates where message will be stored
5 | ///
6 | public enum MessageClass
7 | {
8 | ///
9 | /// Flash message only to display
10 | ///
11 | ImmediateDisplay,
12 | ///
13 | /// Default store
14 | ///
15 | MESpecific,
16 | ///
17 | /// Message for the SIM
18 | ///
19 | SIMSpecific,
20 | ///
21 | /// TE Specific
22 | ///
23 | TESpecific
24 | }
25 | }
--------------------------------------------------------------------------------
/SharpSMS/IndicationType.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Type of message that indication is representing
5 | ///
6 | public enum IndicationType
7 | {
8 | ///
9 | /// Voicmail Message Waiting
10 | ///
11 | Voicemail,
12 | ///
13 | /// Fax Message Waiting
14 | ///
15 | FaxMessage,
16 | ///
17 | /// Email Message Waiting
18 | ///
19 | EmailMessage,
20 | ///
21 | /// Other Message Waiting
22 | ///
23 | OtherMessage
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/SharpSMS/ISmsMessageContent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace SharpSMS
6 | {
7 | ///
8 | /// Abstract class representing body content of the SMS
9 | ///
10 | public interface ISmsMessageContent
11 | {
12 | ///
13 | /// Encoding of the data (7-bit, 8-bit, 16-bit)
14 | ///
15 | DataEncoding DataEncoding { get; set; }
16 |
17 | ///
18 | /// Returns byte array suitable to be send in sms message. Including all user headers (if any)
19 | ///
20 | /// byte array
21 | byte[] GetSMSBytes();
22 |
23 | ///
24 | /// Returns byte array of the user header
25 | ///
26 | ///
27 | byte[] GetUDHBytes();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SharpSMS/Wbxml/ServiceIndicationAction.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS.Wbxml
2 | {
3 | ///
4 | /// Values for Action attribute in Service Indication Message
5 | ///
6 | public enum ServiceIndicationAction : byte
7 | {
8 | ///
9 | /// Action not set
10 | ///
11 | NotSet,
12 | ///
13 | /// No signaling
14 | ///
15 | Signal_none,
16 | ///
17 | /// Low signaling
18 | ///
19 | Signal_low,
20 | ///
21 | /// Medium signaling
22 | ///
23 | Signal_medium,
24 | ///
25 | /// High signaling
26 | ///
27 | Signal_high,
28 | ///
29 | /// Delete after display
30 | ///
31 | Delete
32 | }
33 | }
--------------------------------------------------------------------------------
/SharpSMS/Wap/Wdp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS.Wap
7 | {
8 | ///
9 | /// Helper static class representing Wireless Datagram Protocol
10 | ///
11 | public class Wdp
12 | {
13 | // Ports for the WDP information element, instructing the handset which
14 | // application to load on receving the message
15 | public const byte INFORMATIONELEMENT_IDENTIFIER_APPLICATIONPORT = 0x05; // 16 Bit
16 |
17 | public const int WAP_PORT_PUSH_SESSION_DESTINATION = 0x0B84;
18 | public const int WAP_PORT_PUSH_SESSION_SOURCE = 0x23F0;
19 | public const int WAP_PORT_VCARD = 0x23F4;
20 | public const int WAP_PORT_VCALENDAR = 0x23F5;
21 |
22 | public const int WAP_PORT_NOKIA_RINGTONE = 0x1581;
23 | public const int WAP_PORT_NOKIA_OPERATORLOGO = 0x1582;
24 | public const int WAP_PORT_NOKIA_CLILOGO = 0x1583;
25 | public const int WAP_PORT_NOKIA_MULTIPART = 0x158A;
26 | public const int WAP_PORT_NOKIA_OTASETTINGS = 0xC34F;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SharpSMS.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpSMS", "SharpSMS\SharpSMS.csproj", "{B5C82198-500A-4E4B-A3BF-6015745D3CB0}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{FEEE4A19-03FB-4988-9B88-ADE85487DAF4}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {B5C82198-500A-4E4B-A3BF-6015745D3CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {B5C82198-500A-4E4B-A3BF-6015745D3CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {B5C82198-500A-4E4B-A3BF-6015745D3CB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {B5C82198-500A-4E4B-A3BF-6015745D3CB0}.Release|Any CPU.Build.0 = Release|Any CPU
18 | {FEEE4A19-03FB-4988-9B88-ADE85487DAF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {FEEE4A19-03FB-4988-9B88-ADE85487DAF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {FEEE4A19-03FB-4988-9B88-ADE85487DAF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {FEEE4A19-03FB-4988-9B88-ADE85487DAF4}.Release|Any CPU.Build.0 = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------
/Samples/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SharpSMS Samples")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SharpSMS Samples")]
13 | [assembly: AssemblyCopyright("Copyright © 2007")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("a275974e-d8ab-49ff-bfb0-2d7d16dfe834")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SharpSMS/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SharpSMS")]
9 | [assembly: AssemblyDescription(".NET Class for SMS PDU formating")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SharpSMS")]
13 | [assembly: AssemblyCopyright("Copyright © 2007")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("1da885a0-1953-41d7-8e8f-236358f67989")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("2.0.0.0")]
36 | [assembly: AssemblyFileVersion("2.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SharpSMS/Wap/WdpBinaryMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace SharpSMS.Wap
6 | {
7 | ///
8 | /// Class represents Wap Push binary sms
9 | ///
10 | public class WdpBinaryMessage : WdpMessage, ISmsMessageContent
11 | {
12 |
13 | #region Constuctors
14 |
15 | ///
16 | /// Creates instance of the WapPushBinary
17 | ///
18 | public WdpBinaryMessage()
19 | : this(null)
20 | {
21 | }
22 |
23 | ///
24 | /// Creates instance of the WapPushBinary
25 | ///
26 | /// Binary data of the message
27 | public WdpBinaryMessage(byte[] data)
28 | {
29 | this.DestinationPort = Wdp.WAP_PORT_PUSH_SESSION_DESTINATION;
30 | this.SourcePort = Wdp.WAP_PORT_PUSH_SESSION_SOURCE;
31 |
32 | this.DataEncoding = DataEncoding.Data8bit;
33 | this.Data = data;
34 | }
35 | #endregion
36 |
37 | #region Public methods
38 | ///
39 | /// Returns array of bytes representing the SMS
40 | ///
41 | ///
42 | public byte[] GetSMSBytes()
43 | {
44 | return Data;
45 | }
46 | ///
47 | /// Returns array of bytes repesenting the WDP header
48 | ///
49 | ///
50 | public byte[] GetUDHBytes()
51 | {
52 | return this.GetWdpHeader();
53 | }
54 | #endregion
55 |
56 | #region Properties
57 | ///
58 | /// Data encoding of the message
59 | ///
60 | public DataEncoding DataEncoding { get; set; }
61 | ///
62 | /// Content of the message
63 | ///
64 | public byte[] Data { get; set; }
65 | #endregion
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | x64/
20 | *_i.c
21 | *_p.c
22 | *.ilk
23 | *.meta
24 | *.obj
25 | *.pch
26 | *.pdb
27 | *.pgc
28 | *.pgd
29 | *.rsp
30 | *.sbr
31 | *.tlb
32 | *.tli
33 | *.tlh
34 | *.tmp
35 | *.log
36 | *.vspscc
37 | *.vssscc
38 | .builds
39 |
40 | # Visual C++ cache files
41 | ipch/
42 | *.aps
43 | *.ncb
44 | *.opensdf
45 | *.sdf
46 |
47 | # Visual Studio profiler
48 | *.psess
49 | *.vsp
50 | *.vspx
51 |
52 | # Guidance Automation Toolkit
53 | *.gpState
54 |
55 | # ReSharper is a .NET coding add-in
56 | _ReSharper*
57 |
58 | # NCrunch
59 | *.ncrunch*
60 | .*crunch*.local.xml
61 |
62 | # Installshield output folder
63 | [Ee]xpress
64 |
65 | # DocProject is a documentation generator add-in
66 | DocProject/buildhelp/
67 | DocProject/Help/*.HxT
68 | DocProject/Help/*.HxC
69 | DocProject/Help/*.hhc
70 | DocProject/Help/*.hhk
71 | DocProject/Help/*.hhp
72 | DocProject/Help/Html2
73 | DocProject/Help/html
74 |
75 | # Click-Once directory
76 | publish
77 |
78 | # Publish Web Output
79 | *.Publish.xml
80 |
81 | # NuGet Packages Directory
82 | packages
83 |
84 | # Windows Azure Build Output
85 | csx
86 | *.build.csdef
87 |
88 | # Windows Store app package directory
89 | AppPackages/
90 |
91 | # Others
92 | [Bb]in
93 | [Oo]bj
94 | sql
95 | TestResults
96 | [Tt]est[Rr]esult*
97 | *.Cache
98 | ClientBin
99 | [Ss]tyle[Cc]op.*
100 | ~$*
101 | *.dbmdl
102 | Generated_Code #added for RIA/Silverlight projects
103 |
104 | # Backup & report files from converting an old project file to a newer
105 | # Visual Studio version. Backup files are not needed, because we have git ;-)
106 | _UpgradeReport_Files/
107 | Backup*/
108 | UpgradeLog*.XML
109 |
--------------------------------------------------------------------------------
/SharpSMS/ShortMessageType.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Represents message type field for Protocol Identifier
5 | ///
6 | public enum ShortMessageType : byte
7 | {
8 | ///
9 | /// A short message type 0 indicates that the ME must acknowledge receipt of the short message but may discard its contents.
10 | ///
11 | ShortMessageType0 = 0x00,
12 | ///
13 | /// Replace Short Message Type 1
14 | ///
15 | ReplaceMessageType1 = 0x01,
16 | ///
17 | /// Replace Short Message Type 2
18 | ///
19 | ReplaceMessageType2 = 0x02,
20 | ///
21 | /// Replace Short Message Type 3
22 | ///
23 | ReplaceMessageType3 = 0x03,
24 | ///
25 | /// Replace Short Message Type 4
26 | ///
27 | ReplaceMessageType4 = 0x04,
28 | ///
29 | /// Replace Short Message Type 5
30 | ///
31 | ReplaceMessageType5 = 0x05,
32 | ///
33 | /// Replace Short Message Type 6
34 | ///
35 | ReplaceMessageType6 = 0x06,
36 | ///
37 | /// Replace Short Message Type 7
38 | ///
39 | ReplaceMessageType7 = 0x07,
40 | ///
41 | /// Inicates to the MS to inform the user that a call can be established to the address specified within the TP-OA
42 | ///
43 | ReturnCall = 0x1F,
44 | ///
45 | /// ME Data download is facility whereby the ME shall process the short message in its entirety including all SMS elements contained in the SMS deliver to the ME
46 | ///
47 | MEDataDownload = 0x3D,
48 | ///
49 | /// The ME De-personalization Short Message is an ME-specific message which instructs the ME to de-personalities the ME
50 | ///
51 | MEDePersonlaization = 0x3E,
52 | ///
53 | /// SIM Data download is a facility whereby the ME must pass the short message in its entirety including all SMS elements
54 | ///
55 | SIMDataDownload = 0x3F,
56 | }
57 | }
--------------------------------------------------------------------------------
/SharpSMS/Wap/WdpMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS.Wap
7 | {
8 | ///
9 | /// Represents WDP Message
10 | ///
11 | public abstract class WdpMessage
12 | {
13 | ///
14 | /// Source port of the message
15 | ///
16 | public int SourcePort { get; set; }
17 | ///
18 | /// Destination port of the message
19 | ///
20 | public int DestinationPort { get; set; }
21 |
22 | ///
23 | /// Generates the WDP (Wireless Datagram Protocol) or UDH (User Data Header) for the
24 | /// SMS message. In the case comprising the Application Port information element
25 | /// indicating to the handset which application to start on receipt of the message
26 | ///
27 | /// byte array comprising the header
28 | public byte[] GetWdpHeader()
29 | {
30 | MemoryStream stream = new MemoryStream();
31 |
32 | stream.WriteByte(Wdp.INFORMATIONELEMENT_IDENTIFIER_APPLICATIONPORT);
33 |
34 | byte[] destPort = ToBigEndian(DestinationPort, 2);
35 | byte[] sourcePort = ToBigEndian(SourcePort, 2);
36 |
37 | // Length of port information = 2*16 bit numbers = 4 bytes
38 | stream.WriteByte((byte)(destPort.Length + sourcePort.Length));
39 | stream.Write(destPort, 0, destPort.Length);
40 | stream.Write(sourcePort, 0, sourcePort.Length);
41 |
42 | MemoryStream headerStream = new MemoryStream();
43 |
44 | stream.WriteTo(headerStream);
45 | return headerStream.ToArray();
46 | }
47 |
48 | ///
49 | /// Converts number into BigEndian array of bytes
50 | ///
51 | /// Number
52 | /// Number of bytes to return
53 | private byte[] ToBigEndian(long number, byte byteLen)
54 | {
55 | byte[] outputArray = new byte[byteLen];
56 |
57 | outputArray[byteLen - 1] = (byte)number;
58 | for (int i = byteLen - 2; i >= 0; i--)
59 | outputArray[i] = (byte)(number >> (8 * (i + 1)));
60 |
61 | return outputArray;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/SharpSMS/TelematicDevice.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Represents Telematic device
5 | ///
6 | public enum TelematicDevice : byte
7 | {
8 | ///
9 | /// implicit - device type is specific or can be concluded on the basis of the address
10 | ///
11 | Implicit = 0x00,
12 | ///
13 | /// Telex (or teletex reduced to telex format)
14 | ///
15 | Telex = 0x01,
16 | ///
17 | /// Group 3 telefax
18 | ///
19 | Group3Telefax = 0x02,
20 | ///
21 | /// Group 4 telefax
22 | ///
23 | Group4Telefax = 0x03,
24 | ///
25 | /// Voice telephone
26 | ///
27 | VoiceTelephone = 0x04,
28 | ///
29 | /// ERMES
30 | ///
31 | ERMES = 0x05,
32 | ///
33 | /// National Paging System
34 | ///
35 | NationalPagingSystem = 0x06,
36 | ///
37 | /// Videotex (T.100/T.101)
38 | ///
39 | Videotex = 0x07,
40 | ///
41 | /// Teletex
42 | ///
43 | Teletex = 0x08,
44 | ///
45 | /// Teletex, in PSPDN
46 | ///
47 | TeletexPSDN = 0x09,
48 | ///
49 | /// Teletex, in CSPDN
50 | ///
51 | TeletexCSPDN =0x0A,
52 | ///
53 | /// Teletex, in analog PSTN
54 | ///
55 | TeletexPSTN = 0x0B,
56 | ///
57 | /// Teletex, in digital ISDN
58 | ///
59 | TeletexISDN = 0X0C,
60 | ///
61 | /// UCI (Universal Computer Interface, ETSI DE/PS 3 01-3)
62 | ///
63 | UCI = 0x0D,
64 | ///
65 | /// S message handling facility
66 | ///
67 | MessageHandlingFacility = 0x10,
68 | ///
69 | /// Sny public X.400-based message handling system
70 | ///
71 | X400 = 0x11,
72 | ///
73 | /// Internet Electronic Mail
74 | ///
75 | Email = 0x12,
76 | ///
77 | /// A GSM mobile station
78 | ///
79 | GSMMobileStation = 0x1F
80 | }
81 | }
--------------------------------------------------------------------------------
/SharpSMS/TextMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace SharpSMS
5 | {
6 | ///
7 | /// Class represent standard plain text message
8 | ///
9 | public class TextMessage : ISmsMessageContent
10 | {
11 | #region Constuctor
12 | ///
13 | /// Creates new plain text message
14 | ///
15 | public TextMessage()
16 | {
17 | this.DataEncoding = DataEncoding.Default7bit;
18 | this.Text = string.Empty;
19 | }
20 | #endregion
21 |
22 | #region Override Methods
23 | ///
24 | /// Method returns byte array suitable to be send in SMS
25 | ///
26 | /// byte array
27 | public byte[] GetSMSBytes()
28 | {
29 | byte[] text = GetDataBytes();
30 | return text;
31 | }
32 |
33 | ///
34 | /// Method returns user header bytes. In plain text message it's always empty
35 | ///
36 | ///
37 | public byte[] GetUDHBytes()
38 | {
39 | return new byte[]{};
40 | }
41 | #endregion
42 |
43 | #region Private Methods
44 |
45 | ///
46 | /// Returns message text encoded in DataEncoding
47 | ///
48 | /// byte array
49 | protected byte[] GetDataBytes()
50 | {
51 | byte[] data = new byte[] {};
52 |
53 | switch (DataEncoding)
54 | {
55 | case DataEncoding.Default7bit:
56 | data = Encoding.ASCII.GetBytes(Text);
57 | break;
58 | case DataEncoding.Data8bit:
59 | data = Encoding.GetEncoding("iso-8859-1").GetBytes(Text);
60 | break;
61 | case DataEncoding.UCS2_16bit:
62 | data = Encoding.BigEndianUnicode.GetBytes(Text);
63 | break;
64 | }
65 |
66 | return data;
67 | }
68 | #endregion
69 |
70 | #region Properties
71 |
72 | ///
73 | /// Text of the message
74 | ///
75 | public string Text { get; set; }
76 |
77 | ///
78 | /// Encoding of the data (7-bit, 8-bit, 16-bit)
79 | ///
80 | public DataEncoding DataEncoding { get; set; }
81 | #endregion
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Samples/Samples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {FEEE4A19-03FB-4988-9B88-ADE85487DAF4}
8 | Exe
9 | Properties
10 | Samples
11 | Samples
12 | v2.0
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {b5c82198-500a-4e4b-a3bf-6015745d3cb0}
47 | SharpSMS
48 |
49 |
50 |
51 |
58 |
--------------------------------------------------------------------------------
/Samples/Program.cs:
--------------------------------------------------------------------------------
1 | using SharpSMS;
2 | using System;
3 |
4 | namespace Samples
5 | {
6 | class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | Modem modem = new Modem("COM3", 115200);
11 |
12 | while (true)
13 | {
14 | PrintMenu();
15 | ConsoleKeyInfo key = Console.ReadKey(true);
16 | SMSSubmit sms = new SMSSubmit();
17 |
18 | switch (key.KeyChar)
19 | {
20 | case '1':
21 | sms = SampleMessage.PlainText();
22 | break;
23 | case '2':
24 | sms = SampleMessage.FlashMessage();
25 | break;
26 | case '3':
27 | sms = SampleMessage.ReplacebleMessage("This message will be replaced: FOO");
28 | break;
29 | case '4':
30 | sms = SampleMessage.ReplacebleMessage("This is replacement message: BAR");
31 | break;
32 | case '5':
33 | sms = SampleMessage.ActivateVoicemailIndication();
34 | break;
35 | case '6':
36 | sms = SampleMessage.DeactivateVoicemailIndication();
37 | break;
38 | case '7':
39 | sms = SampleMessage.ServiceIndicationMessage();
40 | break;
41 | case '8':
42 | sms = SampleMessage.ServiceLoadingMessage();
43 | break;
44 | case '9':
45 | sms = SampleMessage.WapPushConfiguration();
46 | break;
47 | case '0':
48 | return;
49 | }
50 |
51 | Console.Write("\nEnter phone number: ");
52 | sms.PhoneNumber = Console.ReadLine();
53 |
54 | modem.SendSMS(sms);
55 | }
56 | }
57 |
58 | static void PrintMenu()
59 | {
60 | Console.WriteLine("[1] Send \"Hello world\" message");
61 | Console.WriteLine("[2] Send flash message");
62 | Console.WriteLine("[3] Send replacable message");
63 | Console.WriteLine("[4] Send replacement message");
64 | Console.WriteLine("[5] Activate Voicemail indication");
65 | Console.WriteLine("[6] Deactivate Voicemail indication");
66 | Console.WriteLine("[7] Send SI wap push message");
67 | Console.WriteLine("[8] Send SL wap push message");
68 | Console.WriteLine("[9] Send Configuration wap push message");
69 | Console.WriteLine("[0] Exit");
70 |
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/SharpSMS/ProtocoldentifierBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Class helps to assemble the TP-PID Protocol Identifier value
5 | ///
6 | public class ProtocoldentifierBuilder
7 | {
8 | #region Constants
9 | ///
10 | /// Bits 7, 6, 5 to identify SME to SME message
11 | ///
12 | public const byte TP_PID_SME_TO_SME = 0x00;
13 | ///
14 | /// Bits 7, 6, 5 to identify Telematic device message
15 | ///
16 | public const byte TP_PID_TELEMATIC_DEVICE = 0x20;
17 | ///
18 | /// Bits 7, 6 to identify Short Message
19 | ///
20 | public const byte TP_PID_MESSAGE_TYPE = 0x40;
21 | ///
22 | /// Bits 7, 6 to identify SC specific protocol
23 | ///
24 | public const byte TP_PID_SC_SPECIFIC = 0x0C;
25 | #endregion
26 |
27 | #region Constructors
28 | ///
29 | /// Creates new TP-PID with value 0x00
30 | ///
31 | public ProtocoldentifierBuilder()
32 | {
33 | PTBits = 0x00;
34 | }
35 |
36 | ///
37 | /// Creates new TP-PID for given message type
38 | ///
39 | /// Message type
40 | public ProtocoldentifierBuilder(ShortMessageType messageType)
41 | {
42 | SetMessageType(messageType);
43 | }
44 | #endregion
45 |
46 | #region Public Methods
47 | ///
48 | /// Set message type for TP-PID
49 | ///
50 | ///
51 | public void SetMessageType(ShortMessageType messageType)
52 | {
53 | PTBits = (byte)(TP_PID_MESSAGE_TYPE | (byte)messageType);
54 | }
55 |
56 | ///
57 | /// Set telematic device for TP-PID
58 | ///
59 | ///
60 | public void SetTelematicDevice(TelematicDevice device)
61 | {
62 | PTBits = (byte)(TP_PID_TELEMATIC_DEVICE | (byte)device);
63 | }
64 |
65 | ///
66 | /// Set protocol value for SME to SME communication
67 | ///
68 | ///
69 | public void SetSMEtoSME(byte protocolBits)
70 | {
71 | protocolBits <<= 2;
72 | PTBits = (byte)(TP_PID_SME_TO_SME | (protocolBits >> 2));
73 | }
74 |
75 | ///
76 | /// Set protocol value for SC communication
77 | ///
78 | /// SC specific bits
79 | public void SetSCSpecific(byte protocolBits)
80 | {
81 | protocolBits <<= 2;
82 | PTBits = (byte)(TP_PID_SC_SPECIFIC | (protocolBits >> 2));
83 | }
84 |
85 | ///
86 | /// Returns value that can be used as Protocol Identifier for message
87 | ///
88 | ///
89 | public byte ToByte()
90 | {
91 | return PTBits;
92 | }
93 | #endregion
94 |
95 | #region Fields
96 | ///
97 | /// Property to store bits fo the TP-PID value
98 | ///
99 | private byte PTBits { get; set; }
100 | #endregion
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/SharpSMS/SharpSMS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {B5C82198-500A-4E4B-A3BF-6015745D3CB0}
9 | Library
10 | Properties
11 | SharpSMS
12 | SharpSMS
13 | v2.0
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 | MinimumRecommendedRules.ruleset
30 | true
31 | bin\Debug\SharpSMS.XML
32 |
33 |
34 | pdbonly
35 | true
36 | bin\Release\
37 | TRACE
38 | prompt
39 | 4
40 | AllRules.ruleset
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
80 |
--------------------------------------------------------------------------------
/Samples/SampleMessage.cs:
--------------------------------------------------------------------------------
1 | using SharpSMS;
2 | using SharpSMS.Wap;
3 | using SharpSMS.Wbxml;
4 | using System;
5 | using System.Text;
6 |
7 | namespace Samples
8 | {
9 | class SampleMessage
10 | {
11 | public static SMSSubmit PlainText()
12 | {
13 | TextMessage textMessage = new TextMessage();
14 | textMessage.DataEncoding = DataEncoding.Default7bit;
15 | textMessage.Text = "Hello World from SharpSMS!";
16 |
17 | SMSSubmit sms = new SMSSubmit();
18 | sms.MessageToSend = textMessage;
19 |
20 | // SMS center will retry the delivery for 5 days
21 | sms.ValidityPeriod = new TimeSpan(5, 0, 0, 0);
22 |
23 | return sms;
24 | }
25 |
26 | public static SMSSubmit FlashMessage()
27 | {
28 | TextMessage textMessage = new TextMessage();
29 | textMessage.DataEncoding = DataEncoding.Default7bit;
30 | textMessage.Text = "Flash message from SharpSMS!";
31 |
32 | SMSSubmit sms = new SMSSubmit();
33 | sms.MessageToSend = textMessage;
34 | sms.Indication.Class = MessageClass.ImmediateDisplay;
35 |
36 | return sms;
37 | }
38 |
39 | public static SMSSubmit ReplacebleMessage(string text)
40 | {
41 | TextMessage textMessage = new TextMessage();
42 | textMessage.Text = text;
43 |
44 | SMSSubmit sms = new SMSSubmit();
45 | sms.MessageToSend = textMessage;
46 |
47 | sms.ProtocolIdentifier = new ProtocoldentifierBuilder(ShortMessageType.ReplaceMessageType1).ToByte();
48 |
49 | return sms;
50 | }
51 |
52 | public static SMSSubmit ActivateVoicemailIndication()
53 | {
54 | TextMessage textMessage = new TextMessage();
55 | textMessage.Text = "You have a voicemail.";
56 |
57 | SMSSubmit sms = new SMSSubmit();
58 | sms.MessageToSend = textMessage;
59 |
60 | sms.Indication.Operation = MessageIndicationOperation.Discard;
61 | sms.Indication.Type = IndicationType.Voicemail;
62 | sms.Indication.IsActive = true;
63 |
64 | return sms;
65 | }
66 |
67 | public static SMSSubmit DeactivateVoicemailIndication()
68 | {
69 | SMSSubmit sms = new SMSSubmit(new TextMessage());
70 |
71 | sms.Indication.Operation = MessageIndicationOperation.Discard;
72 | sms.Indication.Type = IndicationType.Voicemail;
73 | sms.Indication.IsActive = false;
74 |
75 | return sms;
76 | }
77 |
78 | public static SMSSubmit ServiceIndicationMessage()
79 | {
80 | ServiceIndication si = new ServiceIndication();
81 | si.Action = ServiceIndicationAction.Signal_medium;
82 | si.Text = "Service indication from SharpSMS";
83 | si.Href = "https://github.com/pbansky/SharpSMS";
84 | si.Expires = DateTime.Now.AddDays(3);
85 |
86 | WapPushMessage wapPushMessage = new WapPushMessage(si);
87 | wapPushMessage.XWapInitiatorURI = "SharpSMS";
88 |
89 | return new SMSSubmit(wapPushMessage);
90 | }
91 |
92 | public static SMSSubmit ServiceLoadingMessage()
93 | {
94 | ServiceLoading sl = new ServiceLoading();
95 | sl.Action = ServiceLoadingAction.Execute_high;
96 | // This is a cab file with Total Commander for Windows Mobile 5/6/6.5
97 | sl.Href = "http://ghisler.fileburst.com/ce/tcmdphone.cab";
98 |
99 | WapPushMessage wapPushMessage = new WapPushMessage(sl);
100 | wapPushMessage.XWapInitiatorURI = "SharpSMS";
101 | return new SMSSubmit(wapPushMessage);
102 | }
103 |
104 | public static SMSSubmit WapPushConfiguration()
105 | {
106 | // This is a configuration XML for Windows Mobile Internet Explorer Favorites
107 | string configXML = @"";
108 |
109 | WapPushMessage wapPushMessage = new WapPushMessage();
110 | wapPushMessage.XWapInitiatorURI = "SharpSMS";
111 |
112 | wapPushMessage.ContentType = "text/vnd.wap.connectivity-xml";
113 | wapPushMessage.Data = Encoding.UTF8.GetBytes(configXML);
114 |
115 | wapPushMessage.Security = Wsp.SecurityMethod.USERPIN;
116 | wapPushMessage.UserPin = "1234";
117 |
118 | return new SMSSubmit(wapPushMessage);
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SharpSMS
2 | ========
3 |
4 | SharpSMS is a library to create SMS messages in PDU format. It provides full control over the SMS including the UDH header, it supports WAP Push messages, Service Indication and Service Location. Messages can be signed for authentication with IMSI or user PIN.
5 |
6 |
7 | Creating simple text message
8 | ----------------------------
9 |
10 | Following code will create plain text message with content *Hello World from SharpSMS!* to be delivered to phone number *+1 (234) 456 7890*
11 |
12 | TextMessage textMessage = new TextMessage();
13 | textMessage.Text = "Hello World from SharpSMS!";
14 |
15 | SMSSubmit sms = new SMSSubmit();
16 | sms.PhoneNumber = "+1234567890";
17 | sms.MessageToSend = textMessage;
18 |
19 | List messageList = sms.GetPDUList();
20 |
21 | Now, the **messageList** contains PDU formatted message(s) that are ready to be send over GSM modem or internet gateway.
22 |
23 |
24 | Tweaking SMS header
25 | -------------------
26 |
27 | SMS message have several header fields defined according to GSM 03.40 specification. These fields are exposed as properties of **SMSSubmit** class:
28 |
29 | Indication
30 | MessageReference
31 | PhoneNumber
32 | ProtocolIdentifier
33 | RequestDeliveryConfirmation
34 | ValidityPeriod
35 |
36 | ### Flash Message
37 |
38 | Flash message refers to message that is displayed on the phone screen but not stored. This type of message is usually sent by GSM operator to display prepaid balance or confirm message delivery. To create a flash message **MessageClass** must be set. Following code demonstrates how to create flash message:
39 |
40 | TextMessage textMessage = new TextMessage();
41 | textMessage.DataEncoding = DataEncoding.Default7bit;
42 | textMessage.Text = "Flash message from SharpSMS!";
43 |
44 | SMSSubmit sms = new SMSSubmit();
45 | sms.PhoneNumber = "+1234567890";
46 | sms.MessageToSend = textMessage;
47 | sms.Indication.Class = MessageClass.ImmediateDisplay;
48 |
49 |
50 | ### Voicemail Indication
51 |
52 | GSM 03.40 specification defines several *Indication types*, for example voicemail, email etc. For each indication can be defined *Operation*, which determines how the indication will be treated when received by the phone. Following code creates SMS message that will make the **voicemail** icon appear on the phone:
53 |
54 | TextMessage textMessage = new TextMessage();
55 | textMessage.Text = "You have a voicemail.";
56 |
57 | SMSSubmit sms = new SMSSubmit();
58 | sms.PhoneNumber = "+1234567890";
59 | sms.MessageToSend = textMessage;
60 |
61 | sms.Indication.Operation = MessageIndicationOperation.Discard;
62 | sms.Indication.Type = IndicationType.Voicemail;
63 | sms.Indication.IsActive = true;
64 |
65 | Following code will remove the **voicemail** icon from the phone status bar:
66 |
67 | TextMessage textMessage = new TextMessage();
68 |
69 | SMSSubmit sms = new SMSSubmit();
70 | sms.PhoneNumber = "+1234567890";
71 | sms.MessageToSend = textMessage;
72 |
73 | sms.Indication.Operation = MessageIndicationOperation.Discard;
74 | sms.Indication.Type = IndicationType.Voicemail;
75 | sms.Indication.IsActive = false;
76 |
77 |
78 | User Data Header and Wap Push
79 | -----------------------------
80 |
81 | SMS message can contain user data header, which extends possibilities to address specific functions of the phone; including ring tones, logos or configurations delivered using SMS message. This functionality is called Over The Air Provisioning (OTA). Modern smartphones like Android, Windows Phone or iPhone do not support this type of messages anymore.
82 | However, some Android devices are equipped with push router, that can process OTA provisioning messages.
83 | Following code will create Wap Push message with configuration for Windows Mobile. Message will be signed with user PIN, so user has to enter the PIN in order for message to be processed:
84 |
85 | string configXML = @"";
86 |
87 | WapPushMessage wapPushMessage = new WapPushMessage();
88 | wapPushMessage.XWapInitiatorURI = "SharpSMS";
89 |
90 | wapPushMessage.ContentType = "text/vnd.wap.connectivity-xml";
91 | wapPushMessage.Data = Encoding.UTF8.GetBytes(configXML);
92 |
93 | wapPushMessage.Security = Wsp.SecurityMethod.USERPIN;
94 | wapPushMessage.UserPin = "1234";
95 |
96 | SMSSubmit sms = new SMSSubmit();
97 | sms.PhoneNumber = "+1234567890";
98 | sms.MessageToSend = wapPushMessage
99 |
100 |
101 | Sending the message
102 | -------------------
103 |
104 | PDU formatted message(s) obtained using **SMSSubmit.GetPDUList()** method can be send via GSM modem using **AT+CMGS** command. This is demonstrated in sample codes. Internet gateways can be used as an alternative to modem.
105 |
--------------------------------------------------------------------------------
/SharpSMS/Wbxml/WBXMLDocument.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS.Wbxml
7 | {
8 | ///
9 | /// Abstract class for creating messages tokenized into WBXML document
10 | ///
11 | public abstract class WBXMLDocument
12 | {
13 | #region Constants
14 | ///
15 | /// Binary representation of NULL
16 | ///
17 | public const byte NULL = 0x00;
18 |
19 | ///
20 | /// Binary representation of v1.1
21 | ///
22 | public const byte VERSION_1_1 = 0x01;
23 | ///
24 | /// Binary representation of v1.2
25 | ///
26 | public const byte VERSION_1_2 = 0x02;
27 | ///
28 | /// Binary representation of UTF-8 encoding
29 | ///
30 | public const byte CHARSET_UTF_8 = 0x6A;
31 | ///
32 | /// Binary representation of token end
33 | ///
34 | public const byte TAGTOKEN_END = 0x01;
35 | ///
36 | /// Binary representation of inline string identifier
37 | ///
38 | public const byte TOKEN_INLINE_STRING_FOLLOWS = 0x03;
39 | ///
40 | /// Binary representation of quoted data
41 | ///
42 | public const byte TOKEN_OPAQUEDATA_FOLLOWS = 0xC3;
43 | #endregion
44 |
45 | #region Properties
46 | ///
47 | /// MIME Content type of the message
48 | ///
49 | public string ContentType { get; set; }
50 | #endregion
51 |
52 | #region Abstract Method
53 |
54 | ///
55 | /// Method return tokenized XML
56 | ///
57 | /// byte of array
58 | public abstract byte[] GetWBXMLBytes();
59 |
60 | #endregion
61 |
62 | #region Public Methods
63 |
64 | ///
65 | /// Methods returns tokenized tag according to conditions
66 | ///
67 | /// original value of the tokenized-tag
68 | ///
69 | ///
70 | /// Modified value of the tokenized tag
71 | public byte SetTagTokenIndications(byte token, bool hasAttributes, bool hasContent)
72 | {
73 | if (hasAttributes)
74 | token |= 0x80;
75 | if (hasContent)
76 | token |= 0x40;
77 |
78 | return token;
79 | }
80 |
81 | ///
82 | /// Methods writes text into stream, tokenized to be used in the WBXML document
83 | ///
84 | /// Output Stream
85 | /// Text tobe tokenized
86 | protected void WriteInlineString(MemoryStream stream, string text)
87 | {
88 | stream.WriteByte(WBXMLDocument.TOKEN_INLINE_STRING_FOLLOWS);
89 |
90 | byte[] bytes = Encoding.UTF8.GetBytes(text);
91 | stream.Write(bytes, 0, bytes.Length);
92 |
93 | stream.WriteByte(WBXMLDocument.NULL); // end of the string
94 | }
95 |
96 | ///
97 | /// Method writes date into stream, tokenized to be used in the WBXML document
98 | ///
99 | ///
100 | ///
101 | protected void WriteDate(MemoryStream stream, DateTime date)
102 | {
103 | byte[] buffer = new byte[7];
104 |
105 | buffer[0] = Convert.ToByte(Convert.ToString(date.Year / 100), 16);
106 | buffer[1] = Convert.ToByte(Convert.ToString(date.Year % 100), 16);
107 | buffer[2] = Convert.ToByte(Convert.ToString(date.Month), 16);
108 | buffer[3] = Convert.ToByte(Convert.ToString(date.Day), 16);
109 |
110 | int dateLength = 4;
111 |
112 | if (date.Hour > 0)
113 | {
114 | buffer[4] = Convert.ToByte(Convert.ToString(date.Hour), 16);
115 | dateLength = 5;
116 | }
117 |
118 | if (date.Minute > 0)
119 | {
120 | buffer[5] = Convert.ToByte(Convert.ToString(date.Minute), 16);
121 | dateLength = 6;
122 | }
123 |
124 | if (date.Second > 0)
125 | {
126 | buffer[6] = Convert.ToByte(Convert.ToString(date.Second), 16);
127 | dateLength = 7;
128 | }
129 |
130 | // write to stream
131 | stream.WriteByte(WBXMLDocument.TOKEN_OPAQUEDATA_FOLLOWS);
132 | stream.WriteByte((byte)dateLength);
133 | stream.Write(buffer, 0, dateLength);
134 | }
135 |
136 | #endregion
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Samples/Modem.cs:
--------------------------------------------------------------------------------
1 | using SharpSMS;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO.Ports;
5 | using System.Threading;
6 |
7 | namespace Samples
8 | {
9 | class Modem
10 | {
11 | public Modem(string serialPortName, int baudRate)
12 | {
13 | commPort = new SerialPort(serialPortName, baudRate);
14 | }
15 |
16 | ///
17 | /// Send given message to the serial port and initialize modem
18 | ///
19 | ///
20 | public void SendSMS(SMSSubmit sms)
21 | {
22 | List messageList = sms.GetPDUList();
23 |
24 | if (!InitModem())
25 | Console.WriteLine("Error: No response from modem.");
26 |
27 | foreach (byte[] messagePart in messageList)
28 | {
29 | SendMessageToModem(messagePart);
30 | Thread.Sleep(500);
31 | Console.WriteLine("Message sent to modem.");
32 | }
33 | }
34 |
35 | ///
36 | /// Initialize modem
37 | ///
38 | /// True if modem was successfuly initialized
39 | bool InitModem()
40 | {
41 | try
42 | {
43 | if (!commPort.IsOpen)
44 | {
45 | WriteModemLog("Initializing modem....\r\n");
46 | //commPort.Handshake = Handshake.RequestToSend;
47 | commPort.WriteBufferSize = 2048;
48 | commPort.ReadTimeout = 5000;
49 | commPort.ReadTimeout = 5000;
50 | commPort.Open();
51 | commPort.NewLine = "\r";
52 |
53 | string dataRead = string.Empty;
54 | int initLoop = 0;
55 | while (initLoop < 15)
56 | {
57 | commPort.WriteLine("AT");
58 | commPort.WriteLine("AT");
59 | commPort.WriteLine("AT");
60 | dataRead = ReadFromPort(commPort, 1);
61 |
62 | if (dataRead.Contains("OK"))
63 | break;
64 | else
65 | initLoop++;
66 | }
67 | commPort.WriteLine("ATZ");
68 | dataRead = ReadFromPort(commPort, 1);
69 |
70 | if (dataRead.Contains("OK"))
71 | return true;
72 | else
73 | return false;
74 | }
75 | }
76 | catch
77 | {
78 | return false;
79 | }
80 |
81 | return true;
82 | }
83 |
84 | ///
85 | /// Send message bytes to serial port modem
86 | ///
87 | ///
88 | ///
89 | bool SendMessageToModem(byte[] messageBytes)
90 | {
91 | bool retValue;
92 | if (commPort != null && commPort.IsOpen && messageBytes.Length > 0)
93 | {
94 | int messageLength = messageBytes.Length - 1;
95 | string message = BytesToHexString(messageBytes);
96 | string dataRead = string.Empty;
97 | commPort.WriteLine(string.Format("AT+CMGS={0}", messageLength));
98 | Thread.Sleep(250);
99 |
100 | dataRead = ReadFromPort(commPort, 1);
101 |
102 | if (dataRead.Contains(">"))
103 | {
104 | commPort.Write(message);
105 | byte[] escChar = new byte[1] { 26 };
106 | commPort.Write(escChar, 0, escChar.Length);
107 | Thread.Sleep(250);
108 |
109 | dataRead = "0000";
110 | while (!(dataRead.Contains("OK") || dataRead.Contains("ERROR") || dataRead == string.Empty))
111 | dataRead = ReadFromPort(commPort, 5);
112 |
113 | retValue = true;
114 | }
115 | else
116 | retValue = false;
117 | }
118 | else
119 | retValue = false;
120 |
121 | return retValue;
122 | }
123 |
124 | ///
125 | /// Reads string from given serial port
126 | ///
127 | /// SerialPort to read
128 | /// Time out for reading operations in seconds
129 | ///
130 | string ReadFromPort(SerialPort port, int secondsTimeOut)
131 | {
132 | int timeOut = 0;
133 | string dataRead = string.Empty;
134 |
135 | while (timeOut < secondsTimeOut * 10)
136 | {
137 | if (port.BytesToRead > 0)
138 | {
139 | dataRead = port.ReadExisting();
140 | break;
141 | }
142 | timeOut++;
143 | Thread.Sleep(100);
144 | }
145 |
146 | WriteModemLog(dataRead);
147 | return dataRead;
148 | }
149 |
150 | ///
151 | /// Converts array of byte into string of hexa values
152 | ///
153 | ///
154 | ///
155 | string BytesToHexString(byte[] srcArray)
156 | {
157 | string retString = string.Empty;
158 | foreach (byte b in srcArray)
159 | retString += b.ToString("X2");
160 |
161 | return retString;
162 | }
163 |
164 | ///
165 | /// Writes informations into modem log
166 | ///
167 | ///
168 | private void WriteModemLog(string message)
169 | {
170 | Console.WriteLine("Modem: "+message);
171 | }
172 |
173 | SerialPort commPort;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/SharpSMS/Wbxml/ServiceLoading.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS.Wbxml
7 | {
8 | ///
9 | /// Class represents Service Loading Messages
10 | ///
11 | public class ServiceLoading : WBXMLDocument
12 | {
13 | #region Constants
14 | // ServiceIndication 1.0 Public Identifier
15 | public const byte DOCUMENT_DTD_ServiceIndication = 0x06;
16 |
17 | // Tag Tokens
18 | public const byte TAGTOKEN_sl = 0x05;
19 |
20 | // Attribute Tokens
21 | public const byte ATTRIBUTESTARTTOKEN_action_execute_low = 0x05;
22 | public const byte ATTRIBUTESTARTTOKEN_action_execute_high = 0x06;
23 | public const byte ATTRIBUTESTARTTOKEN_action_cache = 0x07;
24 | public const byte ATTRIBUTESTARTTOKEN_href = 0x08;
25 | public const byte ATTRIBUTESTARTTOKEN_href_http = 0x09; // http://
26 | public const byte ATTRIBUTESTARTTOKEN_href_http_www = 0x0A; // http://www.
27 | public const byte ATTRIBUTESTARTTOKEN_href_https = 0x0B; // https://
28 | public const byte ATTRIBUTESTARTTOKEN_href_https_www = 0x0C; // https://www.
29 |
30 | // Attribute Value Tokens
31 | public const byte ATTRIBUTEVALUETOKEN_com = 0x85; // .com/
32 | public const byte ATTRIBUTEVALUETOKEN_edu = 0x86; // .edu/
33 | public const byte ATTRIBUTEVALUETOKEN_net = 0x87; // .net/
34 | public const byte ATTRIBUTEVALUETOKEN_org = 0x88; // .org/
35 | #endregion
36 |
37 | #region Properties
38 |
39 | ///
40 | /// Location of the file to download
41 | ///
42 | public string Href { get; set; }
43 |
44 | ///
45 | /// Action to do after download
46 | ///
47 | public ServiceLoadingAction Action { get; set; }
48 | #endregion
49 |
50 | #region Fields
51 | private Dictionary hrefStartTokens;
52 | private Dictionary attributeValueTokens;
53 | #endregion
54 |
55 | #region Constructors
56 |
57 | ///
58 | /// Default constructor
59 | ///
60 | public ServiceLoading()
61 | {
62 | // Set the content type
63 | this.ContentType = "application/vnd.wap.slc";
64 |
65 | // Create token dictonary
66 | hrefStartTokens = new Dictionary();
67 | hrefStartTokens.Add("https://www.", ATTRIBUTESTARTTOKEN_href_https_www);
68 | hrefStartTokens.Add("http://www.", ATTRIBUTESTARTTOKEN_href_http_www);
69 | hrefStartTokens.Add("https://", ATTRIBUTESTARTTOKEN_href_https);
70 | hrefStartTokens.Add("http://", ATTRIBUTESTARTTOKEN_href_http);
71 |
72 | attributeValueTokens = new Dictionary();
73 | attributeValueTokens.Add(".com/", ATTRIBUTEVALUETOKEN_com);
74 | attributeValueTokens.Add(".edu/", ATTRIBUTEVALUETOKEN_edu);
75 | attributeValueTokens.Add(".net/", ATTRIBUTEVALUETOKEN_net);
76 | attributeValueTokens.Add(".org/", ATTRIBUTEVALUETOKEN_org);
77 | }
78 |
79 | ///
80 | /// Creates Service Loading Message
81 | ///
82 | /// Link to the file
83 | /// Action after download
84 | public ServiceLoading(string href, ServiceLoadingAction action) : this()
85 | {
86 | this.Href = href;
87 | this.Action = action;
88 | }
89 |
90 | ///
91 | /// Creates Service Loading Message
92 | ///
93 | /// Link to the file
94 | public ServiceLoading(string href)
95 | : this(href, ServiceLoadingAction.NotSet)
96 | {
97 | }
98 | #endregion
99 |
100 | #region Override Methods
101 |
102 | ///
103 | /// Returns tokenized Service Loading message.
104 | ///
105 | /// byte array
106 | public override byte[] GetWBXMLBytes()
107 | {
108 | MemoryStream wbxmlBytes = new MemoryStream();
109 |
110 | // header
111 | wbxmlBytes.WriteByte(WBXMLDocument.VERSION_1_2);
112 | wbxmlBytes.WriteByte(DOCUMENT_DTD_ServiceIndication);
113 | wbxmlBytes.WriteByte(WBXMLDocument.CHARSET_UTF_8);
114 | wbxmlBytes.WriteByte(WBXMLDocument.NULL); // String table length
115 |
116 | // xml
117 | wbxmlBytes.WriteByte(SetTagTokenIndications(TAGTOKEN_sl, true, false)); // with attributes
118 |
119 | #region Tokenizing of the HREF
120 |
121 | // find the starting attribute token (http:// https:// etc..)
122 | int pos = 0;
123 | byte hrefTagToken = ATTRIBUTESTARTTOKEN_href;
124 | foreach (string startString in hrefStartTokens.Keys)
125 | {
126 | if (this.Href.StartsWith(startString))
127 | {
128 | hrefTagToken = hrefStartTokens[startString];
129 | pos = startString.Length;
130 | break;
131 | }
132 | }
133 |
134 | // find the `A` domain token. (.com .net .org etc..)
135 | int domainPos = -1;
136 | string domain = string.Empty;
137 | foreach (string domainString in attributeValueTokens.Keys)
138 | {
139 | domainPos = this.Href.IndexOf(domainString);
140 | if (domainPos >= 0)
141 | {
142 | domain = domainString;
143 | break;
144 | }
145 | }
146 | #endregion
147 |
148 | // write href url
149 | if (domainPos >= 0) // encode domain
150 | {
151 | wbxmlBytes.WriteByte(hrefTagToken);
152 | WriteInlineString(wbxmlBytes, Href.Substring(pos, domainPos - pos));
153 | wbxmlBytes.WriteByte(attributeValueTokens[domain]);
154 | WriteInlineString(wbxmlBytes, Href.Substring(domainPos + domain.Length));
155 | }
156 | else // no domain to encode
157 | {
158 | wbxmlBytes.WriteByte(hrefTagToken);
159 | WriteInlineString(wbxmlBytes, Href.Substring(pos));
160 | }
161 |
162 | // writes action signal
163 | if (Action != ServiceLoadingAction.NotSet)
164 | wbxmlBytes.WriteByte(GetActionToken(Action));
165 |
166 | // End tag
167 | wbxmlBytes.WriteByte(WBXMLDocument.TAGTOKEN_END);
168 |
169 | byte[] wbxmlBytesArray = wbxmlBytes.ToArray();
170 | wbxmlBytes.Close();
171 |
172 | return wbxmlBytesArray;
173 | }
174 | #endregion
175 |
176 | #region Private Methods
177 |
178 | ///
179 | /// Returns token for give Action
180 | ///
181 | /// Service Loading Action
182 | /// Token
183 | protected byte GetActionToken(ServiceLoadingAction action)
184 | {
185 | byte actionToken;
186 |
187 | switch (action)
188 | {
189 | case ServiceLoadingAction.Execute_high:
190 | actionToken = ATTRIBUTESTARTTOKEN_action_execute_high;
191 | break;
192 | case ServiceLoadingAction.Execute_low:
193 | actionToken = ATTRIBUTESTARTTOKEN_action_execute_low;
194 | break;
195 | default:
196 | actionToken = ATTRIBUTESTARTTOKEN_action_cache;
197 | break;
198 | }
199 |
200 | return actionToken;
201 | }
202 | #endregion
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/SharpSMS/MessageIndication.cs:
--------------------------------------------------------------------------------
1 | namespace SharpSMS
2 | {
3 | ///
4 | /// Class represents Message Indication/Data Coding scheme of the message
5 | ///
6 | public class MessageIndication
7 | {
8 | #region Constants
9 | ///
10 | /// Coding Scheme Bit 4 set; message class is specified by least significant bits 1,0
11 | ///
12 | const byte CODING_SCHEME_MESSAGE_CLASS_SPECIFIED = 0x10;
13 | ///
14 | /// Coding Scheme Bits 3,2; default encoding
15 | ///
16 | const byte CODING_SCHEME_DEFAULT_ENCODING = 0x00;
17 | ///
18 | /// Coding Scheme Bits 3,2; 8-bit encoding
19 | ///
20 | const byte CODING_SCHEME_8BIT_ENCODING = 0x04;
21 | ///
22 | /// Coding Scheme Bits 3,2; UCS2 encoding
23 | ///
24 | const byte CODING_SCHEME_UCS2_ENCODING = 0x08;
25 | ///
26 | /// Coding Scheme Bits 1,0; Message Class - Immediate Display
27 | ///
28 | const byte CODING_SCHEME_MESSAGE_CLASS_IMMEDIATE_DISPLAY = 0x00;
29 | ///
30 | /// Coding Scheme Bits 1,0; Message Class - ME Specific
31 | ///
32 | const byte CODING_SCHEME_MESSAGE_CLASS_MESPECIFIC = 0x01;
33 | ///
34 | /// Coding Scheme Bits 1,0; Message Class - SIM Specific
35 | ///
36 | const byte CODING_SCHEME_MESSAGE_CLASS_SIMSPECIFIC = 0x02;
37 | ///
38 | /// Coding Scheme Bits 1,0; Message Class - TE Specific
39 | ///
40 | const byte CODING_SCHEME_MESSAGE_CLASS_TESPECIFIC = 0x03;
41 | ///
42 | /// Coding Scheme Bits 7..4; Message Indication Discard
43 | ///
44 | const byte CODING_SCHEME_MESSAGE_INDICATION_DISCARD = 0xC0;
45 | ///
46 | /// Coding Scheme Bits 7..4; Message Indication Store - default encoding
47 | ///
48 | const byte CODING_SCHEME_MESSAGE_INDICATION_STORE_DEFAULT_ENCODING = 0xD0;
49 | ///
50 | /// Coding Scheme Bits 7..4; Message Indication Store - UCS2 encoding
51 | ///
52 | const byte CODING_SCHEME_MESSAGE_INDICATION_STORE_UCS2_ENCODING = 0xE0;
53 | ///
54 | /// Coding Scheme Bit 3; Message Indication set to active
55 | ///
56 | const byte CODING_SCHEME_INDICATION_ACTIVE = 0x08;
57 | ///
58 | /// Coding Scheme Bit 1,0; Message Indication - Voicemail
59 | ///
60 | const byte CODING_SCHEME_INDICATION_VOICEMAIL = 0x00;
61 | ///
62 | /// Coding Scheme Bit 1,0; Message Indication - Fax
63 | ///
64 | const byte CODING_SCHEME_INDICATION_FAX = 0x01;
65 | ///
66 | /// Coding Scheme Bit 1,0; Message Indication - Email
67 | ///
68 | const byte CODING_SCHEME_INDICATION_EMAIL = 0x02;
69 | ///
70 | /// Coding Scheme Bit 1,0; Message Indication - Other
71 | ///
72 | const byte CODING_SCHEME_INDICATION_OTHER = 0x03;
73 | #endregion
74 |
75 | ///
76 | /// Creates new message indication with default options
77 | ///
78 | public MessageIndication()
79 | : this(MessageClass.MESpecific)
80 | {
81 | }
82 |
83 | ///
84 | /// Creates new message indication
85 | ///
86 | /// Define if and where message will be stored
87 | public MessageIndication(MessageClass messageClass)
88 | {
89 | this.Class = messageClass;
90 | this.Type = IndicationType.Voicemail;
91 | this.Operation = MessageIndicationOperation.NotSet;
92 | this.IsActive = false;
93 | }
94 |
95 | ///
96 | /// Returns TP-DCS Data coding scheme value that represents the message indication information
97 | ///
98 | /// Data encoding used in the message
99 | ///
100 | public byte ToByte(DataEncoding dataEncoding)
101 | {
102 | byte dataCodingScheme = CODING_SCHEME_MESSAGE_CLASS_SPECIFIED;
103 |
104 | // Set encoding
105 | if (dataEncoding == DataEncoding.Default7bit)
106 | dataCodingScheme |= CODING_SCHEME_DEFAULT_ENCODING;
107 | else if (dataEncoding == DataEncoding.Data8bit)
108 | dataCodingScheme |= CODING_SCHEME_8BIT_ENCODING;
109 | else if (dataEncoding == DataEncoding.UCS2_16bit)
110 | dataCodingScheme |= CODING_SCHEME_UCS2_ENCODING;
111 |
112 | // Set indication
113 | if (this.Class == SharpSMS.MessageClass.ImmediateDisplay)
114 | dataCodingScheme |= CODING_SCHEME_MESSAGE_CLASS_IMMEDIATE_DISPLAY;
115 | else if (this.Class == SharpSMS.MessageClass.MESpecific)
116 | dataCodingScheme |= CODING_SCHEME_MESSAGE_CLASS_MESPECIFIC;
117 | else if (this.Class == SharpSMS.MessageClass.SIMSpecific)
118 | dataCodingScheme |= CODING_SCHEME_MESSAGE_CLASS_SIMSPECIFIC;
119 | else if (this.Class == SharpSMS.MessageClass.TESpecific)
120 | dataCodingScheme |= CODING_SCHEME_MESSAGE_CLASS_TESPECIFIC;
121 |
122 | if (this.Operation != MessageIndicationOperation.NotSet)
123 | dataCodingScheme = GetWaitingIndication(dataEncoding);
124 |
125 | return dataCodingScheme;
126 | }
127 |
128 | ///
129 | /// Returns coding scheme part for MessageWaitingIndication
130 | ///
131 | ///
132 | private byte GetWaitingIndication(DataEncoding dataEncoding)
133 | {
134 | byte result = 0x00;
135 |
136 | // Is it Discard or Store? Store depends on alphabet encoding
137 | if (this.Operation == MessageIndicationOperation.Discard)
138 | result |= CODING_SCHEME_MESSAGE_INDICATION_DISCARD;
139 | else if (this.Operation == MessageIndicationOperation.Store && dataEncoding == DataEncoding.Default7bit)
140 | result |= CODING_SCHEME_MESSAGE_INDICATION_STORE_DEFAULT_ENCODING;
141 | else if (this.Operation == MessageIndicationOperation.Store && dataEncoding == DataEncoding.UCS2_16bit)
142 | result |= CODING_SCHEME_MESSAGE_INDICATION_STORE_UCS2_ENCODING;
143 |
144 | // Activeting or Deactivating Indication
145 | if (this.IsActive)
146 | result |= CODING_SCHEME_INDICATION_ACTIVE;
147 |
148 | switch (this.Type)
149 | {
150 | case IndicationType.Voicemail:
151 | result |= CODING_SCHEME_INDICATION_VOICEMAIL;
152 | break;
153 | case IndicationType.FaxMessage:
154 | result |= CODING_SCHEME_INDICATION_FAX;
155 | break;
156 | case IndicationType.EmailMessage:
157 | result |= CODING_SCHEME_INDICATION_EMAIL;
158 | break;
159 | case IndicationType.OtherMessage:
160 | result |= CODING_SCHEME_INDICATION_OTHER;
161 | break;
162 | }
163 |
164 | return result;
165 | }
166 |
167 | ///
168 | /// Message classs pecifies how message will be treated on recipients device
169 | ///
170 | public MessageClass Class { get; set; }
171 |
172 | ///
173 | /// Type of the Indication (Voicemail, Fax, Email ...)
174 | ///
175 | public IndicationType Type { get; set; }
176 |
177 | ///
178 | /// Defines if indication message will be discarded or stored on the device
179 | ///
180 | public MessageIndicationOperation Operation { get; set; }
181 |
182 | ///
183 | /// True sets indication to active state. False removes the indication
184 | ///
185 | public bool IsActive { get; set; }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/SharpSMS/Wap/WapPushMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 | using System.Security.Cryptography;
6 | using SharpSMS.Wbxml;
7 |
8 | namespace SharpSMS.Wap
9 | {
10 | ///
11 | /// Class representing Wap Push sms
12 | ///
13 | public class WapPushMessage : WdpMessage, ISmsMessageContent
14 | {
15 | #region Constructors
16 |
17 | ///
18 | /// Creates instance of the WapPushMessage. Data will be taken from WBXML document
19 | ///
20 | public WapPushMessage()
21 | {
22 | this.DestinationPort = Wdp.WAP_PORT_PUSH_SESSION_DESTINATION;
23 | this.SourcePort = Wdp.WAP_PORT_PUSH_SESSION_SOURCE;
24 |
25 | this.DataEncoding = DataEncoding.Data8bit;
26 | this.PushFlag = 0x00;
27 | this.Security = Wsp.SecurityMethod.None;
28 | this.ContentType = "application/vnd.wap.connectivity-wbxml";
29 | }
30 |
31 | ///
32 | /// Creates instance of the WapPushMessage
33 | ///
34 | /// Tokenized XML message
35 | public WapPushMessage(WBXMLDocument wbxmlDocument)
36 | : this()
37 | {
38 | this.Data = wbxmlDocument.GetWBXMLBytes();
39 | this.ContentType = wbxmlDocument.ContentType;
40 | }
41 |
42 | ///
43 | /// Creates instance of the WapPushMessage
44 | ///
45 | /// Binary data representing the message
46 | public WapPushMessage(byte[] data)
47 | {
48 | this.Data = data;
49 | }
50 |
51 | ///
52 | /// Creates instance of the WapPushMessage
53 | ///
54 | /// Message content
55 | public WapPushMessage(string message)
56 | {
57 | this.Data = Encoding.UTF8.GetBytes(message);
58 | this.DataEncoding = DataEncoding.Data8bit;
59 | }
60 |
61 | #endregion
62 |
63 | #region Public methods
64 |
65 | ///
66 | /// Returns byte array suitable to be send in sms message. All wraped in the Wap header
67 | ///
68 | /// byte array
69 | public byte[] GetSMSBytes()
70 | {
71 | byte[] headerBuffer = GetWAPWSPHeaderBytes();
72 |
73 | MemoryStream stream = new MemoryStream();
74 | stream.Write(headerBuffer, 0, headerBuffer.Length);
75 | stream.Write(this.Data, 0, this.Data.Length);
76 | return stream.ToArray();
77 | }
78 |
79 | ///
80 | /// Generates the WDP (Wireless Datagram Protocol) or UDH (User Data Header) for the
81 | /// SMS message. In the case comprising the Application Port information element
82 | /// indicating to the handset which application to start on receipt of the message
83 | ///
84 | /// byte array comprising the header
85 | public byte[] GetUDHBytes()
86 | {
87 | return this.GetWdpHeader();
88 | }
89 |
90 | ///
91 | /// Get security MAC for the message
92 | ///
93 | ///
94 | public byte[] GetSecurityKey()
95 | {
96 | byte[] key = new byte[] { 0x00 };
97 |
98 | switch (Security)
99 | {
100 | case Wsp.SecurityMethod.NETWPIN:
101 | key = ImsitoKey(this.NetworkPin);
102 | break;
103 | case Wsp.SecurityMethod.USERPIN:
104 | key = Encoding.ASCII.GetBytes(this.UserPin);
105 | break;
106 | case Wsp.SecurityMethod.USERNETWPIN:
107 | byte[] userPin = Encoding.ASCII.GetBytes(this.UserPin);
108 | byte[] netPin = ImsitoKey(this.NetworkPin);
109 | key = new byte[userPin.Length + netPin.Length];
110 | netPin.CopyTo(key, 0);
111 | userPin.CopyTo(key, netPin.Length);
112 | break;
113 | case Wsp.SecurityMethod.USERPINMAC:
114 | throw new NotSupportedException("USERPINMAC is not supported");
115 | }
116 |
117 | return key;
118 | }
119 | #endregion
120 |
121 | #region Private methods
122 |
123 | /// Generates the WAP and WSP (Wireless Session Protocol) headers with the well known
124 | /// byte values specfic to a ContentType
125 | private byte[] GetWAPWSPHeaderBytes()
126 | {
127 | MemoryStream stream = new MemoryStream();
128 | stream.WriteByte(Wsp.TRANSACTIONID_CONNECTIONLESSWSP);
129 | stream.WriteByte(Wsp.PDUTYPE_PUSH);
130 |
131 | #region WSP Header
132 | MemoryStream headersStream = new MemoryStream();
133 |
134 | #region Security header
135 | MemoryStream securityStream = new MemoryStream();
136 |
137 | // Write content type
138 | Wsp.WriteHeaderContentType(securityStream, this.ContentType);
139 |
140 | // Accept charset header 0x01 | 0x80 = 0x81
141 | // Accept charset value: UTF8 = 0x61 | 0x80 = 0xEA
142 |
143 | // Security method and MAC
144 | if (this.Security != Wsp.SecurityMethod.None)
145 | {
146 | byte[] macKey = ComputeMac(GetSecurityKey(), this.Data);
147 | Wsp.WriteHeaderSecurity(securityStream, this.Security, macKey);
148 | }
149 |
150 | // Security Header Length
151 | Wsp.WriteValueLength(headersStream, securityStream.Length);
152 |
153 | // Write security header to header stream
154 | securityStream.WriteTo(headersStream);
155 | #endregion
156 |
157 | // Push Flag
158 | if (this.PushFlag != 0x00)
159 | Wsp.WriteHeaderPushFlag(headersStream, this.PushFlag);
160 |
161 | // X WAP Initiator URI
162 | if (!string.IsNullOrEmpty(this.XWapInitiatorURI))
163 | Wsp.WriteHeaderXWAPInitiatorURI(headersStream, this.XWapInitiatorURI);
164 |
165 | // X WAP Content URI
166 | if (!string.IsNullOrEmpty(this.XWapContentURI))
167 | Wsp.WriteHeaderXWAPContentURI(headersStream, this.XWapContentURI);
168 |
169 | // X WAP Application ID
170 | if (!string.IsNullOrEmpty(this.XWapApplicationType))
171 | Wsp.WriteHeaderXWAPApplicationID(headersStream, this.XWapApplicationType);
172 |
173 | // Write complete header length
174 | stream.WriteByte((byte)headersStream.Length);
175 | headersStream.WriteTo(stream);
176 | #endregion
177 |
178 | return stream.ToArray();
179 | }
180 |
181 | private byte[] ComputeMac(byte[] key, byte[] message)
182 | {
183 | HMACSHA1 hmac = new HMACSHA1(key, true);
184 | return hmac.ComputeHash(message);
185 | }
186 |
187 | private byte[] ImsitoKey(string imsi)
188 | {
189 | imsi = imsi.Trim();
190 |
191 | if ((imsi.Length % 2) == 1)
192 | {
193 | imsi = "9" + imsi;
194 | }
195 | else
196 | {
197 | imsi = "1" + imsi;
198 | imsi = imsi + "F";
199 | }
200 |
201 | int numDigit = imsi.Length;
202 | string temp;
203 | char c1;
204 | char c2;
205 | byte[] key = new byte[numDigit / 2]; // always even
206 | int t = 0;
207 | for (int i = 0; i < numDigit; i++)
208 | {
209 | c1 = imsi[i];
210 | c2 = imsi[++i];
211 | temp = "" + c2 + c1;
212 |
213 | if (!byte.TryParse(temp, System.Globalization.NumberStyles.HexNumber, null, out key[t]))
214 | {
215 | throw new Exception("No chars in IMSI");
216 | }
217 |
218 | t++;
219 | }
220 |
221 | return key;
222 | }
223 |
224 | #endregion
225 |
226 | #region Properties
227 | ///
228 | /// Encoding of the data (7-bit, 8-bit, 16-bit)
229 | ///
230 | public DataEncoding DataEncoding { get; set; }
231 |
232 | ///
233 | /// MIME Content type of the message
234 | ///
235 | public string ContentType { get; set; }
236 |
237 | ///
238 | /// Data (body) of the message that will be wrapped into the WAP push headers
239 | ///
240 | public byte[] Data { get; set; }
241 |
242 | ///
243 | /// X-Wap-Initiator-URI
244 | ///
245 | public string XWapInitiatorURI { get; set; }
246 |
247 | ///
248 | /// X-Wap-Content-URI
249 | ///
250 | public string XWapContentURI { get; set; }
251 |
252 | ///
253 | /// X-Wap-Application-ID Type
254 | ///
255 | public string XWapApplicationType { get; set; }
256 |
257 | ///
258 | /// Push Flag
259 | ///
260 | public byte PushFlag { get; set; }
261 |
262 | ///
263 | /// Security method
264 | ///
265 | public Wsp.SecurityMethod Security { get; set; }
266 |
267 | ///
268 | /// User shared secret
269 | ///
270 | public string UserPin { get; set; }
271 |
272 | ///
273 | /// Network pin (IMSI) - Subscriber ID
274 | ///
275 | public string NetworkPin { get; set; }
276 |
277 | #endregion
278 | }
279 |
280 | }
281 |
--------------------------------------------------------------------------------
/SharpSMS/Wbxml/ServiceIndication.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS.Wbxml
7 | {
8 | ///
9 | /// Class represents Service Indication Messages
10 | ///
11 | public class ServiceIndication : WBXMLDocument
12 | {
13 | #region Constants
14 | // ServiceIndication 1.0 Public Identifier
15 | public const byte DOCUMENT_DTD_ServiceIndication = 0x05;
16 |
17 | // Tag Tokens
18 | public const byte TAGTOKEN_si = 0x05;
19 | public const byte TAGTOKEN_indication = 0x06;
20 | public const byte TAGTOKEN_info = 0x07;
21 | public const byte TAGTOKEN_item = 0x08;
22 |
23 | // Attribute Tokens
24 | public const byte ATTRIBUTESTARTTOKEN_action_signal_none = 0x05;
25 | public const byte ATTRIBUTESTARTTOKEN_action_signal_low = 0x06;
26 | public const byte ATTRIBUTESTARTTOKEN_action_signal_medium = 0x07;
27 | public const byte ATTRIBUTESTARTTOKEN_action_signal_high = 0x08;
28 | public const byte ATTRIBUTESTARTTOKEN_action_signal_delete = 0x09;
29 | public const byte ATTRIBUTESTARTTOKEN_created = 0x0A;
30 | public const byte ATTRIBUTESTARTTOKEN_href = 0x0B;
31 | public const byte ATTRIBUTESTARTTOKEN_href_http = 0x0C; // http://
32 | public const byte ATTRIBUTESTARTTOKEN_href_http_www = 0x0D; // http://www.
33 | public const byte ATTRIBUTESTARTTOKEN_href_https = 0x0E; // https://
34 | public const byte ATTRIBUTESTARTTOKEN_href_https_www = 0x0F; // https://www.
35 | public const byte ATTRIBUTESTARTTOKEN_si_expires = 0x10;
36 | public const byte ATTRIBUTESTARTTOKEN_si_id = 0x11;
37 | public const byte ATTRIBUTESTARTTOKEN_class = 0x12;
38 |
39 | // Attribute Value Tokens
40 | public const byte ATTRIBUTEVALUETOKEN_com = 0x85; // .com/
41 | public const byte ATTRIBUTEVALUETOKEN_edu = 0x86; // .edu/
42 | public const byte ATTRIBUTEVALUETOKEN_net = 0x87; // .net/
43 | public const byte ATTRIBUTEVALUETOKEN_org = 0x88; // .org/
44 | #endregion
45 |
46 | #region Properties
47 | ///
48 | /// Text of the service Indication Message
49 | ///
50 | public string Text { get; set; }
51 |
52 | ///
53 | /// Href - link in the Service Indication Message
54 | ///
55 | public string Href { get; set; }
56 |
57 | ///
58 | /// Level of notification to the user (optional)
59 | ///
60 | public ServiceIndicationAction Action { get; set; }
61 |
62 | ///
63 | /// Creation DateTime of the SI message (optional)
64 | ///
65 | public DateTime Created {get; set; }
66 |
67 | ///
68 | /// Expiration DateTime of the SI message (optional)
69 | ///
70 | public DateTime Expires { get; set; }
71 |
72 | ///
73 | /// ID of the SI message (optional)
74 | ///
75 | public string Id { get; set; }
76 | #endregion
77 |
78 | #region Fields
79 | private Dictionary hrefStartTokens;
80 | private Dictionary attributeValueTokens;
81 | #endregion
82 |
83 | #region Constructors
84 | ///
85 | /// Default constructor
86 | ///
87 | public ServiceIndication()
88 | {
89 | // Set the content type
90 | this.ContentType = "application/vnd.wap.sic"; // HEADER_CONTENTTYPE_application_vnd_wap_sic;
91 |
92 | // Create token dictonary
93 | hrefStartTokens = new Dictionary();
94 | hrefStartTokens.Add("https://www.", ATTRIBUTESTARTTOKEN_href_https_www);
95 | hrefStartTokens.Add("http://www.", ATTRIBUTESTARTTOKEN_href_http_www);
96 | hrefStartTokens.Add("https://", ATTRIBUTESTARTTOKEN_href_https);
97 | hrefStartTokens.Add("http://", ATTRIBUTESTARTTOKEN_href_http);
98 |
99 | attributeValueTokens = new Dictionary();
100 | attributeValueTokens.Add(".com/", ATTRIBUTEVALUETOKEN_com);
101 | attributeValueTokens.Add(".edu/", ATTRIBUTEVALUETOKEN_edu);
102 | attributeValueTokens.Add(".net/", ATTRIBUTEVALUETOKEN_net);
103 | attributeValueTokens.Add(".org/", ATTRIBUTEVALUETOKEN_org);
104 | }
105 |
106 | ///
107 | /// Creates Service Indication Message
108 | ///
109 | /// Text of the message
110 | /// Link in the message
111 | /// Action to the user
112 | public ServiceIndication(string text, string href, ServiceIndicationAction action) : this()
113 | {
114 | this.Text = text;
115 | this.Href = href;
116 | this.Action = action;
117 | }
118 |
119 | ///
120 | /// Creates Service Indication Message
121 | ///
122 | /// Text of the message
123 | /// Link in the message
124 | public ServiceIndication(string text, string href)
125 | : this(text, href, ServiceIndicationAction.NotSet)
126 | {
127 | }
128 |
129 | #endregion
130 |
131 | #region Override Methods
132 |
133 | ///
134 | /// Returns tokenized Service Indication message.
135 | ///
136 | /// byte array
137 | public override byte[] GetWBXMLBytes()
138 | {
139 | MemoryStream wbxmlBytes = new MemoryStream();
140 |
141 | // header
142 | wbxmlBytes.WriteByte(WBXMLDocument.VERSION_1_2);
143 | wbxmlBytes.WriteByte(DOCUMENT_DTD_ServiceIndication);
144 | wbxmlBytes.WriteByte(WBXMLDocument.CHARSET_UTF_8);
145 | wbxmlBytes.WriteByte(WBXMLDocument.NULL); // String table length
146 |
147 | // xml
148 | wbxmlBytes.WriteByte(SetTagTokenIndications(TAGTOKEN_si, false, true)); // with content
149 | wbxmlBytes.WriteByte(SetTagTokenIndications(TAGTOKEN_indication, true, true)); // with cont. and attr.
150 |
151 | #region Tokenizing of the HREF
152 |
153 | // find the starting attribute token (http:// https:// etc..)
154 | int pos = 0;
155 | byte hrefTagToken = ATTRIBUTESTARTTOKEN_href;
156 | foreach (string startString in hrefStartTokens.Keys)
157 | {
158 | if (this.Href.StartsWith(startString))
159 | {
160 | hrefTagToken = hrefStartTokens[startString];
161 | pos = startString.Length;
162 | break;
163 | }
164 | }
165 |
166 | // find the `A` domain token. (.com .net .org etc..)
167 | int domainPos = -1;
168 | string domain = string.Empty;
169 | foreach (string domainString in attributeValueTokens.Keys)
170 | {
171 | domainPos = this.Href.IndexOf(domainString);
172 | if (domainPos >= 0)
173 | {
174 | domain = domainString;
175 | break;
176 | }
177 | }
178 | #endregion
179 |
180 | // write href url
181 | if (domainPos >= 0) // encode domain
182 | {
183 | wbxmlBytes.WriteByte(hrefTagToken);
184 | WriteInlineString(wbxmlBytes, Href.Substring(pos, domainPos - pos));
185 | wbxmlBytes.WriteByte(attributeValueTokens[domain]);
186 | WriteInlineString(wbxmlBytes, Href.Substring(domainPos + domain.Length));
187 | }
188 | else // no domain to encode
189 | {
190 | wbxmlBytes.WriteByte(hrefTagToken);
191 | WriteInlineString(wbxmlBytes, Href.Substring(pos));
192 | }
193 |
194 | // writes action signal
195 | if (Action != ServiceIndicationAction.NotSet)
196 | wbxmlBytes.WriteByte(GetActionToken(Action));
197 |
198 | // writes Created dateTime signal (if set)
199 | if (Created != DateTime.MinValue)
200 | {
201 | wbxmlBytes.WriteByte(ATTRIBUTESTARTTOKEN_created);
202 | WriteDate(wbxmlBytes, Created);
203 | }
204 |
205 | // writes Experi dateTime signal (if set)
206 | if (Expires != DateTime.MinValue)
207 | {
208 | wbxmlBytes.WriteByte(ATTRIBUTESTARTTOKEN_si_expires);
209 | WriteDate(wbxmlBytes, Expires);
210 | }
211 |
212 | // writes Si-id (if set)
213 | if (Id != null)
214 | {
215 | wbxmlBytes.WriteByte(ATTRIBUTESTARTTOKEN_si_id);
216 | WriteInlineString(wbxmlBytes, Id);
217 | }
218 |
219 | // end indication
220 | wbxmlBytes.WriteByte(WBXMLDocument.TAGTOKEN_END); //
221 |
222 | // Writes texts
223 | WriteInlineString(wbxmlBytes, Text);
224 |
225 | // End tags
226 | wbxmlBytes.WriteByte(WBXMLDocument.TAGTOKEN_END);
227 | wbxmlBytes.WriteByte(WBXMLDocument.TAGTOKEN_END);
228 |
229 | byte[] wbxmlBytesArray = wbxmlBytes.ToArray();
230 | wbxmlBytes.Close();
231 |
232 | return wbxmlBytesArray;
233 | }
234 |
235 | #endregion
236 |
237 | #region Private Methods
238 |
239 | ///
240 | /// Returns token for give Action
241 | ///
242 | /// Service Indication Action
243 | /// Token
244 | protected byte GetActionToken(ServiceIndicationAction action)
245 | {
246 | byte actionToken;
247 |
248 | switch (action)
249 | {
250 | case ServiceIndicationAction.Delete:
251 | actionToken = ATTRIBUTESTARTTOKEN_action_signal_delete;
252 | break;
253 | case ServiceIndicationAction.Signal_high:
254 | actionToken = ATTRIBUTESTARTTOKEN_action_signal_high;
255 | break;
256 | case ServiceIndicationAction.Signal_low:
257 | actionToken = ATTRIBUTESTARTTOKEN_action_signal_low;
258 | break;
259 | case ServiceIndicationAction.Signal_medium:
260 | actionToken = ATTRIBUTESTARTTOKEN_action_signal_medium;
261 | break;
262 | default:
263 | actionToken = ATTRIBUTESTARTTOKEN_action_signal_none;
264 | break;
265 | }
266 |
267 | return actionToken;
268 | }
269 | #endregion
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/SharpSMS/Wap/Wsp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS.Wap
7 | {
8 | ///
9 | /// Helper static class representing Wireless Session Protocol
10 | ///
11 | public class Wsp
12 | {
13 | #region Constans
14 | public const byte TRANSACTIONID_CONNECTIONLESSWSP = 0x01; // Can be random number
15 | public const byte PDUTYPE_PUSH = 0x06;
16 |
17 | public const byte WSP_ENCODING_VERSION_1_1 = 0x11;
18 | public const byte WSP_ENCODING_VERSION_1_2 = 0x12;
19 | public const byte WSP_ENCODING_VERSION_1_3 = 0x13;
20 | public const byte WSP_ENCODING_VERSION_1_4 = 0x14;
21 |
22 | public const byte HEADER_PUSHFLAG = 0x34; // 0x34 | 0x80
23 | public const byte HEADER_X_WAP_CONTENT_URI = 0x30; // 0x30 | 0x80
24 | public const byte HEADER_X_WAP_INITIATOR_URI = 0x31; // 0x31 | 0x80
25 | public const byte HEADER_X_WAP_APPLICATION_ID = 0x2F; // 0x2F | 0x80
26 | public const byte HEADER_SEC = 0x11; // 0x11 | 0x80
27 | public const byte HEADER_MAC = 0x12; // 0x12 | 0x80
28 |
29 | public const byte SEC_NETWPIN = 0x00;
30 | public const byte SEC_USERPIN = 0x01;
31 | public const byte SEC_USERNETWPIN = 0x02;
32 | public const byte SEC_USERPINMAC = 0x03;
33 |
34 | #endregion
35 |
36 | ///
37 | /// Static constructor
38 | ///
39 | static Wsp()
40 | {
41 | // Fill the dictionary
42 | WspContentTypes = new Dictionary();
43 | WspContentTypes.Add("*/*", 0x00);
44 | WspContentTypes.Add("text/*", 0x01);
45 | WspContentTypes.Add("text/html", 0x02);
46 | WspContentTypes.Add("text/plain", 0x03);
47 | WspContentTypes.Add("text/x-hdml", 0x04);
48 | WspContentTypes.Add("text/x-ttml", 0x05);
49 | WspContentTypes.Add("text/x-vCalendar", 0x06);
50 | WspContentTypes.Add("text/x-vCard", 0x07);
51 | WspContentTypes.Add("text/vnd.wap.wml", 0x08);
52 | WspContentTypes.Add("text/vnd.wap.wmlscript", 0x09);
53 | WspContentTypes.Add("text/vnd.wap.wta-event", 0x0A);
54 | WspContentTypes.Add("multipart/*", 0x0B);
55 | WspContentTypes.Add("multipart/mixed", 0x0C);
56 | WspContentTypes.Add("multipart/form-data", 0x0D);
57 | WspContentTypes.Add("multipart/byteranges", 0x0E);
58 | WspContentTypes.Add("multipart/alternative", 0x0F);
59 | WspContentTypes.Add("application/*", 0x10);
60 | WspContentTypes.Add("application/java-vm", 0x11);
61 | WspContentTypes.Add("application/x-www-form-urlencoded", 0x12);
62 | WspContentTypes.Add("application/x-hdmlc", 0x13);
63 | WspContentTypes.Add("application/vnd.wap.wmlc", 0x14);
64 | WspContentTypes.Add("application/vnd.wap.wmlscriptc", 0x15);
65 | WspContentTypes.Add("application/vnd.wap.wta-eventc", 0x16);
66 | WspContentTypes.Add("application/vnd.wap.uaprof", 0x17);
67 | WspContentTypes.Add("application/vnd.wap.wtls-ca-certificate", 0x18);
68 | WspContentTypes.Add("application/vnd.wap.wtls-user-certificate", 0x19);
69 | WspContentTypes.Add("application/x-x509-ca-cert", 0x1A);
70 | WspContentTypes.Add("application/x-x509-user-cert", 0x1B);
71 | WspContentTypes.Add("image/*", 0x1C);
72 | WspContentTypes.Add("image/gif", 0x1D);
73 | WspContentTypes.Add("image/jpeg", 0x1E);
74 | WspContentTypes.Add("image/tiff", 0x1F);
75 | WspContentTypes.Add("image/png", 0x20);
76 | WspContentTypes.Add("image/vnd.wap.wbmp", 0x21);
77 | WspContentTypes.Add("application/vnd.wap.multipart.*", 0x22);
78 | WspContentTypes.Add("application/vnd.wap.multipart.mixed", 0x23);
79 | WspContentTypes.Add("application/vnd.wap.multipart.form-data", 0x24);
80 | WspContentTypes.Add("application/vnd.wap.multipart.byteranges", 0x25);
81 | WspContentTypes.Add("application/vnd.wap.multipart.alternative", 0x26);
82 | WspContentTypes.Add("application/xml", 0x27);
83 | WspContentTypes.Add("text/xml", 0x28);
84 | WspContentTypes.Add("application/vnd.wap.wbxml", 0x29);
85 | WspContentTypes.Add("application/x-x968-cross-cert", 0x2A);
86 | WspContentTypes.Add("application/x-x968-ca-cert", 0x2B);
87 | WspContentTypes.Add("application/x-x968-user-cert", 0x2C);
88 | WspContentTypes.Add("text/vnd.wap.si", 0x2D);
89 |
90 | // WSP 1.2
91 | WspContentTypes.Add("application/vnd.wap.sic", 0x2E);
92 | WspContentTypes.Add("text/vnd.wap.sl", 0x2F);
93 | WspContentTypes.Add("application/vnd.wap.slc", 0x30);
94 | WspContentTypes.Add("text/vnd.wap.co", 0x31);
95 | WspContentTypes.Add("application/vnd.wap.coc", 0x32);
96 | WspContentTypes.Add("application/vnd.wap.multipart.related", 0x33);
97 | WspContentTypes.Add("application/vnd.wap.sia", 0x34);
98 |
99 | // WSP 1.3
100 | WspContentTypes.Add("text/vnd.wap.connectivity-xml", 0x35);
101 | WspContentTypes.Add("application/vnd.wap.connectivity-wbxml", 0x36);
102 |
103 | // WSP 1.4
104 | WspContentTypes.Add("application/pkcs7-mime", 0x37);
105 | WspContentTypes.Add("application/vnd.wap.hashed-certificate", 0x38);
106 | WspContentTypes.Add("application/vnd.wap.signed-certificate", 0x39);
107 | WspContentTypes.Add("application/vnd.wap.cert-response", 0x3A);
108 | WspContentTypes.Add("application/xhtml+xml", 0x3B);
109 | WspContentTypes.Add("application/wml+xml", 0x3C);
110 | WspContentTypes.Add("text/css", 0x3D);
111 | WspContentTypes.Add("application/vnd.wap.mms-message", 0x3E);
112 | WspContentTypes.Add("application/vnd.wap.rollover-certificate", 0x3F);
113 |
114 | // WSP 1.5
115 | WspContentTypes.Add("application/vnd.wap.locc+wbxml", 0x40);
116 | WspContentTypes.Add("application/vnd.wap.loc+xml", 0x41);
117 | WspContentTypes.Add("application/vnd.syncml.dm+wbxml", 0x42);
118 | WspContentTypes.Add("application/vnd.syncml.dm+xml", 0x43);
119 | WspContentTypes.Add("application/vnd.syncml.notification", 0x44);
120 | WspContentTypes.Add("application/vnd.wap.xhtml+xml", 0x45);
121 | WspContentTypes.Add("application/vnd.wv.csp.cir", 0x46);
122 | WspContentTypes.Add("application/vnd.oma.dd+xml", 0x47);
123 | WspContentTypes.Add("application/vnd.oma.drm.message", 0x48);
124 | WspContentTypes.Add("application/vnd.oma.drm.content", 0x49);
125 | WspContentTypes.Add("application/vnd.oma.drm.rights+xml", 0x4A);
126 | WspContentTypes.Add("application/vnd.oma.drm.rights+wbxml", 0x4B);
127 |
128 | // http://www.wapforum.org/wina/push-app-id.htm
129 | WspPushAppTypes = new Dictionary();
130 | WspPushAppTypes.Add("x-wap-application:*", 0x00);
131 | WspPushAppTypes.Add("x-wap-application:push.sia", 0x01);
132 | WspPushAppTypes.Add("x-wap-application:wml.ua", 0x02);
133 | WspPushAppTypes.Add("x-wap-application:wta.ua", 0x03);
134 | WspPushAppTypes.Add("x-wap-application:mms.ua", 0x04);
135 | WspPushAppTypes.Add("x-wap-application:push.syncml", 0x05);
136 | WspPushAppTypes.Add("x-wap-application:loc.ua", 0x06);
137 | WspPushAppTypes.Add("x-wap-application:syncml.dm", 0x07);
138 | WspPushAppTypes.Add("x-wap-application:drm.ua", 0x08);
139 | WspPushAppTypes.Add("x-wap-application:emn.ua", 0x09);
140 | WspPushAppTypes.Add("x-wap-application:wv.ua", 0x0A);
141 |
142 | WspPushAppTypes.Add("x-wap-microsoft:localcontent.ua", 0x8000);
143 | WspPushAppTypes.Add("x-wap-microsoft:imclient.ua ", 0x8001);
144 | WspPushAppTypes.Add("x-wap-docomo:imode.mail.ua ", 0x8002);
145 | WspPushAppTypes.Add("x-wap-docomo:imode.mr.ua", 0x8003);
146 | WspPushAppTypes.Add("x-wap-docomo:imode.mf.ua", 0x8004);
147 | WspPushAppTypes.Add("x-motorola:location.ua ", 0x8005);
148 | WspPushAppTypes.Add("x-motorola:now.ua", 0x8006);
149 | WspPushAppTypes.Add("x-motorola:otaprov.ua", 0x8007);
150 | WspPushAppTypes.Add("x-motorola:browser.ua", 0x8008);
151 | WspPushAppTypes.Add("x-motorola:splash.ua", 0x8009);
152 | WspPushAppTypes.Add("x-wap-nai:mvsw.command ", 0x800B);
153 | WspPushAppTypes.Add("x-wap-openwave:iota.ua", 0x8010);
154 | }
155 |
156 | #region Dictionaries Query Methods
157 |
158 | ///
159 | /// Method returns token for given Content Type. If not found returns -1
160 | ///
161 | /// Content Type to tokenize
162 | /// Token
163 | public static int GetContentType(string contentType)
164 | {
165 | if (WspContentTypes.ContainsKey(contentType))
166 | return WspContentTypes[contentType];
167 | else
168 | return -1;
169 | }
170 |
171 | ///
172 | /// Method returns token for given Application Type. If not found returns -1
173 | ///
174 | /// Application Type to tokenize
175 | /// Token
176 | public static int GetpplicationType(string applicationType)
177 | {
178 | if (WspPushAppTypes.ContainsKey(applicationType))
179 | return WspPushAppTypes[applicationType];
180 | else
181 | return -1;
182 | }
183 | #endregion
184 |
185 | #region Methods to Write WSP Values
186 |
187 | ///
188 | /// Writes "extension media" (null terminated string) to stream, suitable for WSP protocol
189 | ///
190 | /// Output stream
191 | /// Text to write
192 | public static void WriteExtensionMedia(MemoryStream stream, string extension)
193 | {
194 | byte[] bytes = Encoding.UTF8.GetBytes(extension);
195 |
196 | // write the text
197 | stream.Write(bytes, 0, bytes.Length);
198 |
199 | // string must be null terminated
200 | stream.WriteByte(0x00);
201 | }
202 |
203 | ///
204 | /// Writes text to stream, suitable for WSP protocol
205 | ///
206 | /// Output stream
207 | /// Text to wrap
208 | public static void WriteTextString(MemoryStream stream, string text)
209 | {
210 | byte[] bytes = Encoding.UTF8.GetBytes(text);
211 |
212 | // If the first character in the TEXT is in the range of 128-255, a Quote character must precede it.
213 | // Otherwise the Quote character must be omitted. The Quote is not part of the contents
214 | if ((text[0] & 0x80) > 0x00)
215 | {
216 | stream.WriteByte(0x7f);
217 | }
218 |
219 | // write the text
220 | stream.Write(bytes, 0, bytes.Length);
221 |
222 | // string must be null terminated
223 | stream.WriteByte(0x00);
224 | }
225 |
226 | ///
227 | /// Writes integer value to the stream, according to WSP specifications
228 | ///
229 | /// Output stream
230 | /// value
231 | public static void WriteInteger(MemoryStream stream, long theValue)
232 | {
233 | if (theValue < 128)
234 | WriteShortInteger(stream, (byte)theValue);
235 | else
236 | WriteLongInteger(stream, theValue);
237 | }
238 |
239 | ///
240 | /// Writes WSP-short-integer (byte) value to the stream, according to WSP specifications
241 | ///
242 | /// Output stream
243 | /// value
244 | public static void WriteShortInteger(MemoryStream stream, byte theValue)
245 | {
246 | stream.WriteByte((byte)(theValue | (byte)0x80));
247 | }
248 |
249 | ///
250 | /// Writes long-integer value to the stream, according to WSP specifications
251 | ///
252 | /// Output stream
253 | /// value
254 | public static void WriteLongInteger(MemoryStream stream, long number)
255 | {
256 | int nOctets = 0;
257 | while ((number >> (8 * nOctets)) > 0)
258 | {
259 | nOctets++;
260 | }
261 | stream.WriteByte((byte) nOctets);
262 |
263 | for (int i = nOctets; i > 0; i--)
264 | {
265 | byte octet = (byte) (number >> (8 * (i - 1)));
266 | byte byteValue = (byte) ((byte) octet & (byte) (0xff));
267 | stream.WriteByte(byteValue);
268 | }
269 | }
270 |
271 | ///
272 | /// Writes "UIntVar" value to the stream, according to WSP specifications
273 | ///
274 | /// Output stream
275 | /// value
276 | public static void WriteUintvar(MemoryStream stream, long number)
277 | {
278 | int nOctets = 1;
279 | while ((number >> (7 * nOctets)) > 0)
280 | {
281 | nOctets++;
282 | }
283 |
284 | for (int i = nOctets; i > 0; i--)
285 | {
286 | byte octet = (byte) (number >> (7 * (i - 1)));
287 | byte byteValue = (byte) ((byte) octet & (byte) 0x7f);
288 | if (i > 1)
289 | {
290 | byteValue = (byte) (byteValue | (byte) 0x80);
291 | }
292 | stream.WriteByte(byteValue);
293 | }
294 | }
295 |
296 | ///
297 | /// Writes number into the stream
298 | ///
299 | /// Output stream
300 | /// value
301 | public static void WriteValueLength(MemoryStream stream, long number)
302 | {
303 | // ShortLength | (Length-quote Length)
304 | if (number >=0 && number <= 30)
305 | {
306 | // Short-length
307 | stream.WriteByte((byte)number);
308 | }
309 | else
310 | {
311 | // Length-quote == Octet 31
312 | stream.WriteByte(31);
313 | WriteUintvar(stream, number);
314 | }
315 | }
316 | #endregion
317 |
318 | #region WSP Header Writing Methods
319 |
320 | ///
321 | /// Writes header for given Content Type into stream
322 | ///
323 | /// Output Stream
324 | /// Content Type
325 | public static void WriteHeaderContentType(MemoryStream stream, string contentType)
326 | {
327 | int wellKnownContentType = GetContentType(contentType.ToLower());
328 |
329 | if (wellKnownContentType == -1)
330 | {
331 | WriteValueLength(stream, contentType.Length + 1);
332 | WriteExtensionMedia(stream, contentType);
333 | }
334 | else
335 | {
336 | WriteShortInteger(stream, (byte)wellKnownContentType);
337 | }
338 | }
339 |
340 | ///
341 | /// Writes header for given X-WAP-Application-ID
342 | ///
343 | /// Output stream
344 | /// Application Type
345 | public static void WriteHeaderXWAPApplicationID(MemoryStream stream, string applicationType)
346 | {
347 | int wellKnownAppId = GetpplicationType(applicationType.ToLower());
348 |
349 | WriteShortInteger(stream, HEADER_X_WAP_APPLICATION_ID);
350 |
351 | if (wellKnownAppId == -1)
352 | WriteTextString(stream, applicationType);
353 | else
354 | WriteInteger(stream, wellKnownAppId);
355 | }
356 |
357 | ///
358 | /// Writes header for given X-WAP-Initiator-URI
359 | ///
360 | /// Output stream
361 | /// Initiator URI
362 | public static void WriteHeaderXWAPInitiatorURI(MemoryStream stream, string initiatorURI)
363 | {
364 | WriteShortInteger(stream, HEADER_X_WAP_INITIATOR_URI);
365 | WriteTextString(stream, initiatorURI);
366 | }
367 |
368 | ///
369 | /// Writes header for given X-WAP-Content-URI
370 | ///
371 | /// Output stream
372 | /// Content URI
373 | public static void WriteHeaderXWAPContentURI(MemoryStream stream, string contentURI)
374 | {
375 | WriteShortInteger(stream, HEADER_X_WAP_CONTENT_URI);
376 | WriteTextString(stream, contentURI);
377 | }
378 |
379 | ///
380 | /// Writes header for given Push Flag
381 | ///
382 | /// Output stream
383 | /// Push Flag
384 | public static void WriteHeaderPushFlag(MemoryStream stream, byte pushFlag)
385 | {
386 | WriteShortInteger(stream, HEADER_PUSHFLAG);
387 | WriteShortInteger(stream, pushFlag);
388 | }
389 |
390 | ///
391 | /// Writes security header
392 | ///
393 | /// Ouput stream
394 | /// Security method
395 | /// Key
396 | public static void WriteHeaderSecurity(MemoryStream stream, SecurityMethod method, byte[] macKey)
397 | {
398 | WriteShortInteger(stream, HEADER_SEC);
399 |
400 | switch (method)
401 | {
402 | case SecurityMethod.NETWPIN:
403 | WriteShortInteger(stream, SEC_NETWPIN);
404 | break;
405 | case SecurityMethod.USERPIN:
406 | WriteShortInteger(stream, SEC_USERPIN);
407 | break;
408 | case SecurityMethod.USERNETWPIN:
409 | WriteShortInteger(stream, SEC_USERNETWPIN);
410 | break;
411 | case SecurityMethod.USERPINMAC:
412 | WriteShortInteger(stream, SEC_USERPINMAC);
413 | break;
414 | }
415 |
416 | WriteShortInteger(stream, HEADER_MAC);
417 |
418 | string macKeyString = string.Empty;
419 | foreach (byte b in macKey)
420 | macKeyString += b.ToString("X2");
421 | WriteTextString(stream, macKeyString);
422 | }
423 |
424 | #endregion
425 |
426 | #region Fields
427 |
428 | ///
429 | /// Represent Content Type to Token Dictionary
430 | ///
431 | private static Dictionary WspContentTypes;
432 |
433 | ///
434 | /// Represent Application Type to Token Dictionary
435 | ///
436 | private static Dictionary WspPushAppTypes;
437 | #endregion
438 |
439 | ///
440 | /// Security method for message signing
441 | ///
442 | public enum SecurityMethod
443 | {
444 | ///
445 | /// No security will be applied
446 | ///
447 | None,
448 | ///
449 | /// Message is signed with network PIN
450 | ///
451 | NETWPIN,
452 | ///
453 | /// Message is signed with user PIN
454 | ///
455 | USERPIN,
456 | ///
457 | /// Message is signed with network PIN and user PIN
458 | ///
459 | USERNETWPIN,
460 | ///
461 | /// Not supported
462 | ///
463 | USERPINMAC,
464 | }
465 | }
466 | }
467 |
--------------------------------------------------------------------------------
/SharpSMS/SMSSubmit.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 |
6 | namespace SharpSMS
7 | {
8 | ///
9 | /// Class represents SMS message to send
10 | ///
11 | public class SMSSubmit
12 | {
13 | #region Constants
14 | ///
15 | /// First Octet Bits 1,0 - SMS-SUBMIT
16 | ///
17 | public const byte FIRST_OCTET_SMS_SUBMIT = 0x01;
18 | ///
19 | /// First Octet Bits 4,3 - Validity period is in relative format
20 | ///
21 | public const byte FIRST_OCTET_VALIDITY_PERIOD_RELATIVEFORMAT = 0x10;
22 | ///
23 | /// First Octet Bit 5 - Status delivery requested
24 | ///
25 | public const byte FIRST_OCTET_REQUEST_DELIVERY_CONFIRMATION = 0x20;
26 | ///
27 | /// First Octet Bit 6 - User data header is present
28 | ///
29 | public const byte FIRST_OCTET_USER_DATA_HEADER = 0x40;
30 | ///
31 | /// User data header - concated message
32 | ///
33 | public const byte PDU_UDH_CONCATED_MESSAGE_16BIT = 0x08;
34 | ///
35 | /// User data header - Length of concated message length
36 | ///
37 | public const byte PDU_UDH_CONCATED_MESSAGE_16BIT_HEADERLENGTH = 0x04;
38 | ///
39 | /// User data header - Length of concated header
40 | ///
41 | public const byte CONCATED_UDH_FULL_LENGTH = 0x07;
42 |
43 | #endregion
44 |
45 | #region Properties
46 |
47 | ///
48 | /// Request for message delivery confirmation (optional)
49 | ///
50 | public bool RequestDeliveryConfirmation { get; set; }
51 |
52 | ///
53 | /// Validation period of the message (optional)
54 | ///
55 | public TimeSpan ValidityPeriod { get; set; }
56 |
57 | ///
58 | /// Message reference (optional)
59 | ///
60 | public byte MessageReference { get; set; }
61 |
62 | ///
63 | /// Message protocol identifier (optional)
64 | ///
65 | public byte ProtocolIdentifier { get; set; }
66 |
67 | ///
68 | /// Phone number of the message recipient
69 | ///
70 | public string PhoneNumber { get; set; }
71 |
72 | ///
73 | /// Specifies how message will be displayed and stored on the phone
74 | ///
75 | public MessageIndication Indication { get; set; }
76 |
77 | ///
78 | /// Message data
79 | ///
80 | public ISmsMessageContent MessageToSend { get; set; }
81 |
82 | #endregion
83 |
84 | #region Constructor
85 |
86 | ///
87 | /// Creates instance of the SubmitSMS message
88 | ///
89 | public SMSSubmit()
90 | : this(null)
91 | {
92 | }
93 |
94 | ///
95 | /// Creates instance of the SubmitSMS message
96 | ///
97 | /// Message to be sent
98 | public SMSSubmit(ISmsMessageContent messageToSent)
99 | {
100 | // Set the default values
101 | this.RequestDeliveryConfirmation = false;
102 | this.ValidityPeriod = TimeSpan.MinValue;
103 | this.MessageReference = 0x00;
104 | this.ProtocolIdentifier = 0x00;
105 | this.MessageToSend = messageToSent;
106 |
107 | this.Indication = new MessageIndication(MessageClass.MESpecific);
108 | this.Indication.Type = IndicationType.Voicemail;
109 | this.Indication.Operation = MessageIndicationOperation.NotSet;
110 | this.Indication.IsActive = false;
111 | }
112 | #endregion
113 |
114 | #region Public Methods
115 | ///
116 | /// Method returns list of PDU formated messages.
117 | /// If data fits into one message than the list has only one item.
118 | /// Otherwise the data is concated into more messages.
119 | ///
120 | ///
121 | public List GetPDUList()
122 | {
123 | int maxOctetsCount;
124 |
125 | switch (MessageToSend.DataEncoding)
126 | {
127 | case DataEncoding.Default7bit:
128 | maxOctetsCount = 160;
129 | break;
130 | case DataEncoding.Data8bit:
131 | maxOctetsCount = 140; // Might be lower, some roaming partners don't like it when set to 140
132 | break;
133 | case DataEncoding.UCS2_16bit:
134 | maxOctetsCount = 140;
135 | break;
136 | default:
137 | maxOctetsCount = 140;
138 | break;
139 | }
140 |
141 | byte[] body = MessageToSend.GetSMSBytes();
142 | byte[] messageUdh = MessageToSend.GetUDHBytes();
143 |
144 | // If UDH exists then reserve one more byte for UDH length
145 | int maxLen = maxOctetsCount - messageUdh.Length;
146 | maxLen -= (messageUdh.Length > 0) ? 1 : 0;
147 |
148 | // Count parts
149 | int parts = (int)Math.Ceiling((double)body.Length / (double)maxLen);
150 | parts = (int)Math.Ceiling((double)(body.Length + parts*messageUdh.Length) / (double)maxLen);
151 |
152 | if (parts > 1)
153 | parts = (int)Math.Ceiling((double)(body.Length + parts * messageUdh.Length + parts * (CONCATED_UDH_FULL_LENGTH + 1)) / (double)maxLen);
154 |
155 | List messagePart = new List();
156 | byte[] udhByteArray;
157 | byte[] messageByteArray;
158 | ConcatedUDH conUDH = new ConcatedUDH();
159 |
160 | // This will hold the byteCount send in message (without UDH)
161 | int bytesCount = maxOctetsCount;
162 | // Thiis will hold the position in the message array
163 | int messagePos = 0;
164 |
165 | if (parts > 1)
166 | {
167 | conUDH.Reference = 01;
168 | conUDH.Parts = (byte)parts;
169 |
170 | for (int i = 0; i < parts; i++)
171 | {
172 | conUDH.Sequence = (byte)(i + 1);
173 | udhByteArray = GetConcatPdu(conUDH, messageUdh);
174 |
175 | // if it's not last message then
176 | if (i < (parts - 1))
177 | bytesCount = maxOctetsCount - udhByteArray.Length - 1;
178 | else
179 | bytesCount = body.Length - messagePos;
180 |
181 | byte[] bodyPart = new byte[bytesCount];
182 | Array.Copy(body, messagePos, bodyPart, 0, bytesCount);
183 |
184 | // Encode message with given encoding
185 | byte[] encodedBody = GetEncodedMessage(bodyPart);
186 |
187 | // Takes the message length before encoding
188 | int messageLength = (bodyPart.Length) + udhByteArray.Length;
189 |
190 | // There is a align in 7bit encoding
191 | if (MessageToSend.DataEncoding == DataEncoding.Default7bit)
192 | messageLength++;
193 |
194 | //creates message
195 | messageByteArray = new byte[encodedBody.Length + udhByteArray.Length];
196 | Array.Copy(udhByteArray, messageByteArray, udhByteArray.Length);
197 | Array.Copy(encodedBody, 0, messageByteArray, udhByteArray.Length, encodedBody.Length);
198 | messagePos += bytesCount;
199 |
200 | messageByteArray = GetPDUBytes(messageByteArray, messageLength, (udhByteArray.Length > 0));
201 | messagePart.Add(messageByteArray);
202 | }
203 | }
204 | else
205 | {
206 | conUDH.Parts = 1;
207 | udhByteArray = GetConcatPdu(conUDH, messageUdh);
208 |
209 | // Encode body according to given Encoding :)
210 | byte[] encodedBody = GetEncodedMessage(body);
211 |
212 | // Takes the message length before encoding
213 | int messageLength = (body.Length) + udhByteArray.Length;
214 |
215 | messageByteArray = new byte[encodedBody.Length + udhByteArray.Length];
216 | Array.Copy(udhByteArray, messageByteArray, udhByteArray.Length);
217 | Array.Copy(encodedBody, 0, messageByteArray, udhByteArray.Length, encodedBody.Length);
218 |
219 | messageByteArray = GetPDUBytes(messageByteArray, messageLength, (udhByteArray.Length > 0));
220 | messagePart.Add(messageByteArray);
221 | }
222 |
223 | return messagePart;
224 | }
225 | #endregion
226 |
227 | #region Private Methods
228 |
229 | private byte[] GetEncodedMessage(byte[] messageData)
230 | {
231 | switch (MessageToSend.DataEncoding)
232 | {
233 | case DataEncoding.Default7bit:
234 | return OctetsToSeptets(messageData);
235 | case DataEncoding.Data8bit:
236 | return messageData;
237 | case DataEncoding.UCS2_16bit:
238 | return messageData;
239 | default:
240 | return messageData;
241 | }
242 | }
243 |
244 | ///
245 | /// Method adds `Concat` information into the User Header
246 | ///
247 | /// stucture representing the
248 | /// message user header
249 | private byte[] GetConcatPdu(ConcatedUDH udhStruct, byte[] messageUDH)
250 | {
251 | MemoryStream stream = new MemoryStream();
252 |
253 | // If we have message for concating, write concating header
254 | if (udhStruct.Parts > 1)
255 | {
256 | MemoryStream concatHeader = new MemoryStream();
257 | concatHeader.WriteByte(PDU_UDH_CONCATED_MESSAGE_16BIT);
258 | concatHeader.WriteByte(PDU_UDH_CONCATED_MESSAGE_16BIT_HEADERLENGTH);
259 |
260 | concatHeader.WriteByte((byte)(udhStruct.Reference >> 8));
261 | concatHeader.WriteByte((byte)udhStruct.Reference);
262 |
263 | concatHeader.WriteByte(udhStruct.Parts);
264 | concatHeader.WriteByte(udhStruct.Sequence);
265 |
266 | stream.WriteByte((byte)(concatHeader.Length + messageUDH.Length));
267 | concatHeader.WriteTo(stream);
268 | }
269 | // if we have some messageuUDH, write the header length
270 | else if (messageUDH.Length > 0)
271 | stream.WriteByte((byte)messageUDH.Length);
272 |
273 | stream.Write(messageUDH, 0, messageUDH.Length);
274 | return stream.ToArray();
275 | }
276 |
277 | ///
278 | /// Returns given bytes PDU formated with the user header
279 | ///
280 | /// Message body
281 | /// Length of the message
282 | /// Does message have custom header inside
283 | private byte[] GetPDUBytes(byte[] messageBody, int messageLength, bool hasCustomHeader)
284 | {
285 | MemoryStream message = new MemoryStream();
286 |
287 | byte[] header = GetPDUHeader(messageLength, hasCustomHeader);
288 |
289 | message.Write(header, 0, header.Length);
290 | message.Write(messageBody, 0, messageBody.Length);
291 |
292 | byte[] messageBytes = message.ToArray();
293 | message.Close();
294 |
295 | return messageBytes;
296 | }
297 |
298 | ///
299 | /// Returns PDU header of the text message.
300 | /// Including phone number and other flags
301 | ///
302 | /// lenght of data in message
303 | /// Does message have custom header inside
304 | private byte[] GetPDUHeader(int dataLength, bool hasCustomHeader)
305 | {
306 | MemoryStream header = new MemoryStream();
307 | header.WriteByte(0x00); // Length of SMSC
308 | // TP-MTI Message type
309 | header.WriteByte(GetFirstOctet(hasCustomHeader));
310 | header.WriteByte(this.MessageReference); // TP-MR Message Reference
311 |
312 | WritePhoneNumber(header, PhoneNumber);
313 | // TP-PID. Protocol identifier.
314 | header.WriteByte(ProtocolIdentifier);
315 | // TP-DCS Data coding scheme
316 | header.WriteByte(this.Indication.ToByte(this.MessageToSend.DataEncoding));
317 |
318 | // TP-SCTS. Time stamp (semi-octets)
319 | if (ValidityPeriod > TimeSpan.MinValue)
320 | header.WriteByte(GetValidityPeriod());
321 |
322 | header.WriteByte((byte)(dataLength)); // +1 is to count also this byte
323 |
324 | byte[] headerBytes = header.ToArray();
325 | header.Close();
326 |
327 | return headerBytes;
328 | }
329 |
330 | ///
331 | /// Return first octet of the PDU header depending message properties settings
332 | ///
333 | ///
334 | private byte GetFirstOctet(bool hasCustomHeader)
335 | {
336 | byte firstOctet = FIRST_OCTET_SMS_SUBMIT;
337 |
338 | if (ValidityPeriod > TimeSpan.MinValue)
339 | firstOctet |= FIRST_OCTET_VALIDITY_PERIOD_RELATIVEFORMAT;
340 |
341 | if (RequestDeliveryConfirmation)
342 | firstOctet |= FIRST_OCTET_REQUEST_DELIVERY_CONFIRMATION;
343 |
344 | if (hasCustomHeader)
345 | firstOctet |= FIRST_OCTET_USER_DATA_HEADER;
346 |
347 | return firstOctet;
348 | }
349 |
350 | ///
351 | /// Returns value representing current validity period
352 | ///
353 | ///
354 | private byte GetValidityPeriod()
355 | {
356 | TimeSpan value = ValidityPeriod;
357 | byte validity;
358 |
359 | if (value.Days > 441)
360 | value = new TimeSpan(440,0,0,0);
361 |
362 | if (value.Days > 30) //Up to 441 days
363 | validity = (byte) (192 + (int) (value.Days / 7));
364 | else if (value.Days > 1) //Up to 30 days
365 | validity = (byte) (166 + value.Days);
366 | else if (value.Hours > 12) //Up to 24 hours
367 | validity = (byte) (143 + (value.Hours - 12) * 2 + value.Minutes / 30);
368 | else if (value.Hours > 1 || value.Minutes > 1) //Up to 12 days
369 | validity = (byte) (value.Hours * 12 + value.Minutes / 5 - 1);
370 | else
371 | validity = 0x00;
372 |
373 | return validity;
374 | }
375 |
376 | ///
377 | /// Writes phone in PDU format
378 | ///
379 | /// Destination stream
380 | /// Phone number to write
381 | private void WritePhoneNumber(MemoryStream stream, string phoneNumber)
382 | {
383 | if (string.IsNullOrEmpty(phoneNumber))
384 | throw new ArgumentException("Phone number is not set");
385 |
386 | bool isInternational = phoneNumber.StartsWith("+");
387 | byte numberFormat = 0x81;
388 |
389 | if (isInternational)
390 | {
391 | phoneNumber = phoneNumber.Remove(0, 1);
392 | numberFormat = 0x91;
393 | }
394 |
395 | byte numberLength = (byte)phoneNumber.Length;
396 |
397 | stream.WriteByte(numberLength);
398 | stream.WriteByte(numberFormat);
399 | WriteBcdNumber(stream, phoneNumber);
400 | }
401 |
402 | ///
403 | /// Encodes phone number into BCD.
404 | ///
405 | /// Destination stream
406 | /// Phone number to write
407 | private void WriteBcdNumber(MemoryStream stream, string phoneNumber)
408 | {
409 | int bcd = 0x00;
410 | int n = 0;
411 |
412 | // First convert to a "half octet" value
413 | for (int i = 0; i < phoneNumber.Length; i++)
414 | {
415 | switch (phoneNumber[i])
416 | {
417 | case '0':
418 | bcd |= 0x00;
419 | break;
420 | case '1':
421 | bcd |= 0x10;
422 | break;
423 | case '2':
424 | bcd |= 0x20;
425 | break;
426 | case '3':
427 | bcd |= 0x30;
428 | break;
429 | case '4':
430 | bcd |= 0x40;
431 | break;
432 | case '5':
433 | bcd |= 0x50;
434 | break;
435 | case '6':
436 | bcd |= 0x60;
437 | break;
438 | case '7':
439 | bcd |= 0x70;
440 | break;
441 | case '8':
442 | bcd |= 0x80;
443 | break;
444 | case '9':
445 | bcd |= 0x90;
446 | break;
447 | case '*':
448 | bcd |= 0xA0;
449 | break;
450 | case '#':
451 | bcd |= 0xB0;
452 | break;
453 | case 'a':
454 | bcd |= 0xC0;
455 | break;
456 | case 'b':
457 | bcd |= 0xE0;
458 | break;
459 | }
460 |
461 | n++;
462 |
463 | if (n == 2)
464 | {
465 | stream.WriteByte((byte)bcd);
466 | n = 0;
467 | bcd = 0x00;
468 | }
469 | else
470 | {
471 | bcd >>= 4;
472 | }
473 | }
474 |
475 | if (n == 1)
476 | {
477 | bcd |= 0xF0;
478 | stream.WriteByte((byte)bcd);
479 | }
480 | }
481 |
482 | ///
483 | /// Converts array of characters into septets
484 | ///
485 | /// Array of octets
486 | private byte[] OctetsToSeptets(byte[] octetsArray)
487 | {
488 | int arrayLength = octetsArray.Length;
489 | // Extend octets array with byte
490 | byte[] workingArray = new byte[arrayLength + 1];
491 | Array.Copy(octetsArray, workingArray, arrayLength);
492 |
493 | arrayLength = workingArray.Length;
494 |
495 | MemoryStream octets = new MemoryStream();
496 |
497 | for (int i = 0; i < arrayLength; ++i)
498 | {
499 | int m = i % 8;
500 | if (m != 7)
501 | {
502 | int n;
503 | if (i == arrayLength - 1)
504 | n = 0 & PowSum(0, m);
505 | else
506 | {
507 | n = workingArray[i + 1] & PowSum(0, m);
508 | workingArray[i + 1] -= (byte)n;
509 | }
510 |
511 | workingArray[i] /= (byte)Math.Pow(2, m);
512 | workingArray[i] += (byte)(Math.Pow(2, 7 - m) * n);
513 |
514 | // We dont want the last 0x00 byte to be added
515 | if ((arrayLength - i) > 1)
516 | octets.WriteByte(workingArray[i]);
517 | }
518 | }
519 |
520 | return octets.ToArray();
521 | }
522 |
523 | ///
524 | /// Sum POW(2, i), where i goes through 0 to n.
525 | ///
526 | /// Start bit
527 | /// Number of bits
528 | private static int PowSum(int startBit, int n)
529 | {
530 | int sum = 0;
531 | for (int i = n; i >= startBit; --i)
532 | {
533 | sum += (int)Math.Pow(2, i);
534 | }
535 | return sum;
536 | }
537 |
538 | #endregion
539 |
540 | #region Private Structs
541 | private struct ConcatedUDH
542 | {
543 | public byte Parts;
544 | public byte Sequence;
545 | public byte Reference;
546 | }
547 | #endregion
548 | }
549 | }
550 |
--------------------------------------------------------------------------------