Repository: linxGnu/gosmpp
Branch: master
Commit: 577c73e414d2
Files: 115
Total size: 344.5 KB
Directory structure:
gitextract_d637zljl/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ └── go.yml
├── .gitignore
├── .golangci.yml
├── LICENSE
├── README.md
├── connect.go
├── connect_test.go
├── connection.go
├── connection_test.go
├── data/
│ ├── 7bit.go
│ ├── 7bit_test.go
│ ├── codings.go
│ ├── codings_test.go
│ ├── header_data.go
│ ├── header_data_string.go
│ ├── other_codings.go
│ ├── pkg.go
│ ├── pkg_test.go
│ ├── utils.go
│ └── utils_test.go
├── errors/
│ ├── pkg.go
│ └── pkg_test.go
├── example/
│ ├── smsc_simulator/
│ │ └── smsc.cpp
│ ├── transceiver_with_auto_response/
│ │ └── main.go
│ ├── transceiver_with_manual_response/
│ │ └── main.go
│ ├── transeiver_with_custom_store/
│ │ ├── CustomStore.go
│ │ └── main.go
│ └── transeiver_with_request_window_and_custom_submitSm/
│ ├── CustomSubmitSM.go
│ └── main.go
├── go.mod
├── go.sum
├── pdu/
│ ├── Address.go
│ ├── AddressRange.go
│ ├── AddressRange_test.go
│ ├── Address_test.go
│ ├── AlertNotification.go
│ ├── AlertNotification_test.go
│ ├── BindRequest.go
│ ├── BindRequest_test.go
│ ├── BindResponse.go
│ ├── BindResponse_test.go
│ ├── Buffer.go
│ ├── Buffer_test.go
│ ├── CancelSM.go
│ ├── CancelSMResp.go
│ ├── CancelSMResp_test.go
│ ├── CancelSM_test.go
│ ├── DataSM.go
│ ├── DataSMResp.go
│ ├── DataSMResp_test.go
│ ├── DataSM_test.go
│ ├── DeliverSM.go
│ ├── DeliverSMResp.go
│ ├── DeliverSMResp_test.go
│ ├── DeliverSM_test.go
│ ├── DestinationAddress.go
│ ├── DestinationAddress_test.go
│ ├── DistributionList.go
│ ├── DistributionList_test.go
│ ├── EnquireLink.go
│ ├── EnquireLinkResp.go
│ ├── EnquireLinkResp_test.go
│ ├── EnquireLink_test.go
│ ├── GenericNack.go
│ ├── GenericNack_test.go
│ ├── Outbind.go
│ ├── Outbind_test.go
│ ├── PDU.go
│ ├── PDUFactory.go
│ ├── PDUFactory_test.go
│ ├── PDUHeader.go
│ ├── PDUHeader_test.go
│ ├── PDU_test.go
│ ├── QuerySM.go
│ ├── QuerySMResp.go
│ ├── QuerySMResp_test.go
│ ├── QuerySM_test.go
│ ├── ReplaceSM.go
│ ├── ReplaceSMResp.go
│ ├── ReplaceSMResp_test.go
│ ├── ReplaceSM_test.go
│ ├── ShortMessage.go
│ ├── ShortMessage_test.go
│ ├── SubmitMulti.go
│ ├── SubmitMultiResp.go
│ ├── SubmitMultiResp_test.go
│ ├── SubmitMulti_test.go
│ ├── SubmitSM.go
│ ├── SubmitSMResp.go
│ ├── SubmitSMResp_test.go
│ ├── SubmitSM_test.go
│ ├── TLV.go
│ ├── UDH.go
│ ├── UDH_test.go
│ ├── Unbind.go
│ ├── UnbindResp.go
│ ├── UnbindResp_test.go
│ ├── Unbind_test.go
│ ├── UnsuccessSME.go
│ ├── UnsuccessSME_test.go
│ └── helper_test.go
├── pkg.go
├── receivable.go
├── receivable_test.go
├── request_store.go
├── session.go
├── session_test.go
├── state.go
├── state_test.go
├── transceivable.go
├── transceivable_test.go
├── transmittable.go
├── transmittable_test.go
└── types.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
================================================
FILE: .github/workflows/go.yml
================================================
name: Build
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v5
- name: Set up GCC
run: |
sudo apt install gcc g++ -y
shell: bash
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.24
# - name: Linter
# uses: golangci/golangci-lint-action@v8
# with:
# version: latest
- name: Start SMSC
run: |
g++ example/smsc_simulator/smsc.cpp -o smsc
./smsc &
shell: bash
- name: Test Coverage
run: go test -v -race -count=1 -coverprofile=coverage.out
- name: Convert coverage to lcov
uses: jandelgado/gcov2lcov-action@v1
with:
version: latest
infile: coverage.out
outfile: coverage.lcov
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
path-to-lcov: coverage.lcov
================================================
FILE: .gitignore
================================================
.idea/
vendor/
*DS_Store
*.out
smsc
================================================
FILE: .golangci.yml
================================================
version: "2"
linters:
# Default set of linters.
# The value can be:
# - `standard`: https://golangci-lint.run/docs/linters/#enabled-by-default
# - `all`: enables all linters by default.
# - `none`: disables all linters by default.
# - `fast`: enables only linters considered as "fast" (`golangci-lint help linters --json | jq '[ .[] | select(.fast==true) ] | map(.name)'`).
# Default: standard
default: all
# Enable specific linter.
enable:
- arangolint
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- containedctx
- contextcheck
- copyloopvar
- cyclop
- decorder
- depguard
- dogsled
- dupl
- dupword
- durationcheck
- embeddedstructfieldcheck
- err113
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- exhaustruct
- exptostd
- fatcontext
- forbidigo
- forcetypeassert
- funcorder
- funlen
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gochecksumtype
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosmopolitan
- govet
- grouper
- iface
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- mirror
- misspell
- mnd
- musttag
- nakedret
- nestif
- nilerr
- nilnesserr
- nilnil
- nlreturn
- noctx
- noinlineerr
- nolintlint
- nonamedreturns
- nosprintfhostport
- paralleltest
- perfsprint
- prealloc
- predeclared
- promlinter
- protogetter
- reassign
- recvcheck
- revive
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- tagalign
- tagliatelle
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- varnamelen
- wastedassign
- whitespace
- wrapcheck
- wsl
- wsl_v5
- zerologlint
# Disable specific linters.
disable:
- staticcheck
- arangolint
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- containedctx
- contextcheck
- copyloopvar
- cyclop
- decorder
- depguard
- dogsled
- dupl
- dupword
- durationcheck
- embeddedstructfieldcheck
- err113
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- exhaustruct
- exptostd
- fatcontext
- forbidigo
- forcetypeassert
- funcorder
- funlen
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gochecksumtype
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosmopolitan
- govet
- grouper
- iface
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- mirror
- misspell
- mnd
- musttag
- nakedret
- nestif
- nilerr
- nilnesserr
- nilnil
- nlreturn
- noctx
- noinlineerr
- nolintlint
- nonamedreturns
- nosprintfhostport
- paralleltest
- perfsprint
- prealloc
- predeclared
- promlinter
- protogetter
- reassign
- recvcheck
- revive
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- tagalign
- tagliatelle
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- varnamelen
- wastedassign
- whitespace
- wrapcheck
- wsl
- wsl_v5
- zerologlint
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# gosmpp
[]()
[](https://goreportcard.com/report/github.com/linxGnu/gosmpp)
[](https://coveralls.io/github/linxGnu/gosmpp?branch=master)
[](https://godoc.org/github.com/linxGnu/gosmpp)
SMPP (3.4) Client Library in pure Go.
This library is well tested with SMSC simulators:
- [Melroselabs SMSC](https://melroselabs.com/services/smsc-simulator/#smsc-simulator-try)
## Installation
```
go get -u github.com/linxGnu/gosmpp
```
## Usage
### Highlight
- From `v0.1.4`, gosmpp is written in event-based style and fully-manage your smpp session, connection, error, rebinding, etc. You only need to implement some hooks:
```go
trans, err := gosmpp.NewSession(
gosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),
gosmpp.Settings{
EnquireLink: 5 * time.Second,
ReadTimeout: 10 * time.Second,
OnSubmitError: func(_ pdu.PDU, err error) {
log.Fatal("SubmitPDU error:", err)
},
OnReceivingError: func(err error) {
fmt.Println("Receiving PDU/Network error:", err)
},
OnRebindingError: func(err error) {
fmt.Println("Rebinding but error:", err)
},
OnPDU: handlePDU(),
OnClosed: func(state gosmpp.State) {
fmt.Println(state)
},
}, 5*time.Second)
if err != nil {
log.Println(err)
}
defer func() {
_ = trans.Close()
}()
```
### Version (0.1.4.RC+)
- Full example could be found: [here](https://github.com/linxGnu/gosmpp/blob/master/example)
- In this example, you should run smsc first:
- Build and run SMSC Simulator:
```bash
g++ -std=c++11 example/smsc_simulator/smsc.cpp -o smsc
./smsc &
```
- Run smpp client in the example: https://github.com/linxGnu/gosmpp/blob/master/example/main.go
```bash
go run example/main.go
```
### Old version (0.1.3 and previous)
Full example could be found: [gist](https://gist.github.com/linxGnu/b488997a0e62b3f6a7060ba2af6391ea)
## Supported PDUs
- [x] bind_transmitter
- [x] bind_transmitter_resp
- [x] bind_receiver
- [x] bind_receiver_resp
- [x] bind_transceiver
- [x] bind_transceiver_resp
- [x] outbind
- [x] unbind
- [x] unbind_resp
- [x] submit_sm
- [x] submit_sm_resp
- [x] submit_sm_multi
- [x] submit_sm_multi_resp
- [x] data_sm
- [x] data_sm_resp
- [x] deliver_sm
- [x] deliver_sm_resp
- [x] query_sm
- [x] query_sm_resp
- [x] cancel_sm
- [x] cancel_sm_resp
- [x] replace_sm
- [x] replace_sm_resp
- [x] enquire_link
- [x] enquire_link_resp
- [x] alert_notification
- [x] generic_nack
================================================
FILE: connect.go
================================================
package gosmpp
import (
"fmt"
"net"
"github.com/linxGnu/gosmpp/data"
"github.com/linxGnu/gosmpp/pdu"
)
var (
// NonTLSDialer is non-tls connection dialer.
NonTLSDialer = func(addr string) (net.Conn, error) {
return net.Dial("tcp", addr)
}
)
// Dialer is connection dialer.
type Dialer func(addr string) (net.Conn, error)
// Auth represents basic authentication to SMSC.
type Auth struct {
// SMSC is SMSC address.
SMSC string
SystemID string
Password string
SystemType string
}
type BindError struct {
CommandStatus data.CommandStatusType
}
func (err BindError) Error() string {
return fmt.Sprintf("binding error (%s): %s", err.CommandStatus, err.CommandStatus.Desc())
}
func newBindRequest(s Auth, bindingType pdu.BindingType, addressRange pdu.AddressRange) (bindReq *pdu.BindRequest) {
bindReq = pdu.NewBindRequest(bindingType)
bindReq.SystemID = s.SystemID
bindReq.Password = s.Password
bindReq.SystemType = s.SystemType
bindReq.AddressRange = addressRange
return
}
// Connector is connection factory interface.
type Connector interface {
Connect() (conn *Connection, err error)
GetBindType() pdu.BindingType
}
type connector struct {
dialer Dialer
auth Auth
bindingType pdu.BindingType
addressRange pdu.AddressRange
}
func (c *connector) GetBindType() pdu.BindingType {
return c.bindingType
}
func (c *connector) Connect() (conn *Connection, err error) {
conn, err = connect(c.dialer, c.auth.SMSC, newBindRequest(c.auth, c.bindingType, c.addressRange))
return
}
func connect(dialer Dialer, addr string, bindReq *pdu.BindRequest) (c *Connection, err error) {
conn, err := dialer(addr)
if err != nil {
return
}
// create wrapped connection
c = NewConnection(conn)
// send binding request
_, err = c.WritePDU(bindReq)
if err != nil {
_ = conn.Close()
return
}
// catching response
var (
p pdu.PDU
resp *pdu.BindResp
)
for {
if p, err = pdu.Parse(c); err != nil {
_ = conn.Close()
return
}
if pd, ok := p.(*pdu.BindResp); ok {
resp = pd
break
}
}
if resp.CommandStatus != data.ESME_ROK {
err = BindError{CommandStatus: resp.CommandStatus}
_ = conn.Close()
} else {
c.systemID = resp.SystemID
}
return
}
// TXConnector returns a Transmitter (TX) connector.
func TXConnector(dialer Dialer, auth Auth) Connector {
return &connector{
dialer: dialer,
auth: auth,
bindingType: pdu.Transmitter,
}
}
// RXConnector returns a Receiver (RX) connector.
func RXConnector(dialer Dialer, auth Auth, opts ...connectorOption) Connector {
c := &connector{
dialer: dialer,
auth: auth,
bindingType: pdu.Receiver,
}
for _, opt := range opts {
opt(c)
}
return c
}
// TRXConnector returns a Transceiver (TRX) connector.
func TRXConnector(dialer Dialer, auth Auth, opts ...connectorOption) Connector {
c := &connector{
dialer: dialer,
auth: auth,
bindingType: pdu.Transceiver,
}
for _, opt := range opts {
opt(c)
}
return c
}
type connectorOption func(c *connector)
func WithAddressRange(addressRange pdu.AddressRange) connectorOption {
return func(c *connector) {
c.addressRange = addressRange
}
}
================================================
FILE: connect_test.go
================================================
package gosmpp
import (
"github.com/linxGnu/gosmpp/pdu"
"sync/atomic"
"testing"
"github.com/stretchr/testify/require"
)
var currentAuth int32
var auths = [][2]string{
{"689528", "1a97ae"},
}
const (
smscAddr = "127.0.0.1:2775"
mess = "Thử nghiệm: chuẩn bị nế mễ"
)
func nextAuth() Auth {
pair := int(atomic.AddInt32(¤tAuth, 1)) % len(auths)
return Auth{
SMSC: smscAddr,
SystemID: auths[pair][0],
Password: auths[pair][1],
SystemType: "",
}
}
func TestBindingSMSC(t *testing.T) {
checker := func(t *testing.T, c Connector) {
conn, err := c.Connect()
require.Nil(t, err)
require.NotNil(t, conn)
_ = conn.Close()
}
t.Run("TX", func(t *testing.T) {
checker(t, TXConnector(NonTLSDialer, nextAuth()))
})
t.Run("RX", func(t *testing.T) {
checker(t, RXConnector(NonTLSDialer, nextAuth()))
})
t.Run("RX", func(t *testing.T) {
addrRange := pdu.AddressRange{}
addrRange.AddressRange = "31218"
checker(t, RXConnector(NonTLSDialer, nextAuth(), WithAddressRange(addrRange)))
})
t.Run("TRX", func(t *testing.T) {
checker(t, TRXConnector(NonTLSDialer, nextAuth()))
})
t.Run("TRX", func(t *testing.T) {
addrRange := pdu.AddressRange{}
addrRange.AddressRange = "31218"
checker(t, TRXConnector(NonTLSDialer, nextAuth(), WithAddressRange(addrRange)))
})
}
func TestBindingSMSC_Error(t *testing.T) {
auth := Auth{SMSC: smscAddr, SystemID: "invalid"}
checker := func(t *testing.T, c Connector) {
conn, err := c.Connect()
require.ErrorContains(t, err, "Invalid System ID")
_ = conn.Close()
}
t.Run("TX", func(t *testing.T) {
checker(t, TXConnector(NonTLSDialer, auth))
})
t.Run("RX", func(t *testing.T) {
checker(t, RXConnector(NonTLSDialer, auth))
})
t.Run("TRX", func(t *testing.T) {
checker(t, TRXConnector(NonTLSDialer, auth))
})
}
func TestBindingType(t *testing.T) {
auth := Auth{SMSC: smscAddr, SystemID: "invalid"}
t.Run("TX", func(t *testing.T) {
c := TXConnector(NonTLSDialer, auth)
require.Equal(t, c.GetBindType(), pdu.Transmitter)
})
t.Run("RX", func(t *testing.T) {
c := RXConnector(NonTLSDialer, auth)
require.Equal(t, c.GetBindType(), pdu.Receiver)
})
t.Run("TRX", func(t *testing.T) {
c := TRXConnector(NonTLSDialer, auth)
require.Equal(t, c.GetBindType(), pdu.Transceiver)
})
}
================================================
FILE: connection.go
================================================
package gosmpp
import (
"bufio"
"net"
"time"
"github.com/linxGnu/gosmpp/pdu"
)
// Connection wraps over net.Conn with buffered data reader.
type Connection struct {
systemID string
conn net.Conn
reader *bufio.Reader
}
// NewConnection returns a Connection.
func NewConnection(conn net.Conn) (c *Connection) {
c = &Connection{
conn: conn,
reader: bufio.NewReaderSize(conn, 128<<10),
}
return
}
// Read reads data from the connection.
// Read can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *Connection) Read(b []byte) (n int, err error) {
n, err = c.reader.Read(b)
return
}
// Write writes data to the connection.
// Write can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (c *Connection) Write(b []byte) (n int, err error) {
n, err = c.conn.Write(b)
return
}
// WritePDU data to the connection.
func (c *Connection) WritePDU(p pdu.PDU) (n int, err error) {
buf := pdu.NewBuffer(make([]byte, 0, 64))
p.Marshal(buf)
n, err = c.conn.Write(buf.Bytes())
return
}
// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
func (c *Connection) Close() error {
return c.conn.Close()
}
// LocalAddr returns the local network address.
func (c *Connection) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
// RemoteAddr returns the remote network address.
func (c *Connection) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future and pending
// I/O, not just the immediately following call to Read or
// Write. After a deadline has been exceeded, the connection
// can be refreshed by setting a deadline in the future.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
//
// Note that if a TCP connection has keep-alive turned on,
// which is the default unless overridden by Dialer.KeepAlive
// or ListenConfig.KeepAlive, then a keep-alive failure may
// also return a timeout error. On Unix systems a keep-alive
// failure on I/O can be detected using
// errors.Is(err, syscall.ETIMEDOUT).
func (c *Connection) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
// SetReadDeadline sets the deadline for future Read calls
// and any currently-blocked Read call.
// A zero value for t means Read will not time out.
func (c *Connection) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
// SetReadTimeout is equivalent to ReadDeadline(now + timeout)
func (c *Connection) SetReadTimeout(t time.Duration) error {
return c.conn.SetReadDeadline(time.Now().Add(t))
}
// SetWriteDeadline sets the deadline for future Write calls
// and any currently-blocked Write call.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (c *Connection) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
// SetWriteTimeout is equivalent to WriteDeadline(now + timeout)
func (c *Connection) SetWriteTimeout(t time.Duration) error {
return c.conn.SetWriteDeadline(time.Now().Add(t))
}
================================================
FILE: connection_test.go
================================================
package gosmpp
import (
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestConnection(t *testing.T) {
conn, err := net.Dial("tcp", "smscsim.melroselabs.com:2775")
require.Nil(t, err)
c := NewConnection(conn)
defer func() {
_ = c.Close()
}()
t.Log(c.LocalAddr())
t.Log(c.RemoteAddr())
require.Nil(t, c.SetDeadline(time.Now().Add(5*time.Second)))
require.Nil(t, c.SetWriteDeadline(time.Now().Add(5*time.Second)))
require.Nil(t, c.SetReadDeadline(time.Now().Add(5*time.Second)))
}
================================================
FILE: data/7bit.go
================================================
package data
// Source code in this file is copied from: https://github.com/fiorix/go-smpp/master/smpp/encoding/gsm7.go
import (
"bytes"
"errors"
"math"
"golang.org/x/text/encoding"
"golang.org/x/text/transform"
)
// ErrInvalidCharacter means a given character can not be represented in GSM 7-bit encoding.
//
// This can only happen during encoding.
var ErrInvalidCharacter = errors.New("invalid gsm7 character")
// ErrInvalidByte means that a given byte is outside of the GSM 7-bit encoding range.
//
// This can only happen during decoding.
var ErrInvalidByte = errors.New("invalid gsm7 byte")
/*
GSM 7-bit default alphabet and extension table
Source: https://en.wikipedia.org/wiki/GSM_03.38#GSM_7-bit_default_alphabet_and_extension_table_of_3GPP_TS_23.038_/_GSM_03.38
*/
const escapeSequence = 0x1B
var forwardLookup = map[rune]byte{
'@': 0x00, '£': 0x01, '$': 0x02, '¥': 0x03, 'è': 0x04, 'é': 0x05, 'ù': 0x06, 'ì': 0x07,
'ò': 0x08, 'Ç': 0x09, '\n': 0x0a, 'Ø': 0x0b, 'ø': 0x0c, '\r': 0x0d, 'Å': 0x0e, 'å': 0x0f,
'Δ': 0x10, '_': 0x11, 'Φ': 0x12, 'Γ': 0x13, 'Λ': 0x14, 'Ω': 0x15, 'Π': 0x16, 'Ψ': 0x17,
'Σ': 0x18, 'Θ': 0x19, 'Ξ': 0x1a /* 0x1B */, 'Æ': 0x1c, 'æ': 0x1d, 'ß': 0x1e, 'É': 0x1f,
' ': 0x20, '!': 0x21, '"': 0x22, '#': 0x23, '¤': 0x24, '%': 0x25, '&': 0x26, '\'': 0x27,
'(': 0x28, ')': 0x29, '*': 0x2a, '+': 0x2b, ',': 0x2c, '-': 0x2d, '.': 0x2e, '/': 0x2f,
'0': 0x30, '1': 0x31, '2': 0x32, '3': 0x33, '4': 0x34, '5': 0x35, '6': 0x36, '7': 0x37,
'8': 0x38, '9': 0x39, ':': 0x3a, ';': 0x3b, '<': 0x3c, '=': 0x3d, '>': 0x3e, '?': 0x3f,
'¡': 0x40, 'A': 0x41, 'B': 0x42, 'C': 0x43, 'D': 0x44, 'E': 0x45, 'F': 0x46, 'G': 0x47,
'H': 0x48, 'I': 0x49, 'J': 0x4a, 'K': 0x4b, 'L': 0x4c, 'M': 0x4d, 'N': 0x4e, 'O': 0x4f,
'P': 0x50, 'Q': 0x51, 'R': 0x52, 'S': 0x53, 'T': 0x54, 'U': 0x55, 'V': 0x56, 'W': 0x57,
'X': 0x58, 'Y': 0x59, 'Z': 0x5a, 'Ä': 0x5b, 'Ö': 0x5c, 'Ñ': 0x5d, 'Ü': 0x5e, '§': 0x5f,
'¿': 0x60, 'a': 0x61, 'b': 0x62, 'c': 0x63, 'd': 0x64, 'e': 0x65, 'f': 0x66, 'g': 0x67,
'h': 0x68, 'i': 0x69, 'j': 0x6a, 'k': 0x6b, 'l': 0x6c, 'm': 0x6d, 'n': 0x6e, 'o': 0x6f,
'p': 0x70, 'q': 0x71, 'r': 0x72, 's': 0x73, 't': 0x74, 'u': 0x75, 'v': 0x76, 'w': 0x77,
'x': 0x78, 'y': 0x79, 'z': 0x7a, 'ä': 0x7b, 'ö': 0x7c, 'ñ': 0x7d, 'ü': 0x7e, 'à': 0x7f,
}
var forwardEscape = map[rune]byte{
'\f': 0x0A, '^': 0x14, '{': 0x28, '}': 0x29, '\\': 0x2F, '[': 0x3C, '~': 0x3D, ']': 0x3E, '|': 0x40, '€': 0x65,
}
var reverseLookup = map[byte]rune{
0x00: '@', 0x01: '£', 0x02: '$', 0x03: '¥', 0x04: 'è', 0x05: 'é', 0x06: 'ù', 0x07: 'ì',
0x08: 'ò', 0x09: 'Ç', 0x0a: '\n', 0x0b: 'Ø', 0x0c: 'ø', 0x0d: '\r', 0x0e: 'Å', 0x0f: 'å',
0x10: 'Δ', 0x11: '_', 0x12: 'Φ', 0x13: 'Γ', 0x14: 'Λ', 0x15: 'Ω', 0x16: 'Π', 0x17: 'Ψ',
0x18: 'Σ', 0x19: 'Θ', 0x1a: 'Ξ' /* 0x1B */, 0x1c: 'Æ', 0x1d: 'æ', 0x1e: 'ß', 0x1f: 'É',
0x20: ' ', 0x21: '!', 0x22: '"', 0x23: '#', 0x24: '¤', 0x25: '%', 0x26: '&', 0x27: '\'',
0x28: '(', 0x29: ')', 0x2a: '*', 0x2b: '+', 0x2c: ',', 0x2d: '-', 0x2e: '.', 0x2f: '/',
0x30: '0', 0x31: '1', 0x32: '2', 0x33: '3', 0x34: '4', 0x35: '5', 0x36: '6', 0x37: '7',
0x38: '8', 0x39: '9', 0x3a: ':', 0x3b: ';', 0x3c: '<', 0x3d: '=', 0x3e: '>', 0x3f: '?',
0x40: '¡', 0x41: 'A', 0x42: 'B', 0x43: 'C', 0x44: 'D', 0x45: 'E', 0x46: 'F', 0x47: 'G',
0x48: 'H', 0x49: 'I', 0x4a: 'J', 0x4b: 'K', 0x4c: 'L', 0x4d: 'M', 0x4e: 'N', 0x4f: 'O',
0x50: 'P', 0x51: 'Q', 0x52: 'R', 0x53: 'S', 0x54: 'T', 0x55: 'U', 0x56: 'V', 0x57: 'W',
0x58: 'X', 0x59: 'Y', 0x5a: 'Z', 0x5b: 'Ä', 0x5c: 'Ö', 0x5d: 'Ñ', 0x5e: 'Ü', 0x5f: '§',
0x60: '¿', 0x61: 'a', 0x62: 'b', 0x63: 'c', 0x64: 'd', 0x65: 'e', 0x66: 'f', 0x67: 'g',
0x68: 'h', 0x69: 'i', 0x6a: 'j', 0x6b: 'k', 0x6c: 'l', 0x6d: 'm', 0x6e: 'n', 0x6f: 'o',
0x70: 'p', 0x71: 'q', 0x72: 'r', 0x73: 's', 0x74: 't', 0x75: 'u', 0x76: 'v', 0x77: 'w',
0x78: 'x', 0x79: 'y', 0x7a: 'z', 0x7b: 'ä', 0x7c: 'ö', 0x7d: 'ñ', 0x7e: 'ü', 0x7f: 'à',
}
var reverseEscape = map[byte]rune{
0x0A: '\f', 0x14: '^', 0x28: '{', 0x29: '}', 0x2F: '\\', 0x3C: '[', 0x3D: '~', 0x3E: ']', 0x40: '|', 0x65: '€',
}
// ValidateGSM7String returns the characters, in the given text, that can not be represented in GSM 7-bit encoding.
func ValidateGSM7String(text string) []rune {
invalidChars := make([]rune, 0, 4)
for _, r := range text {
if _, ok := forwardLookup[r]; !ok {
if _, ok := forwardEscape[r]; !ok {
invalidChars = append(invalidChars, r)
}
}
}
return invalidChars
}
// ValidateGSM7Buffer returns the bytes, in the given buffer, that are outside of the GSM 7-bit encoding range.
func ValidateGSM7Buffer(buffer []byte) []byte {
invalidBytes := make([]byte, 0, 4)
count := 0
for count < len(buffer) {
b := buffer[count]
if b == escapeSequence {
count++
if count >= len(buffer) {
invalidBytes = append(invalidBytes, b)
break
}
e := buffer[count]
if _, ok := reverseEscape[e]; !ok {
invalidBytes = append(invalidBytes, b, e)
}
} else if _, ok := reverseLookup[b]; !ok {
invalidBytes = append(invalidBytes, b)
}
count++
}
return invalidBytes
}
// GetEscapeChars returns the escape characters in the given text, that doesn't exist in GSM 7-bit DEFAULT alphabet table
func GetEscapeChars(runeText []rune) []rune {
eChars := make([]rune, 0, 4)
for _, r := range runeText {
if _, ok := forwardEscape[r]; ok {
eChars = append(eChars, r)
}
}
return eChars
}
// IsEscapeChar checks if the given rune is an escape char
func IsEscapeChar(c rune) bool {
_, exists := forwardEscape[c]
return exists
}
// GSM7 returns a GSM 7-bit Bit Encoding.
//
// Set the packed flag to true if you wish to convert septets to octets,
// this should be false for most SMPP providers.
func GSM7(packed bool) encoding.Encoding {
return gsm7Encoding{packed: packed}
}
type gsm7Encoding struct {
packed bool
}
func (g gsm7Encoding) NewDecoder() *encoding.Decoder {
return &encoding.Decoder{Transformer: &gsm7Decoder{
packed: g.packed,
}}
}
func (g gsm7Encoding) NewEncoder() *encoding.Encoder {
return &encoding.Encoder{Transformer: &gsm7Encoder{
packed: g.packed,
}}
}
func (g gsm7Encoding) String() string {
if g.packed {
return "GSM 7-bit (Packed)"
}
return "GSM 7-bit (Unpacked)"
}
type gsm7Decoder struct {
packed bool
}
func (g *gsm7Decoder) Reset() { /* not needed */ }
func unpack(src []byte, packed bool) (septets []byte) {
septets = src
if packed {
septets = make([]byte, 0, len(src))
count := 0
for remain := len(src) - count; remain > 0; {
// Unpack by converting octets into septets.
switch {
case remain >= 7:
septets = append(septets, src[count+0]&0x7F<<0)
septets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))
septets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))
septets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))
septets = append(septets, (src[count+4]&0x07<<4)|(src[count+3]&0xF0>>4))
septets = append(septets, (src[count+5]&0x03<<5)|(src[count+4]&0xF8>>3))
septets = append(septets, (src[count+6]&0x01<<6)|(src[count+5]&0xFC>>2))
if src[count+6] > 0 {
septets = append(septets, src[count+6]&0xFE>>1)
}
count += 7
case remain >= 6:
septets = append(septets, src[count+0]&0x7F<<0)
septets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))
septets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))
septets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))
septets = append(septets, (src[count+4]&0x07<<4)|(src[count+3]&0xF0>>4))
septets = append(septets, (src[count+5]&0x03<<5)|(src[count+4]&0xF8>>3))
count += 6
case remain >= 5:
septets = append(septets, src[count+0]&0x7F<<0)
septets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))
septets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))
septets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))
septets = append(septets, (src[count+4]&0x07<<4)|(src[count+3]&0xF0>>4))
count += 5
case remain >= 4:
septets = append(septets, src[count+0]&0x7F<<0)
septets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))
septets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))
septets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))
count += 4
case remain >= 3:
septets = append(septets, src[count+0]&0x7F<<0)
septets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))
septets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))
count += 3
case remain >= 2:
septets = append(septets, src[count+0]&0x7F<<0)
septets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))
count += 2
case remain >= 1:
septets = append(septets, src[count+0]&0x7F<<0)
count++
default:
return
}
remain = len(src) - count
}
}
return
}
func (g *gsm7Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
if len(src) == 0 {
return 0, 0, nil
}
septets := unpack(src, g.packed)
nSeptet := 0
builder := bytes.NewBufferString("")
for nSeptet < len(septets) {
b := septets[nSeptet]
if b == escapeSequence {
nSeptet++
if nSeptet >= len(septets) {
return 0, 0, ErrInvalidByte
}
e := septets[nSeptet]
if r, ok := reverseEscape[e]; ok {
builder.WriteRune(r)
} else {
return 0, 0, ErrInvalidByte
}
} else if r, ok := reverseLookup[b]; ok {
builder.WriteRune(r)
} else {
return 0, 0, ErrInvalidByte
}
nSeptet++
}
text := builder.Bytes()
nDst = len(text)
if len(dst) < nDst {
return 0, 0, transform.ErrShortDst
}
copy(dst, text)
return
}
type gsm7Encoder struct {
packed bool
}
func (g *gsm7Encoder) Reset() {
/* no needed */
}
func pack(dst []byte, septets []byte) (nDst int) {
nSeptet := 0
for remain := len(septets); remain > 0; {
// Pack by converting septets into octets.
switch {
case remain >= 8:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)
dst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)
dst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)
dst[nDst+4] = (septets[nSeptet+4] & 0x70 >> 4) | (septets[nSeptet+5] & 0x1F << 3)
dst[nDst+5] = (septets[nSeptet+5] & 0x60 >> 5) | (septets[nSeptet+6] & 0x3F << 2)
dst[nDst+6] = (septets[nSeptet+6] & 0x40 >> 6) | (septets[nSeptet+7] & 0x7F << 1)
nSeptet += 8
nDst += 7
case remain >= 7:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)
dst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)
dst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)
dst[nDst+4] = (septets[nSeptet+4] & 0x70 >> 4) | (septets[nSeptet+5] & 0x1F << 3)
dst[nDst+5] = (septets[nSeptet+5] & 0x60 >> 5) | (septets[nSeptet+6] & 0x3F << 2)
dst[nDst+6] = septets[nSeptet+6] & 0x40 >> 6
nSeptet += 7
nDst += 7
case remain >= 6:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)
dst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)
dst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)
dst[nDst+4] = (septets[nSeptet+4] & 0x70 >> 4) | (septets[nSeptet+5] & 0x1F << 3)
dst[nDst+5] = septets[nSeptet+5] & 0x60 >> 5
nSeptet += 6
nDst += 6
case remain >= 5:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)
dst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)
dst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)
dst[nDst+4] = septets[nSeptet+4] & 0x70 >> 4
nSeptet += 5
nDst += 5
case remain >= 4:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)
dst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)
dst[nDst+3] = septets[nSeptet+3] & 0x78 >> 3
nSeptet += 4
nDst += 4
case remain >= 3:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)
dst[nDst+2] = septets[nSeptet+2] & 0x7C >> 2
nSeptet += 3
nDst += 3
case remain >= 2:
dst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)
dst[nDst+1] = septets[nSeptet+1] & 0x7E >> 1
nSeptet += 2
nDst += 2
case remain >= 1:
dst[nDst+0] = septets[nSeptet+0] & 0x7F >> 0
nSeptet++
nDst++
default:
return
}
remain = len(septets) - nSeptet
}
return
}
func (g *gsm7Encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
if len(src) == 0 {
return 0, 0, nil
}
text := string(src) // work with []rune (a.k.a string) instead of []byte
septets := make([]byte, 0, len(text))
for _, r := range text {
if v, ok := forwardLookup[r]; ok {
septets = append(septets, v)
} else if v, ok := forwardEscape[r]; ok {
septets = append(septets, escapeSequence, v)
} else {
return 0, 0, ErrInvalidCharacter
}
nSrc++
}
nDst = len(septets)
if g.packed {
nDst = int(math.Ceil(float64(len(septets)) * 7 / 8))
}
if len(dst) < nDst {
return 0, 0, transform.ErrShortDst
}
if !g.packed {
copy(dst, septets)
return nDst, nSrc, nil
}
nDst = pack(dst, septets)
return
}
================================================
FILE: data/7bit_test.go
================================================
package data
// Source code in this file is copied from: https://github.com/fiorix
import (
"encoding/hex"
"fmt"
"reflect"
"testing"
"golang.org/x/text/transform"
)
var validationStringTests = []struct {
Text string
Expected []rune
}{
{Text: "12345678", Expected: []rune{}},
{Text: "12345[6]", Expected: []rune{}},
{Text: "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\f^{}\\[~]|€ÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà", Expected: []rune{}},
{Text: "你", Expected: []rune{'你'}},
}
var validationBufferTests = []struct {
Buffer []byte
Expected []byte
}{
{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, Expected: []byte{}},
{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B, 0x3C, 0x36, 0x1B, 0x3E}, Expected: []byte{}},
{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B}, Expected: []byte{0x1B}},
{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B, 0x00}, Expected: []byte{0x1B, 0x00}},
{Buffer: []byte{0x80, 0x81, 0x82, 0x83}, Expected: []byte{0x80, 0x81, 0x82, 0x83}},
}
var packedTests = []struct {
Text string
Buff []byte
}{
{Text: "", Buff: []byte{}},
{Text: "1", Buff: []byte{0x31}},
{Text: "12", Buff: []byte{0x31, 0x19}},
{Text: "123", Buff: []byte{0x31, 0xD9, 0x0C}},
{Text: "1234", Buff: []byte{0x31, 0xD9, 0x8C, 0x06}},
{Text: "12345", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0x03}},
{Text: "123456", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0x01}},
{Text: "1234567", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0xDD, 0x00}},
{Text: "12345678", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0xDD, 0x70}},
{Text: "123456789", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0xDD, 0x70, 0x39}},
{Text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur nec nunc venenatis, ultricies ipsum id, volutpat ante. Sed pretium ac metus a interdum metus.", Buff: []byte("\xCC\xB7\xBC\xDC\x06\xA5\xE1\xF3\x7A\x1B\x44\x7E\xB3\xDF\x72\xD0\x3C\x4D\x07\x85\xDB\x65\x3A\x0B\x34\x7E\xBB\xE7\xE5\x31\xBD\x4C\xAF\xCB\x41\x61\x72\x1A\x9E\x9E\x8F\xD3\xEE\x33\xA8\xCC\x4E\xD3\x5D\xA0\x61\x5D\x1E\x16\xA7\xE9\x75\x39\xC8\x5D\x1E\x83\xDC\x75\xF7\x18\x64\x2F\xBB\xCB\xEE\x30\x3D\x3D\x67\x81\xEA\x6C\xBA\x3C\x3D\x4E\x97\xE7\xA0\x34\x7C\x5E\x6F\x83\xD2\x64\x16\xC8\xFE\x66\xD7\xE9\xF0\x30\x1D\x14\x76\xD3\xCB\x2E\xD0\xB4\x4C\x06\xC1\xE5\x65\x7A\xBA\xDE\x06\x85\xC7\xA0\x76\x99\x5E\x9F\x83\xC2\xA0\xB4\x9B\x5E\x96\x93\xEB\x6D\x50\xBB\x4C\xAF\xCF\x5D")},
{Text: "\n", Buff: []byte{0x0A}},
{Text: "\r", Buff: []byte{0x0D}},
{Text: "\f", Buff: []byte{0x1B, 0x05}},
{Text: "^{}\\[~]|€", Buff: []byte{0x1B, 0xCA, 0x06, 0xB5, 0x49, 0x6D, 0x5E, 0x1B, 0xDE, 0xA6, 0xB7, 0xF1, 0x6D, 0x80, 0x9B, 0x32}},
{Text: "@£$¥èéùìòÇØøÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà", Buff: []byte("\x80\x80\x60\x40\x28\x18\x0E\x88\xC4\x82\xE1\x78\x40\x22\x92\x09\xA5\x62\xB9\x60\x32\x1A\x4E\xC7\xF3\x01\x85\x44\x23\x52\xC9\x74\x42\xA5\x54\x2B\x56\xCB\xF5\x82\xC5\x64\x33\x5A\xCD\x76\xC3\xE5\x74\x3B\x5E\xCF\xF7\x03\x06\x85\x43\x62\xD1\x78\x44\x26\x95\x4B\x66\xD3\xF9\x84\x46\xA5\x53\x6A\xD5\x7A\xC5\x66\xB5\x5B\x6E\xD7\xFB\x05\x87\xC5\x63\x72\xD9\x7C\x46\xA7\xD5\x6B\x76\xDB\xFD\x86\xC7\xE5\x73\x7A\xDD\x7E\xC7\xE7\xF5\x7B\x7E\xDF\xFF\x07")},
}
var unpackedTests = []struct {
Text string
Buff []byte
}{
{Text: "", Buff: []byte{}},
{Text: "1", Buff: []byte{0x31}},
{Text: "12", Buff: []byte{0x31, 0x32}},
{Text: "123", Buff: []byte{0x31, 0x32, 0x33}},
{Text: "1234", Buff: []byte{0x31, 0x32, 0x33, 0x34}},
{Text: "12345", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35}},
{Text: "123456", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36}},
{Text: "1234567", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37}},
{Text: "12345678", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}},
{Text: "123456789", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39}},
{Text: "12345[6", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B, 0x3C, 0x36}},
{Text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur nec nunc venenatis, ultricies ipsum id, volutpat ante. Sed pretium ac metus a interdum metus.", Buff: []byte("\x4C\x6F\x72\x65\x6D\x20\x69\x70\x73\x75\x6D\x20\x64\x6F\x6C\x6F\x72\x20\x73\x69\x74\x20\x61\x6D\x65\x74\x2C\x20\x63\x6F\x6E\x73\x65\x63\x74\x65\x74\x75\x72\x20\x61\x64\x69\x70\x69\x73\x63\x69\x6E\x67\x20\x65\x6C\x69\x74\x2E\x20\x43\x75\x72\x61\x62\x69\x74\x75\x72\x20\x6E\x65\x63\x20\x6E\x75\x6E\x63\x20\x76\x65\x6E\x65\x6E\x61\x74\x69\x73\x2C\x20\x75\x6C\x74\x72\x69\x63\x69\x65\x73\x20\x69\x70\x73\x75\x6D\x20\x69\x64\x2C\x20\x76\x6F\x6C\x75\x74\x70\x61\x74\x20\x61\x6E\x74\x65\x2E\x20\x53\x65\x64\x20\x70\x72\x65\x74\x69\x75\x6D\x20\x61\x63\x20\x6D\x65\x74\x75\x73\x20\x61\x20\x69\x6E\x74\x65\x72\x64\x75\x6D\x20\x6D\x65\x74\x75\x73\x2E")},
{Text: "\n", Buff: []byte{0x0A}},
{Text: "\r", Buff: []byte{0x0D}},
{Text: "\f", Buff: []byte{0x1B, 0x0A}},
{Text: "^{}\\[~]|€", Buff: []byte{0x1B, 0x14, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2F, 0x1B, 0x3C, 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x40, 0x1B, 0x65}},
{Text: "@£$¥èéùìòÇØøÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà", Buff: []byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F")},
}
var invalidCharacterTests = []struct {
Packed bool
Text string
}{
{Packed: true, Text: "你"},
{Packed: false, Text: "你"},
}
var invalidByteTests = []struct {
Packed bool
Buff []byte
}{
{Packed: false, Buff: []byte{0x80}},
{Packed: false, Buff: []byte{0x1B}},
{Packed: false, Buff: []byte{0x1B, 0x80}},
}
func TestGSM7EncodingString(t *testing.T) {
tests := []struct {
Packed bool
Expected string
}{
{Packed: true, Expected: "GSM 7-bit (Packed)"},
{Packed: false, Expected: "GSM 7-bit (Unpacked)"},
}
for index, row := range tests {
actual := fmt.Sprint(GSM7(row.Packed))
if actual != row.Expected {
t.Fatalf("%d: expected '%s' but got '%s'", index, row.Expected, actual)
}
}
}
func TestValidateGSM7String(t *testing.T) {
for index, row := range validationStringTests {
actual := ValidateGSM7String(row.Text)
if !reflect.DeepEqual(actual, row.Expected) {
t.Fatalf("%2d: actual did not equal expected.\nactual: %s\nexpect: %s", index, string(actual), string(row.Expected))
}
}
}
func TestValidateGSM7Buffer(t *testing.T) {
for index, row := range validationBufferTests {
actual := ValidateGSM7Buffer(row.Buffer)
if !reflect.DeepEqual(actual, row.Expected) {
t.Fatalf("%2d: actual did not equal expected.\nactual: %s\nexpect: %s", index, hex.EncodeToString(actual), hex.EncodeToString(row.Expected))
}
}
}
func TestPackedEncoder(t *testing.T) {
encoder := GSM7(true).NewEncoder()
for index, row := range packedTests {
es, _, err := transform.Bytes(encoder, []byte(row.Text))
if err != nil {
t.Fatalf("%2d: unexpected error: '%s'", index, err.Error())
}
if !reflect.DeepEqual(es, row.Buff) {
t.Fatalf("%2d: actual did not equal expected.\nactual: %s\nexpect: %s", index, hex.EncodeToString(es), hex.EncodeToString(row.Buff))
}
}
}
func TestUnpackedEncoder(t *testing.T) {
encoder := GSM7(false).NewEncoder()
for index, row := range unpackedTests {
es, _, err := transform.Bytes(encoder, []byte(row.Text))
if err != nil {
t.Fatalf("%2d: unexpected error: '%s'", index, err.Error())
}
if !reflect.DeepEqual(es, row.Buff) {
t.Fatalf("%2d: actual did not equal expected.\nactual: %s\nexpect: %s", index, hex.EncodeToString(es), hex.EncodeToString(row.Buff))
}
}
}
func TestPackedDecoder(t *testing.T) {
decoder := GSM7(true).NewDecoder()
for index, row := range packedTests {
es, _, err := transform.Bytes(decoder, row.Buff)
if err != nil {
t.Fatalf("%2d: unexpected error: '%s'", index, err.Error())
}
if string(es) != row.Text {
t.Fatalf("%2d: actual did not equal expected.\nactual: %s\nexpect: %s", index, string(es), row.Text)
}
}
}
func TestUnpackedDecoder(t *testing.T) {
encoder := GSM7(false).NewDecoder()
for index, row := range unpackedTests {
es, _, err := transform.Bytes(encoder, row.Buff)
if err != nil {
t.Fatalf("%2d: unexpected error: '%s'", index, err.Error())
}
if string(es) != row.Text {
t.Fatalf("%2d: actual did not equal expected.\nactual: %s\nexpect: %s", index, string(es), row.Text)
}
}
}
func TestInvalidCharacter(t *testing.T) {
for index, row := range invalidCharacterTests {
encoder := GSM7(row.Packed).NewEncoder()
_, _, err := transform.Bytes(encoder, []byte(row.Text))
if err == nil {
t.Fatalf("%2d: expected error but got no error", index)
}
if err != ErrInvalidCharacter {
t.Fatalf("%2d: expected '%s' but got '%s'", index, ErrInvalidCharacter, err.Error())
}
}
}
func TestInvalidByte(t *testing.T) {
for index, row := range invalidByteTests {
decoder := GSM7(row.Packed).NewDecoder()
_, _, err := transform.Bytes(decoder, row.Buff)
if err == nil {
t.Fatalf("%2d: expected error but got no error", index)
}
if err != ErrInvalidByte {
t.Fatalf("%2d: expected '%s' but got '%s'", index, ErrInvalidByte, err.Error())
}
}
}
================================================
FILE: data/codings.go
================================================
package data
import (
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/encoding/unicode"
)
const (
// GSM7BITCoding is gsm-7bit coding
GSM7BITCoding byte = 0x00
// ASCIICoding is ascii coding
ASCIICoding byte = 0x01
// BINARY8BIT1Coding is 8-bit binary coding
BINARY8BIT1Coding byte = 0x02
// LATIN1Coding is iso-8859-1 coding
LATIN1Coding byte = 0x03
// BINARY8BIT2Coding is 8-bit binary coding
BINARY8BIT2Coding byte = 0x04
// CYRILLICCoding is iso-8859-5 coding
CYRILLICCoding byte = 0x06
// HEBREWCoding is iso-8859-8 coding
HEBREWCoding byte = 0x07
// UCS2Coding is UCS2 coding
UCS2Coding byte = 0x08
)
// EncDec wraps encoder and decoder interface.
type EncDec interface {
Encode(str string) ([]byte, error)
Decode([]byte) (string, error)
}
// Encoding interface.
type Encoding interface {
EncDec
DataCoding() byte
}
func encode(str string, encoder *encoding.Encoder) ([]byte, error) {
return encoder.Bytes([]byte(str))
}
func decode(data []byte, decoder *encoding.Decoder) (st string, err error) {
tmp, err := decoder.Bytes(data)
if err == nil {
st = string(tmp)
}
return
}
// CustomEncoding is wrapper for user-defined data encoding.
type CustomEncoding struct {
encDec EncDec
coding byte
}
// NewCustomEncoding creates new custom encoding.
func NewCustomEncoding(coding byte, encDec EncDec) Encoding {
return &CustomEncoding{
coding: coding,
encDec: encDec,
}
}
// Encode string.
func (c *CustomEncoding) Encode(str string) ([]byte, error) {
return c.encDec.Encode(str)
}
// Decode data to string.
func (c *CustomEncoding) Decode(data []byte) (string, error) {
return c.encDec.Decode(data)
}
// DataCoding flag.
func (c *CustomEncoding) DataCoding() byte {
return c.coding
}
type gsm7bit struct {
packed bool
}
func (c *gsm7bit) Encode(str string) ([]byte, error) {
return encode(str, GSM7(c.packed).NewEncoder())
}
func (c *gsm7bit) Decode(data []byte) (string, error) {
return decode(data, GSM7(c.packed).NewDecoder())
}
func (c *gsm7bit) DataCoding() byte { return GSM7BITCoding }
func (c *gsm7bit) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
if c.packed {
return uint((len(text)*7+7)/8) > octetLimit
} else {
return uint(len(text)) > octetLimit
}
}
func (c *gsm7bit) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
if octetLimit < 64 {
octetLimit = 134
}
allSeg = [][]byte{}
runeSlice := []rune(text)
fr, to := 0, int(octetLimit)
for fr < len(runeSlice) {
if to > len(runeSlice) {
to = len(runeSlice)
}
seg, err := c.Encode(string(runeSlice[fr:to]))
if err != nil {
return nil, err
}
allSeg = append(allSeg, seg)
fr, to = to, to+int(octetLimit)
}
return
}
type gsm7bitPacked struct {
}
func (c *gsm7bitPacked) Encode(str string) ([]byte, error) {
return encode(str, GSM7(true).NewEncoder())
}
func (c *gsm7bitPacked) Decode(data []byte) (string, error) {
return decode(data, GSM7(true).NewDecoder())
}
func (c *gsm7bitPacked) DataCoding() byte { return GSM7BITCoding }
func (c *gsm7bitPacked) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
runeSlice := []rune(text)
tLen := len(runeSlice)
escCharsLen := len(GetEscapeChars(runeSlice))
regCharsLen := tLen - escCharsLen
// Esacpe characters occupy 2 octets/septets
// https://en.wikipedia.org/wiki/GSM_03.38
// https://www.developershome.com/sms/gsmAlphabet.asp
return uint((regCharsLen*7+escCharsLen*2*7+7)/8) > octetLimit
}
func (c *gsm7bitPacked) GetSeptetCount(runeSlice []rune) int {
tLen := len(runeSlice)
escCharsLen := len(GetEscapeChars(runeSlice))
regCharsLen := tLen - escCharsLen
return escCharsLen*2 + regCharsLen
}
func (c *gsm7bitPacked) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
if octetLimit < 64 {
octetLimit = 134
}
allSeg = [][]byte{}
runeSlice := []rune(text)
lim := int(octetLimit * 8 / 7)
fr, to := 0, lim
for fr < len(runeSlice) {
if to > len(runeSlice) {
to = len(runeSlice)
}
to = determineTo(fr, to, lim, runeSlice)
seg, err := c.Encode(string(runeSlice[fr:to]))
if err != nil {
return nil, err
}
includeLSB := false
nSeptet := c.GetSeptetCount(runeSlice[fr:to])
if nSeptet != lim && nSeptet%8 == 0 { // The last octet's LSB should be included during shift
includeLSB = true
}
seg = shiftBitsLeftOne(seg, includeLSB)
allSeg = append(allSeg, seg)
fr, to = to, to+lim
}
return
}
func determineTo(from int, to int, lim int, runeSlice []rune) int {
nSeptet := 0
for nSeptet < lim {
if IsEscapeChar(runeSlice[from]) { // esc chars counted as 2 septes
nSeptet += 2
} else {
nSeptet++
}
from++
if from == to {
break
}
}
to = from
if IsEscapeChar(runeSlice[to-1]) { // 9.2.3.24.1 Concatenated Short Messages "A character represented by an escape-sequence shall not be split in the middle."
if nSeptet > lim {
to--
}
}
return to
}
// Shifts the given byte stream one position left, in order to put a padding bit in between UDH and the beginning of the septets of an actual message
// Ref1: https://www.etsi.org/deliver/etsi_ts/123000_123099/123040/16.00.00_60/ts_123040v160000p.pdf Page 74
// Ref2: https://help.goacoustic.com/hc/en-us/articles/360043843154--How-character-encoding-affects-SMS-message-length Pls. ref. to the note "..It is added as padding so that the actual 7-bit encoding data begins on a septet boundary—the 50th bit."
// Ref3: https://en.wikipedia.org/wiki/Concatenated_SMS "..This means up to 6 bits of zeros need to be inserted at the start of the [message]."
func shiftBitsLeftOne(input []byte, includeLSB bool) []byte {
shifted := make([]byte, len(input))
for i, b := range input {
shifted[i] = b << 1
if i > 0 {
shifted[i] |= input[i-1] >> 7
}
}
if includeLSB {
lastOctet := (input[len(input)-1] >> 7 & 0x01) | (0x0D << 1) /* https://en.wikipedia.org/wiki/GSM_03.38 Ref tekst: "..When there are 7 spare bits in the last octet of a message..."*/
shifted = append(shifted, lastOctet)
}
return shifted
}
type ascii struct{}
func (*ascii) Encode(str string) ([]byte, error) {
return []byte(str), nil
}
func (*ascii) Decode(data []byte) (string, error) {
return string(data), nil
}
func (*ascii) DataCoding() byte { return ASCIICoding }
type iso88591 struct{}
func (*iso88591) Encode(str string) ([]byte, error) {
return encode(str, charmap.ISO8859_1.NewEncoder())
}
func (*iso88591) Decode(data []byte) (string, error) {
return decode(data, charmap.ISO8859_1.NewDecoder())
}
func (*iso88591) DataCoding() byte { return LATIN1Coding }
type binary8bit1 struct{}
func (*binary8bit1) Encode(_ string) ([]byte, error) {
return []byte{}, ErrNotImplEncode
}
func (*binary8bit1) Decode(_ []byte) (string, error) {
return "", ErrNotImplDecode
}
func (*binary8bit1) DataCoding() byte { return BINARY8BIT1Coding }
type binary8bit2 struct{}
func (*binary8bit2) Encode(_ string) ([]byte, error) {
return []byte{}, ErrNotImplEncode
}
func (*binary8bit2) Decode(_ []byte) (string, error) {
return "", ErrNotImplDecode
}
func (*binary8bit2) DataCoding() byte { return BINARY8BIT2Coding }
type iso88595 struct{}
func (*iso88595) Encode(str string) ([]byte, error) {
return encode(str, charmap.ISO8859_5.NewEncoder())
}
func (*iso88595) Decode(data []byte) (string, error) {
return decode(data, charmap.ISO8859_5.NewDecoder())
}
func (*iso88595) DataCoding() byte { return CYRILLICCoding }
type iso88598 struct{}
func (*iso88598) Encode(str string) ([]byte, error) {
return encode(str, charmap.ISO8859_8.NewEncoder())
}
func (*iso88598) Decode(data []byte) (string, error) {
return decode(data, charmap.ISO8859_8.NewDecoder())
}
func (*iso88598) DataCoding() byte { return HEBREWCoding }
type ucs2 struct{}
func (*ucs2) Encode(str string) ([]byte, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
return encode(str, tmp.NewEncoder())
}
func (*ucs2) Decode(data []byte) (string, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
return decode(data, tmp.NewDecoder())
}
func (*ucs2) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
runeSlice := []rune(text)
return uint(len(runeSlice)*2) > octetLimit
}
func (c *ucs2) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
if octetLimit < 64 {
octetLimit = 134
}
allSeg = [][]byte{}
runeSlice := []rune(text)
hextetLim := int(octetLimit / 2) // round down
// hextet = 16 bits, the correct terms should be hexadectet
fr, to := 0, hextetLim
for fr < len(runeSlice) {
if to > len(runeSlice) {
to = len(runeSlice)
}
seg, err := c.Encode(string(runeSlice[fr:to]))
if err != nil {
return nil, err
}
allSeg = append(allSeg, seg)
fr, to = to, to+hextetLim
}
return
}
func (*ucs2) DataCoding() byte { return UCS2Coding }
var (
// GSM7BIT is gsm-7bit encoding.
GSM7BIT Encoding = &gsm7bit{packed: false}
// GSM7BITPACKED is packed gsm-7bit encoding.
// Most of SMSC(s) use unpack version.
// Should be tested before using.
GSM7BITPACKED Encoding = &gsm7bitPacked{}
// ASCII is ascii encoding.
ASCII Encoding = &ascii{}
// BINARY8BIT1 is binary 8-bit encoding.
BINARY8BIT1 Encoding = &binary8bit1{}
// LATIN1 encoding.
LATIN1 Encoding = &iso88591{}
// BINARY8BIT2 is binary 8-bit encoding.
BINARY8BIT2 Encoding = &binary8bit2{}
// CYRILLIC encoding.
CYRILLIC Encoding = &iso88595{}
// HEBREW encoding.
HEBREW Encoding = &iso88598{}
// UCS2 encoding.
UCS2 Encoding = &ucs2{}
)
var codingMap = map[byte]Encoding{
GSM7BITCoding: GSM7BIT,
ASCIICoding: ASCII,
BINARY8BIT1Coding: BINARY8BIT1,
LATIN1Coding: LATIN1,
BINARY8BIT2Coding: BINARY8BIT2,
CYRILLICCoding: CYRILLIC,
HEBREWCoding: HEBREW,
UCS2Coding: UCS2,
}
// FromDataCoding returns encoding from DataCoding value.
func FromDataCoding(code byte) (enc Encoding) {
enc, ok := codingMap[code]
if !ok { // if encoding is reserved (custom)
enc = NewCustomEncoding(code, GSM7BIT) // GSM7BIT is a temporary patch to apply to Encode/Decode methods
}
return
}
// Splitter extend encoding object by defining a split function
// that split a string into multiple segments
// Each segment string, when encoded, must be within a certain octet limit
type Splitter interface {
// ShouldSplit check if the encoded data of given text should be splitted under octetLimit
ShouldSplit(text string, octetLimit uint) (should bool)
EncodeSplit(text string, octetLimit uint) ([][]byte, error)
}
================================================
FILE: data/codings_test.go
================================================
package data
import (
"encoding/hex"
"log"
"testing"
"github.com/stretchr/testify/require"
)
func fromHex(h string) (v []byte) {
var err error
v, err = hex.DecodeString(h)
if err != nil {
log.Fatal(err)
}
return
}
func testEncoding(t *testing.T, enc EncDec, original, expected string) {
encoded, err := enc.Encode(original)
require.Nil(t, err)
require.Equal(t, fromHex(expected), encoded)
decoded, err := enc.Decode(encoded)
require.Nil(t, err)
require.Equal(t, original, decoded)
}
func testEncodingSplit(t *testing.T, enc EncDec, octetLim uint, original string, expected []string, expectDecode []string) {
splitter, ok := enc.(Splitter)
require.Truef(t, ok, "Encoding must implement Splitter interface")
segEncoded, err := splitter.EncodeSplit(original, octetLim)
require.Nil(t, err)
for i, seg := range segEncoded {
require.Equal(t, fromHex(expected[i]), seg)
require.LessOrEqualf(t, uint(len(seg)), octetLim,
"Segment len must be less than or equal to %d, got %d", octetLim, len(seg))
if enc == GSM7BITPACKED {
seg = shiftBitsOneRight(seg)
}
decoded, err := enc.Decode(seg)
require.Nil(t, err)
require.Equal(t, expectDecode[i], decoded)
}
}
func shiftBitsOneRight(input []byte) []byte {
carry := byte(0)
for i := len(input) - 1; i >= 0; i-- {
// Save the carry bit from the previous byte
nextCarry := input[i] & 0b00000001
// Shift the current byte to the right
input[i] >>= 1
// Apply the carry from the previous byte to the current byte
input[i] |= carry << 7
// Update the carry for the next byte
carry = nextCarry
}
return input
}
func TestCoding(t *testing.T) {
require.Nil(t, FromDataCoding(12))
require.Equal(t, GSM7BIT, FromDataCoding(0))
require.Equal(t, ASCII, FromDataCoding(1))
require.Equal(t, UCS2, FromDataCoding(8))
require.Equal(t, LATIN1, FromDataCoding(3))
require.Equal(t, CYRILLIC, FromDataCoding(6))
require.Equal(t, HEBREW, FromDataCoding(7))
}
func TestGSM7Bit(t *testing.T) {
require.EqualValues(t, 0, GSM7BITPACKED.DataCoding())
testEncoding(t, GSM7BITPACKED, "gjwklgjkwP123+?", "67f57dcd3eabd777684c365bfd00")
}
func TestShouldSplit(t *testing.T) {
t.Run("testShouldSplit_GSM7BIT", func(t *testing.T) {
octetLim := uint(140)
expect := map[string]bool{
"": false,
"1": false,
"12312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311234121212": false,
"123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112342212121": true,
}
splitter, _ := GSM7BIT.(Splitter)
for k, v := range expect {
ok := splitter.ShouldSplit(k, octetLim)
require.Equalf(t, ok, v, "Test case %s", k)
}
})
t.Run("testShouldSplit_UCS2", func(t *testing.T) {
octetLim := uint(140)
expect := map[string]bool{
"": false,
"1": false,
"ởỀÊộẩừỰÉÊỗọễệớỡồỰỬỪựởặỬ̀ỵổẤỨợỶẰỢộứẶHữẹ̃ẾỆằỄéậÃỡẰộ̀ỀỗứẲữỪữộÊỵòALữộòC": false, /* 70 UCS2 chars */
"ợÁÊGỷẹííỡỮÂIỆàúễẠỮỊệÂỖÍắẵYẠừẲíộờíẵỠựẤằờởể̃ởỵởềệổồUỡỵầễÁÝởÝNè̉ỚổôỊộợKỨệ́": true, /* 71 UCS2 chars */
}
splitter, _ := UCS2.(Splitter)
for k, v := range expect {
ok := splitter.ShouldSplit(k, octetLim)
require.Equalf(t, ok, v, "Test case %s", k)
}
})
t.Run("testShouldSplit_GSM7BITPACKED", func(t *testing.T) {
octetLim := uint(140)
expect := map[string]bool{
"": false,
"1": false,
"12312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311234121212": false,
"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqw": false, /* 160 regular basic alphabet chars */
"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqwd": true, /* 161 regular basic alphabet chars */
"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqw{": true, /* 159 regular basic alphabet chars + 1 escape char at the end */
"|}€€|]|€[~€^]€~{~^{|]]|[{|~€^|]^[[{€^]^{€}}^~~]€]~€[€€[]~~[}}]{^}{|}~~]]€^{^|€{^": false, /* 80 escape chars */
"|}€€|]|€[~€^]€~{~^{|]]|[{|~€^|]^[[{€^]^{€}}^~~]€]~€[€€[]~~[}}]{^}{|}~~]]€^{^|€{^{": true, /* 81 escape chars */
}
splitter, _ := GSM7BITPACKED.(Splitter)
for k, v := range expect {
ok := splitter.ShouldSplit(k, octetLim)
require.Equalf(t, ok, v, "Test case %s", k)
}
})
}
func TestSplit(t *testing.T) {
require.EqualValues(t, 0o0, GSM7BITPACKED.DataCoding())
t.Run("testSplitGSM7Empty", func(t *testing.T) {
testEncodingSplit(t, GSM7BIT,
134,
"",
[]string{
"",
},
[]string{
"",
})
})
t.Run("testSplitUCS2", func(t *testing.T) {
testEncodingSplit(t, UCS2,
134,
"biggest gift của Christmas là có nhiều big/challenging/meaningful problems để sấp mặt làm",
[]string{
"006200690067006700650073007400200067006900660074002000631ee700610020004300680072006900730074006d006100730020006c00e00020006300f30020006e006800691ec100750020006200690067002f006300680061006c006c0065006e00670069006e0067002f006d00650061006e0069006e006700660075006c00200070",
"0072006f0062006c0065006d0073002001111ec3002000731ea500700020006d1eb700740020006c00e0006d",
},
[]string{
"biggest gift của Christmas là có nhiều big/challenging/meaningful p",
"roblems để sấp mặt làm",
})
})
t.Run("testSplitUCS2Empty", func(t *testing.T) {
testEncodingSplit(t, UCS2,
134,
"",
[]string{
"",
},
[]string{
"",
})
})
// UCS2 character should not be splitted in the middle
// here 54 character is encoded to 108 octet, but since there are 107 octet limit,
// a whole 2 octet has to be carried over to the next segment
t.Run("testSplit_Middle_UCS2", func(t *testing.T) {
testEncodingSplit(t, UCS2,
107,
"biggest gift của Christmas là có nhiều big/challenging",
[]string{
"006200690067006700650073007400200067006900660074002000631ee700610020004300680072006900730074006d006100730020006c00e00020006300f30020006e006800691ec100750020006200690067002f006300680061006c006c0065006e00670069006e",
"0067", // 0x00 0x67 is "g"
},
[]string{
"biggest gift của Christmas là có nhiều big/challengin",
"g",
})
})
}
func TestSplit_GSM7BITPACKED(t *testing.T) {
require.EqualValues(t, 0o0, GSM7BITPACKED.DataCoding())
t.Run("testSplit_Escape_GSM7BITPACKED", func(t *testing.T) {
testEncodingSplit(t, GSM7BITPACKED,
134,
"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqw{",
[]string{
"ceeafb9a7d56afefd0986cb6facdc37372784e0ec7efe4f89d1cbf93e37772fc4e8edfc9f13b397e27c7efe4f89d1cbf93e37772fc4e8edfc9f13b397e27c7efe438397e27c7efc4e8951cbf93e37772fc4e8edfeff13b397e27c7ef6472fc4e8edfc9f13b397e27c7efe4f89d1cbf93e37772fc4e8edfc9f13b394ebec7c9f17bfc4e8edfc9",
"e2f7f89d1cbf6f50",
},
[]string{
"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwd",
"qwqwdqw{",
})
})
/*
Total char count = 160,
Esc char count = 1,
Regular char count = 159,
Seg1 => 153->€
Expected behaviour: Should not split in the middle of ESC chars
*/
t.Run("testSplit_EscEndOfSeg1_GSM7BITPACKED", func(t *testing.T) {
testEncodingSplit(t, GSM7BITPACKED,
134,
"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp€ppppppp",
[]string{
"e070381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c31b",
"3665381c0e87c3e1",
},
[]string{
"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp\r",
"€ppppppp",
})
})
/*
Total char count = 160,
Esc char count = 2,
Regular char count = 158,
Seg1 => 152-> ....{
Seg2 => 1-> ....{
Expected behaviour: Should not split in the middle of ESC chars
*/
t.Run("testSplit_EscEndOfSeg1AndSeg2_1_GSM7BITPACKED", func(t *testing.T) {
testEncodingSplit(t, GSM7BITPACKED,
134,
"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp{{pppppppp",
[]string{
"e070381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0edfa01a",
"3628381c0e87c3e170",
},
[]string{
"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp{\r",
"{pppppppp",
})
})
/*
Total char count = 160,
Esc char count = 2,
Regular char count = 158,
Seg1 => 152-> ....€
Seg2 => 1-> ....€
Expected behaviour: Should not split in the middle of ESC chars
*/
t.Run("testSplit_EscEndOfSeg1AndSeg2_2_GSM7BITPACKED", func(t *testing.T) {
testEncodingSplit(t, GSM7BITPACKED,
134,
"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp€€pppppppp",
[]string{
"e070381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0edf941b",
"3665381c0e87c3e170",
},
[]string{
"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp€\r",
"€pppppppp",
})
})
/*
Total char count = 162,
Esc char count = 0,
Regular char count = 162,
Seg1 => 153
Seg2 => 9
Scenario: All charcters in the GSM7Bit Basic Character Set table (non-escape chars) https://en.wikipedia.org/wiki/GSM_03.38
*/
t.Run("testSplit_AllGSM7BitBasicCharset_GSM7BITPACKED", func(t *testing.T) {
testEncodingSplit(t, GSM7BITPACKED,
134,
"ΩØ;19Ξòå1-¤6aΞΘANanΣ¡>)òΦ3L;aøΛ-o@>I¥1=-ü!N¤&o9Hmda3jΞ@ÅΣlhEE§/:Çù0Θ&:_&Π;KLÅÅ@fÜ-kFH?ΠB5/ÆΓ?55=<Ω¡N2ñ¥*L¤aÖ! ÖΘ+øF£_Ç?øΔΓ-lèòCìnEBmhÉF*<Åi/aΩ¥CDøfGÇ$/=Λ'ÅA3ò#fkù",
[]string{
"2a8b5d2ca7413c622d922dacc9049d613706e84b212433e62ecca0b4de005f7210ebb5fc2127c9f4ce21dbe4f04cad0138306c74b1f87de9120658c6a48b982cbb25d3e10098bdadb511f9b3086b2fcee457abf57815a053d61fa898a4303704e266560c632092f8312093169b80181edc45611bfd31aa788ef42b5c190c890cf3312178f528",
"4e8ee00c3132af0d",
},
[]string{
"ΩØ;19Ξòå1-¤6aΞΘANanΣ¡>)òΦ3L;aøΛ-o@>I¥1=-ü!N¤&o9Hmda3jΞ@ÅΣlhEE§/:Çù0Θ&:_&Π;KLÅÅ@fÜ-kFH?ΠB5/ÆΓ?55=<Ω¡N2ñ¥*L¤aÖ! ÖΘ+øF£_Ç?øΔΓ-lèòCìnEBmhÉF*<Åi/aΩ¥CDøfGÇ$/=Λ",
"'ÅA3ò#fkù",
})
})
/*
Total char count = 81,
Esc char count = 81,
Regular char count = 0,
Seg1 => 153
Seg2 => 9
Scenario: All charcters in the GSM7Bit Escape Character Set table https://en.wikipedia.org/wiki/GSM_03.38
*/
t.Run("testSplit_AllGSM7BitBasicCharset_GSM7BITPACKED", func(t *testing.T) {
testEncodingSplit(t, GSM7BITPACKED,
134,
"|{[€|^€[{|€{[|^{~[}€|}|^|^[^]€{[]~}€]{{^|^][€]|€~€^[~}^]{]~{^^€^[~|^]|€~|^€{]{~|}",
[]string{
"36c00d6ac3db9437c00d6553def036a80d7053dea036bc0d7043d9a036bd0d6f93da9437c04d6a03dc5036c00d65c3db5036be4d7983daf036be4d6f93da9437be0d6a83da5036c00d65e3dbf036e58d6f03dc9437bd4d7943d9f036bd4d6a43d9f836a88d6fd3dba036940d6553de5036bc4d6f03dc5036be0d7053def436c00d6553dea01a",
"36be0d6ad3db003729",
},
[]string{
"|{[€|^€[{|€{[|^{~[}€|}|^|^[^]€{[]~}€]{{^|^][€]|€~€^[~}^]{]~{^^€^[~|^]|€~|^€{\r",
"]{~|}",
})
})
}
func TestAscii(t *testing.T) {
require.EqualValues(t, 1, ASCII.DataCoding())
testEncoding(t, ASCII, "agjwklgjkwP", "61676a776b6c676a6b7750")
}
func TestUCS2(t *testing.T) {
require.EqualValues(t, 8, UCS2.DataCoding())
testEncoding(t, UCS2, "agjwklgjkwP", "00610067006a0077006b006c0067006a006b00770050")
}
func TestLatin1(t *testing.T) {
require.EqualValues(t, 3, LATIN1.DataCoding())
testEncoding(t, LATIN1, "agjwklgjkwPÓ", "61676a776b6c676a6b7750d3")
}
func TestCYRILLIC(t *testing.T) {
require.EqualValues(t, 6, CYRILLIC.DataCoding())
testEncoding(t, CYRILLIC, "agjwklgjkwPф", "61676A776B6C676A6B7750E4")
}
func TestHebrew(t *testing.T) {
require.EqualValues(t, 7, HEBREW.DataCoding())
testEncoding(t, HEBREW, "agjwklgjkwPץ", "61676A776B6C676A6B7750F5")
}
func TestOtherCodings(t *testing.T) {
testEncoding(t, UTF16BEM, "ngưỡng cứa cuỗc đợi", "feff006e006701b01ee1006e0067002000631ee900610020006300751ed70063002001111ee30069")
testEncoding(t, UTF16LEM, "ngưỡng cứa cuỗc đợi", "fffe6e006700b001e11e6e00670020006300e91e6100200063007500d71e630020001101e31e6900")
testEncoding(t, UTF16BE, "ngưỡng cứa cuỗc đợi", "006e006701b01ee1006e0067002000631ee900610020006300751ed70063002001111ee30069")
testEncoding(t, UTF16LE, "ngưỡng cứa cuỗc đợi", "6e006700b001e11e6e00670020006300e91e6100200063007500d71e630020001101e31e6900")
}
type noOpEncDec struct{}
func (*noOpEncDec) Encode(str string) ([]byte, error) {
return []byte(str), nil
}
func (*noOpEncDec) Decode(data []byte) (string, error) {
return string(data), nil
}
func TestCustomEncoding(t *testing.T) {
enc := NewCustomEncoding(GSM7BITCoding, &noOpEncDec{})
require.EqualValues(t, GSM7BITCoding, enc.DataCoding())
encoded, err := enc.Encode("abc")
require.NoError(t, err)
require.Equal(t, []byte("abc"), encoded)
decoded, err := enc.Decode(encoded)
require.NoError(t, err)
require.Equal(t, "abc", decoded)
}
================================================
FILE: data/header_data.go
================================================
//go:generate stringer -type=CommandStatusType,CommandIDType -output header_data_string.go
package data
// CommandStatusType is type of command status
type CommandStatusType int32
// CommandIDType is type of command id.
type CommandIDType int32
// nolint
const (
// SMPP Command ID Set
GENERIC_NACK = CommandIDType(-2147483648)
BIND_RECEIVER = CommandIDType(0x00000001)
BIND_RECEIVER_RESP = CommandIDType(-2147483647)
BIND_TRANSMITTER = CommandIDType(0x00000002)
BIND_TRANSMITTER_RESP = CommandIDType(-2147483646)
QUERY_SM = CommandIDType(0x00000003)
QUERY_SM_RESP = CommandIDType(-2147483645)
SUBMIT_SM = CommandIDType(0x00000004)
SUBMIT_SM_RESP = CommandIDType(-2147483644)
DELIVER_SM = CommandIDType(0x00000005)
DELIVER_SM_RESP = CommandIDType(-2147483643)
UNBIND = CommandIDType(0x00000006)
UNBIND_RESP = CommandIDType(-2147483642)
REPLACE_SM = CommandIDType(0x00000007)
REPLACE_SM_RESP = CommandIDType(-2147483641)
CANCEL_SM = CommandIDType(0x00000008)
CANCEL_SM_RESP = CommandIDType(-2147483640)
BIND_TRANSCEIVER = CommandIDType(0x00000009)
BIND_TRANSCEIVER_RESP = CommandIDType(-2147483639)
OUTBIND = CommandIDType(0x0000000B)
ENQUIRE_LINK = CommandIDType(0x00000015)
ENQUIRE_LINK_RESP = CommandIDType(-2147483627)
SUBMIT_MULTI = CommandIDType(0x00000021)
SUBMIT_MULTI_RESP = CommandIDType(-2147483615)
ALERT_NOTIFICATION = CommandIDType(0x00000102)
DATA_SM = CommandIDType(0x00000103)
DATA_SM_RESP = CommandIDType(-2147483389)
)
// nolint
const (
// Command_Status Error Codes
ESME_ROK = CommandStatusType(0x00000000) // No Error
ESME_RINVMSGLEN = CommandStatusType(0x00000001) // Message Length is invalid
ESME_RINVCMDLEN = CommandStatusType(0x00000002) // Command Length is invalid
ESME_RINVCMDID = CommandStatusType(0x00000003) // Invalid Command ID
ESME_RINVBNDSTS = CommandStatusType(0x00000004) // Incorrect BIND Status for given command
ESME_RALYBND = CommandStatusType(0x00000005) // ESME Already in Bound State
ESME_RINVPRTFLG = CommandStatusType(0x00000006) // Invalid Priority Flag
ESME_RINVREGDLVFLG = CommandStatusType(0x00000007) // Invalid Registered Delivery Flag
ESME_RSYSERR = CommandStatusType(0x00000008) // System Error
ESME_RINVSRCADR = CommandStatusType(0x0000000A) // Invalid Source Address
ESME_RINVDSTADR = CommandStatusType(0x0000000B) // Invalid Dest Addr
ESME_RINVMSGID = CommandStatusType(0x0000000C) // Message ID is invalid
ESME_RBINDFAIL = CommandStatusType(0x0000000D) // Bind Failed
ESME_RINVPASWD = CommandStatusType(0x0000000E) // Invalid Password
ESME_RINVSYSID = CommandStatusType(0x0000000F) // Invalid System ID
ESME_RCANCELFAIL = CommandStatusType(0x00000011) // Cancel SM Failed
ESME_RREPLACEFAIL = CommandStatusType(0x00000013) // Replace SM Failed
ESME_RMSGQFUL = CommandStatusType(0x00000014) // Message Queue Full
ESME_RINVSERTYP = CommandStatusType(0x00000015) // Invalid Service Type
ESME_RADDCUSTFAIL = CommandStatusType(0x00000019) // Failed to Add Customer
ESME_RDELCUSTFAIL = CommandStatusType(0x0000001A) // Failed to delete Customer
ESME_RMODCUSTFAIL = CommandStatusType(0x0000001B) // Failed to modify customer
ESME_RENQCUSTFAIL = CommandStatusType(0x0000001C) // Failed to Enquire Customer
ESME_RINVCUSTID = CommandStatusType(0x0000001D) // Invalid Customer ID
ESME_RINVCUSTNAME = CommandStatusType(0x0000001F) // Invalid Customer Name
ESME_RINVCUSTADR = CommandStatusType(0x00000021) // Invalid Customer Address
ESME_RINVADR = CommandStatusType(0x00000022) // Invalid Address
ESME_RCUSTEXIST = CommandStatusType(0x00000023) // Customer Exists
ESME_RCUSTNOTEXIST = CommandStatusType(0x00000024) // Customer does not exist
ESME_RADDDLFAIL = CommandStatusType(0x00000026) // Failed to Add DL
ESME_RMODDLFAIL = CommandStatusType(0x00000027) // Failed to modify DL
ESME_RDELDLFAIL = CommandStatusType(0x00000028) // Failed to Delete DL
ESME_RVIEWDLFAIL = CommandStatusType(0x00000029) // Failed to View DL
ESME_RLISTDLSFAIL = CommandStatusType(0x00000030) // Failed to list DLs
ESME_RPARAMRETFAIL = CommandStatusType(0x00000031) // Param Retrieve Failed
ESME_RINVPARAM = CommandStatusType(0x00000032) // Invalid Param
ESME_RINVNUMDESTS = CommandStatusType(0x00000033) // Invalid number of destinations
ESME_RINVDLNAME = CommandStatusType(0x00000034) // Invalid Distribution List name
ESME_RINVDLMEMBDESC = CommandStatusType(0x00000035) // Invalid DL Member Description
ESME_RINVDLMEMBTYP = CommandStatusType(0x00000038) // Invalid DL Member Type
ESME_RINVDLMODOPT = CommandStatusType(0x00000039) // Invalid DL Modify Option
ESME_RINVDESTFLAG = CommandStatusType(0x00000040) // Destination flag is invalid (submit_multi)
ESME_RINVSUBREP = CommandStatusType(0x00000042) // Invalid ‘submit with replace’ request (i.e. submit_sm with replace_if_present_flag set)
ESME_RINVESMCLASS = CommandStatusType(0x00000043) // Invalid esm_class field data
ESME_RCNTSUBDL = CommandStatusType(0x00000044) // Cannot Submit to Distribution List
ESME_RSUBMITFAIL = CommandStatusType(0x00000045) // submit_sm or submit_multi failed
ESME_RINVSRCTON = CommandStatusType(0x00000048) // Invalid Source address TON
ESME_RINVSRCNPI = CommandStatusType(0x00000049) // Invalid Source address NPI
ESME_RINVDSTTON = CommandStatusType(0x00000050) // Invalid Destination address TON
ESME_RINVDSTNPI = CommandStatusType(0x00000051) // Invalid Destination address NPI
ESME_RINVSYSTYP = CommandStatusType(0x00000053) // Invalid system_type field
ESME_RINVREPFLAG = CommandStatusType(0x00000054) // Invalid replace_if_present flag
ESME_RINVNUMMSGS = CommandStatusType(0x00000055) // Invalid number of messages
ESME_RTHROTTLED = CommandStatusType(0x00000058) // Throttling error (ESME has exceeded allowed message limits)
ESME_RPROVNOTALLWD = CommandStatusType(0x00000059) // Provisioning Not Allowed
ESME_RINVSCHED = CommandStatusType(0x00000061) // Invalid Scheduled Delivery Time
ESME_RINVEXPIRY = CommandStatusType(0x00000062) // Invalid message validity period (Expiry time)
ESME_RINVDFTMSGID = CommandStatusType(0x00000063) // Predefined Message Invalid or Not Found
ESME_RX_T_APPN = CommandStatusType(0x00000064) // ESME Receiver Temporary App Error Code
ESME_RX_P_APPN = CommandStatusType(0x00000065) // ESME Receiver Permanent App Error Code
ESME_RX_R_APPN = CommandStatusType(0x00000066) // ESME Receiver Reject Message Error Code
ESME_RQUERYFAIL = CommandStatusType(0x00000067) // query_sm request failed
ESME_RINVPGCUSTID = CommandStatusType(0x00000080) // Paging Customer ID Invalid No such subscriber
ESME_RINVPGCUSTIDLEN = CommandStatusType(0x00000081) // Paging Customer ID length Invalid
ESME_RINVCITYLEN = CommandStatusType(0x00000082) // City Length Invalid
ESME_RINVSTATELEN = CommandStatusType(0x00000083) // State Length Invalid
ESME_RINVZIPPREFIXLEN = CommandStatusType(0x00000084) // Zip Prefix Length Invalid
ESME_RINVZIPPOSTFIXLEN = CommandStatusType(0x00000085) // Zip Postfix Length Invalid
ESME_RINVMINLEN = CommandStatusType(0x00000086) // MIN Length Invalid
ESME_RINVMIN = CommandStatusType(0x00000087) // MIN Invalid (i.e. No such MIN)
ESME_RINVPINLEN = CommandStatusType(0x00000088) // PIN Length Invalid
ESME_RINVTERMCODELEN = CommandStatusType(0x00000089) // Terminal Code Length Invalid
ESME_RINVCHANNELLEN = CommandStatusType(0x0000008A) // Channel Length Invalid
ESME_RINVCOVREGIONLEN = CommandStatusType(0x0000008B) // Coverage Region Length Invalid
ESME_RINVCAPCODELEN = CommandStatusType(0x0000008C) // Cap Code Length Invalid
ESME_RINVMDTLEN = CommandStatusType(0x0000008D) // Message delivery time Length Invalid
ESME_RINVPRIORMSGLEN = CommandStatusType(0x0000008E) // Priority Message Length Invalid
ESME_RINVPERMSGLEN = CommandStatusType(0x0000008F) // Periodic Messages Length Invalid
ESME_RINVPGALERTLEN = CommandStatusType(0x00000090) // Paging Alerts Length Invalid
ESME_RINVSMUSERLEN = CommandStatusType(0x00000091) // int16 Message User Group Length Invalid
ESME_RINVRTDBLEN = CommandStatusType(0x00000092) // Real Time Data broadcasts Length Invalid
ESME_RINVREGDELLEN = CommandStatusType(0x00000093) // Registered Delivery Length Invalid
ESME_RINVMSGDISTLEN = CommandStatusType(0x00000094) // Message Distribution Length Invalid
ESME_RINVPRIORMSG = CommandStatusType(0x00000095) // Priority Message Length Invalid
ESME_RINVMDT = CommandStatusType(0x00000096) // Message delivery time Invalid
ESME_RINVPERMSG = CommandStatusType(0x00000097) // Periodic Messages Invalid
ESME_RINVMSGDIST = CommandStatusType(0x00000098) // Message Distribution Invalid
ESME_RINVPGALERT = CommandStatusType(0x00000099) // Paging Alerts Invalid
ESME_RINVSMUSER = CommandStatusType(0x0000009A) // int16 Message User Group Invalid
ESME_RINVRTDB = CommandStatusType(0x0000009B) // Real Time Data broadcasts Invalid
ESME_RINVREGDEL = CommandStatusType(0x0000009C) // Registered Delivery Invalid
ESME_RINVOPTPARLEN = CommandStatusType(0x0000009F) // Invalid Optional Parameter Length
ESME_RINVOPTPARSTREAM = CommandStatusType(0x000000C0) // KIF IW Field out of data
ESME_ROPTPARNOTALLWD = CommandStatusType(0x000000C1) // Optional Parameter not allowed
ESME_RINVPARLEN = CommandStatusType(0x000000C2) // Invalid Parameter Length.
ESME_RMISSINGOPTPARAM = CommandStatusType(0x000000C3) // Expected Optional Parameter missing
ESME_RINVOPTPARAMVAL = CommandStatusType(0x000000C4) // Invalid Optional Parameter Value
ESME_RDELIVERYFAILURE = CommandStatusType(0x000000FE) // Delivery Failure (used for data_sm_resp)
ESME_RUNKNOWNERR = CommandStatusType(0x000000FF) // Unknown Error
ESME_LAST_ERROR = CommandStatusType(0x0000012C) // THE VALUE OF THE LAST ERROR CODE
)
================================================
FILE: data/header_data_string.go
================================================
// Code generated by "stringer -type=CommandStatusType,CommandIDType -output header_data_string.go"; DO NOT EDIT.
package data
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ESME_ROK-0]
_ = x[ESME_RINVMSGLEN-1]
_ = x[ESME_RINVCMDLEN-2]
_ = x[ESME_RINVCMDID-3]
_ = x[ESME_RINVBNDSTS-4]
_ = x[ESME_RALYBND-5]
_ = x[ESME_RINVPRTFLG-6]
_ = x[ESME_RINVREGDLVFLG-7]
_ = x[ESME_RSYSERR-8]
_ = x[ESME_RINVSRCADR-10]
_ = x[ESME_RINVDSTADR-11]
_ = x[ESME_RINVMSGID-12]
_ = x[ESME_RBINDFAIL-13]
_ = x[ESME_RINVPASWD-14]
_ = x[ESME_RINVSYSID-15]
_ = x[ESME_RCANCELFAIL-17]
_ = x[ESME_RREPLACEFAIL-19]
_ = x[ESME_RMSGQFUL-20]
_ = x[ESME_RINVSERTYP-21]
_ = x[ESME_RADDCUSTFAIL-25]
_ = x[ESME_RDELCUSTFAIL-26]
_ = x[ESME_RMODCUSTFAIL-27]
_ = x[ESME_RENQCUSTFAIL-28]
_ = x[ESME_RINVCUSTID-29]
_ = x[ESME_RINVCUSTNAME-31]
_ = x[ESME_RINVCUSTADR-33]
_ = x[ESME_RINVADR-34]
_ = x[ESME_RCUSTEXIST-35]
_ = x[ESME_RCUSTNOTEXIST-36]
_ = x[ESME_RADDDLFAIL-38]
_ = x[ESME_RMODDLFAIL-39]
_ = x[ESME_RDELDLFAIL-40]
_ = x[ESME_RVIEWDLFAIL-41]
_ = x[ESME_RLISTDLSFAIL-48]
_ = x[ESME_RPARAMRETFAIL-49]
_ = x[ESME_RINVPARAM-50]
_ = x[ESME_RINVNUMDESTS-51]
_ = x[ESME_RINVDLNAME-52]
_ = x[ESME_RINVDLMEMBDESC-53]
_ = x[ESME_RINVDLMEMBTYP-56]
_ = x[ESME_RINVDLMODOPT-57]
_ = x[ESME_RINVDESTFLAG-64]
_ = x[ESME_RINVSUBREP-66]
_ = x[ESME_RINVESMCLASS-67]
_ = x[ESME_RCNTSUBDL-68]
_ = x[ESME_RSUBMITFAIL-69]
_ = x[ESME_RINVSRCTON-72]
_ = x[ESME_RINVSRCNPI-73]
_ = x[ESME_RINVDSTTON-80]
_ = x[ESME_RINVDSTNPI-81]
_ = x[ESME_RINVSYSTYP-83]
_ = x[ESME_RINVREPFLAG-84]
_ = x[ESME_RINVNUMMSGS-85]
_ = x[ESME_RTHROTTLED-88]
_ = x[ESME_RPROVNOTALLWD-89]
_ = x[ESME_RINVSCHED-97]
_ = x[ESME_RINVEXPIRY-98]
_ = x[ESME_RINVDFTMSGID-99]
_ = x[ESME_RX_T_APPN-100]
_ = x[ESME_RX_P_APPN-101]
_ = x[ESME_RX_R_APPN-102]
_ = x[ESME_RQUERYFAIL-103]
_ = x[ESME_RINVPGCUSTID-128]
_ = x[ESME_RINVPGCUSTIDLEN-129]
_ = x[ESME_RINVCITYLEN-130]
_ = x[ESME_RINVSTATELEN-131]
_ = x[ESME_RINVZIPPREFIXLEN-132]
_ = x[ESME_RINVZIPPOSTFIXLEN-133]
_ = x[ESME_RINVMINLEN-134]
_ = x[ESME_RINVMIN-135]
_ = x[ESME_RINVPINLEN-136]
_ = x[ESME_RINVTERMCODELEN-137]
_ = x[ESME_RINVCHANNELLEN-138]
_ = x[ESME_RINVCOVREGIONLEN-139]
_ = x[ESME_RINVCAPCODELEN-140]
_ = x[ESME_RINVMDTLEN-141]
_ = x[ESME_RINVPRIORMSGLEN-142]
_ = x[ESME_RINVPERMSGLEN-143]
_ = x[ESME_RINVPGALERTLEN-144]
_ = x[ESME_RINVSMUSERLEN-145]
_ = x[ESME_RINVRTDBLEN-146]
_ = x[ESME_RINVREGDELLEN-147]
_ = x[ESME_RINVMSGDISTLEN-148]
_ = x[ESME_RINVPRIORMSG-149]
_ = x[ESME_RINVMDT-150]
_ = x[ESME_RINVPERMSG-151]
_ = x[ESME_RINVMSGDIST-152]
_ = x[ESME_RINVPGALERT-153]
_ = x[ESME_RINVSMUSER-154]
_ = x[ESME_RINVRTDB-155]
_ = x[ESME_RINVREGDEL-156]
_ = x[ESME_RINVOPTPARLEN-159]
_ = x[ESME_RINVOPTPARSTREAM-192]
_ = x[ESME_ROPTPARNOTALLWD-193]
_ = x[ESME_RINVPARLEN-194]
_ = x[ESME_RMISSINGOPTPARAM-195]
_ = x[ESME_RINVOPTPARAMVAL-196]
_ = x[ESME_RDELIVERYFAILURE-254]
_ = x[ESME_RUNKNOWNERR-255]
_ = x[ESME_LAST_ERROR-300]
}
const _CommandStatusType_name = "ESME_ROKESME_RINVMSGLENESME_RINVCMDLENESME_RINVCMDIDESME_RINVBNDSTSESME_RALYBNDESME_RINVPRTFLGESME_RINVREGDLVFLGESME_RSYSERRESME_RINVSRCADRESME_RINVDSTADRESME_RINVMSGIDESME_RBINDFAILESME_RINVPASWDESME_RINVSYSIDESME_RCANCELFAILESME_RREPLACEFAILESME_RMSGQFULESME_RINVSERTYPESME_RADDCUSTFAILESME_RDELCUSTFAILESME_RMODCUSTFAILESME_RENQCUSTFAILESME_RINVCUSTIDESME_RINVCUSTNAMEESME_RINVCUSTADRESME_RINVADRESME_RCUSTEXISTESME_RCUSTNOTEXISTESME_RADDDLFAILESME_RMODDLFAILESME_RDELDLFAILESME_RVIEWDLFAILESME_RLISTDLSFAILESME_RPARAMRETFAILESME_RINVPARAMESME_RINVNUMDESTSESME_RINVDLNAMEESME_RINVDLMEMBDESCESME_RINVDLMEMBTYPESME_RINVDLMODOPTESME_RINVDESTFLAGESME_RINVSUBREPESME_RINVESMCLASSESME_RCNTSUBDLESME_RSUBMITFAILESME_RINVSRCTONESME_RINVSRCNPIESME_RINVDSTTONESME_RINVDSTNPIESME_RINVSYSTYPESME_RINVREPFLAGESME_RINVNUMMSGSESME_RTHROTTLEDESME_RPROVNOTALLWDESME_RINVSCHEDESME_RINVEXPIRYESME_RINVDFTMSGIDESME_RX_T_APPNESME_RX_P_APPNESME_RX_R_APPNESME_RQUERYFAILESME_RINVPGCUSTIDESME_RINVPGCUSTIDLENESME_RINVCITYLENESME_RINVSTATELENESME_RINVZIPPREFIXLENESME_RINVZIPPOSTFIXLENESME_RINVMINLENESME_RINVMINESME_RINVPINLENESME_RINVTERMCODELENESME_RINVCHANNELLENESME_RINVCOVREGIONLENESME_RINVCAPCODELENESME_RINVMDTLENESME_RINVPRIORMSGLENESME_RINVPERMSGLENESME_RINVPGALERTLENESME_RINVSMUSERLENESME_RINVRTDBLENESME_RINVREGDELLENESME_RINVMSGDISTLENESME_RINVPRIORMSGESME_RINVMDTESME_RINVPERMSGESME_RINVMSGDISTESME_RINVPGALERTESME_RINVSMUSERESME_RINVRTDBESME_RINVREGDELESME_RINVOPTPARLENESME_RINVOPTPARSTREAMESME_ROPTPARNOTALLWDESME_RINVPARLENESME_RMISSINGOPTPARAMESME_RINVOPTPARAMVALESME_RDELIVERYFAILUREESME_RUNKNOWNERRESME_LAST_ERROR"
var _CommandStatusType_map = map[CommandStatusType]string{
0: _CommandStatusType_name[0:8],
1: _CommandStatusType_name[8:23],
2: _CommandStatusType_name[23:38],
3: _CommandStatusType_name[38:52],
4: _CommandStatusType_name[52:67],
5: _CommandStatusType_name[67:79],
6: _CommandStatusType_name[79:94],
7: _CommandStatusType_name[94:112],
8: _CommandStatusType_name[112:124],
10: _CommandStatusType_name[124:139],
11: _CommandStatusType_name[139:154],
12: _CommandStatusType_name[154:168],
13: _CommandStatusType_name[168:182],
14: _CommandStatusType_name[182:196],
15: _CommandStatusType_name[196:210],
17: _CommandStatusType_name[210:226],
19: _CommandStatusType_name[226:243],
20: _CommandStatusType_name[243:256],
21: _CommandStatusType_name[256:271],
25: _CommandStatusType_name[271:288],
26: _CommandStatusType_name[288:305],
27: _CommandStatusType_name[305:322],
28: _CommandStatusType_name[322:339],
29: _CommandStatusType_name[339:354],
31: _CommandStatusType_name[354:371],
33: _CommandStatusType_name[371:387],
34: _CommandStatusType_name[387:399],
35: _CommandStatusType_name[399:414],
36: _CommandStatusType_name[414:432],
38: _CommandStatusType_name[432:447],
39: _CommandStatusType_name[447:462],
40: _CommandStatusType_name[462:477],
41: _CommandStatusType_name[477:493],
48: _CommandStatusType_name[493:510],
49: _CommandStatusType_name[510:528],
50: _CommandStatusType_name[528:542],
51: _CommandStatusType_name[542:559],
52: _CommandStatusType_name[559:574],
53: _CommandStatusType_name[574:593],
56: _CommandStatusType_name[593:611],
57: _CommandStatusType_name[611:628],
64: _CommandStatusType_name[628:645],
66: _CommandStatusType_name[645:660],
67: _CommandStatusType_name[660:677],
68: _CommandStatusType_name[677:691],
69: _CommandStatusType_name[691:707],
72: _CommandStatusType_name[707:722],
73: _CommandStatusType_name[722:737],
80: _CommandStatusType_name[737:752],
81: _CommandStatusType_name[752:767],
83: _CommandStatusType_name[767:782],
84: _CommandStatusType_name[782:798],
85: _CommandStatusType_name[798:814],
88: _CommandStatusType_name[814:829],
89: _CommandStatusType_name[829:847],
97: _CommandStatusType_name[847:861],
98: _CommandStatusType_name[861:876],
99: _CommandStatusType_name[876:893],
100: _CommandStatusType_name[893:907],
101: _CommandStatusType_name[907:921],
102: _CommandStatusType_name[921:935],
103: _CommandStatusType_name[935:950],
128: _CommandStatusType_name[950:967],
129: _CommandStatusType_name[967:987],
130: _CommandStatusType_name[987:1003],
131: _CommandStatusType_name[1003:1020],
132: _CommandStatusType_name[1020:1041],
133: _CommandStatusType_name[1041:1063],
134: _CommandStatusType_name[1063:1078],
135: _CommandStatusType_name[1078:1090],
136: _CommandStatusType_name[1090:1105],
137: _CommandStatusType_name[1105:1125],
138: _CommandStatusType_name[1125:1144],
139: _CommandStatusType_name[1144:1165],
140: _CommandStatusType_name[1165:1184],
141: _CommandStatusType_name[1184:1199],
142: _CommandStatusType_name[1199:1219],
143: _CommandStatusType_name[1219:1237],
144: _CommandStatusType_name[1237:1256],
145: _CommandStatusType_name[1256:1274],
146: _CommandStatusType_name[1274:1290],
147: _CommandStatusType_name[1290:1308],
148: _CommandStatusType_name[1308:1327],
149: _CommandStatusType_name[1327:1344],
150: _CommandStatusType_name[1344:1356],
151: _CommandStatusType_name[1356:1371],
152: _CommandStatusType_name[1371:1387],
153: _CommandStatusType_name[1387:1403],
154: _CommandStatusType_name[1403:1418],
155: _CommandStatusType_name[1418:1431],
156: _CommandStatusType_name[1431:1446],
159: _CommandStatusType_name[1446:1464],
192: _CommandStatusType_name[1464:1485],
193: _CommandStatusType_name[1485:1505],
194: _CommandStatusType_name[1505:1520],
195: _CommandStatusType_name[1520:1541],
196: _CommandStatusType_name[1541:1561],
254: _CommandStatusType_name[1561:1582],
255: _CommandStatusType_name[1582:1598],
300: _CommandStatusType_name[1598:1613],
}
func (i CommandStatusType) String() string {
if str, ok := _CommandStatusType_map[i]; ok {
return str
}
return "CommandStatusType(" + strconv.FormatInt(int64(i), 10) + ")"
}
func (i CommandStatusType) Desc() string {
switch i {
case ESME_ROK:
return "No Error"
case ESME_RINVMSGLEN:
return "Message Length is invalid"
case ESME_RINVCMDLEN:
return "Command Length is invalid"
case ESME_RINVCMDID:
return "Invalid Command ID"
case ESME_RINVBNDSTS:
return "Incorrect BIND Status for given command"
case ESME_RALYBND:
return "ESME Already in Bound State"
case ESME_RINVPRTFLG:
return "Invalid Priority Flag"
case ESME_RINVREGDLVFLG:
return "Invalid Registered Delivery Flag"
case ESME_RSYSERR:
return "System Error"
case ESME_RINVSRCADR:
return "Invalid Source Address"
case ESME_RINVDSTADR:
return "Invalid Dest Addr"
case ESME_RINVMSGID:
return "Message ID is invalid"
case ESME_RBINDFAIL:
return "Bind Failed"
case ESME_RINVPASWD:
return "Invalid Password"
case ESME_RINVSYSID:
return "Invalid System ID"
case ESME_RCANCELFAIL:
return "Cancel SM Failed"
case ESME_RREPLACEFAIL:
return "Replace SM Failed"
case ESME_RMSGQFUL:
return "Message Queue Full"
case ESME_RINVSERTYP:
return "Invalid Service Type"
case ESME_RADDCUSTFAIL:
return "Failed to Add Customer"
case ESME_RDELCUSTFAIL:
return "Failed to delete Customer"
case ESME_RMODCUSTFAIL:
return "Failed to modify customer"
case ESME_RENQCUSTFAIL:
return "Failed to Enquire Customer"
case ESME_RINVCUSTID:
return "Invalid Customer ID"
case ESME_RINVCUSTNAME:
return "Invalid Customer Name"
case ESME_RINVCUSTADR:
return "Invalid Customer Address"
case ESME_RINVADR:
return "Invalid Address"
case ESME_RCUSTEXIST:
return "Customer Exists"
case ESME_RCUSTNOTEXIST:
return "Customer does not exist"
case ESME_RADDDLFAIL:
return "Failed to Add DL"
case ESME_RMODDLFAIL:
return "Failed to modify DL"
case ESME_RDELDLFAIL:
return "Failed to Delete DL"
case ESME_RVIEWDLFAIL:
return "Failed to View DL"
case ESME_RLISTDLSFAIL:
return "Failed to list DLs"
case ESME_RPARAMRETFAIL:
return "Param Retrieve Failed"
case ESME_RINVPARAM:
return "Invalid Param"
case ESME_RINVNUMDESTS:
return "Invalid number of destinations"
case ESME_RINVDLNAME:
return "Invalid Distribution List name"
case ESME_RINVDLMEMBDESC:
return "Invalid DL Member Description"
case ESME_RINVDLMEMBTYP:
return "Invalid DL Member Type"
case ESME_RINVDLMODOPT:
return "Invalid DL Modify Option"
case ESME_RINVDESTFLAG:
return "Destination flag is invalid (submit_multi)"
case ESME_RINVSUBREP:
return "Invalid ‘submit with replace’ request (i.e. submit_sm with replace_if_present_flag set)"
case ESME_RINVESMCLASS:
return "Invalid esm_class field data"
case ESME_RCNTSUBDL:
return "Cannot Submit to Distribution List"
case ESME_RSUBMITFAIL:
return "submit_sm or submit_multi failed"
case ESME_RINVSRCTON:
return "Invalid Source address TON"
case ESME_RINVSRCNPI:
return "Invalid Source address NPI"
case ESME_RINVDSTTON:
return "Invalid Destination address TON"
case ESME_RINVDSTNPI:
return "Invalid Destination address NPI"
case ESME_RINVSYSTYP:
return "Invalid system_type field"
case ESME_RINVREPFLAG:
return "Invalid replace_if_present flag"
case ESME_RINVNUMMSGS:
return "Invalid number of messages"
case ESME_RTHROTTLED:
return "Throttling error (ESME has exceeded allowed message limits)"
case ESME_RPROVNOTALLWD:
return "Provisioning Not Allowed"
case ESME_RINVSCHED:
return "Invalid Scheduled Delivery Time"
case ESME_RINVEXPIRY:
return "Invalid message validity period (Expiry time)"
case ESME_RINVDFTMSGID:
return "Predefined Message Invalid or Not Found"
case ESME_RX_T_APPN:
return "ESME Receiver Temporary App Error Code"
case ESME_RX_P_APPN:
return "ESME Receiver Permanent App Error Code"
case ESME_RX_R_APPN:
return "ESME Receiver Reject Message Error Code"
case ESME_RQUERYFAIL:
return "query_sm request failed"
case ESME_RINVPGCUSTID:
return "Paging Customer ID Invalid No such subscriber"
case ESME_RINVPGCUSTIDLEN:
return "Paging Customer ID length Invalid"
case ESME_RINVCITYLEN:
return "City Length Invalid"
case ESME_RINVSTATELEN:
return "State Length Invalid"
case ESME_RINVZIPPREFIXLEN:
return "Zip Prefix Length Invalid"
case ESME_RINVZIPPOSTFIXLEN:
return "Zip Postfix Length Invalid"
case ESME_RINVMINLEN:
return "MIN Length Invalid"
case ESME_RINVMIN:
return "MIN Invalid (i.e. No such MIN)"
case ESME_RINVPINLEN:
return "PIN Length Invalid"
case ESME_RINVTERMCODELEN:
return "Terminal Code Length Invalid"
case ESME_RINVCHANNELLEN:
return "Channel Length Invalid"
case ESME_RINVCOVREGIONLEN:
return "Coverage Region Length Invalid"
case ESME_RINVCAPCODELEN:
return "Cap Code Length Invalid"
case ESME_RINVMDTLEN:
return "Message delivery time Length Invalid"
case ESME_RINVPRIORMSGLEN:
return "Priority Message Length Invalid"
case ESME_RINVPERMSGLEN:
return "Periodic Messages Length Invalid"
case ESME_RINVPGALERTLEN:
return "Paging Alerts Length Invalid"
case ESME_RINVSMUSERLEN:
return "int16 Message User Group Length Invalid"
case ESME_RINVRTDBLEN:
return "Real Time Data broadcasts Length Invalid"
case ESME_RINVREGDELLEN:
return "Registered Delivery Length Invalid"
case ESME_RINVMSGDISTLEN:
return "Message Distribution Length Invalid"
case ESME_RINVPRIORMSG:
return "Priority Message Length Invalid"
case ESME_RINVMDT:
return "Message delivery time Invalid"
case ESME_RINVPERMSG:
return "Periodic Messages Invalid"
case ESME_RINVMSGDIST:
return "Message Distribution Invalid"
case ESME_RINVPGALERT:
return "Paging Alerts Invalid"
case ESME_RINVSMUSER:
return "int16 Message User Group Invalid"
case ESME_RINVRTDB:
return "Real Time Data broadcasts Invalid"
case ESME_RINVREGDEL:
return "Registered Delivery Invalid"
case ESME_RINVOPTPARLEN:
return "Invalid Optional Parameter Length"
case ESME_RINVOPTPARSTREAM:
return "Error in the optional part of the PDU Body."
case ESME_ROPTPARNOTALLWD:
return "Optional Parameter not allowed"
case ESME_RINVPARLEN:
return "Invalid Parameter Length."
case ESME_RMISSINGOPTPARAM:
return "Expected Optional Parameter missing"
case ESME_RINVOPTPARAMVAL:
return "Invalid Optional Parameter Value"
case ESME_RDELIVERYFAILURE:
return "Delivery Failure (used for data_sm_resp)"
case ESME_RUNKNOWNERR:
return "Unknown Error"
case ESME_LAST_ERROR:
return "The value of the last error code"
}
return i.String()
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[GENERIC_NACK - -2147483648]
_ = x[BIND_RECEIVER-1]
_ = x[BIND_RECEIVER_RESP - -2147483647]
_ = x[BIND_TRANSMITTER-2]
_ = x[BIND_TRANSMITTER_RESP - -2147483646]
_ = x[QUERY_SM-3]
_ = x[QUERY_SM_RESP - -2147483645]
_ = x[SUBMIT_SM-4]
_ = x[SUBMIT_SM_RESP - -2147483644]
_ = x[DELIVER_SM-5]
_ = x[DELIVER_SM_RESP - -2147483643]
_ = x[UNBIND-6]
_ = x[UNBIND_RESP - -2147483642]
_ = x[REPLACE_SM-7]
_ = x[REPLACE_SM_RESP - -2147483641]
_ = x[CANCEL_SM-8]
_ = x[CANCEL_SM_RESP - -2147483640]
_ = x[BIND_TRANSCEIVER-9]
_ = x[BIND_TRANSCEIVER_RESP - -2147483639]
_ = x[OUTBIND-11]
_ = x[ENQUIRE_LINK-21]
_ = x[ENQUIRE_LINK_RESP - -2147483627]
_ = x[SUBMIT_MULTI-33]
_ = x[SUBMIT_MULTI_RESP - -2147483615]
_ = x[ALERT_NOTIFICATION-258]
_ = x[DATA_SM-259]
_ = x[DATA_SM_RESP - -2147483389]
}
const (
_CommandIDType_name_0 = "GENERIC_NACKBIND_RECEIVER_RESPBIND_TRANSMITTER_RESPQUERY_SM_RESPSUBMIT_SM_RESPDELIVER_SM_RESPUNBIND_RESPREPLACE_SM_RESPCANCEL_SM_RESPBIND_TRANSCEIVER_RESP"
_CommandIDType_name_1 = "ENQUIRE_LINK_RESP"
_CommandIDType_name_2 = "SUBMIT_MULTI_RESP"
_CommandIDType_name_3 = "DATA_SM_RESP"
_CommandIDType_name_4 = "BIND_RECEIVERBIND_TRANSMITTERQUERY_SMSUBMIT_SMDELIVER_SMUNBINDREPLACE_SMCANCEL_SMBIND_TRANSCEIVER"
_CommandIDType_name_5 = "OUTBIND"
_CommandIDType_name_6 = "ENQUIRE_LINK"
_CommandIDType_name_7 = "SUBMIT_MULTI"
_CommandIDType_name_8 = "ALERT_NOTIFICATIONDATA_SM"
)
var (
_CommandIDType_index_0 = [...]uint8{0, 12, 30, 51, 64, 78, 93, 104, 119, 133, 154}
_CommandIDType_index_4 = [...]uint8{0, 13, 29, 37, 46, 56, 62, 72, 81, 97}
_CommandIDType_index_8 = [...]uint8{0, 18, 25}
)
func (i CommandIDType) String() string {
switch {
case -2147483648 <= i && i <= -2147483639:
i -= -2147483648
return _CommandIDType_name_0[_CommandIDType_index_0[i]:_CommandIDType_index_0[i+1]]
case i == -2147483627:
return _CommandIDType_name_1
case i == -2147483615:
return _CommandIDType_name_2
case i == -2147483389:
return _CommandIDType_name_3
case 1 <= i && i <= 9:
i -= 1
return _CommandIDType_name_4[_CommandIDType_index_4[i]:_CommandIDType_index_4[i+1]]
case i == 11:
return _CommandIDType_name_5
case i == 21:
return _CommandIDType_name_6
case i == 33:
return _CommandIDType_name_7
case 258 <= i && i <= 259:
i -= 258
return _CommandIDType_name_8[_CommandIDType_index_8[i]:_CommandIDType_index_8[i+1]]
default:
return "CommandIDType(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
================================================
FILE: data/other_codings.go
================================================
package data
import (
"golang.org/x/text/encoding/unicode"
)
var (
// UTF16BEM is UTF-16 Big Endian with BOM (byte order mark).
UTF16BEM = &utf16BEM{}
// UTF16LEM is UTF-16 Little Endian with BOM.
UTF16LEM = &utf16LEM{}
// UTF16BE is UTF-16 Big Endian without BOM.
UTF16BE = &utf16BE{}
// UTF16LE is UTF-16 Little Endian without BOM.
UTF16LE = &utf16LE{}
)
type utf16BEM struct{}
func (c utf16BEM) Encode(str string) ([]byte, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
return encode(str, tmp.NewEncoder())
}
func (c utf16BEM) Decode(data []byte) (string, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
return decode(data, tmp.NewDecoder())
}
type utf16LEM struct{}
func (c utf16LEM) Encode(str string) ([]byte, error) {
tmp := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM)
return encode(str, tmp.NewEncoder())
}
func (c utf16LEM) Decode(data []byte) (string, error) {
tmp := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM)
return decode(data, tmp.NewDecoder())
}
type utf16BE struct{}
func (c utf16BE) Encode(str string) ([]byte, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
return encode(str, tmp.NewEncoder())
}
func (c utf16BE) Decode(data []byte) (string, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
return decode(data, tmp.NewDecoder())
}
type utf16LE struct{}
func (c utf16LE) Encode(str string) ([]byte, error) {
tmp := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
return encode(str, tmp.NewEncoder())
}
func (c utf16LE) Decode(data []byte) (string, error) {
tmp := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
return decode(data, tmp.NewDecoder())
}
================================================
FILE: data/pkg.go
================================================
package data
import (
"fmt"
"sync/atomic"
)
//nolint
const (
SM_CONNID_LEN = 16
SM_MSG_LEN = 254
SM_SYSID_LEN = 16
SM_MSGID_LEN = 64
SM_PASS_LEN = 9
SM_DATE_LEN = 17
SM_SRVTYPE_LEN = 6
SM_SYSTYPE_LEN = 13
SM_ADDR_LEN = 21
SM_DATA_ADDR_LEN = 65
SM_ADDR_RANGE_LEN = 41
SM_TYPE_LEN = 13
SM_DL_NAME_LEN = 21
SM_PARAM_NAME_LEN = 10
SM_PARAM_VALUE_LEN = 10
SM_MAX_CNT_DEST_ADDR = 254
// GSM specific, short message must be no larger than 140 octets
SM_GSM_MSG_LEN = 140
CONNECTION_CLOSED = 0
CONNECTION_OPENED = 1
SM_ACK = 1
SM_NO_ACK = 0
SM_RESPONSE_ACK = 0
SM_RESPONSE_TNACK = 1
SM_RESPONSE_PNACK = 2
// Interface_Version
SMPP_V33 int8 = int8(-0x33)
SMPP_V34 = byte(0x34)
// Address_TON
GSM_TON_UNKNOWN = byte(0x00)
GSM_TON_INTERNATIONAL = byte(0x01)
GSM_TON_NATIONAL = byte(0x02)
GSM_TON_NETWORK = byte(0x03)
GSM_TON_SUBSCRIBER = byte(0x04)
GSM_TON_ALPHANUMERIC = byte(0x05)
GSM_TON_ABBREVIATED = byte(0x06)
GSM_TON_RESERVED_EXTN = byte(0x07)
// Address_NPI
GSM_NPI_UNKNOWN = byte(0x00)
GSM_NPI_E164 = byte(0x01)
GSM_NPI_ISDN = GSM_NPI_E164
GSM_NPI_X121 = byte(0x03)
GSM_NPI_TELEX = byte(0x04)
GSM_NPI_LAND_MOBILE = byte(0x06)
GSM_NPI_NATIONAL = byte(0x08)
GSM_NPI_PRIVATE = byte(0x09)
GSM_NPI_ERMES = byte(0x0A)
GSM_NPI_INTERNET = byte(0x0E)
GSM_NPI_WAP_CLIENT_ID = byte(0x12)
GSM_NPI_RESERVED_EXTN = byte(0x0F)
// Service_Type
SERVICE_NULL string = ""
SERVICE_CMT string = "CMT"
SERVICE_CPT string = "CPT"
SERVICE_VMN string = "VMN"
SERVICE_VMA string = "VMA"
SERVICE_WAP string = "WAP"
SERVICE_USSD string = "USSD"
SMPP_PROTOCOL = byte(1)
SMPPP_PROTOCOL = byte(2)
SM_SERVICE_MOBILE_TERMINATED = byte(0)
SM_SERVICE_MOBILE_ORIGINATED = byte(1)
SM_SERVICE_MOBILE_TRANSCEIVER = byte(2)
// State of message at SMSC
SM_STATE_EN_ROUTE = 1 // default state for messages in transit
SM_STATE_DELIVERED = 2 // message is delivered
SM_STATE_EXPIRED = 3 // validity period expired
SM_STATE_DELETED = 4 // message has been deleted
SM_STATE_UNDELIVERABLE = 5 // undeliverable
SM_STATE_ACCEPTED = 6 // message is in accepted state
SM_STATE_INVALID = 7 // message is in invalid state
SM_STATE_REJECTED = 8 // message is in rejected state
//******************
// ESMClass Defines
//******************
// Messaging Mode
SM_ESM_DEFAULT = 0x00 // Default SMSC Mode or Message Type
SM_DATAGRAM_MODE = 0x01 // Use one-shot express mode
SM_FORWARD_MODE = 0x02 // Do not use
SM_STORE_FORWARD_MODE = 0x03 // Use store & forward
// Send/Receive TDMA & CDMA Message Type
SM_SMSC_DLV_RCPT_TYPE = 0x04 // Recv Msg contains SMSC delivery receipt
SM_ESME_DLV_ACK_TYPE = 0x08 // Send/Recv Msg contains ESME delivery acknowledgement
SM_ESME_MAN_USER_ACK_TYPE = 0x10 // Send/Recv Msg contains manual/user acknowledgment
SM_CONV_ABORT_TYPE = 0x18 // Recv Msg contains conversation abort (Korean CDMA)
SM_INTMD_DLV_NOTIFY_TYPE = 0x20 // Recv Msg contains intermediate notification
// GSM Network features
SM_NONE_GSM = 0x00 // No specific features selected
SM_UDH_GSM = 0x40 // User Data Header indicator set
SM_REPLY_PATH_GSM = 0x80 // Reply path set
SM_UDH_REPLY_PATH_GSM = 0xC0 // Both UDH & Reply path
// Optional Parameter Tags, Min and Max Lengths
// Following are the 2 byte tag and min/max lengths for
// supported optional parameter (declann)
OPT_PAR_MSG_WAIT = 2
// Privacy Indicator
OPT_PAR_PRIV_IND = 0x0201
// Source Subaddress
OPT_PAR_SRC_SUBADDR = 0x0202
OPT_PAR_SRC_SUBADDR_MIN = 2
OPT_PAR_SRC_SUBADDR_MAX = 23
// Destination Subaddress
OPT_PAR_DEST_SUBADDR = 0x0203
OPT_PAR_DEST_SUBADDR_MIN = 2
OPT_PAR_DEST_SUBADDR_MAX = 23
// User Message Reference
OPT_PAR_USER_MSG_REF = 0x0204
// User Response Code
OPT_PAR_USER_RESP_CODE = 0x0205
// Language Indicator
OPT_PAR_LANG_IND = 0x020D
// Source Port
OPT_PAR_SRC_PORT = 0x020A
// Destination Port
OPT_PAR_DST_PORT = 0x020B
// Concat Msg Ref Num
OPT_PAR_SAR_MSG_REF_NUM = 0x020C
// Concat Total Segments
OPT_PAR_SAR_TOT_SEG = 0x020E
// Concat Segment Seqnums
OPT_PAR_SAR_SEG_SNUM = 0x020F
// SC Interface Version
OPT_PAR_SC_IF_VER = 0x0210
// Display Time
OPT_PAR_DISPLAY_TIME = 0x1201
// Validity Information
OPT_PAR_MS_VALIDITY = 0x1204
// DPF Result
OPT_PAR_DPF_RES = 0x0420
// Set DPF
OPT_PAR_SET_DPF = 0x0421
// MS Availability Status
OPT_PAR_MS_AVAIL_STAT = 0x0422
// Network Error Code
OPT_PAR_NW_ERR_CODE = 0x0423
OPT_PAR_NW_ERR_CODE_MIN = 3
OPT_PAR_NW_ERR_CODE_MAX = 3
// Extended int16 Message has no size limit
// Delivery Failure Reason
OPT_PAR_DEL_FAIL_RSN = 0x0425
// More Messages to Follow
OPT_PAR_MORE_MSGS = 0x0426
// Message State
OPT_PAR_MSG_STATE = 0x0427
// Callback Number
OPT_PAR_CALLBACK_NUM = 0x0381
OPT_PAR_CALLBACK_NUM_MIN = 4
OPT_PAR_CALLBACK_NUM_MAX = 19
// Callback Number Presentation Indicator
OPT_PAR_CALLBACK_NUM_PRES_IND = 0x0302
// Callback Number Alphanumeric Tag
OPT_PAR_CALLBACK_NUM_ATAG = 0x0303
OPT_PAR_CALLBACK_NUM_ATAG_MIN = 1
OPT_PAR_CALLBACK_NUM_ATAG_MAX = 65
// Number of messages in Mailbox
OPT_PAR_NUM_MSGS = 0x0304
// SMS Received Alert
OPT_PAR_SMS_SIGNAL = 0x1203
// Message Delivery Alert
OPT_PAR_ALERT_ON_MSG_DELIVERY = 0x130C
// ITS Reply Type
OPT_PAR_ITS_REPLY_TYPE = 0x1380
// ITS Session Info
OPT_PAR_ITS_SESSION_INFO = 0x1383
// USSD Service Op
OPT_PAR_USSD_SER_OP = 0x0501
// Priority
SM_NOPRIORITY = 0
SM_PRIORITY = 1
// Registered delivery
// SMSC Delivery Receipt (bits 1 & 0)
SM_SMSC_RECEIPT_MASK = byte(0x03)
SM_SMSC_RECEIPT_NOT_REQUESTED = byte(0x00)
SM_SMSC_RECEIPT_REQUESTED = byte(0x01)
SM_SMSC_RECEIPT_ON_FAILURE = byte(0x02)
// SME originated acknowledgement (bits 3 & 2)
SM_SME_ACK_MASK = byte(0x0c)
SM_SME_ACK_NOT_REQUESTED = byte(0x00)
SM_SME_ACK_DELIVERY_REQUESTED = byte(0x04)
SM_SME_ACK_MANUAL_REQUESTED = byte(0x08)
SM_SME_ACK_BOTH_REQUESTED = byte(0x0c)
// Intermediate notification (bit 5)
SM_NOTIF_MASK = byte(0x010)
SM_NOTIF_NOT_REQUESTED = byte(0x000)
SM_NOTIF_REQUESTED = byte(0x010)
// Replace if Present flag
SM_NOREPLACE = 0
SM_REPLACE = 1
// Destination flag
SM_DEST_SME_ADDRESS = 1
SM_DEST_DL_NAME = 2
// Higher Layer Message Type
SM_LAYER_WDP = 0
SM_LAYER_WCMP = 1
// Operation Class
SM_OPCLASS_DATAGRAM = 0
SM_OPCLASS_TRANSACTION = 3
// Originating MSC Address
OPT_PAR_ORIG_MSC_ADDR = -32639 // int16(0x8081)
OPT_PAR_ORIG_MSC_ADDR_MIN = 1
OPT_PAR_ORIG_MSC_ADDR_MAX = 24
// Destination MSC Address
OPT_PAR_DEST_MSC_ADDR = -32638 // int16(0x8082)
OPT_PAR_DEST_MSC_ADDR_MIN = 1
OPT_PAR_DEST_MSC_ADDR_MAX = 24
// Unused Tag
OPT_PAR_UNUSED = 0xffff
// Destination Address Subunit
OPT_PAR_DST_ADDR_SUBUNIT = 0x0005
// Destination Network Type
OPT_PAR_DST_NW_TYPE = 0x0006
// Destination Bearer Type
OPT_PAR_DST_BEAR_TYPE = 0x0007
// Destination Telematics ID
OPT_PAR_DST_TELE_ID = 0x0008
// Source Address Subunit
OPT_PAR_SRC_ADDR_SUBUNIT = 0x000D
// Source Network Type
OPT_PAR_SRC_NW_TYPE = 0x000E
// Source Bearer Type
OPT_PAR_SRC_BEAR_TYPE = 0x000F
// Source Telematics ID
OPT_PAR_SRC_TELE_ID = 0x0010
// QOS Time to Live
OPT_PAR_QOS_TIME_TO_LIVE = 0x0017
OPT_PAR_QOS_TIME_TO_LIVE_MIN = 1
OPT_PAR_QOS_TIME_TO_LIVE_MAX = 4
// Payload Type
OPT_PAR_PAYLOAD_TYPE = 0x0019
// Additional Status Info Text
OPT_PAR_ADD_STAT_INFO = 0x001D
OPT_PAR_ADD_STAT_INFO_MIN = 1
OPT_PAR_ADD_STAT_INFO_MAX = 256
// Receipted Message ID
OPT_PAR_RECP_MSG_ID = 0x001E
OPT_PAR_RECP_MSG_ID_MIN = 1
OPT_PAR_RECP_MSG_ID_MAX = 65
// Message Payload
OPT_PAR_MSG_PAYLOAD = 0x0424
OPT_PAR_MSG_PAYLOAD_MIN = 1
OPT_PAR_MSG_PAYLOAD_MAX = 1500
// User Data Header
UDH_CONCAT_MSG_8_BIT_REF = byte(0x00)
UDH_CONCAT_MSG_16_BIT_REF = byte(0x08)
/**
* @deprecated As of version 1.3 of the library there are defined
* new encoding constants for base set of encoding supported by Java Runtime.
* The CHAR_ENC is replaced by ENC_ASCII
* and redefined in this respect.
*/
DFLT_MSGID string = ""
DFLT_MSG string = ""
DFLT_SRVTYPE string = ""
DFLT_SYSID string = ""
DFLT_PASS string = ""
DFLT_SYSTYPE string = ""
DFLT_ADDR_RANGE string = ""
DFLT_DATE string = ""
DFLT_ADDR string = ""
DFLT_MSG_STATE byte = 0
DFLT_ERR byte = 0
DFLT_SCHEDULE string = ""
DFLT_VALIDITY string = ""
DFLT_REG_DELIVERY = SM_SMSC_RECEIPT_NOT_REQUESTED | SM_SME_ACK_NOT_REQUESTED | SM_NOTIF_NOT_REQUESTED
DFLT_DFLTMSGID = byte(0)
DFLT_MSG_LEN = byte(0)
DFLT_ESM_CLASS = byte(0)
DFLT_DATA_CODING = byte(0)
DFLT_PROTOCOLID = byte(0)
DFLT_PRIORITY_FLAG = byte(0)
DFTL_REPLACE_IFP = byte(0)
DFLT_DL_NAME string = ""
DFLT_GSM_TON = GSM_TON_UNKNOWN
DFLT_GSM_NPI = GSM_NPI_UNKNOWN
DFLT_DEST_FLAG = byte(0) // not set
MAX_PDU_LEN = 64 << 10
PDU_HEADER_SIZE = 16 // 4 integers
TLV_HEADER_SIZE = 4 // 2 int16s: tag & length
// all times in milliseconds
RECEIVER_TIMEOUT int64 = 60000
CONNECTION_RECEIVE_TIMEOUT int64 = 10000
UNBIND_RECEIVE_TIMEOUT int64 = 5000
CONNECTION_SEND_TIMEOUT int64 = 20000
COMMS_TIMEOUT int64 = 60000
QUEUE_TIMEOUT int64 = 10000
ACCEPT_TIMEOUT int64 = 60000
RECEIVE_BLOCKING int64 = -1
MAX_VALUE_PORT = 65535
MIN_VALUE_PORT = 100
MIN_LENGTH_ADDRESS = 7
)
var (
// ErrNotImplSplitterInterface indicates that encoding does not support Splitter interface
ErrNotImplSplitterInterface = fmt.Errorf("Encoding not implementing Splitter interface")
// ErrNotImplDecode indicates that encoding does not support Decode method
ErrNotImplDecode = fmt.Errorf("Decode is not implemented in this Encoding")
// ErrNotImplEncode indicates that encoding does not support Encode method
ErrNotImplEncode = fmt.Errorf("Encode is not implemented in this Encoding")
)
var defaultTon atomic.Value
var defaultNpi atomic.Value
func init() {
defaultTon.Store(DFLT_GSM_TON)
defaultNpi.Store(DFLT_GSM_NPI)
}
// SetDefaultTon set default ton.
func SetDefaultTon(dfltTon byte) {
defaultTon.Store(dfltTon)
}
// GetDefaultTon get default ton.
func GetDefaultTon() byte {
return defaultTon.Load().(byte)
}
// SetDefaultNpi set default npi.
func SetDefaultNpi(dfltNpi byte) {
defaultNpi.Store(dfltNpi)
}
// GetDefaultNpi get default npi.
func GetDefaultNpi() byte {
return defaultNpi.Load().(byte)
}
================================================
FILE: data/pkg_test.go
================================================
package data
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestDefaultNpi(t *testing.T) {
SetDefaultNpi(13)
require.EqualValues(t, 13, GetDefaultNpi())
}
func TestDefaultTon(t *testing.T) {
SetDefaultTon(19)
require.EqualValues(t, 19, GetDefaultTon())
}
================================================
FILE: data/utils.go
================================================
package data
import "unicode"
// FindEncoding returns suitable encoding for a string.
// If string is ascii, then GSM7Bit. If not, then UCS2.
func FindEncoding(s string) (enc Encoding) {
if isASCII(s) {
enc = GSM7BIT
} else {
enc = UCS2
}
return
}
func isASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return false
}
}
return true
}
================================================
FILE: data/utils_test.go
================================================
package data
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFindEncoding(t *testing.T) {
require.Equal(t, GSM7BIT, FindEncoding("abc30hb3bk2lopzSD=2-^"))
require.Equal(t, UCS2, FindEncoding("Trần Lập và ban nhạc Bức tường huyền thoại"))
require.Equal(t, UCS2, FindEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ"))
}
================================================
FILE: errors/pkg.go
================================================
package errors
import (
"fmt"
"github.com/linxGnu/gosmpp/data"
)
// SmppErr indicates smpp error(s), compatible with OpenSMPP.
type SmppErr struct {
err string
serialVersionUID int64
}
// Error interface.
func (s *SmppErr) Error() string {
return fmt.Sprintf("Error happened: [%s]. SerialVersionUID: [%d]", s.err, s.serialVersionUID)
}
var (
// ErrInvalidPDU indicates invalid pdu payload.
ErrInvalidPDU error = &SmppErr{err: "PDU payload is invalid", serialVersionUID: -6985061862208729984}
// ErrUnknownCommandID indicates unknown command id.
ErrUnknownCommandID error = &SmppErr{err: "Unknown command id", serialVersionUID: -5091873576710864441}
// ErrWrongDateFormat indicates wrong date format.
ErrWrongDateFormat error = &SmppErr{err: "Wrong date format", serialVersionUID: 5831937612139037591}
// ErrShortMessageLengthTooLarge indicates short message length is too large.
ErrShortMessageLengthTooLarge error = &SmppErr{err: fmt.Sprintf("Encoded short message data exceeds size of %d", data.SM_MSG_LEN), serialVersionUID: 78237205927624}
// ErrUDHTooLong UDH-L is larger than total length of short message data
ErrUDHTooLong = fmt.Errorf("User Data Header is too long for PDU short message")
)
================================================
FILE: errors/pkg_test.go
================================================
package errors
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestErr(t *testing.T) {
require.True(t, strings.HasPrefix(ErrInvalidPDU.Error(), "Error happened: ["))
}
================================================
FILE: example/smsc_simulator/smsc.cpp
================================================
//
// smscsimulator.cpp
// SMPPLib
//
// Created by Mark Hay on 12/05/2019.
// Copyright © 2019 Melrose Labs. All rights reserved.
//
// Build: g++ smscsimulator.cpp -o MLSMSCSimulator && ./MLSMSCSimulator
#include
#include
#include
#include
#include
#include
#include