[
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gomod\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Build\non: [push, pull_request]\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out\n        uses: actions/checkout@v5\n      - name: Set up GCC\n        run: |\n          sudo apt install gcc g++ -y\n        shell: bash\n      - name: Set up Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: 1.24\n      # - name: Linter\n      #   uses: golangci/golangci-lint-action@v8\n      #   with:\n      #     version: latest\n      - name: Start SMSC\n        run: |\n          g++ example/smsc_simulator/smsc.cpp -o smsc\n          ./smsc &\n        shell: bash\n      - name: Test Coverage\n        run: go test -v -race -count=1 -coverprofile=coverage.out\n      - name: Convert coverage to lcov\n        uses: jandelgado/gcov2lcov-action@v1\n        with:\n          version: latest\n          infile: coverage.out\n          outfile: coverage.lcov\n      - name: Coveralls\n        uses: coverallsapp/github-action@master\n        with:\n          github-token: ${{ secrets.github_token }}\n          path-to-lcov: coverage.lcov\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\nvendor/\n*DS_Store\n*.out\nsmsc\n"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\nlinters:\n  # Default set of linters.\n  # The value can be:\n  # - `standard`: https://golangci-lint.run/docs/linters/#enabled-by-default\n  # - `all`: enables all linters by default.\n  # - `none`: disables all linters by default.\n  # - `fast`: enables only linters considered as \"fast\" (`golangci-lint help linters --json | jq '[ .[] | select(.fast==true) ] | map(.name)'`).\n  # Default: standard\n  default: all\n  # Enable specific linter.\n  enable:\n    - arangolint\n    - asasalint\n    - asciicheck\n    - bidichk\n    - bodyclose\n    - canonicalheader\n    - containedctx\n    - contextcheck\n    - copyloopvar\n    - cyclop\n    - decorder\n    - depguard\n    - dogsled\n    - dupl\n    - dupword\n    - durationcheck\n    - embeddedstructfieldcheck\n    - err113\n    - errcheck\n    - errchkjson\n    - errname\n    - errorlint\n    - exhaustive\n    - exhaustruct\n    - exptostd\n    - fatcontext\n    - forbidigo\n    - forcetypeassert\n    - funcorder\n    - funlen\n    - ginkgolinter\n    - gocheckcompilerdirectives\n    - gochecknoglobals\n    - gochecknoinits\n    - gochecksumtype\n    - gocognit\n    - goconst\n    - gocritic\n    - gocyclo\n    - godot\n    - godox\n    - goheader\n    - gomoddirectives\n    - gomodguard\n    - goprintffuncname\n    - gosec\n    - gosmopolitan\n    - govet\n    - grouper\n    - iface\n    - importas\n    - inamedparam\n    - ineffassign\n    - interfacebloat\n    - intrange\n    - ireturn\n    - lll\n    - loggercheck\n    - maintidx\n    - makezero\n    - mirror\n    - misspell\n    - mnd\n    - musttag\n    - nakedret\n    - nestif\n    - nilerr\n    - nilnesserr\n    - nilnil\n    - nlreturn\n    - noctx\n    - noinlineerr\n    - nolintlint\n    - nonamedreturns\n    - nosprintfhostport\n    - paralleltest\n    - perfsprint\n    - prealloc\n    - predeclared\n    - promlinter\n    - protogetter\n    - reassign\n    - recvcheck\n    - revive\n    - rowserrcheck\n    - sloglint\n    - spancheck\n    - sqlclosecheck\n    - tagalign\n    - tagliatelle\n    - testableexamples\n    - testifylint\n    - testpackage\n    - thelper\n    - tparallel\n    - unconvert\n    - unparam\n    - unused\n    - usestdlibvars\n    - usetesting\n    - varnamelen\n    - wastedassign\n    - whitespace\n    - wrapcheck\n    - wsl\n    - wsl_v5\n    - zerologlint\n  # Disable specific linters.\n  disable:\n    - staticcheck\n    - arangolint\n    - asasalint\n    - asciicheck\n    - bidichk\n    - bodyclose\n    - canonicalheader\n    - containedctx\n    - contextcheck\n    - copyloopvar\n    - cyclop\n    - decorder\n    - depguard\n    - dogsled\n    - dupl\n    - dupword\n    - durationcheck\n    - embeddedstructfieldcheck\n    - err113\n    - errcheck\n    - errchkjson\n    - errname\n    - errorlint\n    - exhaustive\n    - exhaustruct\n    - exptostd\n    - fatcontext\n    - forbidigo\n    - forcetypeassert\n    - funcorder\n    - funlen\n    - ginkgolinter\n    - gocheckcompilerdirectives\n    - gochecknoglobals\n    - gochecknoinits\n    - gochecksumtype\n    - gocognit\n    - goconst\n    - gocritic\n    - gocyclo\n    - godot\n    - godox\n    - goheader\n    - gomoddirectives\n    - gomodguard\n    - goprintffuncname\n    - gosec\n    - gosmopolitan\n    - govet\n    - grouper\n    - iface\n    - importas\n    - inamedparam\n    - ineffassign\n    - interfacebloat\n    - intrange\n    - ireturn\n    - lll\n    - loggercheck\n    - maintidx\n    - makezero\n    - mirror\n    - misspell\n    - mnd\n    - musttag\n    - nakedret\n    - nestif\n    - nilerr\n    - nilnesserr\n    - nilnil\n    - nlreturn\n    - noctx\n    - noinlineerr\n    - nolintlint\n    - nonamedreturns\n    - nosprintfhostport\n    - paralleltest\n    - perfsprint\n    - prealloc\n    - predeclared\n    - promlinter\n    - protogetter\n    - reassign\n    - recvcheck\n    - revive\n    - rowserrcheck\n    - sloglint\n    - spancheck\n    - sqlclosecheck\n    - staticcheck\n    - tagalign\n    - tagliatelle\n    - testableexamples\n    - testifylint\n    - testpackage\n    - thelper\n    - tparallel\n    - unconvert\n    - unparam\n    - unused\n    - usestdlibvars\n    - usetesting\n    - varnamelen\n    - wastedassign\n    - whitespace\n    - wrapcheck\n    - wsl\n    - wsl_v5\n    - zerologlint\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# gosmpp\n\n[![](https://github.com/linxGnu/gosmpp/workflows/Build/badge.svg)]()\n[![Go Report Card](https://goreportcard.com/badge/github.com/linxGnu/gosmpp)](https://goreportcard.com/report/github.com/linxGnu/gosmpp)\n[![Coverage Status](https://coveralls.io/repos/github/linxGnu/gosmpp/badge.svg?branch=master)](https://coveralls.io/github/linxGnu/gosmpp?branch=master)\n[![godoc](https://img.shields.io/badge/docs-GoDoc-green.svg)](https://godoc.org/github.com/linxGnu/gosmpp)\n\nSMPP (3.4) Client Library in pure Go.\n\nThis library is well tested with SMSC simulators:\n- [Melroselabs SMSC](https://melroselabs.com/services/smsc-simulator/#smsc-simulator-try)\n\n## Installation\n```\ngo get -u github.com/linxGnu/gosmpp\n```\n\n## Usage\n\n### Highlight\n\n- 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:\n\n```go\n\t\ttrans, err := gosmpp.NewSession(\n\t\tgosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),\n\t\tgosmpp.Settings{\n\t\t\tEnquireLink: 5 * time.Second,\n\n\t\t\tReadTimeout: 10 * time.Second,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tlog.Fatal(\"SubmitPDU error:\", err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tfmt.Println(\"Receiving PDU/Network error:\", err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tfmt.Println(\"Rebinding but error:\", err)\n\t\t\t},\n\n\t\t\tOnPDU: handlePDU(),\n\n\t\t\tOnClosed: func(state gosmpp.State) {\n\t\t\t\tfmt.Println(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\t\tif err != nil {\n\t\t  log.Println(err)\n\t\t}\n\t\tdefer func() {\n\t\t  _ = trans.Close()\n\t\t}()\n```\n\n### Version (0.1.4.RC+)\n\n- Full example could be found: [here](https://github.com/linxGnu/gosmpp/blob/master/example)\n  - In this example, you should run smsc first:\n    - Build and run SMSC Simulator:\n\t```bash\n\tg++ -std=c++11 example/smsc_simulator/smsc.cpp -o smsc\n\t./smsc &\n\t```\n    - Run smpp client in the example: https://github.com/linxGnu/gosmpp/blob/master/example/main.go\n    ```bash\n\tgo run example/main.go\n\t```\n\n### Old version (0.1.3 and previous)\nFull example could be found: [gist](https://gist.github.com/linxGnu/b488997a0e62b3f6a7060ba2af6391ea)\n\n## Supported PDUs\n\n- [x] bind_transmitter\n- [x] bind_transmitter_resp\n- [x] bind_receiver\n- [x] bind_receiver_resp\n- [x] bind_transceiver\n- [x] bind_transceiver_resp\n- [x] outbind\n- [x] unbind\n- [x] unbind_resp\n- [x] submit_sm\n- [x] submit_sm_resp\n- [x] submit_sm_multi\n- [x] submit_sm_multi_resp\n- [x] data_sm\n- [x] data_sm_resp\n- [x] deliver_sm\n- [x] deliver_sm_resp\n- [x] query_sm\n- [x] query_sm_resp\n- [x] cancel_sm\n- [x] cancel_sm_resp\n- [x] replace_sm\n- [x] replace_sm_resp\n- [x] enquire_link\n- [x] enquire_link_resp\n- [x] alert_notification\n- [x] generic_nack\n"
  },
  {
    "path": "connect.go",
    "content": "package gosmpp\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\nvar (\n\t// NonTLSDialer is non-tls connection dialer.\n\tNonTLSDialer = func(addr string) (net.Conn, error) {\n\t\treturn net.Dial(\"tcp\", addr)\n\t}\n)\n\n// Dialer is connection dialer.\ntype Dialer func(addr string) (net.Conn, error)\n\n// Auth represents basic authentication to SMSC.\ntype Auth struct {\n\t// SMSC is SMSC address.\n\tSMSC       string\n\tSystemID   string\n\tPassword   string\n\tSystemType string\n}\n\ntype BindError struct {\n\tCommandStatus data.CommandStatusType\n}\n\nfunc (err BindError) Error() string {\n\treturn fmt.Sprintf(\"binding error (%s): %s\", err.CommandStatus, err.CommandStatus.Desc())\n}\n\nfunc newBindRequest(s Auth, bindingType pdu.BindingType, addressRange pdu.AddressRange) (bindReq *pdu.BindRequest) {\n\tbindReq = pdu.NewBindRequest(bindingType)\n\tbindReq.SystemID = s.SystemID\n\tbindReq.Password = s.Password\n\tbindReq.SystemType = s.SystemType\n\tbindReq.AddressRange = addressRange\n\treturn\n}\n\n// Connector is connection factory interface.\ntype Connector interface {\n\tConnect() (conn *Connection, err error)\n\tGetBindType() pdu.BindingType\n}\n\ntype connector struct {\n\tdialer       Dialer\n\tauth         Auth\n\tbindingType  pdu.BindingType\n\taddressRange pdu.AddressRange\n}\n\nfunc (c *connector) GetBindType() pdu.BindingType {\n\treturn c.bindingType\n}\n\nfunc (c *connector) Connect() (conn *Connection, err error) {\n\tconn, err = connect(c.dialer, c.auth.SMSC, newBindRequest(c.auth, c.bindingType, c.addressRange))\n\treturn\n}\n\nfunc connect(dialer Dialer, addr string, bindReq *pdu.BindRequest) (c *Connection, err error) {\n\tconn, err := dialer(addr)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// create wrapped connection\n\tc = NewConnection(conn)\n\n\t// send binding request\n\t_, err = c.WritePDU(bindReq)\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n\n\t// catching response\n\tvar (\n\t\tp    pdu.PDU\n\t\tresp *pdu.BindResp\n\t)\n\n\tfor {\n\t\tif p, err = pdu.Parse(c); err != nil {\n\t\t\t_ = conn.Close()\n\t\t\treturn\n\t\t}\n\n\t\tif pd, ok := p.(*pdu.BindResp); ok {\n\t\t\tresp = pd\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif resp.CommandStatus != data.ESME_ROK {\n\t\terr = BindError{CommandStatus: resp.CommandStatus}\n\t\t_ = conn.Close()\n\t} else {\n\t\tc.systemID = resp.SystemID\n\t}\n\n\treturn\n}\n\n// TXConnector returns a Transmitter (TX) connector.\nfunc TXConnector(dialer Dialer, auth Auth) Connector {\n\treturn &connector{\n\t\tdialer:      dialer,\n\t\tauth:        auth,\n\t\tbindingType: pdu.Transmitter,\n\t}\n}\n\n// RXConnector returns a Receiver (RX) connector.\nfunc RXConnector(dialer Dialer, auth Auth, opts ...connectorOption) Connector {\n\tc := &connector{\n\t\tdialer:      dialer,\n\t\tauth:        auth,\n\t\tbindingType: pdu.Receiver,\n\t}\n\tfor _, opt := range opts {\n\t\topt(c)\n\t}\n\treturn c\n}\n\n// TRXConnector returns a Transceiver (TRX) connector.\nfunc TRXConnector(dialer Dialer, auth Auth, opts ...connectorOption) Connector {\n\tc := &connector{\n\t\tdialer:      dialer,\n\t\tauth:        auth,\n\t\tbindingType: pdu.Transceiver,\n\t}\n\tfor _, opt := range opts {\n\t\topt(c)\n\t}\n\treturn c\n}\n\ntype connectorOption func(c *connector)\n\nfunc WithAddressRange(addressRange pdu.AddressRange) connectorOption {\n\treturn func(c *connector) {\n\t\tc.addressRange = addressRange\n\t}\n}\n"
  },
  {
    "path": "connect_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar currentAuth int32\n\nvar auths = [][2]string{\n\t{\"689528\", \"1a97ae\"},\n}\n\nconst (\n\tsmscAddr = \"127.0.0.1:2775\"\n\tmess     = \"Thử nghiệm: chuẩn bị nế mễ\"\n)\n\nfunc nextAuth() Auth {\n\tpair := int(atomic.AddInt32(&currentAuth, 1)) % len(auths)\n\treturn Auth{\n\t\tSMSC:       smscAddr,\n\t\tSystemID:   auths[pair][0],\n\t\tPassword:   auths[pair][1],\n\t\tSystemType: \"\",\n\t}\n}\n\nfunc TestBindingSMSC(t *testing.T) {\n\tchecker := func(t *testing.T, c Connector) {\n\t\tconn, err := c.Connect()\n\t\trequire.Nil(t, err)\n\t\trequire.NotNil(t, conn)\n\t\t_ = conn.Close()\n\t}\n\n\tt.Run(\"TX\", func(t *testing.T) {\n\t\tchecker(t, TXConnector(NonTLSDialer, nextAuth()))\n\t})\n\n\tt.Run(\"RX\", func(t *testing.T) {\n\t\tchecker(t, RXConnector(NonTLSDialer, nextAuth()))\n\t})\n\tt.Run(\"RX\", func(t *testing.T) {\n\t\taddrRange := pdu.AddressRange{}\n\t\taddrRange.AddressRange = \"31218\"\n\t\tchecker(t, RXConnector(NonTLSDialer, nextAuth(), WithAddressRange(addrRange)))\n\t})\n\n\tt.Run(\"TRX\", func(t *testing.T) {\n\t\tchecker(t, TRXConnector(NonTLSDialer, nextAuth()))\n\t})\n\n\tt.Run(\"TRX\", func(t *testing.T) {\n\t\taddrRange := pdu.AddressRange{}\n\t\taddrRange.AddressRange = \"31218\"\n\t\tchecker(t, TRXConnector(NonTLSDialer, nextAuth(), WithAddressRange(addrRange)))\n\t})\n}\n\nfunc TestBindingSMSC_Error(t *testing.T) {\n\tauth := Auth{SMSC: smscAddr, SystemID: \"invalid\"}\n\tchecker := func(t *testing.T, c Connector) {\n\t\tconn, err := c.Connect()\n\t\trequire.ErrorContains(t, err, \"Invalid System ID\")\n\t\t_ = conn.Close()\n\t}\n\n\tt.Run(\"TX\", func(t *testing.T) {\n\t\tchecker(t, TXConnector(NonTLSDialer, auth))\n\t})\n\n\tt.Run(\"RX\", func(t *testing.T) {\n\t\tchecker(t, RXConnector(NonTLSDialer, auth))\n\t})\n\n\tt.Run(\"TRX\", func(t *testing.T) {\n\t\tchecker(t, TRXConnector(NonTLSDialer, auth))\n\t})\n}\n\nfunc TestBindingType(t *testing.T) {\n\tauth := Auth{SMSC: smscAddr, SystemID: \"invalid\"}\n\n\tt.Run(\"TX\", func(t *testing.T) {\n\t\tc := TXConnector(NonTLSDialer, auth)\n\t\trequire.Equal(t, c.GetBindType(), pdu.Transmitter)\n\t})\n\n\tt.Run(\"RX\", func(t *testing.T) {\n\t\tc := RXConnector(NonTLSDialer, auth)\n\t\trequire.Equal(t, c.GetBindType(), pdu.Receiver)\n\t})\n\n\tt.Run(\"TRX\", func(t *testing.T) {\n\t\tc := TRXConnector(NonTLSDialer, auth)\n\t\trequire.Equal(t, c.GetBindType(), pdu.Transceiver)\n\t})\n}\n"
  },
  {
    "path": "connection.go",
    "content": "package gosmpp\n\nimport (\n\t\"bufio\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\n// Connection wraps over net.Conn with buffered data reader.\ntype Connection struct {\n\tsystemID string\n\tconn     net.Conn\n\treader   *bufio.Reader\n}\n\n// NewConnection returns a Connection.\nfunc NewConnection(conn net.Conn) (c *Connection) {\n\tc = &Connection{\n\t\tconn:   conn,\n\t\treader: bufio.NewReaderSize(conn, 128<<10),\n\t}\n\treturn\n}\n\n// Read reads data from the connection.\n// Read can be made to time out and return an Error with Timeout() == true\n// after a fixed time limit; see SetDeadline and SetReadDeadline.\nfunc (c *Connection) Read(b []byte) (n int, err error) {\n\tn, err = c.reader.Read(b)\n\treturn\n}\n\n// Write writes data to the connection.\n// Write can be made to time out and return an Error with Timeout() == true\n// after a fixed time limit; see SetDeadline and SetWriteDeadline.\nfunc (c *Connection) Write(b []byte) (n int, err error) {\n\tn, err = c.conn.Write(b)\n\treturn\n}\n\n// WritePDU data to the connection.\nfunc (c *Connection) WritePDU(p pdu.PDU) (n int, err error) {\n\tbuf := pdu.NewBuffer(make([]byte, 0, 64))\n\tp.Marshal(buf)\n\tn, err = c.conn.Write(buf.Bytes())\n\treturn\n}\n\n// Close closes the connection.\n// Any blocked Read or Write operations will be unblocked and return errors.\nfunc (c *Connection) Close() error {\n\treturn c.conn.Close()\n}\n\n// LocalAddr returns the local network address.\nfunc (c *Connection) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\n// RemoteAddr returns the remote network address.\nfunc (c *Connection) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\n// SetDeadline sets the read and write deadlines associated\n// with the connection. It is equivalent to calling both\n// SetReadDeadline and SetWriteDeadline.\n//\n// A deadline is an absolute time after which I/O operations\n// fail with a timeout (see type Error) instead of\n// blocking. The deadline applies to all future and pending\n// I/O, not just the immediately following call to Read or\n// Write. After a deadline has been exceeded, the connection\n// can be refreshed by setting a deadline in the future.\n//\n// An idle timeout can be implemented by repeatedly extending\n// the deadline after successful Read or Write calls.\n//\n// A zero value for t means I/O operations will not time out.\n//\n// Note that if a TCP connection has keep-alive turned on,\n// which is the default unless overridden by Dialer.KeepAlive\n// or ListenConfig.KeepAlive, then a keep-alive failure may\n// also return a timeout error. On Unix systems a keep-alive\n// failure on I/O can be detected using\n// errors.Is(err, syscall.ETIMEDOUT).\nfunc (c *Connection) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\n// SetReadDeadline sets the deadline for future Read calls\n// and any currently-blocked Read call.\n// A zero value for t means Read will not time out.\nfunc (c *Connection) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\n// SetReadTimeout is equivalent to ReadDeadline(now + timeout)\nfunc (c *Connection) SetReadTimeout(t time.Duration) error {\n\treturn c.conn.SetReadDeadline(time.Now().Add(t))\n}\n\n// SetWriteDeadline sets the deadline for future Write calls\n// and any currently-blocked Write call.\n// Even if write times out, it may return n > 0, indicating that\n// some of the data was successfully written.\n// A zero value for t means Write will not time out.\nfunc (c *Connection) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n\n// SetWriteTimeout is equivalent to WriteDeadline(now + timeout)\nfunc (c *Connection) SetWriteTimeout(t time.Duration) error {\n\treturn c.conn.SetWriteDeadline(time.Now().Add(t))\n}\n"
  },
  {
    "path": "connection_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnection(t *testing.T) {\n\tconn, err := net.Dial(\"tcp\", \"smscsim.melroselabs.com:2775\")\n\trequire.Nil(t, err)\n\n\tc := NewConnection(conn)\n\tdefer func() {\n\t\t_ = c.Close()\n\t}()\n\tt.Log(c.LocalAddr())\n\tt.Log(c.RemoteAddr())\n\n\trequire.Nil(t, c.SetDeadline(time.Now().Add(5*time.Second)))\n\trequire.Nil(t, c.SetWriteDeadline(time.Now().Add(5*time.Second)))\n\trequire.Nil(t, c.SetReadDeadline(time.Now().Add(5*time.Second)))\n}\n"
  },
  {
    "path": "data/7bit.go",
    "content": "package data\n\n// Source code in this file is copied from: https://github.com/fiorix/go-smpp/master/smpp/encoding/gsm7.go\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"math\"\n\n\t\"golang.org/x/text/encoding\"\n\t\"golang.org/x/text/transform\"\n)\n\n// ErrInvalidCharacter means a given character can not be represented in GSM 7-bit encoding.\n//\n// This can only happen during encoding.\nvar ErrInvalidCharacter = errors.New(\"invalid gsm7 character\")\n\n// ErrInvalidByte means that a given byte is outside of the GSM 7-bit encoding range.\n//\n// This can only happen during decoding.\nvar ErrInvalidByte = errors.New(\"invalid gsm7 byte\")\n\n/*\nGSM 7-bit default alphabet and extension table\nSource: https://en.wikipedia.org/wiki/GSM_03.38#GSM_7-bit_default_alphabet_and_extension_table_of_3GPP_TS_23.038_/_GSM_03.38\n*/\nconst escapeSequence = 0x1B\n\nvar forwardLookup = map[rune]byte{\n\t'@': 0x00, '£': 0x01, '$': 0x02, '¥': 0x03, 'è': 0x04, 'é': 0x05, 'ù': 0x06, 'ì': 0x07,\n\t'ò': 0x08, 'Ç': 0x09, '\\n': 0x0a, 'Ø': 0x0b, 'ø': 0x0c, '\\r': 0x0d, 'Å': 0x0e, 'å': 0x0f,\n\t'Δ': 0x10, '_': 0x11, 'Φ': 0x12, 'Γ': 0x13, 'Λ': 0x14, 'Ω': 0x15, 'Π': 0x16, 'Ψ': 0x17,\n\t'Σ': 0x18, 'Θ': 0x19, 'Ξ': 0x1a /* 0x1B */, 'Æ': 0x1c, 'æ': 0x1d, 'ß': 0x1e, 'É': 0x1f,\n\t' ': 0x20, '!': 0x21, '\"': 0x22, '#': 0x23, '¤': 0x24, '%': 0x25, '&': 0x26, '\\'': 0x27,\n\t'(': 0x28, ')': 0x29, '*': 0x2a, '+': 0x2b, ',': 0x2c, '-': 0x2d, '.': 0x2e, '/': 0x2f,\n\t'0': 0x30, '1': 0x31, '2': 0x32, '3': 0x33, '4': 0x34, '5': 0x35, '6': 0x36, '7': 0x37,\n\t'8': 0x38, '9': 0x39, ':': 0x3a, ';': 0x3b, '<': 0x3c, '=': 0x3d, '>': 0x3e, '?': 0x3f,\n\t'¡': 0x40, 'A': 0x41, 'B': 0x42, 'C': 0x43, 'D': 0x44, 'E': 0x45, 'F': 0x46, 'G': 0x47,\n\t'H': 0x48, 'I': 0x49, 'J': 0x4a, 'K': 0x4b, 'L': 0x4c, 'M': 0x4d, 'N': 0x4e, 'O': 0x4f,\n\t'P': 0x50, 'Q': 0x51, 'R': 0x52, 'S': 0x53, 'T': 0x54, 'U': 0x55, 'V': 0x56, 'W': 0x57,\n\t'X': 0x58, 'Y': 0x59, 'Z': 0x5a, 'Ä': 0x5b, 'Ö': 0x5c, 'Ñ': 0x5d, 'Ü': 0x5e, '§': 0x5f,\n\t'¿': 0x60, 'a': 0x61, 'b': 0x62, 'c': 0x63, 'd': 0x64, 'e': 0x65, 'f': 0x66, 'g': 0x67,\n\t'h': 0x68, 'i': 0x69, 'j': 0x6a, 'k': 0x6b, 'l': 0x6c, 'm': 0x6d, 'n': 0x6e, 'o': 0x6f,\n\t'p': 0x70, 'q': 0x71, 'r': 0x72, 's': 0x73, 't': 0x74, 'u': 0x75, 'v': 0x76, 'w': 0x77,\n\t'x': 0x78, 'y': 0x79, 'z': 0x7a, 'ä': 0x7b, 'ö': 0x7c, 'ñ': 0x7d, 'ü': 0x7e, 'à': 0x7f,\n}\nvar forwardEscape = map[rune]byte{\n\t'\\f': 0x0A, '^': 0x14, '{': 0x28, '}': 0x29, '\\\\': 0x2F, '[': 0x3C, '~': 0x3D, ']': 0x3E, '|': 0x40, '€': 0x65,\n}\nvar reverseLookup = map[byte]rune{\n\t0x00: '@', 0x01: '£', 0x02: '$', 0x03: '¥', 0x04: 'è', 0x05: 'é', 0x06: 'ù', 0x07: 'ì',\n\t0x08: 'ò', 0x09: 'Ç', 0x0a: '\\n', 0x0b: 'Ø', 0x0c: 'ø', 0x0d: '\\r', 0x0e: 'Å', 0x0f: 'å',\n\t0x10: 'Δ', 0x11: '_', 0x12: 'Φ', 0x13: 'Γ', 0x14: 'Λ', 0x15: 'Ω', 0x16: 'Π', 0x17: 'Ψ',\n\t0x18: 'Σ', 0x19: 'Θ', 0x1a: 'Ξ' /* 0x1B */, 0x1c: 'Æ', 0x1d: 'æ', 0x1e: 'ß', 0x1f: 'É',\n\t0x20: ' ', 0x21: '!', 0x22: '\"', 0x23: '#', 0x24: '¤', 0x25: '%', 0x26: '&', 0x27: '\\'',\n\t0x28: '(', 0x29: ')', 0x2a: '*', 0x2b: '+', 0x2c: ',', 0x2d: '-', 0x2e: '.', 0x2f: '/',\n\t0x30: '0', 0x31: '1', 0x32: '2', 0x33: '3', 0x34: '4', 0x35: '5', 0x36: '6', 0x37: '7',\n\t0x38: '8', 0x39: '9', 0x3a: ':', 0x3b: ';', 0x3c: '<', 0x3d: '=', 0x3e: '>', 0x3f: '?',\n\t0x40: '¡', 0x41: 'A', 0x42: 'B', 0x43: 'C', 0x44: 'D', 0x45: 'E', 0x46: 'F', 0x47: 'G',\n\t0x48: 'H', 0x49: 'I', 0x4a: 'J', 0x4b: 'K', 0x4c: 'L', 0x4d: 'M', 0x4e: 'N', 0x4f: 'O',\n\t0x50: 'P', 0x51: 'Q', 0x52: 'R', 0x53: 'S', 0x54: 'T', 0x55: 'U', 0x56: 'V', 0x57: 'W',\n\t0x58: 'X', 0x59: 'Y', 0x5a: 'Z', 0x5b: 'Ä', 0x5c: 'Ö', 0x5d: 'Ñ', 0x5e: 'Ü', 0x5f: '§',\n\t0x60: '¿', 0x61: 'a', 0x62: 'b', 0x63: 'c', 0x64: 'd', 0x65: 'e', 0x66: 'f', 0x67: 'g',\n\t0x68: 'h', 0x69: 'i', 0x6a: 'j', 0x6b: 'k', 0x6c: 'l', 0x6d: 'm', 0x6e: 'n', 0x6f: 'o',\n\t0x70: 'p', 0x71: 'q', 0x72: 'r', 0x73: 's', 0x74: 't', 0x75: 'u', 0x76: 'v', 0x77: 'w',\n\t0x78: 'x', 0x79: 'y', 0x7a: 'z', 0x7b: 'ä', 0x7c: 'ö', 0x7d: 'ñ', 0x7e: 'ü', 0x7f: 'à',\n}\nvar reverseEscape = map[byte]rune{\n\t0x0A: '\\f', 0x14: '^', 0x28: '{', 0x29: '}', 0x2F: '\\\\', 0x3C: '[', 0x3D: '~', 0x3E: ']', 0x40: '|', 0x65: '€',\n}\n\n// ValidateGSM7String returns the characters, in the given text, that can not be represented in GSM 7-bit encoding.\nfunc ValidateGSM7String(text string) []rune {\n\tinvalidChars := make([]rune, 0, 4)\n\tfor _, r := range text {\n\t\tif _, ok := forwardLookup[r]; !ok {\n\t\t\tif _, ok := forwardEscape[r]; !ok {\n\t\t\t\tinvalidChars = append(invalidChars, r)\n\t\t\t}\n\t\t}\n\t}\n\treturn invalidChars\n}\n\n// ValidateGSM7Buffer returns the bytes, in the given buffer, that are outside of the GSM 7-bit encoding range.\nfunc ValidateGSM7Buffer(buffer []byte) []byte {\n\tinvalidBytes := make([]byte, 0, 4)\n\tcount := 0\n\tfor count < len(buffer) {\n\t\tb := buffer[count]\n\t\tif b == escapeSequence {\n\t\t\tcount++\n\t\t\tif count >= len(buffer) {\n\t\t\t\tinvalidBytes = append(invalidBytes, b)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\te := buffer[count]\n\t\t\tif _, ok := reverseEscape[e]; !ok {\n\t\t\t\tinvalidBytes = append(invalidBytes, b, e)\n\t\t\t}\n\t\t} else if _, ok := reverseLookup[b]; !ok {\n\t\t\tinvalidBytes = append(invalidBytes, b)\n\t\t}\n\t\tcount++\n\t}\n\treturn invalidBytes\n}\n\n// GetEscapeChars returns the escape characters in the given text, that doesn't exist in GSM 7-bit DEFAULT alphabet table\nfunc GetEscapeChars(runeText []rune) []rune {\n\teChars := make([]rune, 0, 4)\n\tfor _, r := range runeText {\n\t\tif _, ok := forwardEscape[r]; ok {\n\t\t\teChars = append(eChars, r)\n\t\t}\n\t}\n\treturn eChars\n}\n\n// IsEscapeChar checks if the given rune is an escape char\nfunc IsEscapeChar(c rune) bool {\n\t_, exists := forwardEscape[c]\n\treturn exists\n}\n\n// GSM7 returns a GSM 7-bit Bit Encoding.\n//\n// Set the packed flag to true if you wish to convert septets to octets,\n// this should be false for most SMPP providers.\nfunc GSM7(packed bool) encoding.Encoding {\n\treturn gsm7Encoding{packed: packed}\n}\n\ntype gsm7Encoding struct {\n\tpacked bool\n}\n\nfunc (g gsm7Encoding) NewDecoder() *encoding.Decoder {\n\treturn &encoding.Decoder{Transformer: &gsm7Decoder{\n\t\tpacked: g.packed,\n\t}}\n}\n\nfunc (g gsm7Encoding) NewEncoder() *encoding.Encoder {\n\treturn &encoding.Encoder{Transformer: &gsm7Encoder{\n\t\tpacked: g.packed,\n\t}}\n}\n\nfunc (g gsm7Encoding) String() string {\n\tif g.packed {\n\t\treturn \"GSM 7-bit (Packed)\"\n\t}\n\treturn \"GSM 7-bit (Unpacked)\"\n}\n\ntype gsm7Decoder struct {\n\tpacked bool\n}\n\nfunc (g *gsm7Decoder) Reset() { /* not needed */ }\n\nfunc unpack(src []byte, packed bool) (septets []byte) {\n\tseptets = src\n\tif packed {\n\t\tseptets = make([]byte, 0, len(src))\n\t\tcount := 0\n\t\tfor remain := len(src) - count; remain > 0; {\n\t\t\t// Unpack by converting octets into septets.\n\t\t\tswitch {\n\t\t\tcase remain >= 7:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tseptets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))\n\t\t\t\tseptets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))\n\t\t\t\tseptets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))\n\t\t\t\tseptets = append(septets, (src[count+4]&0x07<<4)|(src[count+3]&0xF0>>4))\n\t\t\t\tseptets = append(septets, (src[count+5]&0x03<<5)|(src[count+4]&0xF8>>3))\n\t\t\t\tseptets = append(septets, (src[count+6]&0x01<<6)|(src[count+5]&0xFC>>2))\n\t\t\t\tif src[count+6] > 0 {\n\t\t\t\t\tseptets = append(septets, src[count+6]&0xFE>>1)\n\t\t\t\t}\n\t\t\t\tcount += 7\n\t\t\tcase remain >= 6:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tseptets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))\n\t\t\t\tseptets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))\n\t\t\t\tseptets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))\n\t\t\t\tseptets = append(septets, (src[count+4]&0x07<<4)|(src[count+3]&0xF0>>4))\n\t\t\t\tseptets = append(septets, (src[count+5]&0x03<<5)|(src[count+4]&0xF8>>3))\n\t\t\t\tcount += 6\n\t\t\tcase remain >= 5:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tseptets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))\n\t\t\t\tseptets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))\n\t\t\t\tseptets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))\n\t\t\t\tseptets = append(septets, (src[count+4]&0x07<<4)|(src[count+3]&0xF0>>4))\n\t\t\t\tcount += 5\n\t\t\tcase remain >= 4:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tseptets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))\n\t\t\t\tseptets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))\n\t\t\t\tseptets = append(septets, (src[count+3]&0x0F<<3)|(src[count+2]&0xE0>>5))\n\t\t\t\tcount += 4\n\t\t\tcase remain >= 3:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tseptets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))\n\t\t\t\tseptets = append(septets, (src[count+2]&0x1F<<2)|(src[count+1]&0xC0>>6))\n\t\t\t\tcount += 3\n\t\t\tcase remain >= 2:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tseptets = append(septets, (src[count+1]&0x3F<<1)|(src[count+0]&0x80>>7))\n\t\t\t\tcount += 2\n\t\t\tcase remain >= 1:\n\t\t\t\tseptets = append(septets, src[count+0]&0x7F<<0)\n\t\t\t\tcount++\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t\t}\n\t\t\tremain = len(src) - count\n\t\t}\n\t}\n\treturn\n}\n\nfunc (g *gsm7Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {\n\tif len(src) == 0 {\n\t\treturn 0, 0, nil\n\t}\n\n\tseptets := unpack(src, g.packed)\n\n\tnSeptet := 0\n\tbuilder := bytes.NewBufferString(\"\")\n\tfor nSeptet < len(septets) {\n\t\tb := septets[nSeptet]\n\t\tif b == escapeSequence {\n\t\t\tnSeptet++\n\t\t\tif nSeptet >= len(septets) {\n\t\t\t\treturn 0, 0, ErrInvalidByte\n\t\t\t}\n\t\t\te := septets[nSeptet]\n\t\t\tif r, ok := reverseEscape[e]; ok {\n\t\t\t\tbuilder.WriteRune(r)\n\t\t\t} else {\n\t\t\t\treturn 0, 0, ErrInvalidByte\n\t\t\t}\n\t\t} else if r, ok := reverseLookup[b]; ok {\n\t\t\tbuilder.WriteRune(r)\n\t\t} else {\n\t\t\treturn 0, 0, ErrInvalidByte\n\t\t}\n\t\tnSeptet++\n\t}\n\ttext := builder.Bytes()\n\tnDst = len(text)\n\n\tif len(dst) < nDst {\n\t\treturn 0, 0, transform.ErrShortDst\n\t}\n\n\tcopy(dst, text)\n\treturn\n}\n\ntype gsm7Encoder struct {\n\tpacked bool\n}\n\nfunc (g *gsm7Encoder) Reset() {\n\t/* no needed */\n}\n\nfunc pack(dst []byte, septets []byte) (nDst int) {\n\tnSeptet := 0\n\tfor remain := len(septets); remain > 0; {\n\t\t// Pack by converting septets into octets.\n\t\tswitch {\n\t\tcase remain >= 8:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)\n\t\t\tdst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)\n\t\t\tdst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)\n\t\t\tdst[nDst+4] = (septets[nSeptet+4] & 0x70 >> 4) | (septets[nSeptet+5] & 0x1F << 3)\n\t\t\tdst[nDst+5] = (septets[nSeptet+5] & 0x60 >> 5) | (septets[nSeptet+6] & 0x3F << 2)\n\t\t\tdst[nDst+6] = (septets[nSeptet+6] & 0x40 >> 6) | (septets[nSeptet+7] & 0x7F << 1)\n\t\t\tnSeptet += 8\n\t\t\tnDst += 7\n\t\tcase remain >= 7:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)\n\t\t\tdst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)\n\t\t\tdst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)\n\t\t\tdst[nDst+4] = (septets[nSeptet+4] & 0x70 >> 4) | (septets[nSeptet+5] & 0x1F << 3)\n\t\t\tdst[nDst+5] = (septets[nSeptet+5] & 0x60 >> 5) | (septets[nSeptet+6] & 0x3F << 2)\n\t\t\tdst[nDst+6] = septets[nSeptet+6] & 0x40 >> 6\n\t\t\tnSeptet += 7\n\t\t\tnDst += 7\n\t\tcase remain >= 6:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)\n\t\t\tdst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)\n\t\t\tdst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)\n\t\t\tdst[nDst+4] = (septets[nSeptet+4] & 0x70 >> 4) | (septets[nSeptet+5] & 0x1F << 3)\n\t\t\tdst[nDst+5] = septets[nSeptet+5] & 0x60 >> 5\n\t\t\tnSeptet += 6\n\t\t\tnDst += 6\n\t\tcase remain >= 5:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)\n\t\t\tdst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)\n\t\t\tdst[nDst+3] = (septets[nSeptet+3] & 0x78 >> 3) | (septets[nSeptet+4] & 0x0F << 4)\n\t\t\tdst[nDst+4] = septets[nSeptet+4] & 0x70 >> 4\n\t\t\tnSeptet += 5\n\t\t\tnDst += 5\n\t\tcase remain >= 4:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)\n\t\t\tdst[nDst+2] = (septets[nSeptet+2] & 0x7C >> 2) | (septets[nSeptet+3] & 0x07 << 5)\n\t\t\tdst[nDst+3] = septets[nSeptet+3] & 0x78 >> 3\n\t\t\tnSeptet += 4\n\t\t\tnDst += 4\n\t\tcase remain >= 3:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = (septets[nSeptet+1] & 0x7E >> 1) | (septets[nSeptet+2] & 0x03 << 6)\n\t\t\tdst[nDst+2] = septets[nSeptet+2] & 0x7C >> 2\n\t\t\tnSeptet += 3\n\t\t\tnDst += 3\n\t\tcase remain >= 2:\n\t\t\tdst[nDst+0] = (septets[nSeptet+0] & 0x7F >> 0) | (septets[nSeptet+1] & 0x01 << 7)\n\t\t\tdst[nDst+1] = septets[nSeptet+1] & 0x7E >> 1\n\t\t\tnSeptet += 2\n\t\t\tnDst += 2\n\t\tcase remain >= 1:\n\t\t\tdst[nDst+0] = septets[nSeptet+0] & 0x7F >> 0\n\t\t\tnSeptet++\n\t\t\tnDst++\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t\tremain = len(septets) - nSeptet\n\t}\n\treturn\n}\n\nfunc (g *gsm7Encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {\n\tif len(src) == 0 {\n\t\treturn 0, 0, nil\n\t}\n\n\ttext := string(src) // work with []rune (a.k.a string) instead of []byte\n\tseptets := make([]byte, 0, len(text))\n\tfor _, r := range text {\n\t\tif v, ok := forwardLookup[r]; ok {\n\t\t\tseptets = append(septets, v)\n\t\t} else if v, ok := forwardEscape[r]; ok {\n\t\t\tseptets = append(septets, escapeSequence, v)\n\t\t} else {\n\t\t\treturn 0, 0, ErrInvalidCharacter\n\t\t}\n\t\tnSrc++\n\t}\n\n\tnDst = len(septets)\n\tif g.packed {\n\t\tnDst = int(math.Ceil(float64(len(septets)) * 7 / 8))\n\t}\n\tif len(dst) < nDst {\n\t\treturn 0, 0, transform.ErrShortDst\n\t}\n\n\tif !g.packed {\n\t\tcopy(dst, septets)\n\t\treturn nDst, nSrc, nil\n\t}\n\n\tnDst = pack(dst, septets)\n\treturn\n}\n"
  },
  {
    "path": "data/7bit_test.go",
    "content": "package data\n\n// Source code in this file is copied from: https://github.com/fiorix\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"golang.org/x/text/transform\"\n)\n\nvar validationStringTests = []struct {\n\tText     string\n\tExpected []rune\n}{\n\t{Text: \"12345678\", Expected: []rune{}},\n\t{Text: \"12345[6]\", Expected: []rune{}},\n\t{Text: \"@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\\f^{}\\\\[~]|€ÆæßÉ !\\\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑÜ§¿abcdefghijklmnopqrstuvwxyzäöñüà\", Expected: []rune{}},\n\t{Text: \"你\", Expected: []rune{'你'}},\n}\n\nvar validationBufferTests = []struct {\n\tBuffer   []byte\n\tExpected []byte\n}{\n\t{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, Expected: []byte{}},\n\t{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B, 0x3C, 0x36, 0x1B, 0x3E}, Expected: []byte{}},\n\t{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B}, Expected: []byte{0x1B}},\n\t{Buffer: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B, 0x00}, Expected: []byte{0x1B, 0x00}},\n\t{Buffer: []byte{0x80, 0x81, 0x82, 0x83}, Expected: []byte{0x80, 0x81, 0x82, 0x83}},\n}\n\nvar packedTests = []struct {\n\tText string\n\tBuff []byte\n}{\n\t{Text: \"\", Buff: []byte{}},\n\t{Text: \"1\", Buff: []byte{0x31}},\n\t{Text: \"12\", Buff: []byte{0x31, 0x19}},\n\t{Text: \"123\", Buff: []byte{0x31, 0xD9, 0x0C}},\n\t{Text: \"1234\", Buff: []byte{0x31, 0xD9, 0x8C, 0x06}},\n\t{Text: \"12345\", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0x03}},\n\t{Text: \"123456\", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0x01}},\n\t{Text: \"1234567\", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0xDD, 0x00}},\n\t{Text: \"12345678\", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0xDD, 0x70}},\n\t{Text: \"123456789\", Buff: []byte{0x31, 0xD9, 0x8C, 0x56, 0xB3, 0xDD, 0x70, 0x39}},\n\t{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\")},\n\t{Text: \"\\n\", Buff: []byte{0x0A}},\n\t{Text: \"\\r\", Buff: []byte{0x0D}},\n\t{Text: \"\\f\", Buff: []byte{0x1B, 0x05}},\n\t{Text: \"^{}\\\\[~]|€\", Buff: []byte{0x1B, 0xCA, 0x06, 0xB5, 0x49, 0x6D, 0x5E, 0x1B, 0xDE, 0xA6, 0xB7, 0xF1, 0x6D, 0x80, 0x9B, 0x32}},\n\t{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\")},\n}\n\nvar unpackedTests = []struct {\n\tText string\n\tBuff []byte\n}{\n\t{Text: \"\", Buff: []byte{}},\n\t{Text: \"1\", Buff: []byte{0x31}},\n\t{Text: \"12\", Buff: []byte{0x31, 0x32}},\n\t{Text: \"123\", Buff: []byte{0x31, 0x32, 0x33}},\n\t{Text: \"1234\", Buff: []byte{0x31, 0x32, 0x33, 0x34}},\n\t{Text: \"12345\", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35}},\n\t{Text: \"123456\", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36}},\n\t{Text: \"1234567\", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37}},\n\t{Text: \"12345678\", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}},\n\t{Text: \"123456789\", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39}},\n\t{Text: \"12345[6\", Buff: []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x1B, 0x3C, 0x36}},\n\t{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\")},\n\t{Text: \"\\n\", Buff: []byte{0x0A}},\n\t{Text: \"\\r\", Buff: []byte{0x0D}},\n\t{Text: \"\\f\", Buff: []byte{0x1B, 0x0A}},\n\t{Text: \"^{}\\\\[~]|€\", Buff: []byte{0x1B, 0x14, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2F, 0x1B, 0x3C, 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x40, 0x1B, 0x65}},\n\t{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\")},\n}\n\nvar invalidCharacterTests = []struct {\n\tPacked bool\n\tText   string\n}{\n\t{Packed: true, Text: \"你\"},\n\t{Packed: false, Text: \"你\"},\n}\n\nvar invalidByteTests = []struct {\n\tPacked bool\n\tBuff   []byte\n}{\n\t{Packed: false, Buff: []byte{0x80}},\n\t{Packed: false, Buff: []byte{0x1B}},\n\t{Packed: false, Buff: []byte{0x1B, 0x80}},\n}\n\nfunc TestGSM7EncodingString(t *testing.T) {\n\ttests := []struct {\n\t\tPacked   bool\n\t\tExpected string\n\t}{\n\t\t{Packed: true, Expected: \"GSM 7-bit (Packed)\"},\n\t\t{Packed: false, Expected: \"GSM 7-bit (Unpacked)\"},\n\t}\n\n\tfor index, row := range tests {\n\t\tactual := fmt.Sprint(GSM7(row.Packed))\n\t\tif actual != row.Expected {\n\t\t\tt.Fatalf(\"%d: expected '%s' but got '%s'\", index, row.Expected, actual)\n\t\t}\n\t}\n}\n\nfunc TestValidateGSM7String(t *testing.T) {\n\tfor index, row := range validationStringTests {\n\t\tactual := ValidateGSM7String(row.Text)\n\t\tif !reflect.DeepEqual(actual, row.Expected) {\n\t\t\tt.Fatalf(\"%2d: actual did not equal expected.\\nactual: %s\\nexpect: %s\", index, string(actual), string(row.Expected))\n\t\t}\n\t}\n}\n\nfunc TestValidateGSM7Buffer(t *testing.T) {\n\tfor index, row := range validationBufferTests {\n\t\tactual := ValidateGSM7Buffer(row.Buffer)\n\t\tif !reflect.DeepEqual(actual, row.Expected) {\n\t\t\tt.Fatalf(\"%2d: actual did not equal expected.\\nactual: %s\\nexpect: %s\", index, hex.EncodeToString(actual), hex.EncodeToString(row.Expected))\n\t\t}\n\t}\n}\n\nfunc TestPackedEncoder(t *testing.T) {\n\tencoder := GSM7(true).NewEncoder()\n\tfor index, row := range packedTests {\n\t\tes, _, err := transform.Bytes(encoder, []byte(row.Text))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%2d: unexpected error: '%s'\", index, err.Error())\n\t\t}\n\t\tif !reflect.DeepEqual(es, row.Buff) {\n\t\t\tt.Fatalf(\"%2d: actual did not equal expected.\\nactual: %s\\nexpect: %s\", index, hex.EncodeToString(es), hex.EncodeToString(row.Buff))\n\t\t}\n\t}\n}\n\nfunc TestUnpackedEncoder(t *testing.T) {\n\tencoder := GSM7(false).NewEncoder()\n\tfor index, row := range unpackedTests {\n\t\tes, _, err := transform.Bytes(encoder, []byte(row.Text))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%2d: unexpected error: '%s'\", index, err.Error())\n\t\t}\n\t\tif !reflect.DeepEqual(es, row.Buff) {\n\t\t\tt.Fatalf(\"%2d: actual did not equal expected.\\nactual: %s\\nexpect: %s\", index, hex.EncodeToString(es), hex.EncodeToString(row.Buff))\n\t\t}\n\t}\n}\n\nfunc TestPackedDecoder(t *testing.T) {\n\tdecoder := GSM7(true).NewDecoder()\n\tfor index, row := range packedTests {\n\t\tes, _, err := transform.Bytes(decoder, row.Buff)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%2d: unexpected error: '%s'\", index, err.Error())\n\t\t}\n\t\tif string(es) != row.Text {\n\t\t\tt.Fatalf(\"%2d: actual did not equal expected.\\nactual: %s\\nexpect: %s\", index, string(es), row.Text)\n\t\t}\n\t}\n}\n\nfunc TestUnpackedDecoder(t *testing.T) {\n\tencoder := GSM7(false).NewDecoder()\n\tfor index, row := range unpackedTests {\n\t\tes, _, err := transform.Bytes(encoder, row.Buff)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%2d: unexpected error: '%s'\", index, err.Error())\n\t\t}\n\t\tif string(es) != row.Text {\n\t\t\tt.Fatalf(\"%2d: actual did not equal expected.\\nactual: %s\\nexpect: %s\", index, string(es), row.Text)\n\t\t}\n\t}\n}\n\nfunc TestInvalidCharacter(t *testing.T) {\n\tfor index, row := range invalidCharacterTests {\n\t\tencoder := GSM7(row.Packed).NewEncoder()\n\t\t_, _, err := transform.Bytes(encoder, []byte(row.Text))\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"%2d: expected error but got no error\", index)\n\t\t}\n\t\tif err != ErrInvalidCharacter {\n\t\t\tt.Fatalf(\"%2d: expected '%s' but got '%s'\", index, ErrInvalidCharacter, err.Error())\n\t\t}\n\t}\n}\n\nfunc TestInvalidByte(t *testing.T) {\n\tfor index, row := range invalidByteTests {\n\t\tdecoder := GSM7(row.Packed).NewDecoder()\n\t\t_, _, err := transform.Bytes(decoder, row.Buff)\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"%2d: expected error but got no error\", index)\n\t\t}\n\t\tif err != ErrInvalidByte {\n\t\t\tt.Fatalf(\"%2d: expected '%s' but got '%s'\", index, ErrInvalidByte, err.Error())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "data/codings.go",
    "content": "package data\n\nimport (\n\t\"golang.org/x/text/encoding\"\n\t\"golang.org/x/text/encoding/charmap\"\n\t\"golang.org/x/text/encoding/unicode\"\n)\n\nconst (\n\t// GSM7BITCoding is gsm-7bit coding\n\tGSM7BITCoding byte = 0x00\n\t// ASCIICoding is ascii coding\n\tASCIICoding byte = 0x01\n\t// BINARY8BIT1Coding is 8-bit binary coding\n\tBINARY8BIT1Coding byte = 0x02\n\t// LATIN1Coding is iso-8859-1 coding\n\tLATIN1Coding byte = 0x03\n\t// BINARY8BIT2Coding is 8-bit binary coding\n\tBINARY8BIT2Coding byte = 0x04\n\t// CYRILLICCoding is iso-8859-5 coding\n\tCYRILLICCoding byte = 0x06\n\t// HEBREWCoding is iso-8859-8 coding\n\tHEBREWCoding byte = 0x07\n\t// UCS2Coding is UCS2 coding\n\tUCS2Coding byte = 0x08\n)\n\n// EncDec wraps encoder and decoder interface.\ntype EncDec interface {\n\tEncode(str string) ([]byte, error)\n\tDecode([]byte) (string, error)\n}\n\n// Encoding interface.\ntype Encoding interface {\n\tEncDec\n\tDataCoding() byte\n}\n\nfunc encode(str string, encoder *encoding.Encoder) ([]byte, error) {\n\treturn encoder.Bytes([]byte(str))\n}\n\nfunc decode(data []byte, decoder *encoding.Decoder) (st string, err error) {\n\ttmp, err := decoder.Bytes(data)\n\tif err == nil {\n\t\tst = string(tmp)\n\t}\n\treturn\n}\n\n// CustomEncoding is wrapper for user-defined data encoding.\ntype CustomEncoding struct {\n\tencDec EncDec\n\tcoding byte\n}\n\n// NewCustomEncoding creates new custom encoding.\nfunc NewCustomEncoding(coding byte, encDec EncDec) Encoding {\n\treturn &CustomEncoding{\n\t\tcoding: coding,\n\t\tencDec: encDec,\n\t}\n}\n\n// Encode string.\nfunc (c *CustomEncoding) Encode(str string) ([]byte, error) {\n\treturn c.encDec.Encode(str)\n}\n\n// Decode data to string.\nfunc (c *CustomEncoding) Decode(data []byte) (string, error) {\n\treturn c.encDec.Decode(data)\n}\n\n// DataCoding flag.\nfunc (c *CustomEncoding) DataCoding() byte {\n\treturn c.coding\n}\n\ntype gsm7bit struct {\n\tpacked bool\n}\n\nfunc (c *gsm7bit) Encode(str string) ([]byte, error) {\n\treturn encode(str, GSM7(c.packed).NewEncoder())\n}\n\nfunc (c *gsm7bit) Decode(data []byte) (string, error) {\n\treturn decode(data, GSM7(c.packed).NewDecoder())\n}\n\nfunc (c *gsm7bit) DataCoding() byte { return GSM7BITCoding }\n\nfunc (c *gsm7bit) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {\n\tif c.packed {\n\t\treturn uint((len(text)*7+7)/8) > octetLimit\n\t} else {\n\t\treturn uint(len(text)) > octetLimit\n\t}\n}\n\nfunc (c *gsm7bit) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {\n\tif octetLimit < 64 {\n\t\toctetLimit = 134\n\t}\n\n\tallSeg = [][]byte{}\n\truneSlice := []rune(text)\n\n\tfr, to := 0, int(octetLimit)\n\tfor fr < len(runeSlice) {\n\t\tif to > len(runeSlice) {\n\t\t\tto = len(runeSlice)\n\t\t}\n\t\tseg, err := c.Encode(string(runeSlice[fr:to]))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tallSeg = append(allSeg, seg)\n\t\tfr, to = to, to+int(octetLimit)\n\t}\n\n\treturn\n}\n\ntype gsm7bitPacked struct {\n}\n\nfunc (c *gsm7bitPacked) Encode(str string) ([]byte, error) {\n\treturn encode(str, GSM7(true).NewEncoder())\n}\n\nfunc (c *gsm7bitPacked) Decode(data []byte) (string, error) {\n\treturn decode(data, GSM7(true).NewDecoder())\n}\n\nfunc (c *gsm7bitPacked) DataCoding() byte { return GSM7BITCoding }\n\nfunc (c *gsm7bitPacked) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {\n\truneSlice := []rune(text)\n\ttLen := len(runeSlice)\n\tescCharsLen := len(GetEscapeChars(runeSlice))\n\tregCharsLen := tLen - escCharsLen\n\t// Esacpe characters occupy 2 octets/septets\n\t// https://en.wikipedia.org/wiki/GSM_03.38\n\t// https://www.developershome.com/sms/gsmAlphabet.asp\n\treturn uint((regCharsLen*7+escCharsLen*2*7+7)/8) > octetLimit\n}\n\nfunc (c *gsm7bitPacked) GetSeptetCount(runeSlice []rune) int {\n\ttLen := len(runeSlice)\n\tescCharsLen := len(GetEscapeChars(runeSlice))\n\tregCharsLen := tLen - escCharsLen\n\treturn escCharsLen*2 + regCharsLen\n}\n\nfunc (c *gsm7bitPacked) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {\n\tif octetLimit < 64 {\n\t\toctetLimit = 134\n\t}\n\n\tallSeg = [][]byte{}\n\truneSlice := []rune(text)\n\tlim := int(octetLimit * 8 / 7)\n\n\tfr, to := 0, lim\n\tfor fr < len(runeSlice) {\n\t\tif to > len(runeSlice) {\n\t\t\tto = len(runeSlice)\n\t\t}\n\n\t\tto = determineTo(fr, to, lim, runeSlice)\n\n\t\tseg, err := c.Encode(string(runeSlice[fr:to]))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tincludeLSB := false\n\t\tnSeptet := c.GetSeptetCount(runeSlice[fr:to])\n\t\tif nSeptet != lim && nSeptet%8 == 0 { // The last octet's LSB should be included during shift\n\t\t\tincludeLSB = true\n\t\t}\n\n\t\tseg = shiftBitsLeftOne(seg, includeLSB)\n\n\t\tallSeg = append(allSeg, seg)\n\t\tfr, to = to, to+lim\n\t}\n\n\treturn\n}\n\nfunc determineTo(from int, to int, lim int, runeSlice []rune) int {\n\tnSeptet := 0\n\tfor nSeptet < lim {\n\t\tif IsEscapeChar(runeSlice[from]) { // esc chars counted as 2 septes\n\t\t\tnSeptet += 2\n\t\t} else {\n\t\t\tnSeptet++\n\t\t}\n\t\tfrom++\n\t\tif from == to {\n\t\t\tbreak\n\t\t}\n\t}\n\tto = from\n\n\tif 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.\"\n\t\tif nSeptet > lim {\n\t\t\tto--\n\t\t}\n\t}\n\treturn to\n}\n\n// 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\n// Ref1: https://www.etsi.org/deliver/etsi_ts/123000_123099/123040/16.00.00_60/ts_123040v160000p.pdf Page 74\n// 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.\"\n// 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].\"\nfunc shiftBitsLeftOne(input []byte, includeLSB bool) []byte {\n\tshifted := make([]byte, len(input))\n\tfor i, b := range input {\n\t\tshifted[i] = b << 1\n\t\tif i > 0 {\n\t\t\tshifted[i] |= input[i-1] >> 7\n\t\t}\n\t}\n\n\tif includeLSB {\n\t\tlastOctet := (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...\"*/\n\t\tshifted = append(shifted, lastOctet)\n\t}\n\n\treturn shifted\n}\n\ntype ascii struct{}\n\nfunc (*ascii) Encode(str string) ([]byte, error) {\n\treturn []byte(str), nil\n}\n\nfunc (*ascii) Decode(data []byte) (string, error) {\n\treturn string(data), nil\n}\n\nfunc (*ascii) DataCoding() byte { return ASCIICoding }\n\ntype iso88591 struct{}\n\nfunc (*iso88591) Encode(str string) ([]byte, error) {\n\treturn encode(str, charmap.ISO8859_1.NewEncoder())\n}\n\nfunc (*iso88591) Decode(data []byte) (string, error) {\n\treturn decode(data, charmap.ISO8859_1.NewDecoder())\n}\n\nfunc (*iso88591) DataCoding() byte { return LATIN1Coding }\n\ntype binary8bit1 struct{}\n\nfunc (*binary8bit1) Encode(_ string) ([]byte, error) {\n\treturn []byte{}, ErrNotImplEncode\n}\n\nfunc (*binary8bit1) Decode(_ []byte) (string, error) {\n\treturn \"\", ErrNotImplDecode\n}\n\nfunc (*binary8bit1) DataCoding() byte { return BINARY8BIT1Coding }\n\ntype binary8bit2 struct{}\n\nfunc (*binary8bit2) Encode(_ string) ([]byte, error) {\n\treturn []byte{}, ErrNotImplEncode\n}\n\nfunc (*binary8bit2) Decode(_ []byte) (string, error) {\n\treturn \"\", ErrNotImplDecode\n}\n\nfunc (*binary8bit2) DataCoding() byte { return BINARY8BIT2Coding }\n\ntype iso88595 struct{}\n\nfunc (*iso88595) Encode(str string) ([]byte, error) {\n\treturn encode(str, charmap.ISO8859_5.NewEncoder())\n}\n\nfunc (*iso88595) Decode(data []byte) (string, error) {\n\treturn decode(data, charmap.ISO8859_5.NewDecoder())\n}\n\nfunc (*iso88595) DataCoding() byte { return CYRILLICCoding }\n\ntype iso88598 struct{}\n\nfunc (*iso88598) Encode(str string) ([]byte, error) {\n\treturn encode(str, charmap.ISO8859_8.NewEncoder())\n}\n\nfunc (*iso88598) Decode(data []byte) (string, error) {\n\treturn decode(data, charmap.ISO8859_8.NewDecoder())\n}\n\nfunc (*iso88598) DataCoding() byte { return HEBREWCoding }\n\ntype ucs2 struct{}\n\nfunc (*ucs2) Encode(str string) ([]byte, error) {\n\ttmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)\n\treturn encode(str, tmp.NewEncoder())\n}\n\nfunc (*ucs2) Decode(data []byte) (string, error) {\n\ttmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)\n\treturn decode(data, tmp.NewDecoder())\n}\n\nfunc (*ucs2) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {\n\truneSlice := []rune(text)\n\treturn uint(len(runeSlice)*2) > octetLimit\n}\n\nfunc (c *ucs2) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {\n\tif octetLimit < 64 {\n\t\toctetLimit = 134\n\t}\n\n\tallSeg = [][]byte{}\n\truneSlice := []rune(text)\n\thextetLim := int(octetLimit / 2) // round down\n\n\t// hextet = 16 bits, the correct terms should be hexadectet\n\tfr, to := 0, hextetLim\n\tfor fr < len(runeSlice) {\n\t\tif to > len(runeSlice) {\n\t\t\tto = len(runeSlice)\n\t\t}\n\n\t\tseg, err := c.Encode(string(runeSlice[fr:to]))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tallSeg = append(allSeg, seg)\n\n\t\tfr, to = to, to+hextetLim\n\t}\n\n\treturn\n}\n\nfunc (*ucs2) DataCoding() byte { return UCS2Coding }\n\nvar (\n\t// GSM7BIT is gsm-7bit encoding.\n\tGSM7BIT Encoding = &gsm7bit{packed: false}\n\n\t// GSM7BITPACKED is packed gsm-7bit encoding.\n\t// Most of SMSC(s) use unpack version.\n\t// Should be tested before using.\n\tGSM7BITPACKED Encoding = &gsm7bitPacked{}\n\n\t// ASCII is ascii encoding.\n\tASCII Encoding = &ascii{}\n\n\t// BINARY8BIT1 is binary 8-bit encoding.\n\tBINARY8BIT1 Encoding = &binary8bit1{}\n\n\t// LATIN1 encoding.\n\tLATIN1 Encoding = &iso88591{}\n\n\t// BINARY8BIT2 is binary 8-bit encoding.\n\tBINARY8BIT2 Encoding = &binary8bit2{}\n\n\t// CYRILLIC encoding.\n\tCYRILLIC Encoding = &iso88595{}\n\n\t// HEBREW encoding.\n\tHEBREW Encoding = &iso88598{}\n\n\t// UCS2 encoding.\n\tUCS2 Encoding = &ucs2{}\n)\n\nvar codingMap = map[byte]Encoding{\n\tGSM7BITCoding:     GSM7BIT,\n\tASCIICoding:       ASCII,\n\tBINARY8BIT1Coding: BINARY8BIT1,\n\tLATIN1Coding:      LATIN1,\n\tBINARY8BIT2Coding: BINARY8BIT2,\n\tCYRILLICCoding:    CYRILLIC,\n\tHEBREWCoding:      HEBREW,\n\tUCS2Coding:        UCS2,\n}\n\n// FromDataCoding returns encoding from DataCoding value.\nfunc FromDataCoding(code byte) (enc Encoding) {\n\tenc, ok := codingMap[code]\n\tif !ok { // if encoding is reserved (custom)\n\t\tenc = NewCustomEncoding(code, GSM7BIT) // GSM7BIT is a temporary patch to apply to Encode/Decode methods\n\t}\n\treturn\n}\n\n// Splitter extend encoding object by defining a split function\n// that split a string into multiple segments\n// Each segment string, when encoded, must be within a certain octet limit\ntype Splitter interface {\n\t// ShouldSplit check if the encoded data of given text should be splitted under octetLimit\n\tShouldSplit(text string, octetLimit uint) (should bool)\n\tEncodeSplit(text string, octetLimit uint) ([][]byte, error)\n}\n"
  },
  {
    "path": "data/codings_test.go",
    "content": "package data\n\nimport (\n\t\"encoding/hex\"\n\t\"log\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc fromHex(h string) (v []byte) {\n\tvar err error\n\tv, err = hex.DecodeString(h)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn\n}\n\nfunc testEncoding(t *testing.T, enc EncDec, original, expected string) {\n\tencoded, err := enc.Encode(original)\n\trequire.Nil(t, err)\n\trequire.Equal(t, fromHex(expected), encoded)\n\n\tdecoded, err := enc.Decode(encoded)\n\trequire.Nil(t, err)\n\trequire.Equal(t, original, decoded)\n}\n\nfunc testEncodingSplit(t *testing.T, enc EncDec, octetLim uint, original string, expected []string, expectDecode []string) {\n\tsplitter, ok := enc.(Splitter)\n\trequire.Truef(t, ok, \"Encoding must implement Splitter interface\")\n\n\tsegEncoded, err := splitter.EncodeSplit(original, octetLim)\n\trequire.Nil(t, err)\n\n\tfor i, seg := range segEncoded {\n\t\trequire.Equal(t, fromHex(expected[i]), seg)\n\t\trequire.LessOrEqualf(t, uint(len(seg)), octetLim,\n\t\t\t\"Segment len must be less than or equal to %d, got %d\", octetLim, len(seg))\n\n\t\tif enc == GSM7BITPACKED {\n\t\t\tseg = shiftBitsOneRight(seg)\n\t\t}\n\t\tdecoded, err := enc.Decode(seg)\n\t\trequire.Nil(t, err)\n\t\trequire.Equal(t, expectDecode[i], decoded)\n\t}\n}\n\nfunc shiftBitsOneRight(input []byte) []byte {\n\tcarry := byte(0)\n\tfor i := len(input) - 1; i >= 0; i-- {\n\t\t// Save the carry bit from the previous byte\n\t\tnextCarry := input[i] & 0b00000001\n\t\t// Shift the current byte to the right\n\t\tinput[i] >>= 1\n\t\t// Apply the carry from the previous byte to the current byte\n\t\tinput[i] |= carry << 7\n\t\t// Update the carry for the next byte\n\t\tcarry = nextCarry\n\t}\n\treturn input\n}\n\nfunc TestCoding(t *testing.T) {\n\trequire.Nil(t, FromDataCoding(12))\n\trequire.Equal(t, GSM7BIT, FromDataCoding(0))\n\trequire.Equal(t, ASCII, FromDataCoding(1))\n\trequire.Equal(t, UCS2, FromDataCoding(8))\n\trequire.Equal(t, LATIN1, FromDataCoding(3))\n\trequire.Equal(t, CYRILLIC, FromDataCoding(6))\n\trequire.Equal(t, HEBREW, FromDataCoding(7))\n}\n\nfunc TestGSM7Bit(t *testing.T) {\n\trequire.EqualValues(t, 0, GSM7BITPACKED.DataCoding())\n\ttestEncoding(t, GSM7BITPACKED, \"gjwklgjkwP123+?\", \"67f57dcd3eabd777684c365bfd00\")\n}\n\nfunc TestShouldSplit(t *testing.T) {\n\tt.Run(\"testShouldSplit_GSM7BIT\", func(t *testing.T) {\n\t\toctetLim := uint(140)\n\t\texpect := map[string]bool{\n\t\t\t\"\":  false,\n\t\t\t\"1\": false,\n\t\t\t\"12312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311234121212\":  false,\n\t\t\t\"123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112342212121\": true,\n\t\t}\n\n\t\tsplitter, _ := GSM7BIT.(Splitter)\n\t\tfor k, v := range expect {\n\t\t\tok := splitter.ShouldSplit(k, octetLim)\n\t\t\trequire.Equalf(t, ok, v, \"Test case %s\", k)\n\t\t}\n\t})\n\n\tt.Run(\"testShouldSplit_UCS2\", func(t *testing.T) {\n\t\toctetLim := uint(140)\n\t\texpect := map[string]bool{\n\t\t\t\"\":  false,\n\t\t\t\"1\": false,\n\t\t\t\"ởỀÊộẩừỰÉÊỗọễệớỡồỰỬỪựởặỬ̀ỵổẤỨợỶẰỢộứẶHữẹ̃ẾỆằỄéậÃỡẰộ̀ỀỗứẲữỪữộÊỵòALữộòC\":  false, /* 70 UCS2 chars */\n\t\t\t\"ợÁÊGỷẹííỡỮÂIỆàúễẠỮỊệÂỖÍắẵYẠừẲíộờíẵỠựẤằờởể̃ởỵởềệổồUỡỵầễÁÝởÝNè̉ỚổôỊộợKỨệ́\": true,  /* 71 UCS2 chars */\n\t\t}\n\n\t\tsplitter, _ := UCS2.(Splitter)\n\t\tfor k, v := range expect {\n\t\t\tok := splitter.ShouldSplit(k, octetLim)\n\t\t\trequire.Equalf(t, ok, v, \"Test case %s\", k)\n\t\t}\n\t})\n\n\tt.Run(\"testShouldSplit_GSM7BITPACKED\", func(t *testing.T) {\n\t\toctetLim := uint(140)\n\t\texpect := map[string]bool{\n\t\t\t\"\":  false,\n\t\t\t\"1\": false,\n\t\t\t\"12312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311231231231123123123112312312311234121212\":                      false,\n\t\t\t\"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqw\":  false, /* 160 regular basic alphabet chars */\n\t\t\t\"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqwd\": true,  /* 161 regular basic alphabet chars */\n\t\t\t\"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqw{\": true,  /* 159 regular basic alphabet chars + 1 escape char at the end */\n\t\t\t\"|}€€|]|€[~€^]€~{~^{|]]|[{|~€^|]^[[{€^]^{€}}^~~]€]~€[€€[]~~[}}]{^}{|}~~]]€^{^|€{^\":                                                                                  false, /* 80 escape chars */\n\t\t\t\"|}€€|]|€[~€^]€~{~^{|]]|[{|~€^|]^[[{€^]^{€}}^~~]€]~€[€€[]~~[}}]{^}{|}~~]]€^{^|€{^{\":                                                                                 true,  /* 81 escape chars */\n\t\t}\n\n\t\tsplitter, _ := GSM7BITPACKED.(Splitter)\n\t\tfor k, v := range expect {\n\t\t\tok := splitter.ShouldSplit(k, octetLim)\n\t\t\trequire.Equalf(t, ok, v, \"Test case %s\", k)\n\t\t}\n\t})\n}\nfunc TestSplit(t *testing.T) {\n\trequire.EqualValues(t, 0o0, GSM7BITPACKED.DataCoding())\n\n\tt.Run(\"testSplitGSM7Empty\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BIT,\n\t\t\t134,\n\t\t\t\"\",\n\t\t\t[]string{\n\t\t\t\t\"\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"\",\n\t\t\t})\n\t})\n\n\tt.Run(\"testSplitUCS2\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, UCS2,\n\t\t\t134,\n\t\t\t\"biggest gift của Christmas là có nhiều big/challenging/meaningful problems để sấp mặt làm\",\n\t\t\t[]string{\n\t\t\t\t\"006200690067006700650073007400200067006900660074002000631ee700610020004300680072006900730074006d006100730020006c00e00020006300f30020006e006800691ec100750020006200690067002f006300680061006c006c0065006e00670069006e0067002f006d00650061006e0069006e006700660075006c00200070\",\n\t\t\t\t\"0072006f0062006c0065006d0073002001111ec3002000731ea500700020006d1eb700740020006c00e0006d\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"biggest gift của Christmas là có nhiều big/challenging/meaningful p\",\n\t\t\t\t\"roblems để sấp mặt làm\",\n\t\t\t})\n\t})\n\n\tt.Run(\"testSplitUCS2Empty\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, UCS2,\n\t\t\t134,\n\t\t\t\"\",\n\t\t\t[]string{\n\t\t\t\t\"\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"\",\n\t\t\t})\n\t})\n\n\t// UCS2 character should not be splitted in the middle\n\t// here 54 character is encoded to 108 octet, but since there are 107 octet limit,\n\t// a whole 2 octet has to be carried over to the next segment\n\tt.Run(\"testSplit_Middle_UCS2\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, UCS2,\n\t\t\t107,\n\t\t\t\"biggest gift của Christmas là có nhiều big/challenging\",\n\t\t\t[]string{\n\t\t\t\t\"006200690067006700650073007400200067006900660074002000631ee700610020004300680072006900730074006d006100730020006c00e00020006300f30020006e006800691ec100750020006200690067002f006300680061006c006c0065006e00670069006e\",\n\t\t\t\t\"0067\", // 0x00 0x67 is \"g\"\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"biggest gift của Christmas là có nhiều big/challengin\",\n\t\t\t\t\"g\",\n\t\t\t})\n\t})\n}\n\nfunc TestSplit_GSM7BITPACKED(t *testing.T) {\n\trequire.EqualValues(t, 0o0, GSM7BITPACKED.DataCoding())\n\n\tt.Run(\"testSplit_Escape_GSM7BITPACKED\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BITPACKED,\n\t\t\t134,\n\t\t\t\"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwdqwqwdqw{\",\n\t\t\t[]string{\n\t\t\t\t\"ceeafb9a7d56afefd0986cb6facdc37372784e0ec7efe4f89d1cbf93e37772fc4e8edfc9f13b397e27c7efe4f89d1cbf93e37772fc4e8edfc9f13b397e27c7efe438397e27c7efc4e8951cbf93e37772fc4e8edfeff13b397e27c7ef6472fc4e8edfc9f13b397e27c7efe4f89d1cbf93e37772fc4e8edfc9f13b394ebec7c9f17bfc4e8edfc9\",\n\t\t\t\t\"e2f7f89d1cbf6f50\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"gjwklgjkwP123+?sasdasdaqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdqwdqwDQWdqwdqwdqwdqwwqwdqwdqwddqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqwdqdwqdqwqwdqwd\",\n\t\t\t\t\"qwqwdqw{\",\n\t\t\t})\n\t})\n\n\t/*\n\t\tTotal char count = 160,\n\t\tEsc char count = 1,\n\t\tRegular char count = 159,\n\t\tSeg1 => 153->€\n\t\tExpected behaviour: Should not split in the middle of ESC chars\n\t*/\n\tt.Run(\"testSplit_EscEndOfSeg1_GSM7BITPACKED\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BITPACKED,\n\t\t\t134,\n\t\t\t\"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp€ppppppp\",\n\t\t\t[]string{\n\t\t\t\t\"e070381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c31b\",\n\t\t\t\t\"3665381c0e87c3e1\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp\\r\",\n\t\t\t\t\"€ppppppp\",\n\t\t\t})\n\t})\n\n\t/*\n\t\tTotal char count = 160,\n\t\tEsc char count = 2,\n\t\tRegular char count = 158,\n\t\tSeg1 => 152-> ....{\n\t\tSeg2 => 1-> ....{\n\t\tExpected behaviour: Should not split in the middle of ESC chars\n\t*/\n\tt.Run(\"testSplit_EscEndOfSeg1AndSeg2_1_GSM7BITPACKED\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BITPACKED,\n\t\t\t134,\n\t\t\t\"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp{{pppppppp\",\n\t\t\t[]string{\n\t\t\t\t\"e070381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0edfa01a\",\n\t\t\t\t\"3628381c0e87c3e170\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp{\\r\",\n\t\t\t\t\"{pppppppp\",\n\t\t\t})\n\t})\n\n\t/*\n\t\tTotal char count = 160,\n\t\tEsc char count = 2,\n\t\tRegular char count = 158,\n\t\tSeg1 => 152-> ....€\n\t\tSeg2 => 1-> ....€\n\t\tExpected behaviour: Should not split in the middle of ESC chars\n\t*/\n\tt.Run(\"testSplit_EscEndOfSeg1AndSeg2_2_GSM7BITPACKED\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BITPACKED,\n\t\t\t134,\n\t\t\t\"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp€€pppppppp\",\n\t\t\t[]string{\n\t\t\t\t\"e070381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0e87c3e170381c0edf941b\",\n\t\t\t\t\"3665381c0e87c3e170\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp€\\r\",\n\t\t\t\t\"€pppppppp\",\n\t\t\t})\n\t})\n\n\t/*\n\t\tTotal char count = 162,\n\t\tEsc char count = 0,\n\t\tRegular char count = 162,\n\t\tSeg1 => 153\n\t\tSeg2 => 9\n\t\tScenario: All charcters in the GSM7Bit Basic Character Set table (non-escape chars) https://en.wikipedia.org/wiki/GSM_03.38\n\t*/\n\tt.Run(\"testSplit_AllGSM7BitBasicCharset_GSM7BITPACKED\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BITPACKED,\n\t\t\t134,\n\t\t\t\"ΩØ;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ù\",\n\t\t\t[]string{\n\t\t\t\t\"2a8b5d2ca7413c622d922dacc9049d613706e84b212433e62ecca0b4de005f7210ebb5fc2127c9f4ce21dbe4f04cad0138306c74b1f87de9120658c6a48b982cbb25d3e10098bdadb511f9b3086b2fcee457abf57815a053d61fa898a4303704e266560c632092f8312093169b80181edc45611bfd31aa788ef42b5c190c890cf3312178f528\",\n\t\t\t\t\"4e8ee00c3132af0d\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"ΩØ;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Ç$/=Λ\",\n\t\t\t\t\"'ÅA3ò#fkù\",\n\t\t\t})\n\t})\n\n\t/*\n\t\tTotal char count = 81,\n\t\tEsc char count = 81,\n\t\tRegular char count = 0,\n\t\tSeg1 => 153\n\t\tSeg2 => 9\n\t\tScenario: All charcters in the GSM7Bit Escape Character Set table https://en.wikipedia.org/wiki/GSM_03.38\n\t*/\n\tt.Run(\"testSplit_AllGSM7BitBasicCharset_GSM7BITPACKED\", func(t *testing.T) {\n\t\ttestEncodingSplit(t, GSM7BITPACKED,\n\t\t\t134,\n\t\t\t\"|{[€|^€[{|€{[|^{~[}€|}|^|^[^]€{[]~}€]{{^|^][€]|€~€^[~}^]{]~{^^€^[~|^]|€~|^€{]{~|}\",\n\t\t\t[]string{\n\t\t\t\t\"36c00d6ac3db9437c00d6553def036a80d7053dea036bc0d7043d9a036bd0d6f93da9437c04d6a03dc5036c00d65c3db5036be4d7983daf036be4d6f93da9437be0d6a83da5036c00d65e3dbf036e58d6f03dc9437bd4d7943d9f036bd4d6a43d9f836a88d6fd3dba036940d6553de5036bc4d6f03dc5036be0d7053def436c00d6553dea01a\",\n\t\t\t\t\"36be0d6ad3db003729\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"|{[€|^€[{|€{[|^{~[}€|}|^|^[^]€{[]~}€]{{^|^][€]|€~€^[~}^]{]~{^^€^[~|^]|€~|^€{\\r\",\n\t\t\t\t\"]{~|}\",\n\t\t\t})\n\t})\n}\n\nfunc TestAscii(t *testing.T) {\n\trequire.EqualValues(t, 1, ASCII.DataCoding())\n\ttestEncoding(t, ASCII, \"agjwklgjkwP\", \"61676a776b6c676a6b7750\")\n}\n\nfunc TestUCS2(t *testing.T) {\n\trequire.EqualValues(t, 8, UCS2.DataCoding())\n\ttestEncoding(t, UCS2, \"agjwklgjkwP\", \"00610067006a0077006b006c0067006a006b00770050\")\n}\n\nfunc TestLatin1(t *testing.T) {\n\trequire.EqualValues(t, 3, LATIN1.DataCoding())\n\ttestEncoding(t, LATIN1, \"agjwklgjkwPÓ\", \"61676a776b6c676a6b7750d3\")\n}\n\nfunc TestCYRILLIC(t *testing.T) {\n\trequire.EqualValues(t, 6, CYRILLIC.DataCoding())\n\ttestEncoding(t, CYRILLIC, \"agjwklgjkwPф\", \"61676A776B6C676A6B7750E4\")\n}\n\nfunc TestHebrew(t *testing.T) {\n\trequire.EqualValues(t, 7, HEBREW.DataCoding())\n\ttestEncoding(t, HEBREW, \"agjwklgjkwPץ\", \"61676A776B6C676A6B7750F5\")\n}\n\nfunc TestOtherCodings(t *testing.T) {\n\ttestEncoding(t, UTF16BEM, \"ngưỡng cứa cuỗc đợi\", \"feff006e006701b01ee1006e0067002000631ee900610020006300751ed70063002001111ee30069\")\n\ttestEncoding(t, UTF16LEM, \"ngưỡng cứa cuỗc đợi\", \"fffe6e006700b001e11e6e00670020006300e91e6100200063007500d71e630020001101e31e6900\")\n\ttestEncoding(t, UTF16BE, \"ngưỡng cứa cuỗc đợi\", \"006e006701b01ee1006e0067002000631ee900610020006300751ed70063002001111ee30069\")\n\ttestEncoding(t, UTF16LE, \"ngưỡng cứa cuỗc đợi\", \"6e006700b001e11e6e00670020006300e91e6100200063007500d71e630020001101e31e6900\")\n}\n\ntype noOpEncDec struct{}\n\nfunc (*noOpEncDec) Encode(str string) ([]byte, error) {\n\treturn []byte(str), nil\n}\n\nfunc (*noOpEncDec) Decode(data []byte) (string, error) {\n\treturn string(data), nil\n}\n\nfunc TestCustomEncoding(t *testing.T) {\n\tenc := NewCustomEncoding(GSM7BITCoding, &noOpEncDec{})\n\trequire.EqualValues(t, GSM7BITCoding, enc.DataCoding())\n\n\tencoded, err := enc.Encode(\"abc\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(\"abc\"), encoded)\n\n\tdecoded, err := enc.Decode(encoded)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"abc\", decoded)\n}\n"
  },
  {
    "path": "data/header_data.go",
    "content": "//go:generate stringer -type=CommandStatusType,CommandIDType -output header_data_string.go\n\npackage data\n\n// CommandStatusType is type of command status\ntype CommandStatusType int32\n\n// CommandIDType is type of command id.\ntype CommandIDType int32\n\n// nolint\nconst (\n\t// SMPP Command ID Set\n\tGENERIC_NACK          = CommandIDType(-2147483648)\n\tBIND_RECEIVER         = CommandIDType(0x00000001)\n\tBIND_RECEIVER_RESP    = CommandIDType(-2147483647)\n\tBIND_TRANSMITTER      = CommandIDType(0x00000002)\n\tBIND_TRANSMITTER_RESP = CommandIDType(-2147483646)\n\tQUERY_SM              = CommandIDType(0x00000003)\n\tQUERY_SM_RESP         = CommandIDType(-2147483645)\n\tSUBMIT_SM             = CommandIDType(0x00000004)\n\tSUBMIT_SM_RESP        = CommandIDType(-2147483644)\n\tDELIVER_SM            = CommandIDType(0x00000005)\n\tDELIVER_SM_RESP       = CommandIDType(-2147483643)\n\tUNBIND                = CommandIDType(0x00000006)\n\tUNBIND_RESP           = CommandIDType(-2147483642)\n\tREPLACE_SM            = CommandIDType(0x00000007)\n\tREPLACE_SM_RESP       = CommandIDType(-2147483641)\n\tCANCEL_SM             = CommandIDType(0x00000008)\n\tCANCEL_SM_RESP        = CommandIDType(-2147483640)\n\tBIND_TRANSCEIVER      = CommandIDType(0x00000009)\n\tBIND_TRANSCEIVER_RESP = CommandIDType(-2147483639)\n\tOUTBIND               = CommandIDType(0x0000000B)\n\tENQUIRE_LINK          = CommandIDType(0x00000015)\n\tENQUIRE_LINK_RESP     = CommandIDType(-2147483627)\n\tSUBMIT_MULTI          = CommandIDType(0x00000021)\n\tSUBMIT_MULTI_RESP     = CommandIDType(-2147483615)\n\tALERT_NOTIFICATION    = CommandIDType(0x00000102)\n\tDATA_SM               = CommandIDType(0x00000103)\n\tDATA_SM_RESP          = CommandIDType(-2147483389)\n)\n\n// nolint\nconst (\n\t// Command_Status Error Codes\n\tESME_ROK           = CommandStatusType(0x00000000) // No Error\n\tESME_RINVMSGLEN    = CommandStatusType(0x00000001) // Message Length is invalid\n\tESME_RINVCMDLEN    = CommandStatusType(0x00000002) // Command Length is invalid\n\tESME_RINVCMDID     = CommandStatusType(0x00000003) // Invalid Command ID\n\tESME_RINVBNDSTS    = CommandStatusType(0x00000004) // Incorrect BIND Status for given command\n\tESME_RALYBND       = CommandStatusType(0x00000005) // ESME Already in Bound State\n\tESME_RINVPRTFLG    = CommandStatusType(0x00000006) // Invalid Priority Flag\n\tESME_RINVREGDLVFLG = CommandStatusType(0x00000007) // Invalid Registered Delivery Flag\n\tESME_RSYSERR       = CommandStatusType(0x00000008) // System Error\n\tESME_RINVSRCADR    = CommandStatusType(0x0000000A) // Invalid Source Address\n\tESME_RINVDSTADR    = CommandStatusType(0x0000000B) // Invalid Dest Addr\n\tESME_RINVMSGID     = CommandStatusType(0x0000000C) // Message ID is invalid\n\tESME_RBINDFAIL     = CommandStatusType(0x0000000D) // Bind Failed\n\tESME_RINVPASWD     = CommandStatusType(0x0000000E) // Invalid Password\n\tESME_RINVSYSID     = CommandStatusType(0x0000000F) // Invalid System ID\n\tESME_RCANCELFAIL   = CommandStatusType(0x00000011) // Cancel SM Failed\n\tESME_RREPLACEFAIL  = CommandStatusType(0x00000013) // Replace SM Failed\n\tESME_RMSGQFUL      = CommandStatusType(0x00000014) // Message Queue Full\n\tESME_RINVSERTYP    = CommandStatusType(0x00000015) // Invalid Service Type\n\n\tESME_RADDCUSTFAIL  = CommandStatusType(0x00000019) // Failed to Add Customer\n\tESME_RDELCUSTFAIL  = CommandStatusType(0x0000001A) // Failed to delete Customer\n\tESME_RMODCUSTFAIL  = CommandStatusType(0x0000001B) // Failed to modify customer\n\tESME_RENQCUSTFAIL  = CommandStatusType(0x0000001C) // Failed to Enquire Customer\n\tESME_RINVCUSTID    = CommandStatusType(0x0000001D) // Invalid Customer ID\n\tESME_RINVCUSTNAME  = CommandStatusType(0x0000001F) // Invalid Customer Name\n\tESME_RINVCUSTADR   = CommandStatusType(0x00000021) // Invalid Customer Address\n\tESME_RINVADR       = CommandStatusType(0x00000022) // Invalid Address\n\tESME_RCUSTEXIST    = CommandStatusType(0x00000023) // Customer Exists\n\tESME_RCUSTNOTEXIST = CommandStatusType(0x00000024) // Customer does not exist\n\tESME_RADDDLFAIL    = CommandStatusType(0x00000026) // Failed to Add DL\n\tESME_RMODDLFAIL    = CommandStatusType(0x00000027) // Failed to modify DL\n\tESME_RDELDLFAIL    = CommandStatusType(0x00000028) // Failed to Delete DL\n\tESME_RVIEWDLFAIL   = CommandStatusType(0x00000029) // Failed to View DL\n\tESME_RLISTDLSFAIL  = CommandStatusType(0x00000030) // Failed to list DLs\n\tESME_RPARAMRETFAIL = CommandStatusType(0x00000031) // Param Retrieve Failed\n\tESME_RINVPARAM     = CommandStatusType(0x00000032) // Invalid Param\n\n\tESME_RINVNUMDESTS = CommandStatusType(0x00000033) // Invalid number of destinations\n\tESME_RINVDLNAME   = CommandStatusType(0x00000034) // Invalid Distribution List name\n\n\tESME_RINVDLMEMBDESC = CommandStatusType(0x00000035) // Invalid DL Member Description\n\tESME_RINVDLMEMBTYP  = CommandStatusType(0x00000038) // Invalid DL Member Type\n\tESME_RINVDLMODOPT   = CommandStatusType(0x00000039) // Invalid DL Modify Option\n\n\tESME_RINVDESTFLAG = CommandStatusType(0x00000040) // Destination flag is invalid (submit_multi)\n\tESME_RINVSUBREP   = CommandStatusType(0x00000042) // Invalid ‘submit with replace’ request (i.e. submit_sm with replace_if_present_flag set)\n\tESME_RINVESMCLASS = CommandStatusType(0x00000043) // Invalid esm_class field data\n\tESME_RCNTSUBDL    = CommandStatusType(0x00000044) // Cannot Submit to Distribution List\n\tESME_RSUBMITFAIL  = CommandStatusType(0x00000045) // submit_sm or submit_multi failed\n\tESME_RINVSRCTON   = CommandStatusType(0x00000048) // Invalid Source address TON\n\tESME_RINVSRCNPI   = CommandStatusType(0x00000049) // Invalid Source address NPI\n\tESME_RINVDSTTON   = CommandStatusType(0x00000050) // Invalid Destination address TON\n\tESME_RINVDSTNPI   = CommandStatusType(0x00000051) // Invalid Destination address NPI\n\tESME_RINVSYSTYP   = CommandStatusType(0x00000053) // Invalid system_type field\n\tESME_RINVREPFLAG  = CommandStatusType(0x00000054) // Invalid replace_if_present flag\n\tESME_RINVNUMMSGS  = CommandStatusType(0x00000055) // Invalid number of messages\n\tESME_RTHROTTLED   = CommandStatusType(0x00000058) // Throttling error (ESME has exceeded allowed message limits)\n\n\tESME_RPROVNOTALLWD = CommandStatusType(0x00000059) // Provisioning Not Allowed\n\n\tESME_RINVSCHED    = CommandStatusType(0x00000061) // Invalid Scheduled Delivery Time\n\tESME_RINVEXPIRY   = CommandStatusType(0x00000062) // Invalid message validity period (Expiry time)\n\tESME_RINVDFTMSGID = CommandStatusType(0x00000063) // Predefined Message Invalid or Not Found\n\tESME_RX_T_APPN    = CommandStatusType(0x00000064) // ESME Receiver Temporary App Error Code\n\tESME_RX_P_APPN    = CommandStatusType(0x00000065) // ESME Receiver Permanent App Error Code\n\tESME_RX_R_APPN    = CommandStatusType(0x00000066) // ESME Receiver Reject Message Error Code\n\tESME_RQUERYFAIL   = CommandStatusType(0x00000067) // query_sm request failed\n\n\tESME_RINVPGCUSTID      = CommandStatusType(0x00000080) // Paging Customer ID Invalid No such subscriber\n\tESME_RINVPGCUSTIDLEN   = CommandStatusType(0x00000081) // Paging Customer ID length Invalid\n\tESME_RINVCITYLEN       = CommandStatusType(0x00000082) // City Length Invalid\n\tESME_RINVSTATELEN      = CommandStatusType(0x00000083) // State Length Invalid\n\tESME_RINVZIPPREFIXLEN  = CommandStatusType(0x00000084) // Zip Prefix Length Invalid\n\tESME_RINVZIPPOSTFIXLEN = CommandStatusType(0x00000085) // Zip Postfix Length Invalid\n\tESME_RINVMINLEN        = CommandStatusType(0x00000086) // MIN Length Invalid\n\tESME_RINVMIN           = CommandStatusType(0x00000087) // MIN Invalid (i.e. No such MIN)\n\tESME_RINVPINLEN        = CommandStatusType(0x00000088) // PIN Length Invalid\n\tESME_RINVTERMCODELEN   = CommandStatusType(0x00000089) // Terminal Code Length Invalid\n\tESME_RINVCHANNELLEN    = CommandStatusType(0x0000008A) // Channel Length Invalid\n\tESME_RINVCOVREGIONLEN  = CommandStatusType(0x0000008B) // Coverage Region Length Invalid\n\tESME_RINVCAPCODELEN    = CommandStatusType(0x0000008C) // Cap Code Length Invalid\n\tESME_RINVMDTLEN        = CommandStatusType(0x0000008D) // Message delivery time Length Invalid\n\tESME_RINVPRIORMSGLEN   = CommandStatusType(0x0000008E) // Priority Message Length Invalid\n\tESME_RINVPERMSGLEN     = CommandStatusType(0x0000008F) // Periodic Messages Length Invalid\n\tESME_RINVPGALERTLEN    = CommandStatusType(0x00000090) // Paging Alerts Length Invalid\n\tESME_RINVSMUSERLEN     = CommandStatusType(0x00000091) // int16 Message User Group Length Invalid\n\tESME_RINVRTDBLEN       = CommandStatusType(0x00000092) // Real Time Data broadcasts Length Invalid\n\tESME_RINVREGDELLEN     = CommandStatusType(0x00000093) // Registered Delivery Length Invalid\n\tESME_RINVMSGDISTLEN    = CommandStatusType(0x00000094) // Message Distribution Length Invalid\n\tESME_RINVPRIORMSG      = CommandStatusType(0x00000095) // Priority Message Length Invalid\n\tESME_RINVMDT           = CommandStatusType(0x00000096) // Message delivery time Invalid\n\tESME_RINVPERMSG        = CommandStatusType(0x00000097) // Periodic Messages Invalid\n\tESME_RINVMSGDIST       = CommandStatusType(0x00000098) // Message Distribution Invalid\n\tESME_RINVPGALERT       = CommandStatusType(0x00000099) // Paging Alerts Invalid\n\tESME_RINVSMUSER        = CommandStatusType(0x0000009A) // int16 Message User Group Invalid\n\tESME_RINVRTDB          = CommandStatusType(0x0000009B) // Real Time Data broadcasts Invalid\n\tESME_RINVREGDEL        = CommandStatusType(0x0000009C) // Registered Delivery Invalid\n\tESME_RINVOPTPARLEN     = CommandStatusType(0x0000009F) // Invalid Optional Parameter Length\n\tESME_RINVOPTPARSTREAM  = CommandStatusType(0x000000C0) // KIF IW Field out of data\n\tESME_ROPTPARNOTALLWD   = CommandStatusType(0x000000C1) // Optional Parameter not allowed\n\tESME_RINVPARLEN        = CommandStatusType(0x000000C2) // Invalid Parameter Length.\n\tESME_RMISSINGOPTPARAM  = CommandStatusType(0x000000C3) // Expected Optional Parameter missing\n\tESME_RINVOPTPARAMVAL   = CommandStatusType(0x000000C4) // Invalid Optional Parameter Value\n\tESME_RDELIVERYFAILURE  = CommandStatusType(0x000000FE) // Delivery Failure (used for data_sm_resp)\n\tESME_RUNKNOWNERR       = CommandStatusType(0x000000FF) // Unknown Error\n\n\tESME_LAST_ERROR = CommandStatusType(0x0000012C) // THE VALUE OF THE LAST ERROR CODE\n)\n"
  },
  {
    "path": "data/header_data_string.go",
    "content": "// Code generated by \"stringer -type=CommandStatusType,CommandIDType -output header_data_string.go\"; DO NOT EDIT.\n\npackage data\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[ESME_ROK-0]\n\t_ = x[ESME_RINVMSGLEN-1]\n\t_ = x[ESME_RINVCMDLEN-2]\n\t_ = x[ESME_RINVCMDID-3]\n\t_ = x[ESME_RINVBNDSTS-4]\n\t_ = x[ESME_RALYBND-5]\n\t_ = x[ESME_RINVPRTFLG-6]\n\t_ = x[ESME_RINVREGDLVFLG-7]\n\t_ = x[ESME_RSYSERR-8]\n\t_ = x[ESME_RINVSRCADR-10]\n\t_ = x[ESME_RINVDSTADR-11]\n\t_ = x[ESME_RINVMSGID-12]\n\t_ = x[ESME_RBINDFAIL-13]\n\t_ = x[ESME_RINVPASWD-14]\n\t_ = x[ESME_RINVSYSID-15]\n\t_ = x[ESME_RCANCELFAIL-17]\n\t_ = x[ESME_RREPLACEFAIL-19]\n\t_ = x[ESME_RMSGQFUL-20]\n\t_ = x[ESME_RINVSERTYP-21]\n\t_ = x[ESME_RADDCUSTFAIL-25]\n\t_ = x[ESME_RDELCUSTFAIL-26]\n\t_ = x[ESME_RMODCUSTFAIL-27]\n\t_ = x[ESME_RENQCUSTFAIL-28]\n\t_ = x[ESME_RINVCUSTID-29]\n\t_ = x[ESME_RINVCUSTNAME-31]\n\t_ = x[ESME_RINVCUSTADR-33]\n\t_ = x[ESME_RINVADR-34]\n\t_ = x[ESME_RCUSTEXIST-35]\n\t_ = x[ESME_RCUSTNOTEXIST-36]\n\t_ = x[ESME_RADDDLFAIL-38]\n\t_ = x[ESME_RMODDLFAIL-39]\n\t_ = x[ESME_RDELDLFAIL-40]\n\t_ = x[ESME_RVIEWDLFAIL-41]\n\t_ = x[ESME_RLISTDLSFAIL-48]\n\t_ = x[ESME_RPARAMRETFAIL-49]\n\t_ = x[ESME_RINVPARAM-50]\n\t_ = x[ESME_RINVNUMDESTS-51]\n\t_ = x[ESME_RINVDLNAME-52]\n\t_ = x[ESME_RINVDLMEMBDESC-53]\n\t_ = x[ESME_RINVDLMEMBTYP-56]\n\t_ = x[ESME_RINVDLMODOPT-57]\n\t_ = x[ESME_RINVDESTFLAG-64]\n\t_ = x[ESME_RINVSUBREP-66]\n\t_ = x[ESME_RINVESMCLASS-67]\n\t_ = x[ESME_RCNTSUBDL-68]\n\t_ = x[ESME_RSUBMITFAIL-69]\n\t_ = x[ESME_RINVSRCTON-72]\n\t_ = x[ESME_RINVSRCNPI-73]\n\t_ = x[ESME_RINVDSTTON-80]\n\t_ = x[ESME_RINVDSTNPI-81]\n\t_ = x[ESME_RINVSYSTYP-83]\n\t_ = x[ESME_RINVREPFLAG-84]\n\t_ = x[ESME_RINVNUMMSGS-85]\n\t_ = x[ESME_RTHROTTLED-88]\n\t_ = x[ESME_RPROVNOTALLWD-89]\n\t_ = x[ESME_RINVSCHED-97]\n\t_ = x[ESME_RINVEXPIRY-98]\n\t_ = x[ESME_RINVDFTMSGID-99]\n\t_ = x[ESME_RX_T_APPN-100]\n\t_ = x[ESME_RX_P_APPN-101]\n\t_ = x[ESME_RX_R_APPN-102]\n\t_ = x[ESME_RQUERYFAIL-103]\n\t_ = x[ESME_RINVPGCUSTID-128]\n\t_ = x[ESME_RINVPGCUSTIDLEN-129]\n\t_ = x[ESME_RINVCITYLEN-130]\n\t_ = x[ESME_RINVSTATELEN-131]\n\t_ = x[ESME_RINVZIPPREFIXLEN-132]\n\t_ = x[ESME_RINVZIPPOSTFIXLEN-133]\n\t_ = x[ESME_RINVMINLEN-134]\n\t_ = x[ESME_RINVMIN-135]\n\t_ = x[ESME_RINVPINLEN-136]\n\t_ = x[ESME_RINVTERMCODELEN-137]\n\t_ = x[ESME_RINVCHANNELLEN-138]\n\t_ = x[ESME_RINVCOVREGIONLEN-139]\n\t_ = x[ESME_RINVCAPCODELEN-140]\n\t_ = x[ESME_RINVMDTLEN-141]\n\t_ = x[ESME_RINVPRIORMSGLEN-142]\n\t_ = x[ESME_RINVPERMSGLEN-143]\n\t_ = x[ESME_RINVPGALERTLEN-144]\n\t_ = x[ESME_RINVSMUSERLEN-145]\n\t_ = x[ESME_RINVRTDBLEN-146]\n\t_ = x[ESME_RINVREGDELLEN-147]\n\t_ = x[ESME_RINVMSGDISTLEN-148]\n\t_ = x[ESME_RINVPRIORMSG-149]\n\t_ = x[ESME_RINVMDT-150]\n\t_ = x[ESME_RINVPERMSG-151]\n\t_ = x[ESME_RINVMSGDIST-152]\n\t_ = x[ESME_RINVPGALERT-153]\n\t_ = x[ESME_RINVSMUSER-154]\n\t_ = x[ESME_RINVRTDB-155]\n\t_ = x[ESME_RINVREGDEL-156]\n\t_ = x[ESME_RINVOPTPARLEN-159]\n\t_ = x[ESME_RINVOPTPARSTREAM-192]\n\t_ = x[ESME_ROPTPARNOTALLWD-193]\n\t_ = x[ESME_RINVPARLEN-194]\n\t_ = x[ESME_RMISSINGOPTPARAM-195]\n\t_ = x[ESME_RINVOPTPARAMVAL-196]\n\t_ = x[ESME_RDELIVERYFAILURE-254]\n\t_ = x[ESME_RUNKNOWNERR-255]\n\t_ = x[ESME_LAST_ERROR-300]\n}\n\nconst _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\"\n\nvar _CommandStatusType_map = map[CommandStatusType]string{\n\t0:   _CommandStatusType_name[0:8],\n\t1:   _CommandStatusType_name[8:23],\n\t2:   _CommandStatusType_name[23:38],\n\t3:   _CommandStatusType_name[38:52],\n\t4:   _CommandStatusType_name[52:67],\n\t5:   _CommandStatusType_name[67:79],\n\t6:   _CommandStatusType_name[79:94],\n\t7:   _CommandStatusType_name[94:112],\n\t8:   _CommandStatusType_name[112:124],\n\t10:  _CommandStatusType_name[124:139],\n\t11:  _CommandStatusType_name[139:154],\n\t12:  _CommandStatusType_name[154:168],\n\t13:  _CommandStatusType_name[168:182],\n\t14:  _CommandStatusType_name[182:196],\n\t15:  _CommandStatusType_name[196:210],\n\t17:  _CommandStatusType_name[210:226],\n\t19:  _CommandStatusType_name[226:243],\n\t20:  _CommandStatusType_name[243:256],\n\t21:  _CommandStatusType_name[256:271],\n\t25:  _CommandStatusType_name[271:288],\n\t26:  _CommandStatusType_name[288:305],\n\t27:  _CommandStatusType_name[305:322],\n\t28:  _CommandStatusType_name[322:339],\n\t29:  _CommandStatusType_name[339:354],\n\t31:  _CommandStatusType_name[354:371],\n\t33:  _CommandStatusType_name[371:387],\n\t34:  _CommandStatusType_name[387:399],\n\t35:  _CommandStatusType_name[399:414],\n\t36:  _CommandStatusType_name[414:432],\n\t38:  _CommandStatusType_name[432:447],\n\t39:  _CommandStatusType_name[447:462],\n\t40:  _CommandStatusType_name[462:477],\n\t41:  _CommandStatusType_name[477:493],\n\t48:  _CommandStatusType_name[493:510],\n\t49:  _CommandStatusType_name[510:528],\n\t50:  _CommandStatusType_name[528:542],\n\t51:  _CommandStatusType_name[542:559],\n\t52:  _CommandStatusType_name[559:574],\n\t53:  _CommandStatusType_name[574:593],\n\t56:  _CommandStatusType_name[593:611],\n\t57:  _CommandStatusType_name[611:628],\n\t64:  _CommandStatusType_name[628:645],\n\t66:  _CommandStatusType_name[645:660],\n\t67:  _CommandStatusType_name[660:677],\n\t68:  _CommandStatusType_name[677:691],\n\t69:  _CommandStatusType_name[691:707],\n\t72:  _CommandStatusType_name[707:722],\n\t73:  _CommandStatusType_name[722:737],\n\t80:  _CommandStatusType_name[737:752],\n\t81:  _CommandStatusType_name[752:767],\n\t83:  _CommandStatusType_name[767:782],\n\t84:  _CommandStatusType_name[782:798],\n\t85:  _CommandStatusType_name[798:814],\n\t88:  _CommandStatusType_name[814:829],\n\t89:  _CommandStatusType_name[829:847],\n\t97:  _CommandStatusType_name[847:861],\n\t98:  _CommandStatusType_name[861:876],\n\t99:  _CommandStatusType_name[876:893],\n\t100: _CommandStatusType_name[893:907],\n\t101: _CommandStatusType_name[907:921],\n\t102: _CommandStatusType_name[921:935],\n\t103: _CommandStatusType_name[935:950],\n\t128: _CommandStatusType_name[950:967],\n\t129: _CommandStatusType_name[967:987],\n\t130: _CommandStatusType_name[987:1003],\n\t131: _CommandStatusType_name[1003:1020],\n\t132: _CommandStatusType_name[1020:1041],\n\t133: _CommandStatusType_name[1041:1063],\n\t134: _CommandStatusType_name[1063:1078],\n\t135: _CommandStatusType_name[1078:1090],\n\t136: _CommandStatusType_name[1090:1105],\n\t137: _CommandStatusType_name[1105:1125],\n\t138: _CommandStatusType_name[1125:1144],\n\t139: _CommandStatusType_name[1144:1165],\n\t140: _CommandStatusType_name[1165:1184],\n\t141: _CommandStatusType_name[1184:1199],\n\t142: _CommandStatusType_name[1199:1219],\n\t143: _CommandStatusType_name[1219:1237],\n\t144: _CommandStatusType_name[1237:1256],\n\t145: _CommandStatusType_name[1256:1274],\n\t146: _CommandStatusType_name[1274:1290],\n\t147: _CommandStatusType_name[1290:1308],\n\t148: _CommandStatusType_name[1308:1327],\n\t149: _CommandStatusType_name[1327:1344],\n\t150: _CommandStatusType_name[1344:1356],\n\t151: _CommandStatusType_name[1356:1371],\n\t152: _CommandStatusType_name[1371:1387],\n\t153: _CommandStatusType_name[1387:1403],\n\t154: _CommandStatusType_name[1403:1418],\n\t155: _CommandStatusType_name[1418:1431],\n\t156: _CommandStatusType_name[1431:1446],\n\t159: _CommandStatusType_name[1446:1464],\n\t192: _CommandStatusType_name[1464:1485],\n\t193: _CommandStatusType_name[1485:1505],\n\t194: _CommandStatusType_name[1505:1520],\n\t195: _CommandStatusType_name[1520:1541],\n\t196: _CommandStatusType_name[1541:1561],\n\t254: _CommandStatusType_name[1561:1582],\n\t255: _CommandStatusType_name[1582:1598],\n\t300: _CommandStatusType_name[1598:1613],\n}\n\nfunc (i CommandStatusType) String() string {\n\tif str, ok := _CommandStatusType_map[i]; ok {\n\t\treturn str\n\t}\n\treturn \"CommandStatusType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n}\n\nfunc (i CommandStatusType) Desc() string {\n\tswitch i {\n\tcase ESME_ROK:\n\t\treturn \"No Error\"\n\tcase ESME_RINVMSGLEN:\n\t\treturn \"Message Length is invalid\"\n\tcase ESME_RINVCMDLEN:\n\t\treturn \"Command Length is invalid\"\n\tcase ESME_RINVCMDID:\n\t\treturn \"Invalid Command ID\"\n\tcase ESME_RINVBNDSTS:\n\t\treturn \"Incorrect BIND Status for given command\"\n\tcase ESME_RALYBND:\n\t\treturn \"ESME Already in Bound State\"\n\tcase ESME_RINVPRTFLG:\n\t\treturn \"Invalid Priority Flag\"\n\tcase ESME_RINVREGDLVFLG:\n\t\treturn \"Invalid Registered Delivery Flag\"\n\tcase ESME_RSYSERR:\n\t\treturn \"System Error\"\n\tcase ESME_RINVSRCADR:\n\t\treturn \"Invalid Source Address\"\n\tcase ESME_RINVDSTADR:\n\t\treturn \"Invalid Dest Addr\"\n\tcase ESME_RINVMSGID:\n\t\treturn \"Message ID is invalid\"\n\tcase ESME_RBINDFAIL:\n\t\treturn \"Bind Failed\"\n\tcase ESME_RINVPASWD:\n\t\treturn \"Invalid Password\"\n\tcase ESME_RINVSYSID:\n\t\treturn \"Invalid System ID\"\n\tcase ESME_RCANCELFAIL:\n\t\treturn \"Cancel SM Failed\"\n\tcase ESME_RREPLACEFAIL:\n\t\treturn \"Replace SM Failed\"\n\tcase ESME_RMSGQFUL:\n\t\treturn \"Message Queue Full\"\n\tcase ESME_RINVSERTYP:\n\t\treturn \"Invalid Service Type\"\n\tcase ESME_RADDCUSTFAIL:\n\t\treturn \"Failed to Add Customer\"\n\tcase ESME_RDELCUSTFAIL:\n\t\treturn \"Failed to delete Customer\"\n\tcase ESME_RMODCUSTFAIL:\n\t\treturn \"Failed to modify customer\"\n\tcase ESME_RENQCUSTFAIL:\n\t\treturn \"Failed to Enquire Customer\"\n\tcase ESME_RINVCUSTID:\n\t\treturn \"Invalid Customer ID\"\n\tcase ESME_RINVCUSTNAME:\n\t\treturn \"Invalid Customer Name\"\n\tcase ESME_RINVCUSTADR:\n\t\treturn \"Invalid Customer Address\"\n\tcase ESME_RINVADR:\n\t\treturn \"Invalid Address\"\n\tcase ESME_RCUSTEXIST:\n\t\treturn \"Customer Exists\"\n\tcase ESME_RCUSTNOTEXIST:\n\t\treturn \"Customer does not exist\"\n\tcase ESME_RADDDLFAIL:\n\t\treturn \"Failed to Add DL\"\n\tcase ESME_RMODDLFAIL:\n\t\treturn \"Failed to modify DL\"\n\tcase ESME_RDELDLFAIL:\n\t\treturn \"Failed to Delete DL\"\n\tcase ESME_RVIEWDLFAIL:\n\t\treturn \"Failed to View DL\"\n\tcase ESME_RLISTDLSFAIL:\n\t\treturn \"Failed to list DLs\"\n\tcase ESME_RPARAMRETFAIL:\n\t\treturn \"Param Retrieve Failed\"\n\tcase ESME_RINVPARAM:\n\t\treturn \"Invalid Param\"\n\tcase ESME_RINVNUMDESTS:\n\t\treturn \"Invalid number of destinations\"\n\tcase ESME_RINVDLNAME:\n\t\treturn \"Invalid Distribution List name\"\n\tcase ESME_RINVDLMEMBDESC:\n\t\treturn \"Invalid DL Member Description\"\n\tcase ESME_RINVDLMEMBTYP:\n\t\treturn \"Invalid DL Member Type\"\n\tcase ESME_RINVDLMODOPT:\n\t\treturn \"Invalid DL Modify Option\"\n\tcase ESME_RINVDESTFLAG:\n\t\treturn \"Destination flag is invalid (submit_multi)\"\n\tcase ESME_RINVSUBREP:\n\t\treturn \"Invalid ‘submit with replace’ request (i.e. submit_sm with replace_if_present_flag set)\"\n\tcase ESME_RINVESMCLASS:\n\t\treturn \"Invalid esm_class field data\"\n\tcase ESME_RCNTSUBDL:\n\t\treturn \"Cannot Submit to Distribution List\"\n\tcase ESME_RSUBMITFAIL:\n\t\treturn \"submit_sm or submit_multi failed\"\n\tcase ESME_RINVSRCTON:\n\t\treturn \"Invalid Source address TON\"\n\tcase ESME_RINVSRCNPI:\n\t\treturn \"Invalid Source address NPI\"\n\tcase ESME_RINVDSTTON:\n\t\treturn \"Invalid Destination address TON\"\n\tcase ESME_RINVDSTNPI:\n\t\treturn \"Invalid Destination address NPI\"\n\tcase ESME_RINVSYSTYP:\n\t\treturn \"Invalid system_type field\"\n\tcase ESME_RINVREPFLAG:\n\t\treturn \"Invalid replace_if_present flag\"\n\tcase ESME_RINVNUMMSGS:\n\t\treturn \"Invalid number of messages\"\n\tcase ESME_RTHROTTLED:\n\t\treturn \"Throttling error (ESME has exceeded allowed message limits)\"\n\tcase ESME_RPROVNOTALLWD:\n\t\treturn \"Provisioning Not Allowed\"\n\tcase ESME_RINVSCHED:\n\t\treturn \"Invalid Scheduled Delivery Time\"\n\tcase ESME_RINVEXPIRY:\n\t\treturn \"Invalid message validity period (Expiry time)\"\n\tcase ESME_RINVDFTMSGID:\n\t\treturn \"Predefined Message Invalid or Not Found\"\n\tcase ESME_RX_T_APPN:\n\t\treturn \"ESME Receiver Temporary App Error Code\"\n\tcase ESME_RX_P_APPN:\n\t\treturn \"ESME Receiver Permanent App Error Code\"\n\tcase ESME_RX_R_APPN:\n\t\treturn \"ESME Receiver Reject Message Error Code\"\n\tcase ESME_RQUERYFAIL:\n\t\treturn \"query_sm request failed\"\n\tcase ESME_RINVPGCUSTID:\n\t\treturn \"Paging Customer ID Invalid No such subscriber\"\n\tcase ESME_RINVPGCUSTIDLEN:\n\t\treturn \"Paging Customer ID length Invalid\"\n\tcase ESME_RINVCITYLEN:\n\t\treturn \"City Length Invalid\"\n\tcase ESME_RINVSTATELEN:\n\t\treturn \"State Length Invalid\"\n\tcase ESME_RINVZIPPREFIXLEN:\n\t\treturn \"Zip Prefix Length Invalid\"\n\tcase ESME_RINVZIPPOSTFIXLEN:\n\t\treturn \"Zip Postfix Length Invalid\"\n\tcase ESME_RINVMINLEN:\n\t\treturn \"MIN Length Invalid\"\n\tcase ESME_RINVMIN:\n\t\treturn \"MIN Invalid (i.e. No such MIN)\"\n\tcase ESME_RINVPINLEN:\n\t\treturn \"PIN Length Invalid\"\n\tcase ESME_RINVTERMCODELEN:\n\t\treturn \"Terminal Code Length Invalid\"\n\tcase ESME_RINVCHANNELLEN:\n\t\treturn \"Channel Length Invalid\"\n\tcase ESME_RINVCOVREGIONLEN:\n\t\treturn \"Coverage Region Length Invalid\"\n\tcase ESME_RINVCAPCODELEN:\n\t\treturn \"Cap Code Length Invalid\"\n\tcase ESME_RINVMDTLEN:\n\t\treturn \"Message delivery time Length Invalid\"\n\tcase ESME_RINVPRIORMSGLEN:\n\t\treturn \"Priority Message Length Invalid\"\n\tcase ESME_RINVPERMSGLEN:\n\t\treturn \"Periodic Messages Length Invalid\"\n\tcase ESME_RINVPGALERTLEN:\n\t\treturn \"Paging Alerts Length Invalid\"\n\tcase ESME_RINVSMUSERLEN:\n\t\treturn \"int16 Message User Group Length Invalid\"\n\tcase ESME_RINVRTDBLEN:\n\t\treturn \"Real Time Data broadcasts Length Invalid\"\n\tcase ESME_RINVREGDELLEN:\n\t\treturn \"Registered Delivery Length Invalid\"\n\tcase ESME_RINVMSGDISTLEN:\n\t\treturn \"Message Distribution Length Invalid\"\n\tcase ESME_RINVPRIORMSG:\n\t\treturn \"Priority Message Length Invalid\"\n\tcase ESME_RINVMDT:\n\t\treturn \"Message delivery time Invalid\"\n\tcase ESME_RINVPERMSG:\n\t\treturn \"Periodic Messages Invalid\"\n\tcase ESME_RINVMSGDIST:\n\t\treturn \"Message Distribution Invalid\"\n\tcase ESME_RINVPGALERT:\n\t\treturn \"Paging Alerts Invalid\"\n\tcase ESME_RINVSMUSER:\n\t\treturn \"int16 Message User Group Invalid\"\n\tcase ESME_RINVRTDB:\n\t\treturn \"Real Time Data broadcasts Invalid\"\n\tcase ESME_RINVREGDEL:\n\t\treturn \"Registered Delivery Invalid\"\n\tcase ESME_RINVOPTPARLEN:\n\t\treturn \"Invalid Optional Parameter Length\"\n\tcase ESME_RINVOPTPARSTREAM:\n\t\treturn \"Error in the optional part of the PDU Body.\"\n\tcase ESME_ROPTPARNOTALLWD:\n\t\treturn \"Optional Parameter not allowed\"\n\tcase ESME_RINVPARLEN:\n\t\treturn \"Invalid Parameter Length.\"\n\tcase ESME_RMISSINGOPTPARAM:\n\t\treturn \"Expected Optional Parameter missing\"\n\tcase ESME_RINVOPTPARAMVAL:\n\t\treturn \"Invalid Optional Parameter Value\"\n\tcase ESME_RDELIVERYFAILURE:\n\t\treturn \"Delivery Failure (used for data_sm_resp)\"\n\tcase ESME_RUNKNOWNERR:\n\t\treturn \"Unknown Error\"\n\tcase ESME_LAST_ERROR:\n\t\treturn \"The value of the last error code\"\n\t}\n\treturn i.String()\n}\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[GENERIC_NACK - -2147483648]\n\t_ = x[BIND_RECEIVER-1]\n\t_ = x[BIND_RECEIVER_RESP - -2147483647]\n\t_ = x[BIND_TRANSMITTER-2]\n\t_ = x[BIND_TRANSMITTER_RESP - -2147483646]\n\t_ = x[QUERY_SM-3]\n\t_ = x[QUERY_SM_RESP - -2147483645]\n\t_ = x[SUBMIT_SM-4]\n\t_ = x[SUBMIT_SM_RESP - -2147483644]\n\t_ = x[DELIVER_SM-5]\n\t_ = x[DELIVER_SM_RESP - -2147483643]\n\t_ = x[UNBIND-6]\n\t_ = x[UNBIND_RESP - -2147483642]\n\t_ = x[REPLACE_SM-7]\n\t_ = x[REPLACE_SM_RESP - -2147483641]\n\t_ = x[CANCEL_SM-8]\n\t_ = x[CANCEL_SM_RESP - -2147483640]\n\t_ = x[BIND_TRANSCEIVER-9]\n\t_ = x[BIND_TRANSCEIVER_RESP - -2147483639]\n\t_ = x[OUTBIND-11]\n\t_ = x[ENQUIRE_LINK-21]\n\t_ = x[ENQUIRE_LINK_RESP - -2147483627]\n\t_ = x[SUBMIT_MULTI-33]\n\t_ = x[SUBMIT_MULTI_RESP - -2147483615]\n\t_ = x[ALERT_NOTIFICATION-258]\n\t_ = x[DATA_SM-259]\n\t_ = x[DATA_SM_RESP - -2147483389]\n}\n\nconst (\n\t_CommandIDType_name_0 = \"GENERIC_NACKBIND_RECEIVER_RESPBIND_TRANSMITTER_RESPQUERY_SM_RESPSUBMIT_SM_RESPDELIVER_SM_RESPUNBIND_RESPREPLACE_SM_RESPCANCEL_SM_RESPBIND_TRANSCEIVER_RESP\"\n\t_CommandIDType_name_1 = \"ENQUIRE_LINK_RESP\"\n\t_CommandIDType_name_2 = \"SUBMIT_MULTI_RESP\"\n\t_CommandIDType_name_3 = \"DATA_SM_RESP\"\n\t_CommandIDType_name_4 = \"BIND_RECEIVERBIND_TRANSMITTERQUERY_SMSUBMIT_SMDELIVER_SMUNBINDREPLACE_SMCANCEL_SMBIND_TRANSCEIVER\"\n\t_CommandIDType_name_5 = \"OUTBIND\"\n\t_CommandIDType_name_6 = \"ENQUIRE_LINK\"\n\t_CommandIDType_name_7 = \"SUBMIT_MULTI\"\n\t_CommandIDType_name_8 = \"ALERT_NOTIFICATIONDATA_SM\"\n)\n\nvar (\n\t_CommandIDType_index_0 = [...]uint8{0, 12, 30, 51, 64, 78, 93, 104, 119, 133, 154}\n\t_CommandIDType_index_4 = [...]uint8{0, 13, 29, 37, 46, 56, 62, 72, 81, 97}\n\t_CommandIDType_index_8 = [...]uint8{0, 18, 25}\n)\n\nfunc (i CommandIDType) String() string {\n\tswitch {\n\tcase -2147483648 <= i && i <= -2147483639:\n\t\ti -= -2147483648\n\t\treturn _CommandIDType_name_0[_CommandIDType_index_0[i]:_CommandIDType_index_0[i+1]]\n\tcase i == -2147483627:\n\t\treturn _CommandIDType_name_1\n\tcase i == -2147483615:\n\t\treturn _CommandIDType_name_2\n\tcase i == -2147483389:\n\t\treturn _CommandIDType_name_3\n\tcase 1 <= i && i <= 9:\n\t\ti -= 1\n\t\treturn _CommandIDType_name_4[_CommandIDType_index_4[i]:_CommandIDType_index_4[i+1]]\n\tcase i == 11:\n\t\treturn _CommandIDType_name_5\n\tcase i == 21:\n\t\treturn _CommandIDType_name_6\n\tcase i == 33:\n\t\treturn _CommandIDType_name_7\n\tcase 258 <= i && i <= 259:\n\t\ti -= 258\n\t\treturn _CommandIDType_name_8[_CommandIDType_index_8[i]:_CommandIDType_index_8[i+1]]\n\tdefault:\n\t\treturn \"CommandIDType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\n"
  },
  {
    "path": "data/other_codings.go",
    "content": "package data\n\nimport (\n\t\"golang.org/x/text/encoding/unicode\"\n)\n\nvar (\n\t// UTF16BEM is UTF-16 Big Endian with BOM (byte order mark).\n\tUTF16BEM = &utf16BEM{}\n\n\t// UTF16LEM is UTF-16 Little Endian with BOM.\n\tUTF16LEM = &utf16LEM{}\n\n\t// UTF16BE is UTF-16 Big Endian without BOM.\n\tUTF16BE = &utf16BE{}\n\n\t// UTF16LE is UTF-16 Little Endian without BOM.\n\tUTF16LE = &utf16LE{}\n)\n\ntype utf16BEM struct{}\n\nfunc (c utf16BEM) Encode(str string) ([]byte, error) {\n\ttmp := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)\n\treturn encode(str, tmp.NewEncoder())\n}\n\nfunc (c utf16BEM) Decode(data []byte) (string, error) {\n\ttmp := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)\n\treturn decode(data, tmp.NewDecoder())\n}\n\ntype utf16LEM struct{}\n\nfunc (c utf16LEM) Encode(str string) ([]byte, error) {\n\ttmp := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM)\n\treturn encode(str, tmp.NewEncoder())\n}\n\nfunc (c utf16LEM) Decode(data []byte) (string, error) {\n\ttmp := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM)\n\treturn decode(data, tmp.NewDecoder())\n}\n\ntype utf16BE struct{}\n\nfunc (c utf16BE) Encode(str string) ([]byte, error) {\n\ttmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)\n\treturn encode(str, tmp.NewEncoder())\n}\n\nfunc (c utf16BE) Decode(data []byte) (string, error) {\n\ttmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)\n\treturn decode(data, tmp.NewDecoder())\n}\n\ntype utf16LE struct{}\n\nfunc (c utf16LE) Encode(str string) ([]byte, error) {\n\ttmp := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)\n\treturn encode(str, tmp.NewEncoder())\n}\n\nfunc (c utf16LE) Decode(data []byte) (string, error) {\n\ttmp := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)\n\treturn decode(data, tmp.NewDecoder())\n}\n"
  },
  {
    "path": "data/pkg.go",
    "content": "package data\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n)\n\n//nolint\nconst (\n\tSM_CONNID_LEN        = 16\n\tSM_MSG_LEN           = 254\n\tSM_SYSID_LEN         = 16\n\tSM_MSGID_LEN         = 64\n\tSM_PASS_LEN          = 9\n\tSM_DATE_LEN          = 17\n\tSM_SRVTYPE_LEN       = 6\n\tSM_SYSTYPE_LEN       = 13\n\tSM_ADDR_LEN          = 21\n\tSM_DATA_ADDR_LEN     = 65\n\tSM_ADDR_RANGE_LEN    = 41\n\tSM_TYPE_LEN          = 13\n\tSM_DL_NAME_LEN       = 21\n\tSM_PARAM_NAME_LEN    = 10\n\tSM_PARAM_VALUE_LEN   = 10\n\tSM_MAX_CNT_DEST_ADDR = 254\n\n\t// GSM specific, short message must be no larger than 140 octets\n\tSM_GSM_MSG_LEN = 140\n\n\tCONNECTION_CLOSED = 0\n\tCONNECTION_OPENED = 1\n\n\tSM_ACK            = 1\n\tSM_NO_ACK         = 0\n\tSM_RESPONSE_ACK   = 0\n\tSM_RESPONSE_TNACK = 1\n\tSM_RESPONSE_PNACK = 2\n\n\t// Interface_Version\n\tSMPP_V33 int8 = int8(-0x33)\n\tSMPP_V34      = byte(0x34)\n\n\t// Address_TON\n\tGSM_TON_UNKNOWN       = byte(0x00)\n\tGSM_TON_INTERNATIONAL = byte(0x01)\n\tGSM_TON_NATIONAL      = byte(0x02)\n\tGSM_TON_NETWORK       = byte(0x03)\n\tGSM_TON_SUBSCRIBER    = byte(0x04)\n\tGSM_TON_ALPHANUMERIC  = byte(0x05)\n\tGSM_TON_ABBREVIATED   = byte(0x06)\n\tGSM_TON_RESERVED_EXTN = byte(0x07)\n\n\t// Address_NPI\n\tGSM_NPI_UNKNOWN       = byte(0x00)\n\tGSM_NPI_E164          = byte(0x01)\n\tGSM_NPI_ISDN          = GSM_NPI_E164\n\tGSM_NPI_X121          = byte(0x03)\n\tGSM_NPI_TELEX         = byte(0x04)\n\tGSM_NPI_LAND_MOBILE   = byte(0x06)\n\tGSM_NPI_NATIONAL      = byte(0x08)\n\tGSM_NPI_PRIVATE       = byte(0x09)\n\tGSM_NPI_ERMES         = byte(0x0A)\n\tGSM_NPI_INTERNET      = byte(0x0E)\n\tGSM_NPI_WAP_CLIENT_ID = byte(0x12)\n\tGSM_NPI_RESERVED_EXTN = byte(0x0F)\n\n\t// Service_Type\n\tSERVICE_NULL string = \"\"\n\tSERVICE_CMT  string = \"CMT\"\n\tSERVICE_CPT  string = \"CPT\"\n\tSERVICE_VMN  string = \"VMN\"\n\tSERVICE_VMA  string = \"VMA\"\n\tSERVICE_WAP  string = \"WAP\"\n\tSERVICE_USSD string = \"USSD\"\n\n\tSMPP_PROTOCOL                 = byte(1)\n\tSMPPP_PROTOCOL                = byte(2)\n\tSM_SERVICE_MOBILE_TERMINATED  = byte(0)\n\tSM_SERVICE_MOBILE_ORIGINATED  = byte(1)\n\tSM_SERVICE_MOBILE_TRANSCEIVER = byte(2)\n\n\t// State of message at SMSC\n\tSM_STATE_EN_ROUTE      = 1 // default state for messages in transit\n\tSM_STATE_DELIVERED     = 2 // message is delivered\n\tSM_STATE_EXPIRED       = 3 // validity period expired\n\tSM_STATE_DELETED       = 4 // message has been deleted\n\tSM_STATE_UNDELIVERABLE = 5 // undeliverable\n\tSM_STATE_ACCEPTED      = 6 // message is in accepted state\n\tSM_STATE_INVALID       = 7 // message is in invalid state\n\tSM_STATE_REJECTED      = 8 // message is in rejected state\n\n\t//******************\n\t// ESMClass Defines\n\t//******************\n\n\t// Messaging Mode\n\tSM_ESM_DEFAULT        = 0x00 // Default SMSC Mode or Message Type\n\tSM_DATAGRAM_MODE      = 0x01 // Use one-shot express mode\n\tSM_FORWARD_MODE       = 0x02 // Do not use\n\tSM_STORE_FORWARD_MODE = 0x03 // Use store & forward\n\n\t// Send/Receive TDMA & CDMA Message Type\n\tSM_SMSC_DLV_RCPT_TYPE     = 0x04 // Recv Msg contains SMSC delivery receipt\n\tSM_ESME_DLV_ACK_TYPE      = 0x08 // Send/Recv Msg contains ESME delivery acknowledgement\n\tSM_ESME_MAN_USER_ACK_TYPE = 0x10 // Send/Recv Msg contains manual/user acknowledgment\n\tSM_CONV_ABORT_TYPE        = 0x18 // Recv Msg contains conversation abort (Korean CDMA)\n\tSM_INTMD_DLV_NOTIFY_TYPE  = 0x20 // Recv Msg contains intermediate notification\n\n\t// GSM Network features\n\tSM_NONE_GSM           = 0x00 // No specific features selected\n\tSM_UDH_GSM            = 0x40 // User Data Header indicator set\n\tSM_REPLY_PATH_GSM     = 0x80 // Reply path set\n\tSM_UDH_REPLY_PATH_GSM = 0xC0 // Both UDH & Reply path\n\n\t// Optional Parameter Tags, Min and Max Lengths\n\t// Following are the 2 byte tag and min/max lengths for\n\t// supported optional parameter (declann)\n\n\tOPT_PAR_MSG_WAIT = 2\n\n\t// Privacy Indicator\n\tOPT_PAR_PRIV_IND = 0x0201\n\n\t// Source Subaddress\n\tOPT_PAR_SRC_SUBADDR     = 0x0202\n\tOPT_PAR_SRC_SUBADDR_MIN = 2\n\tOPT_PAR_SRC_SUBADDR_MAX = 23\n\n\t// Destination Subaddress\n\tOPT_PAR_DEST_SUBADDR     = 0x0203\n\tOPT_PAR_DEST_SUBADDR_MIN = 2\n\tOPT_PAR_DEST_SUBADDR_MAX = 23\n\n\t// User Message Reference\n\tOPT_PAR_USER_MSG_REF = 0x0204\n\n\t// User Response Code\n\tOPT_PAR_USER_RESP_CODE = 0x0205\n\n\t// Language Indicator\n\tOPT_PAR_LANG_IND = 0x020D\n\n\t// Source Port\n\tOPT_PAR_SRC_PORT = 0x020A\n\n\t// Destination Port\n\tOPT_PAR_DST_PORT = 0x020B\n\n\t// Concat Msg Ref Num\n\tOPT_PAR_SAR_MSG_REF_NUM = 0x020C\n\n\t// Concat Total Segments\n\tOPT_PAR_SAR_TOT_SEG = 0x020E\n\n\t// Concat Segment Seqnums\n\tOPT_PAR_SAR_SEG_SNUM = 0x020F\n\n\t// SC Interface Version\n\tOPT_PAR_SC_IF_VER = 0x0210\n\n\t// Display Time\n\tOPT_PAR_DISPLAY_TIME = 0x1201\n\n\t// Validity Information\n\tOPT_PAR_MS_VALIDITY = 0x1204\n\n\t// DPF Result\n\tOPT_PAR_DPF_RES = 0x0420\n\n\t// Set DPF\n\tOPT_PAR_SET_DPF = 0x0421\n\n\t// MS Availability Status\n\tOPT_PAR_MS_AVAIL_STAT = 0x0422\n\n\t// Network Error Code\n\tOPT_PAR_NW_ERR_CODE     = 0x0423\n\tOPT_PAR_NW_ERR_CODE_MIN = 3\n\tOPT_PAR_NW_ERR_CODE_MAX = 3\n\n\t// Extended int16 Message has no size limit\n\n\t// Delivery Failure Reason\n\tOPT_PAR_DEL_FAIL_RSN = 0x0425\n\n\t// More Messages to Follow\n\tOPT_PAR_MORE_MSGS = 0x0426\n\n\t// Message State\n\tOPT_PAR_MSG_STATE = 0x0427\n\n\t// Callback Number\n\tOPT_PAR_CALLBACK_NUM     = 0x0381\n\tOPT_PAR_CALLBACK_NUM_MIN = 4\n\tOPT_PAR_CALLBACK_NUM_MAX = 19\n\n\t// Callback Number Presentation  Indicator\n\tOPT_PAR_CALLBACK_NUM_PRES_IND = 0x0302\n\n\t// Callback Number Alphanumeric Tag\n\tOPT_PAR_CALLBACK_NUM_ATAG     = 0x0303\n\tOPT_PAR_CALLBACK_NUM_ATAG_MIN = 1\n\tOPT_PAR_CALLBACK_NUM_ATAG_MAX = 65\n\n\t// Number of messages in Mailbox\n\tOPT_PAR_NUM_MSGS = 0x0304\n\n\t// SMS Received Alert\n\tOPT_PAR_SMS_SIGNAL = 0x1203\n\n\t// Message Delivery Alert\n\tOPT_PAR_ALERT_ON_MSG_DELIVERY = 0x130C\n\n\t// ITS Reply Type\n\tOPT_PAR_ITS_REPLY_TYPE = 0x1380\n\n\t// ITS Session Info\n\tOPT_PAR_ITS_SESSION_INFO = 0x1383\n\n\t// USSD Service Op\n\tOPT_PAR_USSD_SER_OP = 0x0501\n\n\t// Priority\n\tSM_NOPRIORITY = 0\n\tSM_PRIORITY   = 1\n\n\t// Registered delivery\n\t//   SMSC Delivery Receipt (bits 1 & 0)\n\tSM_SMSC_RECEIPT_MASK          = byte(0x03)\n\tSM_SMSC_RECEIPT_NOT_REQUESTED = byte(0x00)\n\tSM_SMSC_RECEIPT_REQUESTED     = byte(0x01)\n\tSM_SMSC_RECEIPT_ON_FAILURE    = byte(0x02)\n\t//   SME originated acknowledgement (bits 3 & 2)\n\tSM_SME_ACK_MASK               = byte(0x0c)\n\tSM_SME_ACK_NOT_REQUESTED      = byte(0x00)\n\tSM_SME_ACK_DELIVERY_REQUESTED = byte(0x04)\n\tSM_SME_ACK_MANUAL_REQUESTED   = byte(0x08)\n\tSM_SME_ACK_BOTH_REQUESTED     = byte(0x0c)\n\t//   Intermediate notification (bit 5)\n\tSM_NOTIF_MASK          = byte(0x010)\n\tSM_NOTIF_NOT_REQUESTED = byte(0x000)\n\tSM_NOTIF_REQUESTED     = byte(0x010)\n\n\t// Replace if Present flag\n\tSM_NOREPLACE = 0\n\tSM_REPLACE   = 1\n\n\t// Destination flag\n\tSM_DEST_SME_ADDRESS = 1\n\tSM_DEST_DL_NAME     = 2\n\n\t// Higher Layer Message Type\n\tSM_LAYER_WDP  = 0\n\tSM_LAYER_WCMP = 1\n\n\t// Operation Class\n\tSM_OPCLASS_DATAGRAM    = 0\n\tSM_OPCLASS_TRANSACTION = 3\n\n\t// Originating MSC Address\n\tOPT_PAR_ORIG_MSC_ADDR     = -32639 // int16(0x8081)\n\tOPT_PAR_ORIG_MSC_ADDR_MIN = 1\n\tOPT_PAR_ORIG_MSC_ADDR_MAX = 24\n\n\t// Destination MSC Address\n\tOPT_PAR_DEST_MSC_ADDR     = -32638 // int16(0x8082)\n\tOPT_PAR_DEST_MSC_ADDR_MIN = 1\n\tOPT_PAR_DEST_MSC_ADDR_MAX = 24\n\n\t// Unused Tag\n\tOPT_PAR_UNUSED = 0xffff\n\n\t// Destination Address Subunit\n\tOPT_PAR_DST_ADDR_SUBUNIT = 0x0005\n\n\t// Destination Network Type\n\tOPT_PAR_DST_NW_TYPE = 0x0006\n\n\t// Destination Bearer Type\n\tOPT_PAR_DST_BEAR_TYPE = 0x0007\n\n\t// Destination Telematics ID\n\tOPT_PAR_DST_TELE_ID = 0x0008\n\n\t// Source Address Subunit\n\tOPT_PAR_SRC_ADDR_SUBUNIT = 0x000D\n\n\t// Source Network Type\n\tOPT_PAR_SRC_NW_TYPE = 0x000E\n\n\t// Source Bearer Type\n\tOPT_PAR_SRC_BEAR_TYPE = 0x000F\n\n\t// Source Telematics ID\n\tOPT_PAR_SRC_TELE_ID = 0x0010\n\n\t// QOS Time to Live\n\tOPT_PAR_QOS_TIME_TO_LIVE     = 0x0017\n\tOPT_PAR_QOS_TIME_TO_LIVE_MIN = 1\n\tOPT_PAR_QOS_TIME_TO_LIVE_MAX = 4\n\n\t// Payload Type\n\tOPT_PAR_PAYLOAD_TYPE = 0x0019\n\n\t// Additional Status Info Text\n\tOPT_PAR_ADD_STAT_INFO     = 0x001D\n\tOPT_PAR_ADD_STAT_INFO_MIN = 1\n\tOPT_PAR_ADD_STAT_INFO_MAX = 256\n\n\t// Receipted Message ID\n\tOPT_PAR_RECP_MSG_ID     = 0x001E\n\tOPT_PAR_RECP_MSG_ID_MIN = 1\n\tOPT_PAR_RECP_MSG_ID_MAX = 65\n\n\t// Message Payload\n\tOPT_PAR_MSG_PAYLOAD     = 0x0424\n\tOPT_PAR_MSG_PAYLOAD_MIN = 1\n\tOPT_PAR_MSG_PAYLOAD_MAX = 1500\n\n\t// User Data Header\n\tUDH_CONCAT_MSG_8_BIT_REF  = byte(0x00)\n\tUDH_CONCAT_MSG_16_BIT_REF = byte(0x08)\n\n\t/**\n\t * @deprecated As of version 1.3 of the library there are defined\n\t * new encoding constants for base set of encoding supported by Java Runtime.\n\t * The <code>CHAR_ENC</code> is replaced by <code>ENC_ASCII</code>\n\t * and redefined in this respect.\n\t */\n\n\tDFLT_MSGID         string = \"\"\n\tDFLT_MSG           string = \"\"\n\tDFLT_SRVTYPE       string = \"\"\n\tDFLT_SYSID         string = \"\"\n\tDFLT_PASS          string = \"\"\n\tDFLT_SYSTYPE       string = \"\"\n\tDFLT_ADDR_RANGE    string = \"\"\n\tDFLT_DATE          string = \"\"\n\tDFLT_ADDR          string = \"\"\n\tDFLT_MSG_STATE     byte   = 0\n\tDFLT_ERR           byte   = 0\n\tDFLT_SCHEDULE      string = \"\"\n\tDFLT_VALIDITY      string = \"\"\n\tDFLT_REG_DELIVERY         = SM_SMSC_RECEIPT_NOT_REQUESTED | SM_SME_ACK_NOT_REQUESTED | SM_NOTIF_NOT_REQUESTED\n\tDFLT_DFLTMSGID            = byte(0)\n\tDFLT_MSG_LEN              = byte(0)\n\tDFLT_ESM_CLASS            = byte(0)\n\tDFLT_DATA_CODING          = byte(0)\n\tDFLT_PROTOCOLID           = byte(0)\n\tDFLT_PRIORITY_FLAG        = byte(0)\n\tDFTL_REPLACE_IFP          = byte(0)\n\tDFLT_DL_NAME       string = \"\"\n\tDFLT_GSM_TON              = GSM_TON_UNKNOWN\n\tDFLT_GSM_NPI              = GSM_NPI_UNKNOWN\n\tDFLT_DEST_FLAG            = byte(0) // not set\n\tMAX_PDU_LEN               = 64 << 10\n\n\tPDU_HEADER_SIZE = 16 // 4 integers\n\tTLV_HEADER_SIZE = 4  // 2 int16s: tag & length\n\n\t// all times in milliseconds\n\tRECEIVER_TIMEOUT           int64 = 60000\n\tCONNECTION_RECEIVE_TIMEOUT int64 = 10000\n\tUNBIND_RECEIVE_TIMEOUT     int64 = 5000\n\tCONNECTION_SEND_TIMEOUT    int64 = 20000\n\tCOMMS_TIMEOUT              int64 = 60000\n\tQUEUE_TIMEOUT              int64 = 10000\n\tACCEPT_TIMEOUT             int64 = 60000\n\n\tRECEIVE_BLOCKING int64 = -1\n\n\tMAX_VALUE_PORT     = 65535\n\tMIN_VALUE_PORT     = 100\n\tMIN_LENGTH_ADDRESS = 7\n)\n\nvar (\n\t// ErrNotImplSplitterInterface indicates that encoding does not support Splitter interface\n\tErrNotImplSplitterInterface = fmt.Errorf(\"Encoding not implementing Splitter interface\")\n\t// ErrNotImplDecode indicates that encoding does not support Decode method\n\tErrNotImplDecode = fmt.Errorf(\"Decode is not implemented in this Encoding\")\n\t// ErrNotImplEncode indicates that encoding does not support Encode method\n\tErrNotImplEncode = fmt.Errorf(\"Encode is not implemented in this Encoding\")\n)\n\nvar defaultTon atomic.Value\nvar defaultNpi atomic.Value\n\nfunc init() {\n\tdefaultTon.Store(DFLT_GSM_TON)\n\tdefaultNpi.Store(DFLT_GSM_NPI)\n}\n\n// SetDefaultTon set default ton.\nfunc SetDefaultTon(dfltTon byte) {\n\tdefaultTon.Store(dfltTon)\n}\n\n// GetDefaultTon get default ton.\nfunc GetDefaultTon() byte {\n\treturn defaultTon.Load().(byte)\n}\n\n// SetDefaultNpi set default npi.\nfunc SetDefaultNpi(dfltNpi byte) {\n\tdefaultNpi.Store(dfltNpi)\n}\n\n// GetDefaultNpi get default npi.\nfunc GetDefaultNpi() byte {\n\treturn defaultNpi.Load().(byte)\n}\n"
  },
  {
    "path": "data/pkg_test.go",
    "content": "package data\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDefaultNpi(t *testing.T) {\n\tSetDefaultNpi(13)\n\trequire.EqualValues(t, 13, GetDefaultNpi())\n}\n\nfunc TestDefaultTon(t *testing.T) {\n\tSetDefaultTon(19)\n\trequire.EqualValues(t, 19, GetDefaultTon())\n}\n"
  },
  {
    "path": "data/utils.go",
    "content": "package data\n\nimport \"unicode\"\n\n// FindEncoding returns suitable encoding for a string.\n// If string is ascii, then GSM7Bit. If not, then UCS2.\nfunc FindEncoding(s string) (enc Encoding) {\n\tif isASCII(s) {\n\t\tenc = GSM7BIT\n\t} else {\n\t\tenc = UCS2\n\t}\n\treturn\n}\n\nfunc isASCII(s string) bool {\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] > unicode.MaxASCII {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "data/utils_test.go",
    "content": "package data\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFindEncoding(t *testing.T) {\n\trequire.Equal(t, GSM7BIT, FindEncoding(\"abc30hb3bk2lopzSD=2-^\"))\n\trequire.Equal(t, UCS2, FindEncoding(\"Trần Lập và ban nhạc Bức tường huyền thoại\"))\n\trequire.Equal(t, UCS2, FindEncoding(\"Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ\"))\n}\n"
  },
  {
    "path": "errors/pkg.go",
    "content": "package errors\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// SmppErr indicates smpp error(s), compatible with OpenSMPP.\ntype SmppErr struct {\n\terr              string\n\tserialVersionUID int64\n}\n\n// Error interface.\nfunc (s *SmppErr) Error() string {\n\treturn fmt.Sprintf(\"Error happened: [%s]. SerialVersionUID: [%d]\", s.err, s.serialVersionUID)\n}\n\nvar (\n\t// ErrInvalidPDU indicates invalid pdu payload.\n\tErrInvalidPDU error = &SmppErr{err: \"PDU payload is invalid\", serialVersionUID: -6985061862208729984}\n\n\t// ErrUnknownCommandID indicates unknown command id.\n\tErrUnknownCommandID error = &SmppErr{err: \"Unknown command id\", serialVersionUID: -5091873576710864441}\n\n\t// ErrWrongDateFormat indicates wrong date format.\n\tErrWrongDateFormat error = &SmppErr{err: \"Wrong date format\", serialVersionUID: 5831937612139037591}\n\n\t// ErrShortMessageLengthTooLarge indicates short message length is too large.\n\tErrShortMessageLengthTooLarge error = &SmppErr{err: fmt.Sprintf(\"Encoded short message data exceeds size of %d\", data.SM_MSG_LEN), serialVersionUID: 78237205927624}\n\n\t// ErrUDHTooLong UDH-L is larger than total length of short message data\n\tErrUDHTooLong = fmt.Errorf(\"User Data Header is too long for PDU short message\")\n)\n"
  },
  {
    "path": "errors/pkg_test.go",
    "content": "package errors\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestErr(t *testing.T) {\n\trequire.True(t, strings.HasPrefix(ErrInvalidPDU.Error(), \"Error happened: [\"))\n}\n"
  },
  {
    "path": "example/smsc_simulator/smsc.cpp",
    "content": "//\n//  smscsimulator.cpp\n//  SMPPLib\n//\n//  Created by Mark Hay on 12/05/2019.\n//  Copyright © 2019 Melrose Labs. All rights reserved.\n//\n\n// Build: g++ smscsimulator.cpp -o MLSMSCSimulator && ./MLSMSCSimulator\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <iostream>\n#include <string>\n#include <exception>\n#include <map>\n#include <list>\n\nusing namespace std;\n\n#include <unistd.h>\n\n#include <sys/ioctl.h>\n#include <time.h>\n#include <sys/time.h>\n\n#include <netdb.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n\n#define TRUE (1 == 1)\n#define FALSE (!TRUE)\n\n//\n\nuint64_t session_id_next = 0;  // ID for each session\n\n// General\n\nuint64_t currentUSecsSinceEpoch(void) {\n  struct timeval tv;\n  gettimeofday(&tv, NULL);\n  return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;\n}\n\n// Client config\n\nclass ClientConfig {\n private:\n  ClientConfig() {}\n\n  ~ClientConfig() {}\n\n public:\n  static ClientConfig& instance() {\n    static ClientConfig c;\n    return c;\n  }\n\n  typedef enum { BINDRESP_WITH_SC_INTERACE_VERSION_TLV } ConfigEntry;\n\n  bool is(string esme_id, ConfigEntry entry) { return false; }\n};\n\n// Message deliverer\n\nclass MessageDeliverer {\n public:\n  class Message {\n   public:\n    uint8_t source_addr_ton;\n    uint8_t source_addr_npi;\n    char source_addr[32];\n    uint8_t dest_addr_ton;\n    uint8_t dest_addr_npi;\n    char destination_addr[32];\n    uint8_t esm_class;\n    uint8_t protocol_id;\n    uint8_t priority_flag;\n    char schedule_delivery_time[32];\n    char validity_period[32];\n    uint8_t registered_delivery;\n    uint8_t replace_if_present_flag;\n    uint8_t data_coding;\n    uint8_t sm_default_msg_id;\n    uint8_t sm_length;\n    uint8_t short_message[160];\n\n    string smscMessageID;\n\n   private:\n    string content;\n\n   public:\n    Message() {}\n    Message(char* in) { content = in; }\n    ~Message() {}\n\n    string getContent(void) { return content; }\n\n    void setSource(uint8_t ton, uint8_t npi, char* addr) {\n      source_addr_ton = ton;\n      source_addr_npi = npi;\n      strcpy(source_addr, addr);\n    }\n    void setDestination(uint8_t ton, uint8_t npi, char* addr) {\n      dest_addr_ton = ton;\n      dest_addr_npi = npi;\n      strcpy(destination_addr, addr);\n    }\n    void setRegisteredDelivery(uint8_t val) { registered_delivery = val; }\n    void setShortMessage(uint8_t* sm_in, uint8_t sm_len_in) {\n      memcpy(short_message, sm_in, sm_len_in);\n      sm_length = sm_len_in;\n    }\n    void setSMSCMessageID(char* id) { smscMessageID = id; }\n  };\n\n private:\n  // time-ordered list [using STL map] of lists containing messages\n  typedef map<uint64_t /*time*/, list<Message>> MessageQueue;\n  MessageQueue mq;\n\n  MessageDeliverer() {}\n\n  typedef map<string, MessageDeliverer*> MDMap;\n  static MDMap mapInstance;\n\n  bool empty(void) { return (mq.size() == 0); }\n\n  bool get(Message& msgout) {\n    if (mq.size() == 0) return false;  // no time-lists\n\n    uint64_t firstListTime = (*mq.begin()).first;\n    uint64_t now = currentUSecsSinceEpoch();\n    if (firstListTime > now) return false;  // nothing due to be deliverered\n\n    // we have a message (or messages) that are due to be delivered\n\n    msgout = (*mq.begin()).second.front();\n\n    (*mq.begin()).second.pop_front();  // remove from list\n\n    if ((*mq.begin()).second.size() == 0)  // now empty - remove list\n      mq.erase(mq.begin());\n\n    return true;\n  }\n\n public:\n  ~MessageDeliverer() {}\n\n  static MessageDeliverer* getInstance(string& systemID) {\n    MDMap::iterator it = mapInstance.find(systemID);\n    if (it == mapInstance.end()) {\n      std::pair<MDMap::iterator, bool> res =\n          mapInstance.insert(MDMap::value_type(systemID, new MessageDeliverer));\n      return (res.first)->second;\n    } else\n      return (*it).second;\n  }\n\n  static bool getInstance_get(string& systemID, Message& msgout) {\n    MDMap::iterator it = mapInstance.find(systemID);\n    if (it == mapInstance.end()) {\n      return false;\n    }\n\n    bool found = (*it).second->get(msgout);\n\n    if ((*it).second->empty()) {\n      delete (*it).second;\n      mapInstance.erase(systemID);\n    }\n\n    return found;\n  }\n\n  void add(uint64_t timeDeliver, Message msg) {\n    MessageQueue::iterator it = mq.find(timeDeliver);\n    if (it == mq.end()) {\n      // no time-list exists - create\n      list<Message> msgList;  // empty message list\n      std::pair<MessageQueue::iterator, bool> ret;\n      ret = mq.insert({timeDeliver, msgList});\n      if (ret.second == false) return;  // failed to insert\n      it = ret.first;                   // iterator to newly inserted time-list\n    }\n\n    (*it).second.push_back(msg);  // add message to time-list\n  }\n};\n\nMessageDeliverer::MDMap MessageDeliverer::mapInstance;\n\n// Sub-SMPP layer\n\nclass SMPPSocket {\n protected:\n  int socket;\n\n public:\n  SMPPSocket() {}\n  virtual ~SMPPSocket() {}\n\n  virtual bool recvA(uint8_t&) = 0;\n  virtual void recv(void) = 0;\n  virtual bool send(uint8_t*, int len) = 0;\n\n  virtual long bytes_to_read(void) = 0;\n};\n\nclass SMPPSocketUnencrypted : public SMPPSocket {\n private:\n public:\n  SMPPSocketUnencrypted() {}\n  SMPPSocketUnencrypted(int socket_in) { socket = socket_in; }\n  ~SMPPSocketUnencrypted() {}\n\n  long bytes_to_read(void) {\n    long count = 0;\n    ioctl((int)socket, FIONREAD, &count);\n    return count;\n  }\n\n  bool recvA(uint8_t& oct) {\n    int n = (int)::recv(socket, (void*)&oct, 1, 0);\n    return (n > 0);\n  }\n  void recv(void) {}\n  bool send(uint8_t* buf, int len) {\n    return ::send(socket, (void*)buf, len, 0);\n  }\n};\n\n// SMPP\n\nclass SMPP {\n public:\n  class CmdStatus {\n   public:\n    static const uint64_t ESME_ROK = 0x00000000;\n    static const uint64_t ESME_RINVBNDSTS = 0x00000004;\n    static const uint64_t ESME_INVDSTADR = 0x0000000B;\n    static const uint64_t ESME_RBINDFAIL = 0x0000000D;\n    static const uint64_t ESME_RINVSYSID = 0x0000000F;\n    static const uint64_t ESME_RSUBMITFAIL = 0x00000045;\n    static const uint64_t ESME_RINVSCHED = 0x00000061;\n  };\n\n  class CmdID {\n   public:\n    static const uint64_t BindReceiver = 0x00000001;\n    static const uint64_t BindTransmitter = 0x00000002;\n    static const uint64_t QuerySM = 0x00000003;\n    static const uint64_t SubmitSM = 0x00000004;\n    static const uint64_t DeliverSM = 0x00000005;\n    static const uint64_t Unbind = 0x00000006;\n    static const uint64_t ReplaceSM = 0x00000007;\n    static const uint64_t CancelSM = 0x00000008;\n    static const uint64_t BindTransceiver = 0x00000009;\n    static const uint64_t Outbind = 0x0000000B;\n    static const uint64_t EnquireLink = 0x00000015;\n    static const uint64_t SubmitMulti = 0x00000021;\n    static const uint64_t AlertNotification = 0x00000102;\n    static const uint64_t DataSM = 0x00000103;\n    static const uint64_t BroadcastSM = 0x00000111;\n    static const uint64_t QueryBroadcastSM = 0x00000112;\n    static const uint64_t CancelBroadcastSM = 0x00000113;\n    static const uint64_t GenericNack = 0x80000000;\n    static const uint64_t BindReceiverResp = 0x80000001;\n    static const uint64_t BindTransmitterResp = 0x80000002;\n    static const uint64_t QuerySMResp = 0x80000003;\n    static const uint64_t SubmitSMResp = 0x80000004;\n    static const uint64_t DeliverSMResp = 0x80000005;\n    static const uint64_t UnbindResp = 0x80000006;\n    static const uint64_t ReplaceSMResp = 0x80000007;\n    static const uint64_t CancelSMResp = 0x80000008;\n    static const uint64_t BindTransceiverResp = 0x80000009;\n    static const uint64_t EnquireLinkResp = 0x80000015;\n    static const uint64_t SubmitMultiResp = 0x80000021;\n    static const uint64_t DataSMResp = 0x80000103;\n    static const uint64_t BroadcastSMResp = 0x80000111;\n    static const uint64_t QueryBroadcastSMResp = 0x80000112;\n    static const uint64_t CancelBroadcastSMResp = 0x80000113;\n  };\n\n  typedef struct {\n    uint64_t cmdid;\n    const char* name;\n  } CmdStrings;\n\n#define CMDEXP(A) CmdID::A, #A\n\n  CmdStrings cmdStrings[34] = {{CMDEXP(BindReceiver)},\n                               {CMDEXP(BindTransmitter)},\n                               {CMDEXP(QuerySM)},\n                               {CMDEXP(SubmitSM)},\n                               {CMDEXP(DeliverSM)},\n                               {CMDEXP(Unbind)},\n                               {CMDEXP(ReplaceSM)},\n                               {CMDEXP(CancelSM)},\n                               {CMDEXP(BindTransceiver)},\n                               {CMDEXP(Outbind)},\n                               {CMDEXP(EnquireLink)},\n                               {CMDEXP(SubmitMulti)},\n                               {CMDEXP(AlertNotification)},\n                               {CMDEXP(DataSM)},\n                               {CMDEXP(BroadcastSM)},\n                               {CMDEXP(QueryBroadcastSM)},\n                               {CMDEXP(CancelBroadcastSM)},\n                               {CMDEXP(GenericNack)},\n                               {CMDEXP(BindReceiverResp)},\n                               {CMDEXP(BindTransmitterResp)},\n                               {CMDEXP(QuerySMResp)},\n                               {CMDEXP(SubmitSMResp)},\n                               {CMDEXP(DeliverSMResp)},\n                               {CMDEXP(UnbindResp)},\n                               {CMDEXP(ReplaceSMResp)},\n                               {CMDEXP(CancelSMResp)},\n                               {CMDEXP(BindTransceiverResp)},\n                               {CMDEXP(EnquireLinkResp)},\n                               {CMDEXP(SubmitMultiResp)},\n                               {CMDEXP(DataSMResp)},\n                               {CMDEXP(BroadcastSMResp)},\n                               {CMDEXP(QueryBroadcastSMResp)},\n                               {CMDEXP(CancelBroadcastSMResp)}};\n\n  const char* cmdString(uint64_t id) {\n    for (int i = 0; i < 34; i++) {\n      if (cmdStrings[i].cmdid == id) return cmdStrings[i].name;\n    }\n    return \"\";\n  }\n\n  static void GSMTimeStringShort(time_t& t, char* szTimestamp, int nLen) {\n    if (t < 0) return;\n\n    szTimestamp[0] = 0x00;\n\n    tm* ptm = gmtime(&t);\n\n    if (ptm == NULL)\n      szTimestamp[0] = 0x00;\n    else\n      strftime(szTimestamp, nLen, \"%y%m%d%H%M\", ptm);\n  }\n\n  static time_t GSMStringTime(const char* szBuf) {\n    tm atm;\n    int tdif = 0;\n    char chDir = 0;\n\n    memset(&atm, 0, sizeof(tm));\n\n    int nRead = sscanf(szBuf, \"%02d%02d%02d%02d%02d%02d%*1d%02d%c\",\n                       &atm.tm_year, &atm.tm_mon, &atm.tm_mday, &atm.tm_hour,\n                       &atm.tm_min, &atm.tm_sec, &tdif, &chDir);\n\n    time_t t = 0;\n\n    if (nRead == 5) {\n      if (atm.tm_year >= 70)\n        atm.tm_year += 1900;\n      else\n        atm.tm_year += 2000;\n\n      t = timegm(&atm);\n\n      if (t == -1) {  // invalid time\n        t = 0;\n      }\n\n      if (chDir == '+') {\n        t = t - (tdif * 15 * 60);\n      }\n      if (chDir == '-') {\n        t = t + (tdif * 15 * 60);\n      }\n    }\n\n    return t;\n  }\n\n  static time_t GSMRelativeTime(char* szBuf) {\n    time_t t = 0;\n    int mday, hour, min, sec;\n    int nRead = sscanf(szBuf, \"%*02d%*02d%02d%02d%02d%02d%*1d%*02d%*c\", &mday,\n                       &hour, &min, &sec);\n    if (nRead == 4) t = ((mday * 24 + hour) * 60 + min) * 60 + sec;\n    return t;\n  }\n};\n\nclass SMPPConnection {\n public:\n  class SMPPException {};\n\n private:\n  const int max_command_body_length = 1024;\n\n  bool debug = false;\n\n  SMPPSocket* socket = NULL;\n\n  char ip[64];\n\n  typedef enum { PV_INVALID, PV_LENGTH, PV_ALL } PDUValidity;\n\n  typedef struct {\n    PDUValidity valid;  // indicate if this header structure has valid contents\n    uint64_t command_length;\n    uint64_t command_id;\n    uint64_t command_status;\n    uint64_t sequence_number;\n  } PDUHeader;\n\n  typedef struct {\n    bool valid;\n    uint8_t* body;\n  } PDUBody;\n\n  PDUHeader command_received_header = {PV_INVALID, 0, 0, 0, 0};\n  PDUBody command_received_body = {false, NULL};\n\n  // configuration\n  int enquire_link_period = 0;  // seconds\n\n  // methods\n\n  uint64_t getInteger(void) {\n    if (socket->bytes_to_read() < 4) throw SMPPException();\n    uint8_t v0, v1, v2, v3;\n    socket->recvA(v0);\n    socket->recvA(v1);\n    socket->recvA(v2);\n    socket->recvA(v3);\n    return (uint64_t)(((v0 << 8) | v1) << 8 | v2) << 8 | v3;\n  }\n\n  uint64_t getBytes(uint64_t len, uint8_t* mem) {\n    if (socket->bytes_to_read() < len) throw SMPPException();\n    for (int i = 0; i < len; i++) {\n      if (socket->recvA(mem[i]) == 0) throw SMPPException();\n    }\n    return 0;\n  }\n\n public:\n  SMPPConnection() {\n    socket = NULL;\n    command_received_header.valid = PV_INVALID;\n    command_received_body.body = new uint8_t[max_command_body_length];\n    ip[0] = '\\0';\n  }\n\n  ~SMPPConnection() {\n    if (command_received_body.body) delete[] command_received_body.body;\n\n    if (socket) delete socket;\n  }\n\n  void setDebug(bool val) { debug = val; }\n\n  void allocateSocket(void) { socket = new SMPPSocketUnencrypted; }\n\n  void allocateSocket(int fdsocket) {\n    socket = new SMPPSocketUnencrypted(fdsocket);\n  }\n\n  void setIP(char* ip_in) {\n    if (ip_in != NULL)\n      strcpy(ip, ip_in);\n    else\n      strcpy(ip, \"\");\n  }\n\n  char* getIP() { return ip; }\n\n  uint64_t endian(uint64_t a) {\n    uint64_t b;\n    ((uint8_t*)&b)[0] = ((uint8_t*)&a)[3];\n    ((uint8_t*)&b)[1] = ((uint8_t*)&a)[2];\n    ((uint8_t*)&b)[2] = ((uint8_t*)&a)[1];\n    ((uint8_t*)&b)[3] = ((uint8_t*)&a)[0];\n    return b;\n  }\n\n  bool put(uint64_t sequence_number, uint64_t cmdID, uint64_t status,\n           uint8_t* param, int len) {\n    int pdu_len = 16 + len;\n    uint8_t buf[pdu_len];\n\n    *((uint64_t*)(buf + 0)) = endian(pdu_len);           // command length\n    *((uint64_t*)(buf + 4)) = endian(cmdID);             // command ID\n    *((uint64_t*)(buf + 8)) = endian(status);            // command status\n    *((uint64_t*)(buf + 12)) = endian(sequence_number);  // sequence number\n    if ((param != NULL) && (len != 0)) memcpy((char*)(buf + 16), param, len);\n\n    // for(int i=0;i<pdu_len;i++) printf(\"%02x \",buf[i]);\n    // printf(\"\\n\");\n\n    return socket->send(buf, pdu_len);\n  }\n\n  uint8_t* getBodyPointer(int& len) {\n    len = (int)(command_received_header.command_length - 16);\n    return command_received_body.body;\n  }\n\n  bool get() {\n    // require \"socket\"\n\n    if (socket == NULL) return false;\n\n    // get header\n\n    if (command_received_header.valid != PV_ALL) {\n      // don't have any of the header yet\n\n      try {\n        command_received_header.command_length = getInteger();\n        command_received_header.command_id = getInteger();\n        command_received_header.command_status = getInteger();\n        command_received_header.sequence_number = getInteger();\n        command_received_header.valid =\n            PV_ALL;  // all header values are now valid\n\n      } catch (SMPPException e) {\n        // no data\n        if (debug) std::cout << \"HEADER No data - ABORT!\" << std::endl;\n        return false;\n      }\n    }\n\n    if (command_received_header.valid != PV_ALL)\n      return false;  // don't let past here until we have the header\n\n    // get body (if present)\n\n    command_received_body.valid = false;\n\n    uint64_t body_length = command_received_header.command_length - 16;\n\n    if (body_length > 0) {\n      try {\n        getBytes(body_length, command_received_body.body);\n      } catch (SMPPException e) {\n        if (debug) std::cout << \"BODY No data\" << std::endl;\n        return false;\n      }\n      command_received_body.valid = true;\n      command_received_header.valid = PV_INVALID;\n\n      return true;\n    } else {\n      // empty body\n      command_received_body.valid = true;  // empty body (valid)\n      command_received_body.body = NULL;\n      command_received_header.valid = PV_INVALID;\n\n      return true;\n    }\n\n    return false;\n  }\n\n  uint64_t pduCommandID(void) { return command_received_header.command_id; }\n  uint64_t pduSequenceNo(void) {\n    return command_received_header.sequence_number;\n  }\n};\n\nclass Session {\n protected:\n  int sessionType;\n\n public:\n  Session() {}\n  virtual ~Session() {}\n\n  virtual bool timedCheck(void) = 0;\n  virtual bool run(void) = 0;\n};\n\nclass AdminSession : public Session {\n public:\n  AdminSession() { sessionType = 0; }\n  AdminSession(int fdsocket) {\n    // conn.allocateSocket(fdsocket);\n  }\n  ~AdminSession() {}\n\n  bool timedCheck(void) {\n    // return true if to close\n    return true;\n  }\n\n  bool run(void) { return true; }\n};\n\nclass SMPPSession : public Session {\n private:\n  SMPPConnection conn;\n\n  uint64_t session_id;\n\n  // session state\n  string system_id;\n  string system_type;\n  long sequence_number_in = -1;\n  long sequence_number_out = 1;\n  uint64_t lastPDUFromESMETime = 0;\n  uint64_t enquireLinkRespPending = 0;\n  uint64_t closingTime = 0;\n\n public:\n  typedef enum { BS_NONE, BS_TRX, BS_TX, BS_RX } BindState;\n  uint8_t version;\n\n  BindState bindState;\n\n public:\n  SMPPSession() {\n    sessionType = 1;\n\n    bindState = BS_NONE;\n    version = 0x00;\n\n    session_id = session_id_next++;\n  }\n\n  SMPPSession(int fdsocket, char* ip) {\n    bindState = BS_NONE;\n    version = 0x00;\n    conn.allocateSocket(fdsocket);\n    conn.setIP(ip);\n\n    session_id = session_id_next++;\n  }\n\n  ~SMPPSession() {\n    if (bindState != BS_NONE) {\n      logCommand(\"session aborted\", \"--\");\n    }\n  }\n\n  void setDebug(bool val) { conn.setDebug(val); }\n\n  bool getCOctetString(uint8_t* buf_ptr, int buf_len, int& idx, string& strout,\n                       int max_param_len) {\n    char str[max_param_len];\n    int i = 0;\n    while (idx != buf_len) {\n      str[i++] = buf_ptr[idx++];\n      if (str[i - 1] == '\\0') {\n        strout.assign(str);\n        return true;\n      }\n      if (i == max_param_len) return false;\n    }\n    return false;\n  }\n\n  bool timedCheck(void) {\n    // return true if session to close\n\n    uint64_t now = currentUSecsSinceEpoch();\n\n    // session management\n    //\n\n    if ((closingTime != 0) && (now >= closingTime))\n      return true;  // session was due to close now\n\n    if ((lastPDUFromESMETime != 0) &&\n        ((now - lastPDUFromESMETime) >\n         (120 *\n          1000000L)))  // wait up to 2 mins of inactivity before enquire link\n    {\n      if (enquireLinkRespPending == 0) {\n        // issue enquire link\n        send(sequence_number_out++, SMPP::CmdID::EnquireLink,\n             SMPP::CmdStatus::ESME_ROK, NULL, 0);\n        enquireLinkRespPending = now;\n        closingTime = 0;\n      } else {\n        if (closingTime == 0) {\n          // waiting on enquire link\n          uint64_t tsinceenquirelinksent = now - enquireLinkRespPending;\n          if (tsinceenquirelinksent >\n              (60 * 1000000L))  // wait up to 1 min on enquire_link response\n          {\n            send(sequence_number_out++, SMPP::CmdID::Unbind,\n                 SMPP::CmdStatus::ESME_ROK, NULL, 0);\n\n            closingTime =\n                now + 5 * 1000000L;  // close session in 5 seconds time\n          }\n        }\n      }\n    }\n\n    // simulate message delivery + delivery receipt generation\n    //\n\n    if ((system_id.length() > 0) &&\n        ((bindState == SMPPSession::BindState::BS_TRX) ||\n         (bindState == SMPPSession::BindState::BS_RX))) {\n      MessageDeliverer::Message msg;\n      while (MessageDeliverer::getInstance_get(system_id, msg)) {\n        // indicate message delivered\n\n        if (msg.registered_delivery !=\n            0)  // ESME requested receipt so generate one\n        {\n          uint8_t source_addr_ton = msg.source_addr_ton,\n                  source_addr_npi = msg.source_addr_npi;\n          string destination_addr = msg.destination_addr;\n          uint8_t dest_addr_ton = msg.dest_addr_ton,\n                  dest_addr_npi = msg.dest_addr_npi;\n          string source_addr = msg.source_addr;\n          string msgid = msg.smscMessageID;\n\n          generateReceipt(source_addr_ton, source_addr_npi, source_addr,\n                          dest_addr_ton, dest_addr_npi, destination_addr,\n                          msgid);\n        }\n\n        if (strstr(msg.destination_addr, system_id.c_str()) !=\n            NULL) {  // system_id is in destination address - assume MO for\n                     // testing purposes\n          generateMO(msg.source_addr_ton, msg.source_addr_npi, msg.source_addr,\n                     msg.dest_addr_ton, msg.dest_addr_npi, msg.destination_addr,\n                     msg.short_message, msg.sm_length, msg.data_coding);\n        }\n      }\n    }\n\n    return false;\n  }\n\n  void generateMO(uint8_t source_addr_ton, uint8_t source_addr_npi,\n                  string source_addr, uint8_t dest_addr_ton,\n                  uint8_t dest_addr_npi, string destination_addr,\n                  uint8_t* short_message, uint8_t sm_length,\n                  uint8_t data_coding) {\n    uint8_t sbuf[1024];\n\n    int sidx = 0;\n\n    sbuf[sidx++] = 0x00;  // service type\n\n    sbuf[sidx++] = source_addr_ton;  //\n    sbuf[sidx++] = source_addr_npi;  //\n    memcpy(sbuf + sidx, source_addr.c_str(),\n           source_addr.length() + 1);  // destination_addr\n    sidx += source_addr.length() + 1;\n\n    sbuf[sidx++] = dest_addr_ton;  //\n    sbuf[sidx++] = dest_addr_npi;  //\n    memcpy(sbuf + sidx, destination_addr.c_str(),\n           destination_addr.length() + 1);  // source_addr\n    sidx += destination_addr.length() + 1;\n\n    sbuf[sidx++] = 0x00;         // esm_class\n    sbuf[sidx++] = 0x00;         // protocol_id\n    sbuf[sidx++] = 0x00;         // priority_flag\n    sbuf[sidx++] = 0x00;         // schedule_delivery_time\n    sbuf[sidx++] = 0x00;         // validity_period\n    sbuf[sidx++] = 0x00;         // registered_delivery\n    sbuf[sidx++] = 0x00;         // replace_if_present_flag\n    sbuf[sidx++] = data_coding;  // data_coding\n    sbuf[sidx++] = 0x00;         // sm_default_msg_id\n\n    sbuf[sidx++] = sm_length;  // sm_length\n\n    memcpy((char*)(sbuf + sidx), short_message, sm_length);\n\n    sidx += sm_length;\n\n    send(sequence_number_out++, SMPP::CmdID::DeliverSM,\n         SMPP::CmdStatus::ESME_ROK, sbuf, sidx);\n  }\n\n  void generateReceipt(uint8_t source_addr_ton, uint8_t source_addr_npi,\n                       string source_addr, uint8_t dest_addr_ton,\n                       uint8_t dest_addr_npi, string destination_addr,\n                       string msgid) {\n    // receipt\n\n    if ((bindState == SMPPSession::BindState::BS_TRX) ||\n        (bindState == SMPPSession::BindState::BS_RX)) {\n      uint8_t sbuf[1024];\n\n      int sidx = 0;\n      sbuf[sidx++] = 0x00;           // service type\n      sbuf[sidx++] = dest_addr_ton;  // source_addr_ton\n      sbuf[sidx++] = dest_addr_npi;  // source_addr_npi\n      memcpy(sbuf + sidx, destination_addr.c_str(),\n             destination_addr.length() + 1);  // source_addr\n      sidx += destination_addr.length() + 1;\n      sbuf[sidx++] = source_addr_ton;  // dest_addr_ton\n      sbuf[sidx++] = source_addr_npi;  // dest_addr_npi\n      memcpy(sbuf + sidx, source_addr.c_str(),\n             source_addr.length() + 1);  // destination_addr\n      sidx += source_addr.length() + 1;\n      sbuf[sidx++] =\n          0x04;  // esm_class [0x04 Short Message contains MC delivery report]\n      sbuf[sidx++] = 0x00;  // protocol_id\n      sbuf[sidx++] = 0x00;  // priority_flag\n      sbuf[sidx++] = 0x00;  // schedule_delivery_time\n      sbuf[sidx++] = 0x00;  // validity_period\n      sbuf[sidx++] = 0x00;  // registered_delivery\n      sbuf[sidx++] = 0x00;  // replace_if_present_flag\n      sbuf[sidx++] = 0x00;  // data_coding\n      sbuf[sidx++] = 0x00;  // sm_default_msg_id\n\n      // - short_message containing receipt in text format\n      time_t tSubmitStamp = time(NULL);\n      time_t tDoneStamp = time(NULL);\n      char szSubmitStamp[32];\n      char szDoneStamp[32];\n      SMPP::GSMTimeStringShort(tSubmitStamp, szSubmitStamp,\n                               sizeof(szSubmitStamp));\n      SMPP::GSMTimeStringShort(tDoneStamp, szDoneStamp, sizeof(szDoneStamp));\n\n      char short_message[160];\n      sprintf(short_message,\n              \"id:%s sub:000 dlvrd:%03d submit date:%s done date:%s stat:%s \"\n              \"err:%03d text:\",\n              msgid.c_str(), 1 /* 1 message delivered */, szSubmitStamp,\n              szDoneStamp, \"DELIVRD\", 0 /*error*/);\n\n      sbuf[sidx++] = strlen(short_message) + 1;  // sm_length\n\n      memcpy((char*)(sbuf + sidx), short_message,\n             (int)(strlen(short_message) + 1));\n\n      sidx += strlen(short_message) + 1;\n\n      // TLVs\n\n      if (bindState == SMPPSession::BindState::BS_TRX) {\n        uint8_t params1[] = {\n            // message_state TLV\n            0x04, 0x27,  // tag\n            0x00, 0x01,  // length\n            0x02,        // value - delivered\n\n            // network_error TLV\n            0x04, 0x23,        // tag\n            0x00, 0x03,        // length\n            0x03, 0x00, 0x00,  // value - GSM, 0, 0\n\n            // receipted_message_id TLV\n            0x00, 0x1e,  // tag\n            0x00, 0x41,  // length\n                         // .. message_id (to be appended)\n        };\n\n        memcpy(sbuf + sidx, params1, sizeof(params1));\n        sidx += sizeof(params1);\n        memcpy(sbuf + sidx, msgid.c_str(), msgid.length() + 1);\n        sidx += msgid.length() + 1;\n      }\n\n      send(sequence_number_out++, SMPP::CmdID::DeliverSM,\n           SMPP::CmdStatus::ESME_ROK, sbuf, sidx);\n    }\n  }\n\n  bool run(void) {\n    // return true if closed\n\n    uint64_t now = currentUSecsSinceEpoch();\n\n    if ((closingTime != 0) && (now >= closingTime))\n      return true;  // session was due to close now\n\n    // handle PDUs from ESME\n    //\n\n    bool allowBind = true;\n\n    uint64_t cmdid, seqno;\n    uint8_t sbuf[1024];\n\n    if (recv(cmdid, seqno)) {\n      // SMPP b;\n      // printf(\"<< 0x%08llx %s\\n\",cmdid,b.cmdString(cmdid));\n\n      lastPDUFromESMETime = now;\n\n      if ((cmdid == SMPP::CmdID::BindTransceiver) ||\n          (cmdid == SMPP::CmdID::BindReceiver) ||\n          (cmdid == SMPP::CmdID::BindTransmitter)) {\n        if (bindState !=\n            SMPPSession::BindState::BS_NONE)  // PROTOCOL must be in unbound\n                                              // state for ESME to send bind\n                                              // command\n        {\n          send(seqno, cmdid + 0x80000000, SMPP::CmdStatus::ESME_RINVBNDSTS,\n               NULL, 0);\n        } else {\n          string esme_system_id = \"\";\n          string esme_password = \"\";\n          string esme_system_type = \"\";\n          uint8_t esme_smpp_ver = 0x00;\n\n          int ptr_max = 0;\n          uint8_t* ptr = conn.getBodyPointer(ptr_max);\n\n          int idx = 0;\n          if (!getCOctetString(ptr, ptr_max, idx, esme_system_id, 16)) {\n            allowBind = false;\n            goto parse_complete;\n          }\n          if (!getCOctetString(ptr, ptr_max, idx, esme_password, 9)) {\n            allowBind = false;\n            goto parse_complete;\n          }\n          if (!getCOctetString(ptr, ptr_max, idx, esme_system_type, 13)) {\n            allowBind = false;\n            goto parse_complete;\n          }\n\n          if (idx < ptr_max)\n            esme_smpp_ver = ptr[idx++];\n          else {\n            allowBind = false;\n            goto parse_complete;\n          }\n\n          if ((esme_smpp_ver < 0x34) && (cmdid == SMPP::CmdID::BindTransceiver))\n            allowBind =\n                false;  // PROTOCOL transceiver bind only allowed for v3.4+\n\n        parse_complete:  // parse complete label\n\n          if (allowBind) {\n            system_id = esme_system_id;\n\n            char smsc_system_id[] = \"MelroseLabsSMSC\";\n            uint64_t cmdStatus = SMPP::CmdStatus::ESME_ROK;\n            if (system_id == \"invalid\") {\n                cmdStatus = SMPP::CmdStatus::ESME_RINVSYSID;\n                // If there is an error in the bind_transmitter or bind_receiver request,\n                // the SMSC system_id is not returned (see SMPP reference 4.1.2 and 4.1.4).\n                if (cmdid == SMPP::CmdID::BindTransmitter || cmdid == SMPP::CmdID::BindReceiver) {\n                    std::fill_n(sbuf, 1024, 0);\n                } else {\n                    memcpy(sbuf, smsc_system_id, strlen(smsc_system_id) + 1);\n                }\n            } else {\n                memcpy(sbuf, smsc_system_id, strlen(smsc_system_id) + 1);\n            }\n\n            if (ClientConfig::instance().is(\n                    esme_system_id,\n                    ClientConfig::ConfigEntry::\n                        BINDRESP_WITH_SC_INTERACE_VERSION_TLV)) {\n              uint8_t params[] = {\n                  // sc_interface_version TLV\n                  0x02, 0x10,  // tag\n                  0x00, 0x01,  // length\n                  0x34,        // value [v3.4 supported]\n              };\n\n              memcpy(sbuf + strlen(smsc_system_id) + 1, params, sizeof(params));\n              send(seqno, cmdid + 0x80000000, cmdStatus, sbuf,\n                   strlen(smsc_system_id) + 1 + sizeof(params));\n            } else\n              send(seqno, cmdid + 0x80000000, cmdStatus, sbuf,\n                   strlen(smsc_system_id) + 1);\n\n            if (cmdid == SMPP::CmdID::BindTransceiver)\n              bindState = SMPPSession::BindState::BS_TRX;\n            else if (cmdid == SMPP::CmdID::BindTransmitter)\n              bindState = SMPPSession::BindState::BS_TX;\n            else if (cmdid == SMPP::CmdID::BindReceiver)\n              bindState = SMPPSession::BindState::BS_RX;\n            else\n              bindState = SMPPSession::BindState::BS_NONE;\n\n            version = esme_smpp_ver;\n          } else {\n            send(seqno, SMPP::CmdID::BindTransceiverResp,\n                 SMPP::CmdStatus::ESME_RBINDFAIL, NULL,\n                 0);  // responding indicating bind unsuccessful\n            bindState = SMPPSession::BindState::BS_NONE;\n          }\n        }\n      } else if (cmdid == SMPP::CmdID::Unbind) {\n        if (bindState != SMPPSession::BindState::BS_NONE) {\n          send(seqno, SMPP::CmdID::UnbindResp, SMPP::CmdStatus::ESME_ROK, NULL,\n               0);\n          bindState = SMPPSession::BindState::BS_NONE;\n        } else {\n          send(seqno, SMPP::CmdID::UnbindResp, SMPP::CmdStatus::ESME_RINVBNDSTS,\n               NULL, 0);\n        }\n      } else if (cmdid == SMPP::CmdID::EnquireLinkResp) {\n        enquireLinkRespPending = 0;  // received pending enquire link\n      } else if (cmdid == SMPP::CmdID::EnquireLink) {\n        send(seqno, SMPP::CmdID::EnquireLinkResp, SMPP::CmdStatus::ESME_ROK,\n             NULL, 0);\n      } else if (cmdid == SMPP::CmdID::GenericNack) {\n        // no response to GenericNack\n      } else if (cmdid == SMPP::CmdID::SubmitSM) {\n        if ((bindState == SMPPSession::BindState::BS_NONE) ||\n            (bindState == SMPPSession::BindState::BS_RX)) {\n          send(seqno, SMPP::CmdID::SubmitSMResp,\n               SMPP::CmdStatus::ESME_RINVBNDSTS, NULL, 0);\n        } else {\n          // process requested\n\n          uint64_t tNow = currentUSecsSinceEpoch();\n\n          uint64_t tESMEDeliveryTimeRequired =\n              tNow;  // default to immediate delivery\n\n          bool allowSubmit = true;\n          uint64_t smppSubmitError = SMPP::CmdStatus::ESME_RSUBMITFAIL;\n\n          string service_type = \"\";\n          uint8_t source_addr_ton = 0;\n          uint8_t source_addr_npi = 0;\n          string source_addr = \"\";\n          uint8_t dest_addr_ton = 0;\n          uint8_t dest_addr_npi = 0;\n          string destination_addr = \"\";\n          string schedule_delivery_time = \"\";\n          string validity_period = \"\";\n          uint8_t registered_delivery = 0;\n          uint8_t sm_length;\n          uint8_t short_message[160];\n          uint8_t data_coding;\n\n          int ptr_max = 0;\n          uint8_t* ptr = conn.getBodyPointer(ptr_max);\n\n          int idx = 0;\n          if (!getCOctetString(ptr, ptr_max, idx, service_type, 6)) {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (idx < ptr_max) {\n            source_addr_ton = ptr[idx++];\n          } else {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (idx < ptr_max) {\n            source_addr_npi = ptr[idx++];\n          } else {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (!getCOctetString(ptr, ptr_max, idx, source_addr, 21)) {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (idx < ptr_max) {\n            dest_addr_ton = ptr[idx++];\n          } else {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (idx < ptr_max) {\n            dest_addr_npi = ptr[idx++];\n          } else {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (!getCOctetString(ptr, ptr_max, idx, destination_addr, 21)) {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n\n          idx++;  // esm_class\n          idx++;  // protocol_id\n          idx++;  // priority_flag\n          if (!getCOctetString(ptr, ptr_max, idx, schedule_delivery_time, 17)) {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          if (!getCOctetString(ptr, ptr_max, idx, validity_period, 17)) {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }\n          registered_delivery = ptr[idx++];  // registered_delivery\n          idx++;                             // replace_if_present_flag\n          data_coding = ptr[idx++];          // data_coding\n          idx++;                             // sm_default_msg_id\n          sm_length = ptr[idx++];\n          memcpy(short_message, ptr + idx, sm_length);\n          idx += sm_length;\n\n          if (schedule_delivery_time.length() > 0)  // scheduled delivery\n          {\n            if (schedule_delivery_time[schedule_delivery_time.length() - 1] ==\n                'R')  // relative\n              tESMEDeliveryTimeRequired =\n                  tNow +\n                  SMPP::GSMRelativeTime((char*)schedule_delivery_time.c_str()) *\n                      1000000L;\n            else\n              tESMEDeliveryTimeRequired =\n                  (SMPP::GSMStringTime(schedule_delivery_time.c_str()) + 3 +\n                   (rand() % 10)) *\n                  1000000L;\n\n            if (tESMEDeliveryTimeRequired <\n                tNow)  // scheduled delivery time is before now\n            {\n              allowSubmit = false;\n              smppSubmitError = SMPP::CmdStatus::ESME_RINVSCHED;\n              goto parse_complete_submit;\n            }\n          }\n\n          // ...\n\n          if (destination_addr == \"333\") {\n            allowSubmit = false;\n            goto parse_complete_submit;\n          }  // force ESME_RSUBMITFAIL\n\n          if (destination_addr.length() < 8) {\n            allowSubmit = false;\n            smppSubmitError = SMPP::CmdStatus::ESME_INVDSTADR;\n            goto parse_complete_submit;\n          }\n\n        parse_complete_submit:\n\n          // send response\n\n          if (allowSubmit) {\n            // accept message and send message ID back to ESME\n\n            int msgid_len =\n                64;  // PROTOCOL v3.4 message_id field is COctet String of up to\n                     // 65 chars (inc NULL terminator)\n            if (version < 0x34)\n              msgid_len =\n                  8;  // PROTOCOL v3.3 message_id field is COctet String (hex)\n                      // of up to 9 characters (inc NULL terminator)\n\n            char msgid[msgid_len + 1];\n            for (int i = 0; i < msgid_len; i++)\n              msgid[i] =\n                  (rand() % 16) < 10 ? ('0' + rand() % 10) : ('a' + rand() % 6);\n            msgid[msgid_len] = 0;\n            send(seqno, SMPP::CmdID::SubmitSMResp, SMPP::CmdStatus::ESME_ROK,\n                 (uint8_t*)msgid, msgid_len + 1);\n\n            // sending back MO\n            generateMO(5, 0, \"FakeFrom\", 1, 1, \"FakeTo\", short_message,\n                       sm_length, data_coding);\n\n            // add message to deliverer so that receipt will then be sent to\n            // ESME\n\n            uint64_t timeDeliver =\n                tESMEDeliveryTimeRequired + (3 + (rand() % 10)) * 1000000L;\n\n            MessageDeliverer::Message msg;\n            msg.setSource(source_addr_ton, source_addr_npi,\n                          (char*)source_addr.c_str());\n            msg.setDestination(dest_addr_ton, dest_addr_npi,\n                               (char*)destination_addr.c_str());\n            msg.setRegisteredDelivery(registered_delivery);\n            msg.setShortMessage(short_message, sm_length);\n            msg.setSMSCMessageID(msgid);\n            MessageDeliverer::getInstance(system_id)->add(timeDeliver, msg);\n          } else\n            send(seqno, SMPP::CmdID::SubmitSMResp, smppSubmitError, NULL,\n                 0);  // don't accept message and send NAK back to ESME\n        }\n      }\n    } else {\n      // error or closed\n      return true;\n    }\n    return false;\n  }\n\n  uint8_t getVersion(void) { return version; }\n  void setVersion(uint8_t version_in) { version = version_in; }\n\n  void logCommand(uint64_t cmdID, const char* direction) {\n    char buf[640];\n    SMPP b;\n    sprintf(buf, \"0x%08llx %s\", cmdID, b.cmdString(cmdID));\n    logCommand(buf, direction);\n  }\n\n  void logCommand(char* logline, const char* direction) {\n    time_t rawtime;\n    struct tm* timeinfo;\n    char buffer[80];\n\n    time(&rawtime);\n    timeinfo = localtime(&rawtime);\n\n    strftime(buffer, 80, \"%F %X \", timeinfo);\n\n    printf(\"%s %s [s%06llx:%-15s] %s\\n\", buffer, direction, session_id,\n           conn.getIP(), logline);\n  }\n\n  bool recv(uint64_t& cmdID, uint64_t& seqNo) {\n    bool ret = conn.get();\n    if (ret) {\n      cmdID = conn.pduCommandID();\n      seqNo = conn.pduSequenceNo();\n      logCommand(cmdID, \">>\");\n    }\n    return ret;\n  }\n\n  bool send(uint64_t seqNo, uint64_t cmdID, uint64_t cmdStatus, uint8_t* param,\n            int len) {\n    // SMPP b;\n    // printf(\"<< 0x%08llx %s\\n\",cmdID,b.cmdString(cmdID));\n    logCommand(cmdID, \"<<\");\n\n    return conn.put(seqNo, cmdID, cmdStatus, param, len);\n  }\n};\n\n// Sockets reference:\n// https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/rzab6/xnonblock.htm\n\nint dolisten(int portno) {\n  struct sockaddr_in serv_addr;\n\n  /* First call to socket() function */\n  int listensockfd = socket(AF_INET, SOCK_STREAM, 0);\n\n  if (listensockfd < 0) {\n    perror(\"ERROR opening socket\");\n    return -1;\n  }\n\n  int rc, on = 1;\n\n  /*************************************************************/\n  /* Allow socket descriptor to be reuseable                   */\n  /*************************************************************/\n  rc = setsockopt(listensockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on,\n                  sizeof(on));\n  if (rc < 0) {\n    perror(\"setsockopt() failed\");\n    close(listensockfd);\n    exit(-1);\n  }\n\n  /*************************************************************/\n  /* Set socket to be nonblocking. All of the sockets for      */\n  /* the incoming connections will also be nonblocking since   */\n  /* they will inherit that state from the listening socket.   */\n  /*************************************************************/\n  rc = ioctl(listensockfd, FIONBIO, (char*)&on);\n  if (rc < 0) {\n    perror(\"ioctl() failed\");\n    close(listensockfd);\n    exit(-1);\n  }\n\n  /* Initialize socket structure */\n  bzero((char*)&serv_addr, sizeof(serv_addr));\n\n  serv_addr.sin_family = AF_INET;\n  serv_addr.sin_addr.s_addr = INADDR_ANY;\n  serv_addr.sin_port = htons(portno);\n\n  /* Now bind the host address using bind() call.*/\n  if (::bind(listensockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) <\n      0) {\n    perror(\"ERROR on binding\");\n    return -1;\n  }\n\n  /* Now start listening for the clients, here\n   * process will go in sleep mode and will wait\n   * for the incoming connection\n   */\n\n  listen(listensockfd, 32);\n\n  return listensockfd;\n}\n\nSession* sessionSockMap[32000];  // array of SMPP session by socket\n\nint main(int argc, const char* argv[]) {\n  printf(\"%s build time: %s %s\\n\", argv[0], __DATE__, __TIME__);\n\n  //\n\n  int portSMPP = 2775;\n\n  int listensockfdSMPP = dolisten(portSMPP);\n\n  if (listensockfdSMPP == -1) {\n    perror(\"Failed to listen on SMPP port\");\n    exit(1);\n  }\n\n  std::cout << \"Listening for SMPP on port \" << portSMPP << std::endl;\n\n  //\n\n  int portAdmin = 8775;\n\n  int listensockfdAdmin = dolisten(portAdmin);\n\n  if (listensockfdAdmin == -1) {\n    perror(\"Failed to listen on admin port\");\n    exit(1);\n  }\n\n  std::cout << \"Listening for admin on port \" << portAdmin << std::endl;\n\n  // Initialize the master fd_set\n  fd_set master_set, working_set;\n  FD_ZERO(&master_set);\n  int max_sd = listensockfdSMPP;\n  FD_SET(listensockfdSMPP, &master_set);\n  if (listensockfdAdmin > max_sd) max_sd = listensockfdAdmin;\n  FD_SET(listensockfdAdmin, &master_set);\n\n  //\n\n  int rc, i, desc_ready;\n  int end_server = FALSE;\n  int new_sd;\n  int close_conn;\n  struct timeval timeout;\n\n  // Loop waiting for incoming connects or for incoming data\n  do {\n    // Copy the master fd_set over to the working fd_set.\n    memcpy(&working_set, &master_set, sizeof(master_set));\n\n    // Initialize the timeval struct to 1 second.  If no\n    // activity after 1 second then wake-up and cycle.\n    timeout.tv_sec = 1;\n    timeout.tv_usec = 0;\n\n    // Call select() and wait for it to complete.\n    rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);\n\n    // Check to see if the select call failed.\n    if (rc < 0) {\n      perror(\"  select() failed\");\n      break;\n    }\n\n    // Check to see if select call timed out.\n    if (rc == 0) {\n      // perform periodic session tasks\n\n      for (i = 0; i <= max_sd; ++i) {\n        if (sessionSockMap[i] != NULL)\n          if (sessionSockMap[i]->timedCheck()) {\n            printf(\"  %ld Force close on connection - %d.\\n\", time(NULL), i);\n\n            if (sessionSockMap[i] != NULL) {\n              delete sessionSockMap[i];\n              sessionSockMap[i] = NULL;\n            }\n\n            /*\n             If the close_conn flag was turned on, we need\n             to clean up this active connection.  This\n             clean up process includes removing the\n             descriptor from the master set and\n             determining the new maximum descriptor value\n             based on the bits that are still turned on in\n             the master set.\n            */\n\n            close(i);\n            FD_CLR(i, &master_set);\n            if (i == max_sd) {\n              while (FD_ISSET(max_sd, &master_set) == FALSE) {\n                max_sd -= 1;\n              }\n            }\n          }\n      }\n\n      continue;\n    }\n\n    /**********************************************************/\n    /* One or more descriptors are readable.  Need to         */\n    /* determine which ones they are.                         */\n    /**********************************************************/\n    desc_ready = rc;\n    for (i = 0; i <= max_sd && desc_ready > 0; ++i) {\n      /*******************************************************/\n      /* Check to see if this descriptor is ready            */\n      /*******************************************************/\n      if (FD_ISSET(i, &working_set)) {\n        /****************************************************/\n        /* A descriptor was found that was readable - one   */\n        /* less has to be looked for.  This is being done   */\n        /* so that we can stop looking at the working set   */\n        /* once we have found all of the descriptors that   */\n        /* were ready.                                      */\n        /****************************************************/\n        desc_ready -= 1;\n\n        /***********************************************************/\n        /* Check to see if this is the listening socket (SMPP)     */\n        /***********************************************************/\n        if (i == listensockfdSMPP) {\n          // printf(\"  Listening socket (SMPP) is readable\\n\");\n\n          /*************************************************/\n          /* Accept all incoming connections that are      */\n          /* queued up on the listening socket before we   */\n          /* loop back and call select again.              */\n          /*************************************************/\n          do {\n            /**********************************************/\n            /* Accept each incoming connection.  If       */\n            /* accept fails with EWOULDBLOCK, then we     */\n            /* have accepted all of them.  Any other      */\n            /* failure on accept will cause us to end the */\n            /* server.                                    */\n            /**********************************************/\n            struct sockaddr_in client_addr;\n            int clen = sizeof(sockaddr_in);\n            new_sd = accept(listensockfdSMPP, (struct sockaddr*)&client_addr,\n                            (socklen_t*)&clen);\n            if (new_sd < 0) {\n              if (errno != EWOULDBLOCK) {\n                perror(\"  accept() failed (SMPP)\");\n                if (errno != EMFILE) end_server = TRUE;\n              }\n              break;\n            }\n\n            char* ip = inet_ntoa(client_addr.sin_addr);\n\n            //\n\n            SMPPSession* newsession = new SMPPSession(new_sd, ip);\n\n            sessionSockMap[new_sd] = newsession;\n\n            /**********************************************/\n            /* Add the new incoming connection to the     */\n            /* master read set                            */\n            /**********************************************/\n            // printf(\"  New incoming connection - %d\\n\", new_sd);\n            FD_SET(new_sd, &master_set);\n            if (new_sd > max_sd) max_sd = new_sd;\n\n            /**********************************************/\n            /* Loop back up and accept another incoming   */\n            /* connection                                 */\n            /**********************************************/\n          } while (new_sd != -1);\n        } else\n            /***********************************************************/\n            /* Check to see if this is the listening socket (admin)    */\n            /***********************************************************/\n            if (i == listensockfdAdmin) {\n          // printf(\"  Listening socket (admin) is readable\\n\");\n          /*************************************************/\n          /* Accept all incoming connections that are      */\n          /* queued up on the listening socket before we   */\n          /* loop back and call select again.              */\n          /*************************************************/\n          do {\n            /**********************************************/\n            /* Accept each incoming connection.  If       */\n            /* accept fails with EWOULDBLOCK, then we     */\n            /* have accepted all of them.  Any other      */\n            /* failure on accept will cause us to end the */\n            /* server.                                    */\n            /**********************************************/\n            new_sd = accept(listensockfdAdmin, NULL, NULL);\n            if (new_sd < 0) {\n              if (errno != EWOULDBLOCK) {\n                perror(\"  accept() failed (admin)\");\n                if (errno != EMFILE) end_server = TRUE;\n              }\n              break;\n            }\n\n            //\n\n            AdminSession* newsession = new AdminSession(new_sd);\n\n            sessionSockMap[new_sd] = newsession;\n\n            /**********************************************/\n            /* Add the new incoming connection to the     */\n            /* master read set                            */\n            /**********************************************/\n            // printf(\"  New incoming connection (Admin) - %d\\n\", new_sd);\n            FD_SET(new_sd, &master_set);\n            if (new_sd > max_sd) max_sd = new_sd;\n\n            /**********************************************/\n            /* Loop back up and accept another incoming   */\n            /* connection                                 */\n            /**********************************************/\n          } while (new_sd != -1);\n        }\n\n        /****************************************************/\n        /* This is not the listening socket, therefore an   */\n        /* existing connection must be readable             */\n        /****************************************************/\n        else {\n          // printf(\"  Descriptor %d is readable\\n\", i);\n          close_conn = FALSE;\n\n          bool closed = sessionSockMap[i]->run();\n\n          if (closed) {\n            // printf(\"  Closing connection - %d\\n\", i);\n\n            if (sessionSockMap[i] != NULL) {\n              delete sessionSockMap[i];\n              sessionSockMap[i] = NULL;\n            }\n\n            close_conn = TRUE;\n          } else\n            close_conn = FALSE;\n\n          /*************************************************/\n          /* If the close_conn flag was turned on, we need */\n          /* to clean up this active connection.  This     */\n          /* clean up process includes removing the        */\n          /* descriptor from the master set and            */\n          /* determining the new maximum descriptor value  */\n          /* based on the bits that are still turned on in */\n          /* the master set.                               */\n          /*************************************************/\n          if (close_conn) {\n            close(i);\n            FD_CLR(i, &master_set);\n            if (i == max_sd) {\n              while (FD_ISSET(max_sd, &master_set) == FALSE) max_sd -= 1;\n            }\n          }\n        } /* End of existing connection is readable */\n      }   /* End of if (FD_ISSET(i, &working_set)) */\n    }     /* End of loop through selectable descriptors */\n\n  } while (end_server == FALSE);\n\n  //\n\n  close(listensockfdSMPP);\n\n  close(listensockfdAdmin);\n\n  std::cout << \"No longer listening on port \" << portSMPP << std::endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "example/transceiver_with_auto_response/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp\"\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\n\twg.Add(1)\n\tgo sendingAndReceiveSMS(&wg)\n\n\twg.Wait()\n}\n\nfunc sendingAndReceiveSMS(wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tauth := gosmpp.Auth{\n\t\tSMSC:       \"localhost:2775\",\n\t\tSystemID:   \"169994\",\n\t\tPassword:   \"EDXPJU\",\n\t\tSystemType: \"\",\n\t}\n\n\ttrans, err := gosmpp.NewSession(\n\t\tgosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),\n\t\tgosmpp.Settings{\n\t\t\tEnquireLink: 5 * time.Second,\n\n\t\t\tReadTimeout: 10 * time.Second,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tlog.Fatal(\"SubmitPDU error:\", err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tfmt.Println(\"Receiving PDU/Network error:\", err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tfmt.Println(\"Rebinding but error:\", err)\n\t\t\t},\n\n\t\t\tOnPDU: handlePDU(),\n\n\t\t\tOnClosed: func(state gosmpp.State) {\n\t\t\t\tfmt.Println(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\t// sending SMS(s)\n\tfor i := 0; i < 1800; i++ {\n\t\tif err = trans.Transceiver().Submit(newSubmitSM()); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t\ttime.Sleep(time.Second)\n\t}\n}\n\nfunc handlePDU() func(pdu.PDU, bool) {\n\tconcatenated := map[uint8][]string{}\n\treturn func(p pdu.PDU, _ bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.SubmitSMResp:\n\t\t\tfmt.Printf(\"SubmitSMResp:%+v\\n\", pd)\n\n\t\tcase *pdu.GenericNack:\n\t\t\tfmt.Println(\"GenericNack Received\")\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tfmt.Println(\"EnquireLinkResp Received\")\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Printf(\"DataSM:%+v\\n\", pd)\n\n\t\tcase *pdu.DeliverSM:\n\t\t\tfmt.Printf(\"DeliverSM:%+v\\n\", pd)\n\t\t\tlog.Println(pd.Message.GetMessage())\n\t\t\t// region concatenated sms (sample code)\n\t\t\tmessage, err := pd.Message.GetMessage()\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\ttotalParts, sequence, reference, found := pd.Message.UDH().GetConcatInfo()\n\t\t\tif found {\n\t\t\t\tif _, ok := concatenated[reference]; !ok {\n\t\t\t\t\tconcatenated[reference] = make([]string, totalParts)\n\t\t\t\t}\n\t\t\t\tconcatenated[reference][sequence-1] = message\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\tlog.Println(message)\n\t\t\t} else if parts, ok := concatenated[reference]; ok && isConcatenatedDone(parts, totalParts) {\n\t\t\t\tlog.Println(strings.Join(parts, \"\"))\n\t\t\t\tdelete(concatenated, reference)\n\t\t\t}\n\t\t\t// endregion\n\t\t}\n\t}\n}\n\nfunc newSubmitSM() *pdu.SubmitSM {\n\t// build up submitSM\n\tsrcAddr := pdu.NewAddress()\n\tsrcAddr.SetTon(5)\n\tsrcAddr.SetNpi(0)\n\t_ = srcAddr.SetAddress(\"00\" + \"522241\")\n\n\tdestAddr := pdu.NewAddress()\n\tdestAddr.SetTon(1)\n\tdestAddr.SetNpi(1)\n\t_ = destAddr.SetAddress(\"99\" + \"522241\")\n\n\tsubmitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)\n\tsubmitSM.SourceAddr = srcAddr\n\tsubmitSM.DestAddr = destAddr\n\t_ = submitSM.Message.SetMessageWithEncoding(\"Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ\", data.UCS2)\n\tsubmitSM.ProtocolID = 0\n\tsubmitSM.RegisteredDelivery = 1\n\tsubmitSM.ReplaceIfPresentFlag = 0\n\tsubmitSM.EsmClass = 0\n\n\treturn submitSM\n}\n\nfunc isConcatenatedDone(parts []string, total byte) bool {\n\tfor _, part := range parts {\n\t\tif part != \"\" {\n\t\t\ttotal--\n\t\t}\n\t}\n\treturn total == 0\n}\n"
  },
  {
    "path": "example/transceiver_with_manual_response/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp\"\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\n\twg.Add(1)\n\tgo sendingAndReceiveSMS(&wg)\n\n\twg.Wait()\n}\n\nfunc sendingAndReceiveSMS(wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tauth := gosmpp.Auth{\n\t\tSMSC:       \"localhost:2775\",\n\t\tSystemID:   \"169994\",\n\t\tPassword:   \"EDXPJU\",\n\t\tSystemType: \"\",\n\t}\n\n\ttrans, err := gosmpp.NewSession(\n\t\tgosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),\n\t\tgosmpp.Settings{\n\t\t\tEnquireLink: 5 * time.Second,\n\n\t\t\tReadTimeout: 10 * time.Second,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tlog.Fatal(\"SubmitPDU error:\", err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tfmt.Println(\"Receiving PDU/Network error:\", err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tfmt.Println(\"Rebinding but error:\", err)\n\t\t\t},\n\n\t\t\tOnAllPDU: handlePDU(),\n\n\t\t\tOnClosed: func(state gosmpp.State) {\n\t\t\t\tfmt.Println(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\t// sending SMS(s)\n\tfor i := 0; i < 30; i++ {\n\t\tif err = trans.Transceiver().Submit(newSubmitSM()); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t\ttime.Sleep(time.Second)\n\t}\n}\n\nfunc handlePDU() func(pdu.PDU) (pdu.PDU, bool) {\n\treturn func(p pdu.PDU) (pdu.PDU, bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Println(\"Unbind Received\")\n\t\t\treturn pd.GetResponse(), true\n\n\t\tcase *pdu.UnbindResp:\n\t\t\tfmt.Println(\"UnbindResp Received\")\n\n\t\tcase *pdu.SubmitSMResp:\n\t\t\tfmt.Println(\"SubmitSMResp Received\")\n\n\t\tcase *pdu.GenericNack:\n\t\t\tfmt.Println(\"GenericNack Received\")\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tfmt.Println(\"EnquireLinkResp Received\")\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Println(\"EnquireLink Received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Println(\"DataSM receiver\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DeliverSM:\n\t\t\tfmt.Println(\"DeliverSM receiver\")\n\t\t\treturn pd.GetResponse(), false\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\nfunc newSubmitSM() *pdu.SubmitSM {\n\t// build up submitSM\n\tsrcAddr := pdu.NewAddress()\n\tsrcAddr.SetTon(5)\n\tsrcAddr.SetNpi(0)\n\t_ = srcAddr.SetAddress(\"00\" + \"522241\")\n\n\tdestAddr := pdu.NewAddress()\n\tdestAddr.SetTon(1)\n\tdestAddr.SetNpi(1)\n\t_ = destAddr.SetAddress(\"99\" + \"522241\")\n\n\tsubmitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)\n\tsubmitSM.SourceAddr = srcAddr\n\tsubmitSM.DestAddr = destAddr\n\t_ = submitSM.Message.SetMessageWithEncoding(\"Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ\", data.UCS2)\n\tsubmitSM.ProtocolID = 0\n\tsubmitSM.RegisteredDelivery = 1\n\tsubmitSM.ReplaceIfPresentFlag = 0\n\tsubmitSM.EsmClass = 0\n\n\treturn submitSM\n}\n"
  },
  {
    "path": "example/transeiver_with_custom_store/CustomStore.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/gob\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/allegro/bigcache/v3\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\n\t\"github.com/linxGnu/gosmpp\"\n)\n\n// This is a just an example how to implement a custom store.\n//\n// Your implementation must be concurrency safe\n//\n// In this example we use bigcache https://github.com/allegro/bigcache\n// Warning:\n//  - This is just an example and should be tested before using in production\n//\t- We are serializing with gob, some field cannot be serialized for simplicity\n//  - We recommend you implement your own serialization/deserialization if you choose to use bigcache\n\ntype CustomStore struct {\n\tstore *bigcache.BigCache\n}\n\nfunc NewCustomStore() CustomStore {\n\tcache, _ := bigcache.New(context.Background(), bigcache.DefaultConfig(30*time.Second))\n\treturn CustomStore{\n\t\tstore: cache,\n\t}\n}\n\nfunc (s CustomStore) Set(ctx context.Context, request gosmpp.Request) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\tfmt.Println(\"Task cancelled\")\n\t\treturn ctx.Err()\n\tdefault:\n\t\tb, _ := serialize(request)\n\t\terr := s.store.Set(strconv.Itoa(int(request.PDU.GetSequenceNumber())), b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc (s CustomStore) Get(ctx context.Context, sequenceNumber int32) (gosmpp.Request, bool) {\n\tselect {\n\tcase <-ctx.Done():\n\t\tfmt.Println(\"Task cancelled\")\n\t\treturn gosmpp.Request{}, false\n\tdefault:\n\t\tbRequest, err := s.store.Get(strconv.Itoa(int(sequenceNumber)))\n\t\tif err != nil {\n\t\t\treturn gosmpp.Request{}, false\n\t\t}\n\t\trequest, err := deserialize(bRequest)\n\t\tif err != nil {\n\t\t\treturn gosmpp.Request{}, false\n\t\t}\n\t\treturn request, true\n\t}\n}\n\nfunc (s CustomStore) List(ctx context.Context) []gosmpp.Request {\n\tvar requests []gosmpp.Request\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn requests\n\tdefault:\n\t\tit := s.store.Iterator()\n\t\tfor it.SetNext() {\n\t\t\tvalue, err := it.Value()\n\t\t\tif err != nil {\n\t\t\t\treturn requests\n\t\t\t}\n\t\t\trequest, _ := deserialize(value.Value())\n\t\t\trequests = append(requests, request)\n\t\t}\n\t\treturn requests\n\t}\n}\n\nfunc (s CustomStore) Delete(ctx context.Context, sequenceNumber int32) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tdefault:\n\t\terr := s.store.Delete(strconv.Itoa(int(sequenceNumber)))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc (s CustomStore) Clear(ctx context.Context) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tdefault:\n\t\terr := s.store.Reset()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc (s CustomStore) Length(ctx context.Context) (int, error) {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn 0, ctx.Err()\n\tdefault:\n\t\treturn s.store.Len(), nil\n\t}\n}\n\nfunc serialize(request gosmpp.Request) ([]byte, error) {\n\tbuf := pdu.NewBuffer(make([]byte, 0, 64))\n\trequest.PDU.Marshal(buf)\n\tb := bytes.Buffer{}\n\te := gob.NewEncoder(&b)\n\terr := e.Encode(requestGob{\n\t\tPdu:      buf.Bytes(),\n\t\tTimeSent: time.Time{},\n\t})\n\tif err != nil {\n\t\treturn b.Bytes()[:], errors.New(\"serialization failed\")\n\t}\n\treturn b.Bytes(), nil\n}\n\nfunc deserialize(bRequest []byte) (request gosmpp.Request, err error) {\n\tr := requestGob{}\n\tb := bytes.Buffer{}\n\t_, err = b.Write(bRequest)\n\tif err != nil {\n\t\treturn request, errors.New(\"deserialization failed\")\n\t}\n\td := gob.NewDecoder(&b)\n\terr = d.Decode(&r)\n\tif err != nil {\n\t\treturn request, errors.New(\"deserialization failed\")\n\t}\n\tp, err := pdu.Parse(bytes.NewReader(r.Pdu))\n\tif err != nil {\n\t\treturn gosmpp.Request{}, err\n\t}\n\treturn gosmpp.Request{\n\t\tPDU:      p,\n\t\tTimeSent: r.TimeSent,\n\t}, nil\n}\n\ntype requestGob struct {\n\tPdu      []byte\n\tTimeSent time.Time\n}\n"
  },
  {
    "path": "example/transeiver_with_custom_store/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp\"\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\n\twg.Add(1)\n\tgo sendingAndReceiveSMS(&wg)\n\n\twg.Wait()\n}\n\nfunc sendingAndReceiveSMS(wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tauth := gosmpp.Auth{\n\t\tSMSC:       \"localhost:2775\",\n\t\tSystemID:   \"169994\",\n\t\tPassword:   \"EDXPJU\",\n\t\tSystemType: \"\",\n\t}\n\n\ttrans, err := gosmpp.NewSession(\n\t\tgosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),\n\t\tgosmpp.Settings{\n\t\t\tEnquireLink: 5 * time.Second,\n\n\t\t\tReadTimeout: 10 * time.Second,\n\n\t\t\tOnSubmitError: func(p pdu.PDU, err error) {\n\t\t\t\tif errors.Is(err, gosmpp.ErrWindowsFull) {\n\t\t\t\t\tlog.Println(\"SubmitPDU error:\", err)\n\t\t\t\t} else {\n\t\t\t\t\tlog.Fatal(\"SubmitPDU error:\", err)\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tfmt.Println(\"Receiving PDU/Network error:\", err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tfmt.Println(\"Rebinding but error:\", err)\n\t\t\t},\n\n\t\t\tOnClosed: func(state gosmpp.State) {\n\t\t\t\tfmt.Println(state)\n\t\t\t},\n\n\t\t\tWindowedRequestTracking: &gosmpp.WindowedRequestTracking{\n\t\t\t\tOnReceivedPduRequest:  handleReceivedPduRequest(),\n\t\t\t\tOnExpectedPduResponse: handleExpectedPduResponse(),\n\t\t\t\tOnExpiredPduRequest:   handleExpirePduRequest(),\n\t\t\t\tOnClosePduRequest:     handleOnClosePduRequest(),\n\t\t\t\tPduExpireTimeOut:      30 * time.Second,\n\t\t\t\tExpireCheckTimer:      10 * time.Second,\n\t\t\t\tMaxWindowSize:         30,\n\t\t\t\tEnableAutoRespond:     false,\n\t\t\t},\n\t\t},\n\t\t5*time.Second,\n\t\tgosmpp.WithRequestStore(NewCustomStore()),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\t// sending SMS(s)\n\tfor i := 0; i < 60; i++ {\n\t\tvar size int\n\t\tsize, err = trans.GetWindowSize()\n\t\tif err != nil {\n\t\t\tfmt.Println(\"could not get window size: \", err)\n\t\t} else {\n\t\t\tfmt.Println(\"Current window size: \", size)\n\t\t}\n\t\tp := newSubmitSM()\n\t\tif err = trans.Transceiver().Submit(p); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t\tfmt.Printf(\"Sent SubmitSM with id: %+v\\n\", p)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\ttime.Sleep(1 * time.Second)\n}\n\nfunc handleExpirePduRequest() func(pdu.PDU) bool {\n\treturn func(p pdu.PDU) bool {\n\t\tswitch p.(type) {\n\n\t\tcase *pdu.SubmitSM:\n\t\t\tfmt.Printf(\"Expired SubmitSM: %+v\\n\", p)\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Printf(\"Expired EnquireLink: %+v\\n\", p)\n\t\t\treturn true // if the enquire_link expired, usually means the bind is stale\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Printf(\"Expired DataSM: %+v\\n\", p)\n\n\t\tdefault:\n\t\t\tfmt.Printf(\"Expired PDU: %+v\\n\", p)\n\t\t}\n\n\t\treturn false\n\t}\n}\n\nfunc handleOnClosePduRequest() func(pdu.PDU) {\n\treturn func(p pdu.PDU) {\n\t\tswitch p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Printf(\"OnClose Unbind: %+v\\n\", p)\n\n\t\tcase *pdu.SubmitSM:\n\t\t\tfmt.Printf(\"OnClose SubmitSM: %+v\\n\", p)\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Printf(\"OnClose EnquireLink: %+v\\n\", p)\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Printf(\"OnClose DataSM: %+v\\n\", p)\n\t\t}\n\t}\n}\n\nfunc handleExpectedPduResponse() func(response gosmpp.Response) {\n\treturn func(response gosmpp.Response) {\n\t\tswitch response.PDU.(type) {\n\t\tcase *pdu.UnbindResp:\n\t\t\tfmt.Println(\"UnbindResp Received\")\n\t\t\tfmt.Printf(\"OriginalSM id:%+v\\n\", response.OriginalRequest.PDU)\n\n\t\tcase *pdu.SubmitSMResp:\n\t\t\tfmt.Printf(\"SubmitSMResp Received: %+v\\n\", response.PDU)\n\t\t\tfmt.Printf(\"OriginalSM SubmitSM:%+v\\n\", response.OriginalRequest.PDU)\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tfmt.Println(\"EnquireLinkResp Received\")\n\t\t\tfmt.Printf(\"Original EnquireLink:%+v\\n\", response.OriginalRequest.PDU)\n\n\t\t}\n\t}\n}\n\nfunc handleReceivedPduRequest() func(pdu.PDU) (pdu.PDU, bool) {\n\treturn func(p pdu.PDU) (pdu.PDU, bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Println(\"Unbind Received\")\n\t\t\treturn pd.GetResponse(), true\n\n\t\tcase *pdu.GenericNack:\n\t\t\tfmt.Println(\"GenericNack Received\")\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tfmt.Println(\"EnquireLinkResp Received\")\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Println(\"EnquireLink Received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Println(\"DataSM Received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DeliverSM:\n\t\t\tfmt.Println(\"DeliverSM Received\")\n\t\t\treturn pd.GetResponse(), false\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\nfunc newSubmitSM() *pdu.SubmitSM {\n\t// build up submitSM\n\tsrcAddr := pdu.NewAddress()\n\tsrcAddr.SetTon(5)\n\tsrcAddr.SetNpi(0)\n\t_ = srcAddr.SetAddress(\"00\" + \"522241\")\n\n\tdestAddr := pdu.NewAddress()\n\tdestAddr.SetTon(1)\n\tdestAddr.SetNpi(1)\n\t_ = destAddr.SetAddress(\"99\" + \"522241\")\n\n\tsubmitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)\n\tsubmitSM.SourceAddr = srcAddr\n\tsubmitSM.DestAddr = destAddr\n\t_ = submitSM.Message.SetMessageWithEncoding(\"Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ\", data.UCS2)\n\tsubmitSM.ProtocolID = 0\n\tsubmitSM.RegisteredDelivery = 1\n\tsubmitSM.ReplaceIfPresentFlag = 0\n\tsubmitSM.EsmClass = 0\n\n\treturn submitSM\n}\n"
  },
  {
    "path": "example/transeiver_with_request_window_and_custom_submitSm/CustomSubmitSM.go",
    "content": "package main\n\nimport (\n\t\"math/rand\"\n\t\"strconv\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\n// CustomSubmitSM by embedding the PDU interface\n// and adding messageId as an extra field to SubmitSM\ntype CustomSubmitSM struct {\n\tpdu.PDU\n\tmessageId string\n}\n\n// newCustomSubmitSM returns CustomSubmitSM PDU.\n// Using rand.Int to generate new id for each CustomSubmitSM\nfunc newCustomSubmitSM() CustomSubmitSM {\n\treturn CustomSubmitSM{\n\t\tPDU:       newSubmitSM(),\n\t\tmessageId: strconv.Itoa(rand.Int()),\n\t}\n}\n"
  },
  {
    "path": "example/transeiver_with_request_window_and_custom_submitSm/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp\"\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\n\twg.Add(1)\n\tgo sendingAndReceiveSMS(&wg)\n\n\twg.Wait()\n}\n\nfunc sendingAndReceiveSMS(wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tauth := gosmpp.Auth{\n\t\tSMSC:       \"localhost:2775\",\n\t\tSystemID:   \"169994\",\n\t\tPassword:   \"EDXPJU\",\n\t\tSystemType: \"\",\n\t}\n\n\ttrans, err := gosmpp.NewSession(\n\t\tgosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),\n\t\tgosmpp.Settings{\n\t\t\tEnquireLink: 5 * time.Second,\n\n\t\t\tReadTimeout: 10 * time.Second,\n\n\t\t\tOnSubmitError: func(p pdu.PDU, err error) {\n\t\t\t\tif errors.Is(err, gosmpp.ErrWindowsFull) {\n\t\t\t\t\tlog.Println(\"SubmitPDU error: \", err)\n\t\t\t\t} else {\n\t\t\t\t\tlog.Fatal(\"SubmitPDU error: \", err)\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tfmt.Println(\"Receiving PDU/Network error: \", err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tfmt.Println(\"Rebinding but error: \", err)\n\t\t\t},\n\n\t\t\tOnClosed: func(state gosmpp.State) {\n\t\t\t\tfmt.Println(\"Bind has been closed: \", state.String())\n\t\t\t},\n\n\t\t\tWindowedRequestTracking: &gosmpp.WindowedRequestTracking{\n\t\t\t\tOnReceivedPduRequest:  handleReceivedPduRequest(),\n\t\t\t\tOnExpectedPduResponse: handleExpectedPduResponse(),\n\t\t\t\tOnExpiredPduRequest:   handleExpirePduRequest(),\n\t\t\t\tOnClosePduRequest:     handleOnClosePduRequest(),\n\t\t\t\tPduExpireTimeOut:      30 * time.Second,\n\t\t\t\tExpireCheckTimer:      10 * time.Second,\n\t\t\t\tMaxWindowSize:         30,\n\t\t\t\tStoreAccessTimeOut:    1 * time.Second,\n\t\t\t\tEnableAutoRespond:     false,\n\t\t\t},\n\t\t}, 5*time.Second)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\t// sending SMS(s)\n\tfor i := 0; i < 60; i++ {\n\t\tvar size int\n\t\tsize, err = trans.GetWindowSize()\n\t\tif err != nil {\n\t\t\tfmt.Println(\"could not get window size: \", err)\n\t\t} else {\n\t\t\tfmt.Println(\"Current window size: \", size)\n\t\t}\n\t\tp := newCustomSubmitSM()\n\t\tif err = trans.Transceiver().Submit(p); err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t\tfmt.Printf(\"Sent CustomSubmitSM with id: %+v\\n\", p.messageId)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\ttime.Sleep(1 * time.Second)\n}\n\nfunc handleExpirePduRequest() func(pdu.PDU) bool {\n\treturn func(p pdu.PDU) bool {\n\t\tswitch p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Printf(\"Expired Unbind :%+v\\n\", p)\n\t\t\tfmt.Println(\"Unbind Expired\")\n\n\t\tcase *pdu.SubmitSM:\n\t\t\tfmt.Printf(\"Expired SubmitSM :%+v\\n\", p)\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Printf(\"Expired EnquireLink:%+v\\n\", p)\n\t\t\treturn true // if the enquire_link expired, usually means the bind is stale\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Printf(\"Expired DataSM:%+v\\n\", p)\n\t\t}\n\t\treturn false\n\t}\n}\n\nfunc handleOnClosePduRequest() func(pdu.PDU) {\n\treturn func(p pdu.PDU) {\n\t\tswitch p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Printf(\"OnClose Unbind:%+v\\n\", p)\n\n\t\tcase *pdu.SubmitSM:\n\t\t\tfmt.Printf(\"OnClose SubmitSM:%+v\\n\", p)\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Printf(\"OnClose EnquireLink:%+v\\n\", p)\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Printf(\"OnClose DataSM:%+v\\n\", p)\n\t\t}\n\t}\n}\n\nfunc handleExpectedPduResponse() func(response gosmpp.Response) {\n\treturn func(response gosmpp.Response) {\n\t\tswitch response.PDU.(type) {\n\t\tcase *pdu.UnbindResp:\n\t\t\tfmt.Println(\"UnbindResp Received\")\n\t\t\tfmt.Printf(\"OriginalSM id:%+v\\n\", response.OriginalRequest.PDU)\n\n\t\tcase *pdu.SubmitSMResp:\n\t\t\tfmt.Printf(\"SubmitSMResp Received: %+v\\n\", response.PDU)\n\t\t\tfmt.Printf(\"OriginalSM SubmitSM:%+v\\n\", response.OriginalRequest.PDU)\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tfmt.Println(\"EnquireLinkResp Received\")\n\t\t\tfmt.Printf(\"Original EnquireLink:%+v\\n\", response.OriginalRequest.PDU)\n\n\t\t}\n\t}\n}\n\nfunc handleReceivedPduRequest() func(pdu.PDU) (pdu.PDU, bool) {\n\treturn func(p pdu.PDU) (pdu.PDU, bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Println(\"Unbind Received\")\n\t\t\treturn pd.GetResponse(), true\n\n\t\tcase *pdu.GenericNack:\n\t\t\tfmt.Println(\"GenericNack Received\")\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tfmt.Println(\"EnquireLinkResp Received\")\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tfmt.Println(\"EnquireLink Received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DataSM:\n\t\t\tfmt.Println(\"DataSM Received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DeliverSM:\n\t\t\tfmt.Println(\"DeliverSM Received\")\n\t\t\treturn pd.GetResponse(), false\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\nfunc newSubmitSM() *pdu.SubmitSM {\n\t// build up submitSM\n\tsrcAddr := pdu.NewAddress()\n\tsrcAddr.SetTon(5)\n\tsrcAddr.SetNpi(0)\n\t_ = srcAddr.SetAddress(\"00\" + \"522241\")\n\n\tdestAddr := pdu.NewAddress()\n\tdestAddr.SetTon(1)\n\tdestAddr.SetNpi(1)\n\t_ = destAddr.SetAddress(\"99\" + \"522241\")\n\n\tsubmitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)\n\tsubmitSM.SourceAddr = srcAddr\n\tsubmitSM.DestAddr = destAddr\n\t_ = submitSM.Message.SetMessageWithEncoding(\"Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ\", data.UCS2)\n\tsubmitSM.ProtocolID = 0\n\tsubmitSM.RegisteredDelivery = 1\n\tsubmitSM.ReplaceIfPresentFlag = 0\n\tsubmitSM.EsmClass = 0\n\n\treturn submitSM\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/linxGnu/gosmpp\n\ngo 1.24.0\n\nrequire (\n\tgithub.com/allegro/bigcache/v3 v3.1.0\n\tgithub.com/orcaman/concurrent-map/v2 v2.0.1\n\tgithub.com/stretchr/testify v1.11.1\n\tgolang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b\n\tgolang.org/x/text v0.33.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=\ngithub.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=\ngithub.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngolang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=\ngolang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "pdu/Address.go",
    "content": "package pdu\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// Address smpp address of src and dst.\ntype Address struct {\n\tton     byte\n\tnpi     byte\n\taddress string\n}\n\n// NewAddress returns new address with default max length.\nfunc NewAddress() Address {\n\treturn Address{ton: data.GetDefaultTon(), npi: data.GetDefaultNpi()}\n}\n\n// NewAddressWithAddr returns new address.\nfunc NewAddressWithAddr(addr string) (a Address, err error) {\n\ta = NewAddress()\n\terr = a.SetAddress(addr)\n\treturn\n}\n\n// NewAddressWithTonNpi returns new address with ton, npi.\nfunc NewAddressWithTonNpi(ton, npi byte) Address {\n\treturn Address{ton: ton, npi: npi}\n}\n\n// NewAddressWithTonNpiAddr returns new address with ton, npi, addr string.\nfunc NewAddressWithTonNpiAddr(ton, npi byte, addr string) (a Address, err error) {\n\ta = NewAddressWithTonNpi(ton, npi)\n\terr = a.SetAddress(addr)\n\treturn\n}\n\n// Unmarshal from buffer.\nfunc (c *Address) Unmarshal(b *ByteBuffer) (err error) {\n\tif c.ton, err = b.ReadByte(); err == nil {\n\t\tif c.npi, err = b.ReadByte(); err == nil {\n\t\t\tc.address, err = b.ReadCString()\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *Address) Marshal(b *ByteBuffer) {\n\tb.Grow(3 + len(c.address))\n\n\t_ = b.WriteByte(c.ton)\n\t_ = b.WriteByte(c.npi)\n\t_ = b.WriteCString(c.address)\n}\n\n// SetTon sets ton.\nfunc (c *Address) SetTon(ton byte) {\n\tc.ton = ton\n}\n\n// SetNpi sets npi.\nfunc (c *Address) SetNpi(npi byte) {\n\tc.npi = npi\n}\n\n// SetAddress sets address.\nfunc (c *Address) SetAddress(addr string) (err error) {\n\tif len(addr) > data.SM_ADDR_LEN {\n\t\terr = fmt.Errorf(\"Address len exceed limit. (%d > %d)\", len(addr), data.SM_ADDR_LEN)\n\t} else {\n\t\tc.address = addr\n\t}\n\treturn\n}\n\n// Ton returns assigned ton.\nfunc (c Address) Ton() byte {\n\treturn c.ton\n}\n\n// Npi returns assigned npi.\nfunc (c Address) Npi() byte {\n\treturn c.npi\n}\n\n// Address returns assigned address (in string).\nfunc (c Address) Address() string {\n\treturn c.address\n}\n\n// String implement stringer interface\nfunc (c Address) String() string {\n\treturn c.address\n}\n"
  },
  {
    "path": "pdu/AddressRange.go",
    "content": "package pdu\n\nimport \"github.com/linxGnu/gosmpp/data\"\n\n// AddressRange smpp address range of src and dst.\ntype AddressRange struct {\n\tTon          byte\n\tNpi          byte\n\tAddressRange string\n}\n\n// NewAddressRange create new AddressRange with default max length.\nfunc NewAddressRange() AddressRange {\n\treturn AddressRange{Ton: data.GetDefaultTon(), Npi: data.GetDefaultNpi()}\n}\n\n// NewAddressRangeWithAddr create new AddressRange.\nfunc NewAddressRangeWithAddr(addr string) AddressRange {\n\tar := NewAddressRange()\n\tar.AddressRange = addr\n\treturn ar\n}\n\n// NewAddressRangeWithTonNpi create new AddressRange with ton, npi.\nfunc NewAddressRangeWithTonNpi(ton, npi byte) AddressRange {\n\treturn AddressRange{Ton: ton, Npi: npi}\n}\n\n// NewAddressRangeWithTonNpiAddr returns new address with ton, npi, addr string.\nfunc NewAddressRangeWithTonNpiAddr(ton, npi byte, addr string) AddressRange {\n\tar := NewAddressRangeWithTonNpi(ton, npi)\n\tar.AddressRange = addr\n\treturn ar\n}\n\n// Unmarshal from buffer.\nfunc (c *AddressRange) Unmarshal(b *ByteBuffer) (err error) {\n\tif c.Ton, err = b.ReadByte(); err == nil {\n\t\tif c.Npi, err = b.ReadByte(); err == nil {\n\t\t\tc.AddressRange, err = b.ReadCString()\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *AddressRange) Marshal(b *ByteBuffer) {\n\tb.Grow(3 + len(c.AddressRange))\n\n\t_ = b.WriteByte(c.Ton)\n\t_ = b.WriteByte(c.Npi)\n\t_ = b.WriteCString(c.AddressRange)\n}\n"
  },
  {
    "path": "pdu/AddressRange_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestAddressRange(t *testing.T) {\n\tt.Run(\"new\", func(t *testing.T) {\n\t\ta := NewAddressRangeWithAddr(\"abc\")\n\t\trequire.Equal(t, \"abc\", a.AddressRange)\n\t})\n\n\tt.Run(\"newTonNpi\", func(t *testing.T) {\n\t\ta := NewAddressRangeWithTonNpi(3, 7)\n\t\ta.AddressRange = \"123456789\"\n\t\trequire.EqualValues(t, 3, a.Ton)\n\t\trequire.EqualValues(t, 7, a.Npi)\n\t\trequire.Equal(t, \"123456789\", a.AddressRange)\n\t})\n\n\tt.Run(\"newTonNpiAddr\", func(t *testing.T) {\n\t\ta := NewAddressRangeWithTonNpiAddr(3, 7, \"123456789\")\n\t\trequire.EqualValues(t, 3, a.Ton)\n\t\trequire.EqualValues(t, 7, a.Npi)\n\t\trequire.Equal(t, \"123456789\", a.AddressRange)\n\t})\n\n\tt.Run(\"unmarshal\", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"315b7068616e746f6d537472696b6500\"))\n\t\tvar a AddressRange\n\t\trequire.Nil(t, a.Unmarshal(buf))\n\t\trequire.Zero(t, buf.Len())\n\t\trequire.Equal(t, \"phantomStrike\", a.AddressRange)\n\t\trequire.EqualValues(t, 49, a.Ton)\n\t\trequire.EqualValues(t, 91, a.Npi)\n\t})\n\n\tt.Run(\"marshal\", func(t *testing.T) {\n\t\ta := AddressRange{}\n\t\ta.AddressRange = \"phantomOpera\"\n\t\ta.Ton = 95\n\t\ta.Npi = 13\n\n\t\tbuf := NewBuffer(nil)\n\t\ta.Marshal(buf)\n\n\t\trequire.Equal(t, fromHex(\"5f0d7068616e746f6d4f7065726100\"), buf.Bytes())\n\t})\n}\n"
  },
  {
    "path": "pdu/Address_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestAddress(t *testing.T) {\n\tt.Run(\"new\", func(t *testing.T) {\n\t\ta, err := NewAddressWithAddr(\"abc\")\n\t\trequire.Nil(t, err)\n\t\trequire.Equal(t, \"abc\", a.Address())\n\t})\n\n\tt.Run(\"newWithAddr\", func(t *testing.T) {\n\t\t_, err := NewAddressWithAddr(\"1234567890123456789012\")\n\t\trequire.NotNil(t, err)\n\t})\n\n\tt.Run(\"newTonNpi\", func(t *testing.T) {\n\t\ta := NewAddressWithTonNpi(3, 7)\n\t\trequire.Nil(t, a.SetAddress(\"123456789\"))\n\t\trequire.EqualValues(t, 3, a.Ton())\n\t\trequire.EqualValues(t, 7, a.Npi())\n\t\trequire.Equal(t, \"123456789\", a.Address())\n\t\ta.SetTon(11)\n\t\ta.SetNpi(19)\n\t\trequire.EqualValues(t, 11, a.Ton())\n\t\trequire.EqualValues(t, 19, a.Npi())\n\t})\n\n\tt.Run(\"newTonNpiAddr\", func(t *testing.T) {\n\t\ta, err := NewAddressWithTonNpiAddr(3, 7, \"123456789\")\n\t\trequire.Nil(t, err)\n\t\trequire.EqualValues(t, 3, a.Ton())\n\t\trequire.EqualValues(t, 7, a.Npi())\n\t\trequire.Equal(t, \"123456789\", a.Address())\n\t\ta.SetTon(11)\n\t\ta.SetNpi(19)\n\t\trequire.EqualValues(t, 11, a.Ton())\n\t\trequire.EqualValues(t, 19, a.Npi())\n\t})\n\n\tt.Run(\"unmarshal\", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"315b7068616e746f6d537472696b6500\"))\n\t\tvar a Address\n\t\trequire.Nil(t, a.Unmarshal(buf))\n\t\trequire.Zero(t, buf.Len())\n\t\trequire.Equal(t, \"phantomStrike\", a.Address())\n\t\trequire.EqualValues(t, 49, a.Ton())\n\t\trequire.EqualValues(t, 91, a.Npi())\n\t})\n\n\tt.Run(\"marshal\", func(t *testing.T) {\n\t\ta, err := NewAddressWithAddr(\"phantomOpera\")\n\t\trequire.Nil(t, err)\n\t\ta.SetTon(95)\n\t\ta.SetNpi(13)\n\n\t\tbuf := NewBuffer(nil)\n\t\ta.Marshal(buf)\n\n\t\trequire.Equal(t, fromHex(\"5f0d7068616e746f6d4f7065726100\"), buf.Bytes())\n\t})\n}\n"
  },
  {
    "path": "pdu/AlertNotification.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// AlertNotification PDU is sent by the SMSC to the ESME, when the SMSC has detected that\n// a particular mobile subscriber has become available and a delivery pending flag had been\n// set for that subscriber from a previous data_sm operation.\ntype AlertNotification struct {\n\tbase\n\tSourceAddr Address\n\tEsmeAddr   Address\n}\n\n// NewAlertNotification create new alert notification pdu.\nfunc NewAlertNotification() PDU {\n\ta := &AlertNotification{\n\t\tbase: newBase(),\n\t}\n\ta.CommandID = data.ALERT_NOTIFICATION\n\treturn a\n}\n\n// CanResponse implements PDU interface.\nfunc (a *AlertNotification) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (a *AlertNotification) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (a *AlertNotification) Marshal(b *ByteBuffer) {\n\ta.base.marshal(b, func(b *ByteBuffer) {\n\t\ta.SourceAddr.Marshal(b)\n\t\ta.EsmeAddr.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (a *AlertNotification) Unmarshal(b *ByteBuffer) error {\n\treturn a.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif err = a.SourceAddr.Unmarshal(b); err == nil {\n\t\t\terr = a.EsmeAddr.Unmarshal(b)\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/AlertNotification_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestAlertNotification(t *testing.T) {\n\ta := NewAlertNotification().(*AlertNotification)\n\trequire.False(t, a.CanResponse())\n\trequire.Nil(t, a.GetResponse())\n\n\t_ = a.SourceAddr.SetAddress(\"Alice\")\n\ta.SourceAddr.SetTon(13)\n\ta.SourceAddr.SetNpi(15)\n\t_ = a.EsmeAddr.SetAddress(\"Bob\")\n\ta.EsmeAddr.SetTon(19)\n\ta.EsmeAddr.SetNpi(7)\n\n\tb := NewBuffer(nil)\n\ta.Marshal(b)\n\n\texpectAfterParse(t, b, a, data.ALERT_NOTIFICATION)\n}\n"
  },
  {
    "path": "pdu/BindRequest.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// BindingType indicates type of binding.\ntype BindingType byte\n\nconst (\n\t// Receiver indicates Receiver binding.\n\tReceiver BindingType = iota\n\t// Transceiver indicates Transceiver binding.\n\tTransceiver\n\t// Transmitter indicate Transmitter binding.\n\tTransmitter\n)\n\n// BindRequest represents a bind request.\ntype BindRequest struct {\n\tbase\n\tSystemID         string\n\tPassword         string\n\tSystemType       string\n\tInterfaceVersion byte\n\tAddressRange     AddressRange\n\tBindingType      BindingType\n}\n\n// NewBindRequest returns new bind request.\nfunc NewBindRequest(t BindingType) (b *BindRequest) {\n\tb = &BindRequest{\n\t\tbase:             newBase(),\n\t\tBindingType:      t,\n\t\tSystemID:         data.DFLT_SYSID,\n\t\tPassword:         data.DFLT_PASS,\n\t\tSystemType:       data.DFLT_SYSTYPE,\n\t\tAddressRange:     AddressRange{},\n\t\tInterfaceVersion: data.SMPP_V34,\n\t}\n\n\tswitch t {\n\tcase Transceiver:\n\t\tb.CommandID = data.BIND_TRANSCEIVER\n\n\tcase Receiver:\n\t\tb.CommandID = data.BIND_RECEIVER\n\n\tcase Transmitter:\n\t\tb.CommandID = data.BIND_TRANSMITTER\n\t}\n\n\treturn\n}\n\n// NewBindTransmitter returns new bind transmitter pdu.\nfunc NewBindTransmitter() PDU {\n\treturn NewBindRequest(Transmitter)\n}\n\n// NewBindTransceiver returns new bind transceiver pdu.\nfunc NewBindTransceiver() PDU {\n\treturn NewBindRequest(Transceiver)\n}\n\n// NewBindReceiver returns new bind receiver pdu.\nfunc NewBindReceiver() PDU {\n\treturn NewBindRequest(Receiver)\n}\n\n// CanResponse implements PDU interface.\nfunc (b *BindRequest) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (b *BindRequest) GetResponse() PDU {\n\treturn NewBindResp(*b)\n}\n\n// Marshal implements PDU interface.\nfunc (b *BindRequest) Marshal(w *ByteBuffer) {\n\tb.base.marshal(w, func(w *ByteBuffer) {\n\t\tw.Grow(len(b.SystemID) + len(b.Password) + len(b.SystemType) + 4)\n\n\t\t_ = w.WriteCString(b.SystemID)\n\t\t_ = w.WriteCString(b.Password)\n\t\t_ = w.WriteCString(b.SystemType)\n\t\t_ = w.WriteByte(b.InterfaceVersion)\n\t\tb.AddressRange.Marshal(w)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (b *BindRequest) Unmarshal(w *ByteBuffer) error {\n\treturn b.base.unmarshal(w, func(w *ByteBuffer) (err error) {\n\t\tif b.SystemID, err = w.ReadCString(); err == nil {\n\t\t\tif b.Password, err = w.ReadCString(); err == nil {\n\t\t\t\tif b.SystemType, err = w.ReadCString(); err == nil {\n\t\t\t\t\tif b.InterfaceVersion, err = w.ReadByte(); err == nil {\n\t\t\t\t\t\terr = b.AddressRange.Unmarshal(w)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/BindRequest_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBindRequest(t *testing.T) {\n\tt.Run(\"receiver\", func(t *testing.T) {\n\t\treq := NewBindReceiver().(*BindRequest)\n\t\trequire.True(t, req.CanResponse())\n\t\treq.SequenceNumber = 13\n\n\t\tvalidate(t, req.GetResponse(), \"0000001180000001000000000000000d00\", data.BIND_RECEIVER_RESP)\n\n\t\treq.SystemID = \"system_id_fake\"\n\t\treq.Password = \"password\"\n\t\treq.SystemType = \"only\"\n\t\treq.InterfaceVersion = 44\n\t\treq.AddressRange.AddressRange = \"emptY\"\n\t\treq.AddressRange.Ton = 23\n\t\treq.AddressRange.Npi = 101\n\n\t\tvalidate(t,\n\t\t\treq,\n\t\t\t\"0000003600000001000000000000000d73797374656d5f69645f66616b650070617373776f7264006f6e6c79002c1765656d70745900\",\n\t\t\tdata.BIND_RECEIVER,\n\t\t)\n\t})\n\n\tt.Run(\"transmitter\", func(t *testing.T) {\n\t\treq := NewBindTransmitter().(*BindRequest)\n\t\trequire.True(t, req.CanResponse())\n\t\treq.SequenceNumber = 13\n\n\t\tvalidate(t, req.GetResponse(), \"0000001180000002000000000000000d00\", data.BIND_TRANSMITTER_RESP)\n\n\t\treq.SystemID = \"system_id_fake\"\n\t\treq.Password = \"password\"\n\t\treq.SystemType = \"only\"\n\t\treq.InterfaceVersion = 44\n\t\treq.AddressRange.AddressRange = \"emptY\"\n\t\treq.AddressRange.Ton = 23\n\t\treq.AddressRange.Npi = 101\n\n\t\tvalidate(t,\n\t\t\treq,\n\t\t\t\"0000003600000002000000000000000d73797374656d5f69645f66616b650070617373776f7264006f6e6c79002c1765656d70745900\",\n\t\t\tdata.BIND_TRANSMITTER,\n\t\t)\n\t})\n\n\tt.Run(\"transceiver\", func(t *testing.T) {\n\t\treq := NewBindTransceiver().(*BindRequest)\n\t\trequire.True(t, req.CanResponse())\n\t\treq.SequenceNumber = 13\n\n\t\tvalidate(t, req.GetResponse(), \"0000001180000009000000000000000d00\", data.BIND_TRANSCEIVER_RESP)\n\n\t\treq.SystemID = \"system_id_fake\"\n\t\treq.Password = \"password\"\n\t\treq.SystemType = \"only\"\n\t\treq.InterfaceVersion = 44\n\t\treq.AddressRange.AddressRange = \"emptY\"\n\t\treq.AddressRange.Ton = 23\n\t\treq.AddressRange.Npi = 101\n\n\t\tvalidate(t,\n\t\t\treq,\n\t\t\t\"0000003600000009000000000000000d73797374656d5f69645f66616b650070617373776f7264006f6e6c79002c1765656d70745900\",\n\t\t\tdata.BIND_TRANSCEIVER,\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "pdu/BindResponse.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// BindResp PDU.\ntype BindResp struct {\n\tbase\n\tSystemID string\n}\n\n// NewBindResp returns BindResp.\nfunc NewBindResp(req BindRequest) (c *BindResp) {\n\tc = &BindResp{\n\t\tbase: newBase(),\n\t}\n\tc.SequenceNumber = req.SequenceNumber\n\n\tswitch req.BindingType {\n\tcase Transceiver:\n\t\tc.CommandID = data.BIND_TRANSCEIVER_RESP\n\n\tcase Receiver:\n\t\tc.CommandID = data.BIND_RECEIVER_RESP\n\n\tcase Transmitter:\n\t\tc.CommandID = data.BIND_TRANSMITTER_RESP\n\t}\n\n\treturn\n}\n\n// NewBindTransmitterResp returns new bind transmitter resp.\nfunc NewBindTransmitterResp() PDU {\n\tc := &BindResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.BIND_TRANSMITTER_RESP\n\treturn c\n}\n\n// NewBindTransceiverResp returns new bind transceiver resp.\nfunc NewBindTransceiverResp() PDU {\n\tc := &BindResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.BIND_TRANSCEIVER_RESP\n\treturn c\n}\n\n// NewBindReceiverResp returns new bind receiver resp.\nfunc NewBindReceiverResp() PDU {\n\tc := &BindResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.BIND_RECEIVER_RESP\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *BindResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *BindResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *BindResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(w *ByteBuffer) {\n\t\tw.Grow(len(c.SystemID) + 1)\n\n\t\t_ = w.WriteCString(c.SystemID)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *BindResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(w *ByteBuffer) (err error) {\n\t\tif c.CommandID == data.BIND_TRANSCEIVER_RESP || c.CommandStatus == data.ESME_ROK {\n\t\t\tc.SystemID, err = w.ReadCString()\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/BindResponse_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBindResponse(t *testing.T) {\n\tt.Run(\"receiver\", func(t *testing.T) {\n\t\tv := NewBindReceiverResp().(*BindResp)\n\t\trequire.False(t, v.CanResponse())\n\t\trequire.Nil(t, v.GetResponse())\n\t\tv.SequenceNumber = 13\n\n\t\tv.SystemID = \"system_id_fake\"\n\n\t\tvalidate(t,\n\t\t\tv,\n\t\t\t\"0000001f80000001000000000000000d73797374656d5f69645f66616b6500\",\n\t\t\tdata.BIND_RECEIVER_RESP,\n\t\t)\n\t})\n\n\tt.Run(\"transmitter\", func(t *testing.T) {\n\t\tv := NewBindTransmitterResp().(*BindResp)\n\t\trequire.False(t, v.CanResponse())\n\t\trequire.Nil(t, v.GetResponse())\n\t\tv.SequenceNumber = 13\n\n\t\tv.SystemID = \"system_id_fake\"\n\n\t\tvalidate(t,\n\t\t\tv,\n\t\t\t\"0000001f80000002000000000000000d73797374656d5f69645f66616b6500\",\n\t\t\tdata.BIND_TRANSMITTER_RESP,\n\t\t)\n\t})\n\n\tt.Run(\"transceiver\", func(t *testing.T) {\n\t\tv := NewBindTransceiverResp().(*BindResp)\n\t\trequire.False(t, v.CanResponse())\n\t\trequire.Nil(t, v.GetResponse())\n\t\tv.SequenceNumber = 13\n\n\t\tv.SystemID = \"system_id_fake\"\n\n\t\tvalidate(t,\n\t\t\tv,\n\t\t\t\"0000001f80000009000000000000000d73797374656d5f69645f66616b6500\",\n\t\t\tdata.BIND_TRANSCEIVER_RESP,\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "pdu/Buffer.go",
    "content": "package pdu\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\nconst (\n\t// SizeByte is size of byte.\n\tSizeByte = 1\n\n\t// SizeShort is size of short.\n\tSizeShort = 2\n\n\t// SizeInt is size of int.\n\tSizeInt = 4\n\n\t// SizeLong is size of long.\n\tSizeLong = 8\n)\n\nvar (\n\t// ErrBufferNotEnoughByteToRead indicates not enough byte(s) to read from buffer.\n\tErrBufferNotEnoughByteToRead = fmt.Errorf(\"Not enough byte to read from buffer\")\n\n\tendianese = binary.BigEndian\n)\n\n// ByteBuffer wraps over bytes.Buffer with additional features.\ntype ByteBuffer struct {\n\t*bytes.Buffer\n}\n\n// NewBuffer create new buffer from preallocated buffer array.\nfunc NewBuffer(inp []byte) *ByteBuffer {\n\tif inp == nil {\n\t\tinp = make([]byte, 0, 512)\n\t}\n\treturn &ByteBuffer{Buffer: bytes.NewBuffer(inp)}\n}\n\n// ReadN read n-bytes from buffer.\nfunc (c *ByteBuffer) ReadN(n int) (r []byte, err error) {\n\tif n > 0 {\n\t\tif c.Len() >= n { // optimistic branching\n\t\t\tr = make([]byte, n)\n\t\t\t_, _ = c.Read(r)\n\t\t} else {\n\t\t\terr = ErrBufferNotEnoughByteToRead\n\t\t}\n\t}\n\treturn\n}\n\n// ReadShort reads short from buffer.\nfunc (c *ByteBuffer) ReadShort() (r int16, err error) {\n\tv, err := c.ReadN(SizeShort)\n\tif err == nil {\n\t\tr = int16(endianese.Uint16(v))\n\t}\n\treturn\n}\n\n// WriteShort writes short to buffer.\nfunc (c *ByteBuffer) WriteShort(v int16) {\n\tvar b [SizeShort]byte\n\tendianese.PutUint16(b[:], uint16(v))\n\t_, _ = c.Write(b[:])\n}\n\n// ReadInt reads int from buffer.\nfunc (c *ByteBuffer) ReadInt() (r int32, err error) {\n\tv, err := c.ReadN(SizeInt)\n\tif err == nil {\n\t\tr = int32(endianese.Uint32(v))\n\t}\n\treturn\n}\n\n// WriteInt writes int to buffer.\nfunc (c *ByteBuffer) WriteInt(v int32) {\n\tvar b [SizeInt]byte\n\tendianese.PutUint32(b[:], uint32(v))\n\t_, _ = c.Write(b[:])\n}\n\n// WriteBuffer appends buffer.\nfunc (c *ByteBuffer) WriteBuffer(d *ByteBuffer) {\n\tif d != nil {\n\t\t_, _ = c.Write(d.Bytes())\n\t}\n}\n\nfunc (c *ByteBuffer) writeString(st string, isCString bool, enc data.Encoding) (err error) {\n\tif len(st) > 0 {\n\t\tvar payload []byte\n\t\tif payload, err = enc.Encode(st); err == nil {\n\t\t\t_, _ = c.Write(payload)\n\t\t}\n\t}\n\n\tif err == nil && isCString {\n\t\t_ = c.WriteByte(0)\n\t}\n\n\treturn\n}\n\n// WriteCString writes c-string.\nfunc (c *ByteBuffer) WriteCString(s string) error {\n\treturn c.writeString(s, true, data.ASCII)\n}\n\n// WriteCStringWithEnc write c-string with encoding.\nfunc (c *ByteBuffer) WriteCStringWithEnc(s string, enc data.Encoding) error {\n\treturn c.writeString(s, true, enc)\n}\n\n// ReadCString read c-string.\nfunc (c *ByteBuffer) ReadCString() (st string, err error) {\n\tbuf, err := c.ReadBytes(0)\n\tif err == nil && len(buf) > 0 { // optimistic branching\n\t\tst = string(buf[:len(buf)-1])\n\t}\n\treturn\n}\n\n// HexDump returns hex dump.\nfunc (c *ByteBuffer) HexDump() string {\n\treturn fmt.Sprintf(\"%x\", c.Buffer.Bytes())\n}\n"
  },
  {
    "path": "pdu/Buffer_test.go",
    "content": "package pdu\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBuffer(t *testing.T) {\n\tb := NewBuffer(nil)\n\trequire.Nil(t, b.WriteCStringWithEnc(\"agjwklgjkwPץ\", data.HEBREW))\n\trequire.Equal(t, \"61676A776B6C676A6B7750F500\", strings.ToUpper(b.HexDump()))\n}\n"
  },
  {
    "path": "pdu/CancelSM.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// CancelSM PDU is issued by the ESME to cancel one or more previously submitted short messages\n// that are still pending delivery. The command may specify a particular message to cancel, or\n// all messages for a particular source, destination and service_type are to be cancelled.\ntype CancelSM struct {\n\tbase\n\tServiceType string\n\tMessageID   string\n\tSourceAddr  Address\n\tDestAddr    Address\n}\n\n// NewCancelSM returns CancelSM PDU.\nfunc NewCancelSM() PDU {\n\tc := &CancelSM{\n\t\tbase:        newBase(),\n\t\tServiceType: data.DFLT_SRVTYPE,\n\t\tMessageID:   data.DFLT_MSGID,\n\t\tSourceAddr:  NewAddress(),\n\t\tDestAddr:    NewAddress(),\n\t}\n\tc.CommandID = data.CANCEL_SM\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *CancelSM) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *CancelSM) GetResponse() PDU {\n\treturn NewCancelSMRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *CancelSM) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.ServiceType) + len(c.MessageID) + 2)\n\n\t\t_ = b.WriteCString(c.ServiceType)\n\t\t_ = b.WriteCString(c.MessageID)\n\t\tc.SourceAddr.Marshal(b)\n\t\tc.DestAddr.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *CancelSM) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.ServiceType, err = b.ReadCString(); err == nil {\n\t\t\tif c.MessageID, err = b.ReadCString(); err == nil {\n\t\t\t\tif err = c.SourceAddr.Unmarshal(b); err == nil {\n\t\t\t\t\terr = c.DestAddr.Unmarshal(b)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/CancelSMResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// CancelSMResp PDU.\ntype CancelSMResp struct {\n\tbase\n}\n\n// NewCancelSMResp returns CancelSMResp.\nfunc NewCancelSMResp() PDU {\n\tc := &CancelSMResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.CANCEL_SM_RESP\n\treturn c\n}\n\n// NewCancelSMRespFromReq returns CancelSMResp.\nfunc NewCancelSMRespFromReq(req *CancelSM) PDU {\n\tc := NewCancelSMResp().(*CancelSMResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *CancelSMResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *CancelSMResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *CancelSMResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *CancelSMResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/CancelSMResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCancelSMResp(t *testing.T) {\n\treq := NewCancelSM().(*CancelSM)\n\treq.SequenceNumber = 11\n\n\tv := NewCancelSMRespFromReq(req).(*CancelSMResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001080000008000000000000000b\",\n\t\tdata.CANCEL_SM_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/CancelSM_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCancelSM(t *testing.T) {\n\tv := NewCancelSM().(*CancelSM)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001080000008000000000000000d\",\n\t\tdata.CANCEL_SM_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\tv.MessageID = \"def\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\t_ = v.DestAddr.SetAddress(\"Bobo\")\n\tv.DestAddr.SetTon(30)\n\tv.DestAddr.SetNpi(31)\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000002800000008000000000000000d61626300646566001c1d416c69636572001e1f426f626f00\",\n\t\tdata.CANCEL_SM,\n\t)\n}\n"
  },
  {
    "path": "pdu/DataSM.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// DataSM PDU is used to transfer data between the SMSC and the ESME.\n// It may be used by both the ESME and SMSC.\ntype DataSM struct {\n\tbase\n\tServiceType        string\n\tSourceAddr         Address\n\tDestAddr           Address\n\tEsmClass           byte\n\tRegisteredDelivery byte\n\tDataCoding         byte\n}\n\n// NewDataSM returns new data sm pdu.\nfunc NewDataSM() PDU {\n\tc := &DataSM{\n\t\tbase:               newBase(),\n\t\tServiceType:        data.DFLT_SRVTYPE,\n\t\tSourceAddr:         NewAddress(),\n\t\tDestAddr:           NewAddress(),\n\t\tEsmClass:           data.DFLT_ESM_CLASS,\n\t\tRegisteredDelivery: data.DFLT_REG_DELIVERY,\n\t\tDataCoding:         data.DFLT_DATA_CODING,\n\t}\n\tc.CommandID = data.DATA_SM\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *DataSM) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *DataSM) GetResponse() PDU {\n\treturn NewDataSMRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *DataSM) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.ServiceType) + 4)\n\n\t\t_ = b.WriteCString(c.ServiceType)\n\t\tc.SourceAddr.Marshal(b)\n\t\tc.DestAddr.Marshal(b)\n\t\t_ = b.WriteByte(c.EsmClass)\n\t\t_ = b.WriteByte(c.RegisteredDelivery)\n\t\t_ = b.WriteByte(c.DataCoding)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *DataSM) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.ServiceType, err = b.ReadCString(); err == nil {\n\t\t\tif err = c.SourceAddr.Unmarshal(b); err == nil {\n\t\t\t\tif err = c.DestAddr.Unmarshal(b); err == nil {\n\t\t\t\t\tif c.EsmClass, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\tif c.RegisteredDelivery, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\tc.DataCoding, err = b.ReadByte()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/DataSMResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// DataSMResp PDU.\ntype DataSMResp struct {\n\tbase\n\tMessageID string\n}\n\n// NewDataSMResp returns DataSMResp.\nfunc NewDataSMResp() PDU {\n\tc := &DataSMResp{\n\t\tbase:      newBase(),\n\t\tMessageID: data.DFLT_MSGID,\n\t}\n\tc.CommandID = data.DATA_SM_RESP\n\treturn c\n}\n\n// NewDataSMRespFromReq returns DataSMResp.\nfunc NewDataSMRespFromReq(req *DataSM) PDU {\n\tc := NewDataSMResp().(*DataSMResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *DataSMResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *DataSMResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *DataSMResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + 1)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *DataSMResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tc.MessageID, err = b.ReadCString()\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/DataSMResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDataSMResp(t *testing.T) {\n\treq := NewDataSM().(*DataSM)\n\treq.SequenceNumber = 13\n\n\tv := NewDataSMRespFromReq(req).(*DataSMResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tv.MessageID = \"testMID\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001880000103000000000000000d746573744d494400\",\n\t\tdata.DATA_SM_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/DataSM_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDataSM(t *testing.T) {\n\tv := NewDataSM().(*DataSM)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001180000103000000000000000d00\",\n\t\tdata.DATA_SM_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\t_ = v.DestAddr.SetAddress(\"Bobo\")\n\tv.DestAddr.SetTon(30)\n\tv.DestAddr.SetNpi(31)\n\tv.EsmClass = 77\n\tv.RegisteredDelivery = 83\n\tv.DataCoding = 91\n\tv.RegisterOptionalParam(Field{Tag: TagDestBearerType, Data: []byte{95}})\n\n\ttagged, ok := v.OptionalParameters[TagDestBearerType]\n\trequire.True(t, ok)\n\trequire.Equal(t, TagDestBearerType, tagged.Tag)\n\trequire.Equal(t, []byte{95}, tagged.Data)\n\trequire.Equal(t, \"_\", tagged.String())\n\trequire.Equal(t, \"0007\", tagged.Tag.Hex())\n\ttagged.Data = []byte{95, 0}\n\trequire.Equal(t, \"_\", tagged.String())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000002c00000103000000000000000d616263001c1d416c69636572001e1f426f626f004d535b000700015f\",\n\t\tdata.DATA_SM,\n\t)\n}\n"
  },
  {
    "path": "pdu/DeliverSM.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// DeliverSM PDU is issued by the SMSC to send a message to an ESME.\n// Using this command, the SMSC may route a short message to the ESME for delivery.\ntype DeliverSM struct {\n\tbase\n\tServiceType          string\n\tSourceAddr           Address\n\tDestAddr             Address\n\tEsmClass             byte\n\tProtocolID           byte\n\tPriorityFlag         byte\n\tScheduleDeliveryTime string // not used\n\tValidityPeriod       string // not used\n\tRegisteredDelivery   byte\n\tReplaceIfPresentFlag byte // not used\n\tMessage              ShortMessage\n}\n\n// NewDeliverSM returns DeliverSM PDU.\nfunc NewDeliverSM() PDU {\n\tmessage, _ := NewShortMessage(\"\")\n\tc := &DeliverSM{\n\t\tbase:                 newBase(),\n\t\tServiceType:          data.DFLT_SRVTYPE,\n\t\tSourceAddr:           NewAddress(),\n\t\tDestAddr:             NewAddress(),\n\t\tEsmClass:             data.DFLT_ESM_CLASS,\n\t\tProtocolID:           data.DFLT_PROTOCOLID,\n\t\tPriorityFlag:         data.DFLT_PRIORITY_FLAG,\n\t\tScheduleDeliveryTime: data.DFLT_SCHEDULE,\n\t\tValidityPeriod:       data.DFLT_VALIDITY,\n\t\tRegisteredDelivery:   data.DFLT_REG_DELIVERY,\n\t\tReplaceIfPresentFlag: data.DFTL_REPLACE_IFP,\n\t\tMessage:              message,\n\t}\n\tc.CommandID = data.DELIVER_SM\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *DeliverSM) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *DeliverSM) GetResponse() PDU {\n\treturn NewDeliverSMRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *DeliverSM) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.ServiceType) + len(c.ScheduleDeliveryTime) + len(c.ValidityPeriod) + 10)\n\n\t\t_ = b.WriteCString(c.ServiceType)\n\t\tc.SourceAddr.Marshal(b)\n\t\tc.DestAddr.Marshal(b)\n\t\t_ = b.WriteByte(c.EsmClass)\n\t\t_ = b.WriteByte(c.ProtocolID)\n\t\t_ = b.WriteByte(c.PriorityFlag)\n\t\t_ = b.WriteCString(c.ScheduleDeliveryTime)\n\t\t_ = b.WriteCString(c.ValidityPeriod)\n\t\t_ = b.WriteByte(c.RegisteredDelivery)\n\t\t_ = b.WriteByte(c.ReplaceIfPresentFlag)\n\t\tc.Message.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *DeliverSM) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.ServiceType, err = b.ReadCString(); err == nil {\n\t\t\tif err = c.SourceAddr.Unmarshal(b); err == nil {\n\t\t\t\tif err = c.DestAddr.Unmarshal(b); err == nil {\n\t\t\t\t\tif c.EsmClass, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\tif c.ProtocolID, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\tif c.PriorityFlag, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\tif c.ScheduleDeliveryTime, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\t\t\t\tif c.ValidityPeriod, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\t\t\t\t\tif c.RegisteredDelivery, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\t\t\t\tif c.ReplaceIfPresentFlag, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\t\t\t\t\terr = c.Message.Unmarshal(b, (c.EsmClass&data.SM_UDH_GSM) > 0)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/DeliverSMResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// DeliverSMResp PDU.\ntype DeliverSMResp struct {\n\tbase\n\tMessageID string\n}\n\n// NewDeliverSMResp returns new DeliverSMResp.\nfunc NewDeliverSMResp() PDU {\n\tc := &DeliverSMResp{\n\t\tbase:      newBase(),\n\t\tMessageID: data.DFLT_MSGID,\n\t}\n\tc.CommandID = data.DELIVER_SM_RESP\n\treturn c\n}\n\n// NewDeliverSMRespFromReq returns new DeliverSMResp.\nfunc NewDeliverSMRespFromReq(req *DeliverSM) PDU {\n\tc := NewDeliverSMResp().(*DeliverSMResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *DeliverSMResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *DeliverSMResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *DeliverSMResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + 1)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *DeliverSMResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tc.MessageID, err = b.ReadCString()\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/DeliverSMResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDeliverSMResp(t *testing.T) {\n\treq := NewDeliverSM().(*DeliverSM)\n\treq.SequenceNumber = 13\n\n\tv := NewDeliverSMRespFromReq(req).(*DeliverSMResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tv.MessageID = \"testMID\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001880000005000000000000000d746573744d494400\",\n\t\tdata.DELIVER_SM_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/DeliverSM_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDeliverSM(t *testing.T) {\n\tv := NewDeliverSM().(*DeliverSM)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001180000005000000000000000d00\",\n\t\tdata.DELIVER_SM_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\t_ = v.DestAddr.SetAddress(\"Bobo\")\n\tv.DestAddr.SetTon(30)\n\tv.DestAddr.SetNpi(31)\n\tv.EsmClass = 13\n\tv.ProtocolID = 99\n\tv.PriorityFlag = 61\n\tv.RegisteredDelivery = 83\n\t_ = v.Message.SetMessageWithEncoding(\"nghắ nghiêng nghiễng ngả\", data.UCS2)\n\tv.Message.message = \"\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000005e00000005000000000000000d616263001c1d416c69636572001e1f426f626f000d633d00005300080030006e006700681eaf0020006e00670068006900ea006e00670020006e0067006800691ec5006e00670020006e00671ea3\",\n\t\tdata.DELIVER_SM,\n\t)\n}\n\nfunc TestDeliverSMwithUDH(t *testing.T) {\n\tv := NewDeliverSM().(*DeliverSM)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001180000005000000000000000d00\",\n\t\tdata.DELIVER_SM_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\t_ = v.DestAddr.SetAddress(\"Bobo\")\n\tv.DestAddr.SetTon(30)\n\tv.DestAddr.SetNpi(31)\n\tv.EsmClass = 77\n\tv.ProtocolID = 99\n\tv.PriorityFlag = 61\n\tv.RegisteredDelivery = 83\n\t_ = v.Message.SetMessageWithEncoding(\"nghắ nghiêng nghiễng ngả\", data.UCS2)\n\tv.Message.message = \"\"\n\tv.Message.SetUDH(UDH{NewIEConcatMessage(2, 1, 254)})\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000006400000005000000000000000d616263001c1d416c69636572001e1f426f626f004d633d00005300080036050003fe0201006e006700681eaf0020006e00670068006900ea006e00670020006e0067006800691ec5006e00670020006e00671ea3\",\n\t\tdata.DELIVER_SM,\n\t)\n}\n"
  },
  {
    "path": "pdu/DestinationAddress.go",
    "content": "package pdu\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// DestinationAddress represents Address or Distribution List based on destination flag.\ntype DestinationAddress struct {\n\tdestFlag byte\n\taddress  Address\n\tdl       DistributionList\n}\n\n// NewDestinationAddress returns new DestinationAddress.\nfunc NewDestinationAddress() (c DestinationAddress) {\n\tc.destFlag = data.DFLT_DEST_FLAG\n\treturn\n}\n\n// Unmarshal from buffer.\nfunc (c *DestinationAddress) Unmarshal(b *ByteBuffer) (err error) {\n\tif c.destFlag, err = b.ReadByte(); err == nil {\n\t\tswitch c.destFlag {\n\n\t\tcase data.SM_DEST_SME_ADDRESS:\n\t\t\terr = c.address.Unmarshal(b)\n\n\t\tcase data.SM_DEST_DL_NAME:\n\t\t\terr = c.dl.Unmarshal(b)\n\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"Unrecognize dest_flag %d\", c.destFlag)\n\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *DestinationAddress) Marshal(b *ByteBuffer) {\n\tswitch c.destFlag {\n\tcase data.SM_DEST_DL_NAME:\n\t\t_ = b.WriteByte(data.SM_DEST_DL_NAME)\n\t\tc.dl.Marshal(b)\n\n\tdefault:\n\t\t_ = b.WriteByte(data.SM_DEST_SME_ADDRESS)\n\t\tc.address.Marshal(b)\n\t}\n}\n\n// Address returns underlying Address.\nfunc (c *DestinationAddress) Address() Address {\n\treturn c.address\n}\n\n// DistributionList returns underlying DistributionList.\nfunc (c *DestinationAddress) DistributionList() DistributionList {\n\treturn c.dl\n}\n\n// SetAddress marks DistributionAddress as a SME Address and assign.\nfunc (c *DestinationAddress) SetAddress(addr Address) {\n\tc.destFlag = data.SM_DEST_SME_ADDRESS\n\tc.address = addr\n}\n\n// SetDistributionList marks DistributionAddress as a DistributionList and assign.\nfunc (c *DestinationAddress) SetDistributionList(list DistributionList) {\n\tc.destFlag = data.SM_DEST_DL_NAME\n\tc.dl = list\n}\n\n// HasValue returns true if underlying DistributionList/Address is assigned.\nfunc (c *DestinationAddress) HasValue() bool {\n\treturn c.destFlag != data.DFLT_DEST_FLAG\n}\n\n// IsAddress returns true if DestinationAddress is a SME Address.\nfunc (c *DestinationAddress) IsAddress() bool {\n\treturn c.destFlag == data.SM_DEST_SME_ADDRESS\n}\n\n// IsDistributionList returns true if DestinationAddress is a DistributionList.\nfunc (c *DestinationAddress) IsDistributionList() bool {\n\treturn c.destFlag == byte(data.SM_DEST_DL_NAME)\n}\n\n// DestinationAddresses represents list of DestinationAddress.\ntype DestinationAddresses struct {\n\tl []DestinationAddress\n}\n\n// NewDestinationAddresses returns list of DestinationAddress.\nfunc NewDestinationAddresses() (u DestinationAddresses) {\n\tu.l = make([]DestinationAddress, 0, 8)\n\treturn\n}\n\n// Add to list.\nfunc (c *DestinationAddresses) Add(addresses ...DestinationAddress) {\n\tc.l = append(c.l, addresses...)\n}\n\n// Get list.\nfunc (c *DestinationAddresses) Get() []DestinationAddress {\n\treturn c.l\n}\n\n// Unmarshal from buffer.\nfunc (c *DestinationAddresses) Unmarshal(b *ByteBuffer) (err error) {\n\tvar n byte\n\tif n, err = b.ReadByte(); err == nil {\n\t\tc.l = make([]DestinationAddress, n)\n\n\t\tvar i byte\n\t\tfor ; i < n; i++ {\n\t\t\tif err = c.l[i].Unmarshal(b); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *DestinationAddresses) Marshal(b *ByteBuffer) {\n\tn := byte(len(c.l))\n\t_ = b.WriteByte(n)\n\n\tvar i byte\n\tfor ; i < n; i++ {\n\t\tc.l[i].Marshal(b)\n\t}\n}\n"
  },
  {
    "path": "pdu/DestinationAddress_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDestinationAddress(t *testing.T) {\n\tt.Run(\"validDESTAddr\", func(t *testing.T) {\n\t\taddr := NewAddress()\n\t\trequire.Nil(t, addr.SetAddress(\"Bob1\"))\n\t\td1 := NewDestinationAddress()\n\t\td1.SetAddress(addr)\n\t\trequire.EqualValues(t, data.SM_DEST_SME_ADDRESS, d1.destFlag)\n\t\trequire.True(t, d1.IsAddress())\n\t\trequire.False(t, d1.IsDistributionList())\n\t\trequire.True(t, d1.HasValue())\n\t\trequire.Equal(t, \"Bob1\", d1.Address().Address())\n\t\trequire.Equal(t, \"\", d1.DistributionList().Name())\n\n\t\tdl, err := NewDistributionList(\"List1\")\n\t\trequire.Nil(t, err)\n\t\td2 := NewDestinationAddress()\n\t\td2.SetDistributionList(dl)\n\t\trequire.EqualValues(t, data.SM_DEST_DL_NAME, d2.destFlag)\n\t\trequire.False(t, d2.IsAddress())\n\t\trequire.True(t, d2.IsDistributionList())\n\t\trequire.True(t, d2.HasValue())\n\t\trequire.Equal(t, \"\", d2.Address().Address())\n\t\trequire.Equal(t, \"List1\", d2.DistributionList().Name())\n\t})\n\n\tt.Run(\"invalidDEST\", func(t *testing.T) {\n\t\tbuf := NewBuffer(nil)\n\t\t_ = buf.WriteByte(51)\n\t\tvar d DestinationAddress\n\t\trequire.NotNil(t, d.Unmarshal(buf))\n\t})\n\n\tt.Run(\"invalidDESTs\", func(t *testing.T) {\n\t\tbuf := NewBuffer(nil)\n\t\t_ = buf.WriteByte(1)\n\t\t_ = buf.WriteByte(51)\n\t\tvar d DestinationAddresses\n\t\trequire.NotNil(t, d.Unmarshal(buf))\n\t})\n}\n"
  },
  {
    "path": "pdu/DistributionList.go",
    "content": "package pdu\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// DistributionList represents group of contacts.\ntype DistributionList struct {\n\tname string\n}\n\n// NewDistributionList returns a new DistributionList.\nfunc NewDistributionList(name string) (c DistributionList, err error) {\n\terr = c.SetName(name)\n\treturn\n}\n\n// Unmarshal from buffer.\nfunc (c *DistributionList) Unmarshal(b *ByteBuffer) (err error) {\n\tc.name, err = b.ReadCString()\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *DistributionList) Marshal(b *ByteBuffer) {\n\tb.Grow(1 + len(c.name))\n\n\t_ = b.WriteCString(c.name)\n}\n\n// SetName sets DistributionList name.\nfunc (c *DistributionList) SetName(name string) (err error) {\n\tif len(name) > data.SM_DL_NAME_LEN {\n\t\terr = fmt.Errorf(\"Distribution List name exceed limit. (%d > %d)\", len(name), data.SM_DL_NAME_LEN)\n\t} else {\n\t\tc.name = name\n\t}\n\treturn\n}\n\n// Name returns name of DistributionList\nfunc (c DistributionList) Name() string {\n\treturn c.name\n}\n"
  },
  {
    "path": "pdu/DistributionList_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDistributionList(t *testing.T) {\n\t_, err := NewDistributionList(\"1234567890123456789012\")\n\trequire.NotNil(t, err)\n}\n"
  },
  {
    "path": "pdu/EnquireLink.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// EnquireLink PDU. This message can be sent by either the ESME or SMSC\n// and is used to provide a confidence- check of the communication path between\n// an ESME and an SMSC. On receipt of this request the receiving party should\n// respond with an enquire_link_resp, thus verifying that the application\n// level connection between the SMSC and the ESME is functioning.\n// The ESME may also respond by sending any valid SMPP primitive.\ntype EnquireLink struct {\n\tbase\n}\n\n// NewEnquireLink returns new EnquireLink PDU.\nfunc NewEnquireLink() PDU {\n\tc := &EnquireLink{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.ENQUIRE_LINK\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *EnquireLink) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *EnquireLink) GetResponse() PDU {\n\treturn NewEnquireLinkRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *EnquireLink) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *EnquireLink) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/EnquireLinkResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// EnquireLinkResp PDU.\ntype EnquireLinkResp struct {\n\tbase\n}\n\n// NewEnquireLinkResp returns EnquireLinkResp.\nfunc NewEnquireLinkResp() PDU {\n\tc := &EnquireLinkResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.ENQUIRE_LINK_RESP\n\treturn c\n}\n\n// NewEnquireLinkRespFromReq returns EnquireLinkResp.\nfunc NewEnquireLinkRespFromReq(req *EnquireLink) PDU {\n\tc := NewEnquireLinkResp().(*EnquireLinkResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *EnquireLinkResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *EnquireLinkResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *EnquireLinkResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *EnquireLinkResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/EnquireLinkResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestEnquireLinkResp(t *testing.T) {\n\treq := NewEnquireLink().(*EnquireLink)\n\treq.SequenceNumber = 13\n\n\tv := NewEnquireLinkRespFromReq(req).(*EnquireLinkResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001080000015000000000000000d\",\n\t\tdata.ENQUIRE_LINK_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/EnquireLink_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestEnquireLink(t *testing.T) {\n\tv := NewEnquireLink().(*EnquireLink)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001080000015000000000000000d\",\n\t\tdata.ENQUIRE_LINK_RESP,\n\t)\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001000000015000000000000000d\",\n\t\tdata.ENQUIRE_LINK,\n\t)\n}\n"
  },
  {
    "path": "pdu/GenericNack.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// GenericNack PDU is a generic negative acknowledgement to an SMPP PDU submitted\n// with an invalid message header. A generic_nack response is returned in the following cases:\n//\n// - Invalid command_length\n//   If the receiving SMPP entity, on decoding an SMPP PDU, detects an invalid command_length\n//   (either too short or too long), it should assume that the data is corrupt. In such cases\n//   a generic_nack PDU must be returned to the message originator.\n//\n// - Unknown command_id\n//   If an unknown or invalid command_id is received, a generic_nack PDU must also be returned to the originator.\ntype GenericNack struct {\n\tbase\n}\n\n// NewGenericNack returns new GenericNack PDU.\nfunc NewGenericNack() PDU {\n\tc := &GenericNack{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.GENERIC_NACK\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *GenericNack) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *GenericNack) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *GenericNack) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *GenericNack) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/GenericNack_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestGNack(t *testing.T) {\n\tv := NewGenericNack().(*GenericNack)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\trequire.True(t, v.IsGNack())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001080000000000000000000000d\",\n\t\tdata.GENERIC_NACK,\n\t)\n}\n"
  },
  {
    "path": "pdu/Outbind.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// Outbind PDU is used by the SMSC to signal an ESME to originate a bind_receiver request to the SMSC.\ntype Outbind struct {\n\tbase\n\tSystemID string\n\tPassword string\n}\n\n// NewOutbind returns Outbind PDU.\nfunc NewOutbind() PDU {\n\tc := &Outbind{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.OUTBIND\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *Outbind) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *Outbind) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *Outbind) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.SystemID) + len(c.Password) + 2)\n\n\t\t_ = b.WriteCString(c.SystemID)\n\t\t_ = b.WriteCString(c.Password)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *Outbind) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.SystemID, err = b.ReadCString(); err == nil {\n\t\t\tc.Password, err = b.ReadCString()\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/Outbind_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestOutbind(t *testing.T) {\n\tv := NewOutbind().(*Outbind)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\trequire.True(t, v.IsOk())\n\tv.SequenceNumber = 13\n\n\tv.SystemID = \"inventory\"\n\tv.Password = \"ipassword\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"000000240000000b000000000000000d696e76656e746f7279006970617373776f726400\",\n\t\tdata.OUTBIND,\n\t)\n}\n"
  },
  {
    "path": "pdu/PDU.go",
    "content": "package pdu\n\nimport (\n\t\"io\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/errors\"\n)\n\n// PDU represents PDU interface.\ntype PDU interface {\n\t// Marshal PDU to buffer.\n\tMarshal(*ByteBuffer)\n\n\t// Unmarshal PDU from buffer.\n\tUnmarshal(*ByteBuffer) error\n\n\t// CanResponse indicates that PDU could response to SMSC.\n\tCanResponse() bool\n\n\t// GetResponse PDU.\n\tGetResponse() PDU\n\n\t// RegisterOptionalParam assigns an optional param.\n\tRegisterOptionalParam(Field)\n\n\t// GetHeader returns PDU header.\n\tGetHeader() Header\n\n\t// IsOk returns true if command status is OK.\n\tIsOk() bool\n\n\t// IsGNack returns true if PDU is GNack.\n\tIsGNack() bool\n\n\t// AssignSequenceNumber assigns sequence number auto-incrementally.\n\tAssignSequenceNumber()\n\n\t// ResetSequenceNumber resets sequence number.\n\tResetSequenceNumber()\n\n\t// GetSequenceNumber returns assigned sequence number.\n\tGetSequenceNumber() int32\n\n\t// SetSequenceNumber manually sets sequence number.\n\tSetSequenceNumber(int32)\n}\n\ntype base struct {\n\tHeader\n\tOptionalParameters map[Tag]Field\n}\n\nfunc newBase() (v base) {\n\tv.OptionalParameters = make(map[Tag]Field)\n\tv.AssignSequenceNumber()\n\treturn\n}\n\n// GetHeader returns pdu header.\nfunc (c *base) GetHeader() Header {\n\treturn c.Header\n}\n\nfunc (c *base) unmarshal(b *ByteBuffer, bodyReader func(*ByteBuffer) error) (err error) {\n\tfullLen := b.Len()\n\n\tif err = c.Header.Unmarshal(b); err == nil {\n\n\t\t// try to unmarshal body\n\t\tif bodyReader != nil {\n\t\t\terr = bodyReader(b)\n\t\t}\n\n\t\tif err == nil {\n\t\t\t// command length\n\t\t\tcmdLength := int(c.CommandLength)\n\n\t\t\t// got - total read byte(s)\n\t\t\tgot := fullLen - b.Len()\n\t\t\tif got > cmdLength {\n\t\t\t\terr = errors.ErrInvalidPDU\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// body < command_length, still have optional parameters ?\n\t\t\tif got < cmdLength {\n\t\t\t\tvar optParam []byte\n\t\t\t\tif optParam, err = b.ReadN(cmdLength - got); err == nil {\n\t\t\t\t\terr = c.unmarshalOptionalParam(optParam)\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// validate again\n\t\t\tif b.Len() != fullLen-cmdLength {\n\t\t\t\terr = errors.ErrInvalidPDU\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (c *base) unmarshalOptionalParam(optParam []byte) (err error) {\n\tbuf := NewBuffer(optParam)\n\tfor buf.Len() > 0 {\n\t\tvar field Field\n\t\tif err = field.Unmarshal(buf); err == nil {\n\t\t\tc.OptionalParameters[field.Tag] = field\n\t\t} else {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *base) marshal(b *ByteBuffer, bodyWriter func(*ByteBuffer)) {\n\tbodyBuf := NewBuffer(nil)\n\n\t// body\n\tif bodyWriter != nil {\n\t\tbodyWriter(bodyBuf)\n\t}\n\n\t// optional body\n\tfor _, v := range c.OptionalParameters {\n\t\tv.Marshal(bodyBuf)\n\t}\n\n\t// write header\n\tc.CommandLength = int32(data.PDU_HEADER_SIZE + bodyBuf.Len())\n\tc.Header.Marshal(b)\n\n\t// write body and its optional params\n\tb.WriteBuffer(bodyBuf)\n}\n\n// RegisterOptionalParam register optional param.\nfunc (c *base) RegisterOptionalParam(tlv Field) {\n\tc.OptionalParameters[tlv.Tag] = tlv\n}\n\n// IsOk is status ok.\nfunc (c *base) IsOk() bool {\n\treturn c.CommandStatus == data.ESME_ROK\n}\n\n// IsGNack is generic n-ack.\nfunc (c *base) IsGNack() bool {\n\treturn c.CommandID == data.GENERIC_NACK\n}\n\n// Parse PDU from reader.\nfunc Parse(r io.Reader) (pdu PDU, err error) {\n\tvar headerBytes [16]byte\n\n\tif _, err = io.ReadFull(r, headerBytes[:]); err != nil {\n\t\treturn\n\t}\n\n\theader := ParseHeader(headerBytes)\n\tif header.CommandLength < 16 || header.CommandLength > data.MAX_PDU_LEN {\n\t\terr = errors.ErrInvalidPDU\n\t\treturn\n\t}\n\n\t// read pdu body\n\tbodyBytes := make([]byte, header.CommandLength-16)\n\tif len(bodyBytes) > 0 {\n\t\tif _, err = io.ReadFull(r, bodyBytes); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\t// try to create pdu\n\tif pdu, err = CreatePDUFromCmdID(header.CommandID); err == nil {\n\t\tbuf := NewBuffer(make([]byte, 0, header.CommandLength))\n\t\t_, _ = buf.Write(headerBytes[:])\n\t\tif len(bodyBytes) > 0 {\n\t\t\t_, _ = buf.Write(bodyBytes)\n\t\t}\n\t\terr = pdu.Unmarshal(buf)\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "pdu/PDUFactory.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/errors\"\n)\n\ntype pduGenerator func() PDU\n\nvar pduMap = map[data.CommandIDType]pduGenerator{\n\tdata.BIND_TRANSMITTER:      NewBindTransmitter,\n\tdata.BIND_TRANSMITTER_RESP: NewBindTransmitterResp,\n\tdata.BIND_TRANSCEIVER:      NewBindTransceiver,\n\tdata.BIND_TRANSCEIVER_RESP: NewBindTransceiverResp,\n\tdata.BIND_RECEIVER:         NewBindReceiver,\n\tdata.BIND_RECEIVER_RESP:    NewBindReceiverResp,\n\tdata.UNBIND:                NewUnbind,\n\tdata.UNBIND_RESP:           NewUnbindResp,\n\tdata.OUTBIND:               NewOutbind,\n\tdata.SUBMIT_SM:             NewSubmitSM,\n\tdata.SUBMIT_SM_RESP:        NewSubmitSMResp,\n\tdata.SUBMIT_MULTI:          NewSubmitMulti,\n\tdata.SUBMIT_MULTI_RESP:     NewSubmitMultiResp,\n\tdata.DELIVER_SM:            NewDeliverSM,\n\tdata.DELIVER_SM_RESP:       NewDeliverSMResp,\n\tdata.DATA_SM:               NewDataSM,\n\tdata.DATA_SM_RESP:          NewDataSMResp,\n\tdata.QUERY_SM:              NewQuerySM,\n\tdata.QUERY_SM_RESP:         NewQuerySMResp,\n\tdata.CANCEL_SM:             NewCancelSM,\n\tdata.CANCEL_SM_RESP:        NewCancelSMResp,\n\tdata.REPLACE_SM:            NewReplaceSM,\n\tdata.REPLACE_SM_RESP:       NewReplaceSMResp,\n\tdata.ENQUIRE_LINK:          NewEnquireLink,\n\tdata.ENQUIRE_LINK_RESP:     NewEnquireLinkResp,\n\tdata.ALERT_NOTIFICATION:    NewAlertNotification,\n\tdata.GENERIC_NACK:          NewGenericNack,\n}\n\n// CreatePDUFromCmdID creates PDU from cmd id.\nfunc CreatePDUFromCmdID(cmdID data.CommandIDType) (PDU, error) {\n\tif g, ok := pduMap[cmdID]; ok {\n\t\treturn g(), nil\n\t}\n\treturn nil, errors.ErrUnknownCommandID\n}\n"
  },
  {
    "path": "pdu/PDUFactory_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestInvalidCmdID(t *testing.T) {\n\tv, err := CreatePDUFromCmdID(-12)\n\trequire.Nil(t, v)\n\trequire.NotNil(t, err)\n}\n"
  },
  {
    "path": "pdu/PDUHeader.go",
    "content": "package pdu\n\nimport (\n\t\"encoding/binary\"\n\t\"sync/atomic\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\nfunc nextSequenceNumber(s *int32) (v int32) {\n\t// & 0x7FFFFFFF: cater for integer overflow\n\t// Allowed range is 0x01 to 0x7FFFFFFF. This\n\t// will still result in a single invalid value\n\t// of 0x00 every ~2 billion PDUs (not too bad):\n\tif v = atomic.AddInt32(s, 1) & 0x7FFFFFFF; v <= 0 {\n\t\tv = 1\n\t}\n\treturn\n}\n\n// Header represents PDU header.\ntype Header struct {\n\tCommandLength  int32\n\tCommandID      data.CommandIDType\n\tCommandStatus  data.CommandStatusType\n\tSequenceNumber int32\n}\n\n// ParseHeader parses PDU header.\nfunc ParseHeader(v [16]byte) (h Header) {\n\th.CommandLength = int32(binary.BigEndian.Uint32(v[:]))\n\th.CommandID = data.CommandIDType(binary.BigEndian.Uint32(v[4:]))\n\th.CommandStatus = data.CommandStatusType(binary.BigEndian.Uint32(v[8:]))\n\th.SequenceNumber = int32(binary.BigEndian.Uint32(v[12:]))\n\treturn\n}\n\n// Unmarshal from buffer.\nfunc (c *Header) Unmarshal(b *ByteBuffer) (err error) {\n\tvar id, status int32\n\tc.CommandLength, err = b.ReadInt()\n\tif err == nil {\n\t\tid, err = b.ReadInt()\n\t\tif err == nil {\n\t\t\tc.CommandID = data.CommandIDType(id)\n\t\t\tif status, err = b.ReadInt(); err == nil {\n\t\t\t\tc.CommandStatus = data.CommandStatusType(status)\n\t\t\t\tc.SequenceNumber, err = b.ReadInt()\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nvar sequenceNumber int32\n\n// AssignSequenceNumber assigns sequence number auto-incrementally.\nfunc (c *Header) AssignSequenceNumber() {\n\tc.SetSequenceNumber(nextSequenceNumber(&sequenceNumber))\n}\n\n// ResetSequenceNumber resets sequence number.\nfunc (c *Header) ResetSequenceNumber() {\n\tc.SequenceNumber = 1\n}\n\n// GetSequenceNumber returns assigned sequence number.\nfunc (c *Header) GetSequenceNumber() int32 {\n\treturn c.SequenceNumber\n}\n\n// SetSequenceNumber manually sets sequence number.\nfunc (c *Header) SetSequenceNumber(v int32) {\n\tc.SequenceNumber = v\n}\n\n// Marshal to buffer.\nfunc (c *Header) Marshal(b *ByteBuffer) {\n\tb.Grow(16)\n\tb.WriteInt(c.CommandLength)\n\tb.WriteInt(int32(c.CommandID))\n\tb.WriteInt(int32(c.CommandStatus))\n\tb.WriteInt(c.SequenceNumber)\n}\n"
  },
  {
    "path": "pdu/PDUHeader_test.go",
    "content": "package pdu\n\nimport (\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNextSeq(t *testing.T) {\n\tvar v int32 = math.MaxInt32\n\trequire.EqualValues(t, 1, nextSequenceNumber(&v))\n}\n"
  },
  {
    "path": "pdu/PDU_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/errors\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestParsePDU(t *testing.T) {\n\tt.Run(\"valid\", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"00000010800000060000000000000001\"))\n\t\t_, err := Parse(buf)\n\t\trequire.Nil(t, err)\n\t})\n\n\tt.Run(\"submit_sm_resp with command_status != 0 \", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"00000010800000040000005800000001\"))\n\t\t_, err := Parse(buf)\n\t\trequire.Nil(t, err)\n\t})\n\n\tt.Run(\"eof\", func(t *testing.T) {\n\t\tbuf := NewBuffer(nil)\n\t\t_, err := Parse(buf)\n\t\trequire.NotNil(t, err)\n\t})\n\n\tt.Run(\"invalidCmdLength\", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"0000000f800000060000000000000001\"))\n\t\t_, err := Parse(buf)\n\t\trequire.Equal(t, errors.ErrInvalidPDU, err)\n\n\t\tbuf = NewBuffer(fromHex(\"3800000f800000060000000000000001\"))\n\t\t_, err = Parse(buf)\n\t\trequire.Equal(t, errors.ErrInvalidPDU, err)\n\t})\n\n\tt.Run(\"invalidBody\", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"0000001e00000003000000000000000161776179001c1d416c69636572\"))\n\t\t_, err := Parse(buf)\n\t\trequire.NotNil(t, err)\n\t})\n\n\tt.Run(\"invalidPayload\", func(t *testing.T) {\n\t\tbuf := NewBuffer(fromHex(\"000000118000000400000000000000010012\"))\n\t\tvar b base\n\t\trequire.NotNil(t, b.unmarshal(buf, func(buf *ByteBuffer) error {\n\t\t\treturn nil\n\t\t}))\n\n\t\tbuf = NewBuffer(fromHex(\"000000118000000400000000000000010012333333333333333333\"))\n\t\trequire.NotNil(t, b.unmarshal(buf, func(buf *ByteBuffer) error {\n\t\t\t_, _ = buf.ReadN(8)\n\t\t\treturn nil\n\t\t}))\n\t})\n}\n"
  },
  {
    "path": "pdu/QuerySM.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// QuerySM PDU is issued by the ESME to query the status of a previously submitted short message.\n// The matching mechanism is based on the SMSC assigned message_id and source address. Where the\n// original submit_sm, data_sm or submit_multi ‘source address’ was defaulted to NULL, then the\n// source address in the query_sm command should also be set to NULL.\ntype QuerySM struct {\n\tbase\n\tMessageID  string\n\tSourceAddr Address\n}\n\n// NewQuerySM returns new QuerySM PDU.\nfunc NewQuerySM() PDU {\n\tc := &QuerySM{\n\t\tSourceAddr: NewAddress(),\n\t}\n\tc.CommandID = data.QUERY_SM\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *QuerySM) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *QuerySM) GetResponse() PDU {\n\treturn NewQuerySMRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *QuerySM) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + 1)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t\tc.SourceAddr.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *QuerySM) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.MessageID, err = b.ReadCString(); err == nil {\n\t\t\terr = c.SourceAddr.Unmarshal(b)\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/QuerySMResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// QuerySMResp PDU.\ntype QuerySMResp struct {\n\tbase\n\tMessageID    string\n\tFinalDate    string\n\tMessageState byte\n\tErrorCode    byte\n}\n\n// NewQuerySMResp returns new QuerySM PDU.\nfunc NewQuerySMResp() PDU {\n\tc := &QuerySMResp{\n\t\tbase:         newBase(),\n\t\tFinalDate:    data.DFLT_DATE,\n\t\tMessageState: data.DFLT_MSG_STATE,\n\t\tErrorCode:    data.DFLT_ERR,\n\t}\n\tc.CommandID = data.QUERY_SM_RESP\n\treturn c\n}\n\n// NewQuerySMRespFromReq returns new QuerySM PDU.\nfunc NewQuerySMRespFromReq(req *QuerySM) PDU {\n\tc := NewQuerySMResp().(*QuerySMResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *QuerySMResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *QuerySMResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *QuerySMResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + len(c.FinalDate) + 4)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t\t_ = b.WriteCString(c.FinalDate)\n\t\t_ = b.WriteByte(c.MessageState)\n\t\t_ = b.WriteByte(c.ErrorCode)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *QuerySMResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.MessageID, err = b.ReadCString(); err == nil {\n\t\t\tif c.FinalDate, err = b.ReadCString(); err == nil {\n\t\t\t\tif c.MessageState, err = b.ReadByte(); err == nil {\n\t\t\t\t\tc.ErrorCode, err = b.ReadByte()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/QuerySMResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestQuerySMResp(t *testing.T) {\n\treq := NewQuerySM().(*QuerySM)\n\treq.SequenceNumber = 13\n\n\tv := NewQuerySMRespFromReq(req).(*QuerySMResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001480000003000000000000000d00000000\",\n\t\tdata.QUERY_SM_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/QuerySM_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestQuerySM(t *testing.T) {\n\tv := NewQuerySM().(*QuerySM)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001480000003000000000000000d00000000\",\n\t\tdata.QUERY_SM_RESP,\n\t)\n\n\tv.MessageID = \"away\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001e00000003000000000000000d61776179001c1d416c6963657200\",\n\t\tdata.QUERY_SM,\n\t)\n}\n"
  },
  {
    "path": "pdu/ReplaceSM.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// ReplaceSM PDU is issued by the ESME to replace a previously submitted short message\n// that is still pending delivery. The matching mechanism is based on the message_id and\n// source address of the original message. Where the original submit_sm ‘source address’\n// was defaulted to NULL, then the source address in the replace_sm command should also be NULL.\ntype ReplaceSM struct {\n\tbase\n\tMessageID            string\n\tSourceAddr           Address\n\tScheduleDeliveryTime string\n\tValidityPeriod       string\n\tRegisteredDelivery   byte\n\tMessage              ShortMessage\n}\n\n// NewReplaceSM returns ReplaceSM PDU.\nfunc NewReplaceSM() PDU {\n\tmessage, _ := NewShortMessage(\"\")\n\tmessage.withoutDataCoding = true\n\tc := &ReplaceSM{\n\t\tbase:                 newBase(),\n\t\tSourceAddr:           NewAddress(),\n\t\tScheduleDeliveryTime: data.DFLT_SCHEDULE,\n\t\tValidityPeriod:       data.DFLT_VALIDITY,\n\t\tRegisteredDelivery:   data.DFLT_REG_DELIVERY,\n\t\tMessage:              message,\n\t}\n\tc.CommandID = data.REPLACE_SM\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *ReplaceSM) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *ReplaceSM) GetResponse() PDU {\n\treturn NewReplaceSMRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *ReplaceSM) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + len(c.ScheduleDeliveryTime) + len(c.ValidityPeriod) + 4)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t\tc.SourceAddr.Marshal(b)\n\t\t_ = b.WriteCString(c.ScheduleDeliveryTime)\n\t\t_ = b.WriteCString(c.ValidityPeriod)\n\t\t_ = b.WriteByte(c.RegisteredDelivery)\n\t\tc.Message.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *ReplaceSM) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.MessageID, err = b.ReadCString(); err == nil {\n\t\t\tif err = c.SourceAddr.Unmarshal(b); err == nil {\n\t\t\t\tif c.ScheduleDeliveryTime, err = b.ReadCString(); err == nil {\n\t\t\t\t\tif c.ValidityPeriod, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\tif c.RegisteredDelivery, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\terr = c.Message.Unmarshal(b, false)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/ReplaceSMResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// ReplaceSMResp PDU.\ntype ReplaceSMResp struct {\n\tbase\n}\n\n// NewReplaceSMResp returns ReplaceSMResp.\nfunc NewReplaceSMResp() PDU {\n\tc := &ReplaceSMResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.REPLACE_SM_RESP\n\treturn c\n}\n\n// NewReplaceSMRespFromReq returns ReplaceSMResp.\nfunc NewReplaceSMRespFromReq(req *ReplaceSM) PDU {\n\tc := NewReplaceSMResp().(*ReplaceSMResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *ReplaceSMResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *ReplaceSMResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *ReplaceSMResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *ReplaceSMResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/ReplaceSMResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestReplaceSMResp(t *testing.T) {\n\treq := NewReplaceSM().(*ReplaceSM)\n\treq.SequenceNumber = 13\n\n\tv := NewReplaceSMRespFromReq(req).(*ReplaceSMResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001080000007000000000000000d\",\n\t\tdata.REPLACE_SM_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/ReplaceSM_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestReplaceSM(t *testing.T) {\n\tv := NewReplaceSM().(*ReplaceSM)\n\trequire.True(t, v.CanResponse())\n\trequire.True(t, v.Message.withoutDataCoding)\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001080000007000000000000000d\",\n\t\tdata.REPLACE_SM_RESP,\n\t)\n\n\tv.MessageID = \"ID_Her\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\tv.RegisteredDelivery = 83\n\t_ = v.Message.SetMessageWithEncoding(\"nightwish\", data.GSM7BIT)\n\tv.Message.message = \"\"\n\trequire.Equal(t, data.GSM7BIT, v.Message.Encoding())\n\n\tmessage, err := v.Message.GetMessage()\n\trequire.Nil(t, err)\n\trequire.Equal(t, \"nightwish\", message)\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000002e00000007000000000000000d49445f486572001c1d416c696365720000005300096e6967687477697368\",\n\t\tdata.REPLACE_SM,\n\t)\n}\n"
  },
  {
    "path": "pdu/ShortMessage.go",
    "content": "package pdu\n\nimport (\n\t\"sync/atomic\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/errors\"\n)\n\nvar ref = uint32(0)\n\n// ShortMessage message.\ntype ShortMessage struct {\n\tSmDefaultMsgID    byte\n\tmessage           string\n\tenc               data.Encoding\n\tudHeader          UDH\n\tmessageData       []byte\n\twithoutDataCoding bool // purpose of ReplaceSM usage\n}\n\n// NewShortMessage returns new ShortMessage.\nfunc NewShortMessage(message string) (s ShortMessage, err error) {\n\terr = s.SetMessageWithEncoding(message, data.GSM7BIT)\n\treturn\n}\n\n// NewShortMessageWithEncoding returns new ShortMessage with predefined encoding.\nfunc NewShortMessageWithEncoding(message string, enc data.Encoding) (s ShortMessage, err error) {\n\terr = s.SetMessageWithEncoding(message, enc)\n\treturn\n}\n\n// NewBinaryShortMessage returns new ShortMessage.\nfunc NewBinaryShortMessage(messageData []byte) (s ShortMessage, err error) {\n\terr = s.SetMessageDataWithEncoding(messageData, data.BINARY8BIT2)\n\treturn\n}\n\n// NewBinaryShortMessageWithEncoding returns new ShortMessage with predefined encoding.\nfunc NewBinaryShortMessageWithEncoding(messageData []byte, enc data.Encoding) (s ShortMessage, err error) {\n\terr = s.SetMessageDataWithEncoding(messageData, enc)\n\treturn\n}\n\n// NewLongMessage returns long message splitted into multiple short message\nfunc NewLongMessage(message string) (s []*ShortMessage, err error) {\n\treturn NewLongMessageWithEncoding(message, data.GSM7BIT)\n}\n\n// NewLongMessageWithEncoding returns long message splitted into multiple short message with encoding of choice\nfunc NewLongMessageWithEncoding(message string, enc data.Encoding) (s []*ShortMessage, err error) {\n\tsm := &ShortMessage{\n\t\tmessage: message,\n\t\tenc:     enc,\n\t}\n\treturn sm.split()\n}\n\n// SetMessageWithEncoding sets message with encoding.\nfunc (c *ShortMessage) SetMessageWithEncoding(message string, enc data.Encoding) (err error) {\n\tif c.messageData, err = enc.Encode(message); err == nil {\n\t\tif len(c.messageData) > data.SM_MSG_LEN {\n\t\t\terr = errors.ErrShortMessageLengthTooLarge\n\t\t} else {\n\t\t\tc.message = message\n\t\t\tc.enc = enc\n\t\t}\n\n\t\tif c.enc == data.GSM7BITPACKED { // to prevent unwanted \"@\"\n\t\t\truneSlice := []rune(c.message)\n\t\t\ttLen := len(runeSlice)\n\t\t\tescCharsLen := len(data.GetEscapeChars(runeSlice))\n\t\t\tregCharsLen := tLen - escCharsLen\n\t\t\tnSeptet := escCharsLen*2 + regCharsLen\n\t\t\tif (nSeptet+1)%8 == 0 {\n\t\t\t\tc.messageData[len(c.messageData)-1] = (c.messageData[len(c.messageData)-1] & 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...\"*/\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// SetLongMessageWithEnc sets ShortMessage with message longer than  256 bytes\n// callers are expected to call Split() after this\nfunc (c *ShortMessage) SetLongMessageWithEnc(message string, enc data.Encoding) (err error) {\n\tc.message = message\n\tc.enc = enc\n\treturn\n}\n\n// UDH gets user data header for short message\nfunc (c *ShortMessage) UDH() UDH {\n\treturn c.udHeader\n}\n\n// SetUDH sets user data header for short message\n// also appends udh to the beginning of messageData\nfunc (c *ShortMessage) SetUDH(udh UDH) {\n\tc.udHeader = udh\n}\n\n// SetMessageDataWithEncoding sets underlying raw data which is used for pdu marshalling.\nfunc (c *ShortMessage) SetMessageDataWithEncoding(d []byte, enc data.Encoding) (err error) {\n\tif len(d) > data.SM_MSG_LEN {\n\t\terr = errors.ErrShortMessageLengthTooLarge\n\t} else {\n\t\tc.messageData = d\n\t\tc.enc = enc\n\t}\n\treturn\n}\n\n// GetMessageData returns underlying binary message.\nfunc (c *ShortMessage) GetMessageData() (d []byte, err error) {\n\treturn c.messageData, nil\n}\n\n// GetMessage returns underlying message.\nfunc (c *ShortMessage) GetMessage() (st string, err error) {\n\tenc := c.enc\n\tif enc == nil {\n\t\tenc = data.GSM7BIT\n\t}\n\tst, err = c.GetMessageWithEncoding(enc)\n\treturn\n}\n\n// GetMessageWithEncoding returns (decoded) underlying message.\nfunc (c *ShortMessage) GetMessageWithEncoding(enc data.Encoding) (st string, err error) {\n\tif len(c.messageData) > 0 {\n\t\tst, err = enc.Decode(c.messageData)\n\t}\n\treturn\n}\n\n// split one short message and split into multiple short message, with UDH\n// according to 33GP TS 23.040 section 9.2.3.24.1\n//\n// NOTE: split() will return array of length 1 if data length is still within the limit\n// The encoding interface can implement the data.Splitter interface for ad-hoc splitting rule\nfunc (c *ShortMessage) split() (multiSM []*ShortMessage, err error) {\n\tvar encoding data.Encoding\n\tif c.enc == nil {\n\t\tencoding = data.GSM7BIT\n\t} else {\n\t\tencoding = c.enc\n\t}\n\n\t// check if encoding implements data.Splitter\n\tsplitter, ok := encoding.(data.Splitter)\n\t// check if encoding implements data.Splitter or split is necessary\n\tif !ok || !splitter.ShouldSplit(c.message, data.SM_GSM_MSG_LEN) {\n\t\terr = c.SetMessageWithEncoding(c.message, c.enc)\n\t\tmultiSM = []*ShortMessage{c}\n\t\treturn\n\t}\n\n\t// Reserve 6 bytes for concat message UDH\n\t//\n\t// Good references:\n\t// - https://help.goacoustic.com/hc/en-us/articles/360043843154--How-character-encoding-affects-SMS-message-length\n\t// - https://www.twilio.com/docs/glossary/what-is-gsm-7-character-encoding\n\t//\n\t// Limitation is 160 GSM-7 characters and we also need 6 bytes for UDH\n\t// -> 134 octets per segment\n\t// -> this leaves 153 GSM-7 characters per segment.\n\tsegments, err := splitter.EncodeSplit(c.message, data.SM_GSM_MSG_LEN-6)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// prealloc result\n\tmultiSM = make([]*ShortMessage, 0, len(segments))\n\n\t// all segments will have the same ref id\n\tref := getRefNum()\n\n\t// construct SM(s)\n\tfor i, seg := range segments {\n\t\t// create new SM, encode data\n\t\tmultiSM = append(multiSM, &ShortMessage{\n\t\t\tenc: c.enc,\n\t\t\t// message: we don't really care\n\t\t\tmessageData:       seg,\n\t\t\twithoutDataCoding: c.withoutDataCoding,\n\t\t\tudHeader:          UDH{NewIEConcatMessage(uint8(len(segments)), uint8(i+1), uint8(ref))},\n\t\t})\n\t}\n\n\treturn\n}\n\n// Marshal implements PDU interface.\nfunc (c *ShortMessage) Marshal(b *ByteBuffer) {\n\tvar (\n\t\tudhBin []byte\n\t\tn      = byte(len(c.messageData))\n\t)\n\n\t// Prepend UDH to message data if there are any\n\tif c.udHeader != nil && c.udHeader.UDHL() > 0 {\n\t\tudhBin, _ = c.udHeader.MarshalBinary()\n\t}\n\n\tb.Grow(int(n) + 3)\n\n\tvar coding byte\n\tif c.enc == nil {\n\t\tcoding = data.GSM7BITCoding\n\t} else {\n\t\tcoding = c.enc.DataCoding()\n\t}\n\n\t// data_coding\n\tif !c.withoutDataCoding {\n\t\t_ = b.WriteByte(coding)\n\t}\n\n\t// sm_default_msg_id\n\t_ = b.WriteByte(c.SmDefaultMsgID)\n\n\t// sm_length\n\tif udhBin != nil {\n\t\t_ = b.WriteByte(byte(int(n) + len(udhBin)))\n\t\tb.Write(udhBin)\n\t} else {\n\t\t_ = b.WriteByte(n)\n\t}\n\n\t// short_message\n\t_, _ = b.Write(c.messageData[:n])\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *ShortMessage) Unmarshal(b *ByteBuffer, udhi bool) (err error) {\n\tvar dataCoding, n byte\n\n\tif !c.withoutDataCoding {\n\t\tif dataCoding, err = b.ReadByte(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif c.SmDefaultMsgID, err = b.ReadByte(); err != nil {\n\t\treturn\n\t}\n\n\tif n, err = b.ReadByte(); err != nil {\n\t\treturn\n\t}\n\n\tif c.messageData, err = b.ReadN(int(n)); err != nil {\n\t\treturn\n\t}\n\tc.enc = data.FromDataCoding(dataCoding)\n\n\t// If short message length is non zero, short message contains User-Data Header\n\t// Else UDH should be in TLV field MessagePayload\n\tif udhi && n > 0 {\n\t\tudh := UDH{}\n\t\t_, err = udh.UnmarshalBinary(c.messageData)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tc.udHeader = udh\n\n\t\tf := c.udHeader.UDHL()\n\t\tif f > len(c.messageData) {\n\t\t\terr = errors.ErrUDHTooLong\n\t\t\treturn\n\t\t}\n\n\t\tc.messageData = c.messageData[f:]\n\t}\n\n\treturn\n}\n\n// Encoding returns message encoding.\nfunc (c *ShortMessage) Encoding() data.Encoding {\n\treturn c.enc\n}\n\n// returns an atomically incrementing number each time it's called\nfunc getRefNum() uint32 {\n\treturn atomic.AddUint32(&ref, 1)\n}\n\n// NOTE:\n// When coding splitting function, I have 4 choices of abstraction\n// 1. Split the message before encode\n// 2. Split the message after encoded\n// 3. Split the message DURING encoding (before bit packing)\n// 4. Encode, unpack, split\n//\n// Disadvantages:\n// 1. The only way to really know if each segment will fit into 134 octet limit is\n//\t\tto do some kind of simulated encoding, where you calculate the total octet\n//\t\tby iterating through each character one by one.\n//\t\tToo cumbersome\n//\n// 2. When breaking string at octet position 134, I have to detemeine which\n//\t\tcharacter is it ( by doing some kind of decoding)\n\n//\t\ta. If the character code point does not fit in the octet\n//\t\tboundary, it has to be carried-over to the next segment.\n//\t\tThe remaining bits after extracting the carry-over\n//\t\thas to be filled with zero.\n\n//\t\tb. If it is an escape character, then I have to backtrack\n//\t\teven further since escape chars are not allowed to be splitted\n//\t\tin the middle.\n//\t\tSince the second bytes of escape chars can be confused with\n//\t\tnormal chars, I must always lookback 2 character ( repeat step a for at least 2 septet )\n\n//\t\tc. After extracting the carry-on\n//\t\t-> Option 2 is very hard when bit packing is already applied\n//\n// 3. Options 3 require extending Encoding interface,\n//\tThe not good point is not being able to utilize the encoder's Transform() method\n//\tThe good point is you don't have to do bit packing twice\n\n// 4. Terrible option\n\n// All this headaches really only apply to variable length encoding.\n// When using fixed length encoding, you can really split the source message BEFORE encodes.\n"
  },
  {
    "path": "pdu/ShortMessage_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/errors\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype customEncoder struct{}\n\nfunc (*customEncoder) Encode(str string) ([]byte, error) {\n\treturn []byte(str), nil\n}\n\nfunc (*customEncoder) Decode(data []byte) (string, error) {\n\treturn string(data), nil\n}\n\nfunc TestShortMessage(t *testing.T) {\n\tt.Run(\"invalidCoding\", func(t *testing.T) {\n\t\tvar s ShortMessage\n\t\trequire.NotNil(t, s.SetMessageWithEncoding(\"agjwklgjkwPфngưỡng\", data.LATIN1))\n\t})\n\n\tt.Run(\"customCoding\", func(t *testing.T) {\n\t\tvar s ShortMessage\n\n\t\tcustomCoding := data.NewCustomEncoding(246, &customEncoder{})\n\t\terr := s.SetMessageDataWithEncoding([]byte{0x61, 0x62, 0x63}, customCoding) // \"abc\"\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 246, s.Encoding().DataCoding())\n\n\t\tm, err := s.GetMessage()\n\t\trequire.Nil(t, err)\n\t\trequire.Equal(t, \"abc\", m)\n\n\t\t// try to get message string with other encoding\n\t\tm, err = s.GetMessageWithEncoding(data.FromDataCoding(data.UCS2Coding))\n\t\trequire.Nil(t, err)\n\t\trequire.NotEqual(t, \"abc\", m)\n\n\t\t// get message string with custom encoding\n\t\tm, err = s.GetMessageWithEncoding(customCoding)\n\t\trequire.Nil(t, err)\n\t\trequire.Equal(t, \"abc\", m)\n\t})\n\n\tt.Run(\"customCodingFromPeer\", func(t *testing.T) {\n\t\tvar senderSM ShortMessage\n\n\t\t// set custom data coding for test\n\t\tcustomCoding := data.NewCustomEncoding(0x19, &customEncoder{})\n\n\t\terr := senderSM.SetMessageDataWithEncoding([]byte{0x61, 0x62, 0x63}, customCoding) // \"abc\"\n\t\trequire.NoError(t, err)\n\n\t\tb := NewBuffer(nil)\n\t\tsenderSM.Marshal(b)\n\n\t\t// From here the message is not know anymore to the receiver in terms of encoding methods, but the receiver wants to know the encoding code once receiving the packet\n\t\tvar receivedSM ShortMessage\n\t\terr = receivedSM.Unmarshal(b, false)\n\t\trequire.NoError(t, err)\n\n\t\trequire.NotNil(t, receivedSM.Encoding())\n\t})\n\n\tt.Run(\"invalidSize\", func(t *testing.T) {\n\t\tvar s ShortMessage\n\t\trequire.Equal(t, errors.ErrShortMessageLengthTooLarge,\n\t\t\ts.SetMessageWithEncoding(\"agjwklgjkwPфngưỡngasdfasdfasdfasdagjwklgjkwPфngưỡngasdfasdfasdfasdagjwklgjkwPфngưỡngasdfasdfasdfasdagjwklgjkwPфngưỡngasdfasdfasdfasd\", data.UCS2))\n\t})\n\n\tt.Run(\"getMessageWithoutCoding\", func(t *testing.T) {\n\t\tvar s ShortMessage\n\t\ts.messageData = []byte{0x61, 0x62, 0x63}\n\n\t\tm, err := s.GetMessage()\n\t\trequire.Nil(t, err)\n\t\trequire.Equal(t, \"abc\", m)\n\t})\n\n\tt.Run(\"getMessageData\", func(t *testing.T) {\n\t\ts, err := NewBinaryShortMessage([]byte{0x00, 0x01, 0x02, 0x03})\n\t\trequire.NoError(t, err)\n\n\t\tmessageData, err := s.GetMessageData()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"00010203\", toHex(messageData))\n\t})\n\n\tt.Run(\"marshalBinaryMessage\", func(t *testing.T) {\n\t\ts, err := NewBinaryShortMessage([]byte{0x00, 0x01, 0x02, 0x03, 0x04})\n\t\trequire.NoError(t, err)\n\n\t\tbuf := NewBuffer(nil)\n\t\ts.Marshal(buf)\n\n\t\trequire.Equal(t, \"0400050001020304\", toHex(buf.Bytes()))\n\t})\n\n\tt.Run(\"marshalWithoutCoding\", func(t *testing.T) {\n\t\tvar s ShortMessage\n\t\terr := s.SetMessageDataWithEncoding([]byte(\"abc\"), nil)\n\t\trequire.NoError(t, err)\n\t\ts.messageData = append(s.messageData, 0)\n\t\ts.enc = nil\n\n\t\tbuf := NewBuffer(nil)\n\t\ts.Marshal(buf)\n\t\trequire.Equal(t, \"00000461626300\", toHex(buf.Bytes()))\n\t})\n\n\tt.Run(\"marshalWithCoding\", func(t *testing.T) {\n\t\ts, err := NewShortMessageWithEncoding(\"abc\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\n\t\tbuf := NewBuffer(nil)\n\t\ts.Marshal(buf)\n\t\trequire.Equal(t, \"000003616263\", toHex(buf.Bytes()))\n\t})\n\n\tt.Run(\"marshalWithCoding160chars\", func(t *testing.T) {\n\t\ts, err := NewShortMessageWithEncoding(\"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\n\t\tbuf := NewBuffer(nil)\n\t\ts.Marshal(buf)\n\n\t\trequire.Equal(t, 116, len(buf.Bytes()))\n\t})\n\n\tt.Run(\"marshalGSM7WithUDHConcat\", func(t *testing.T) {\n\t\ts, err := NewShortMessageWithEncoding(\"abc\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\t\ts.SetUDH(UDH{NewIEConcatMessage(2, 1, 12)})\n\n\t\tbuf := NewBuffer(nil)\n\t\ts.Marshal(buf)\n\t\trequire.Equal(t, \"0000090500030c0201616263\", toHex(buf.Bytes()))\n\t})\n\n\tt.Run(\"unmarshalBinaryWithUDHConcat\", func(t *testing.T) {\n\t\ts := &ShortMessage{}\n\n\t\tbuf := NewBuffer([]byte{0x04, 0x00, 0x09, 0x05, 0x00, 0x03, 0x0c, 0x02, 0x01, 0x01, 0x02, 0x03})\n\n\t\t// check encoding\n\t\trequire.NoError(t, s.Unmarshal(buf, true))\n\t\trequire.Equal(t, data.BINARY8BIT2, s.Encoding())\n\n\t\t// check message\n\t\tmessageData, err := s.GetMessageData()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, []byte{0x01, 0x02, 0x03}, messageData)\n\t})\n\n\tt.Run(\"unmarshalGSM7WithUDHConcat\", func(t *testing.T) {\n\t\ts := &ShortMessage{}\n\n\t\tbuf := NewBuffer([]byte{0x00, 0x00, 0x09, 0x05, 0x00, 0x03, 0x0c, 0x02, 0x01, 0x61, 0x62, 0x63})\n\n\t\t// check encoding\n\t\trequire.NoError(t, s.Unmarshal(buf, true))\n\t\trequire.Equal(t, data.GSM7BIT, s.Encoding())\n\n\t\t// check message\n\t\tmessage, err := s.GetMessageWithEncoding(s.Encoding())\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"abc\", message)\n\t})\n\n\tt.Run(\"shortMessageSplitGSM7_169chars\", func(t *testing.T) {\n\t\t// over gsm7 chars limit ( 169/160 ), split\n\t\tsm, err := NewLongMessageWithEncoding(\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234123456789\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, 2, len(sm))\n\t})\n\n\tt.Run(\"shortMessageSplitGSM7_160chars\", func(t *testing.T) {\n\t\t// over gsm7 chars limit ( 160/160 ), split\n\t\tsm, err := NewLongMessageWithEncoding(\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, 2, len(sm))\n\t})\n\n\tt.Run(\"shortMessageSplitUCS2_89chars\", func(t *testing.T) {\n\t\t// over UCS2 chars limit (89/67), split\n\t\tsm, err := NewLongMessageWithEncoding(\"biggest gift của Christmas là có nhiều big/challenging/meaningful problems để sấp mặt làm\", data.UCS2)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, 2, len(sm))\n\t})\n\n\tt.Run(\"shortMessageSplitUCS2_67chars\", func(t *testing.T) {\n\t\t// still within UCS2 chars limit (67/67), not split\n\t\tsm, err := NewLongMessageWithEncoding(\"biggest gift của Christmas là có nhiều big/challenging/meaningful p\", data.UCS2)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, 1, len(sm))\n\t})\n\n\tt.Run(\"shortMessageSplitGSM7_empty\", func(t *testing.T) {\n\t\t// over UCS2 chars limit (89/67), split\n\t\tsm, err := NewLongMessageWithEncoding(\"\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, 1, len(sm))\n\t})\n\n\tt.Run(\"indempotentMarshal\", func(t *testing.T) {\n\t\t// over gsm7 chars limit ( 160/160 ), split\n\t\tmultiSM, err := NewLongMessageWithEncoding(\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234\", data.GSM7BIT)\n\t\trequire.NoError(t, err)\n\n\t\tfor i := range multiSM {\n\t\t\tb1, b2 := NewBuffer(nil), NewBuffer(nil)\n\t\t\tmultiSM[i].Marshal(b1)\n\t\t\tmultiSM[i].Marshal(b2)\n\t\t\trequire.Equal(t, b1.Bytes(), b2.Bytes())\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pdu/SubmitMulti.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// SubmitMulti PDU is used to submit an SMPP message for delivery to multiple recipients\n// or to one or more Distribution Lists. The submit_multi PDU does not support\n// the transaction message mode.\ntype SubmitMulti struct {\n\tbase\n\tServiceType          string\n\tSourceAddr           Address\n\tDestAddrs            DestinationAddresses\n\tEsmClass             byte\n\tProtocolID           byte\n\tPriorityFlag         byte\n\tScheduleDeliveryTime string\n\tValidityPeriod       string // not used\n\tRegisteredDelivery   byte\n\tReplaceIfPresentFlag byte // not used\n\tMessage              ShortMessage\n}\n\n// NewSubmitMulti returns NewSubmitMulti PDU.\nfunc NewSubmitMulti() PDU {\n\tmessage, _ := NewShortMessage(\"\")\n\tc := &SubmitMulti{\n\t\tbase:                 newBase(),\n\t\tServiceType:          data.DFLT_SRVTYPE,\n\t\tSourceAddr:           NewAddress(),\n\t\tDestAddrs:            NewDestinationAddresses(),\n\t\tEsmClass:             data.DFLT_ESM_CLASS,\n\t\tProtocolID:           data.DFLT_PROTOCOLID,\n\t\tPriorityFlag:         data.DFLT_PRIORITY_FLAG,\n\t\tScheduleDeliveryTime: data.DFLT_SCHEDULE,\n\t\tValidityPeriod:       data.DFLT_VALIDITY,\n\t\tRegisteredDelivery:   data.DFLT_REG_DELIVERY,\n\t\tReplaceIfPresentFlag: data.DFTL_REPLACE_IFP,\n\t\tMessage:              message,\n\t}\n\tc.CommandID = data.SUBMIT_MULTI\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *SubmitMulti) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *SubmitMulti) GetResponse() PDU {\n\treturn NewSubmitMultiRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *SubmitMulti) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.ServiceType) + len(c.ScheduleDeliveryTime) + len(c.ValidityPeriod) + 10)\n\n\t\t_ = b.WriteCString(c.ServiceType)\n\t\tc.SourceAddr.Marshal(b)\n\t\tc.DestAddrs.Marshal(b)\n\t\t_ = b.WriteByte(c.EsmClass)\n\t\t_ = b.WriteByte(c.ProtocolID)\n\t\t_ = b.WriteByte(c.PriorityFlag)\n\t\t_ = b.WriteCString(c.ScheduleDeliveryTime)\n\t\t_ = b.WriteCString(c.ValidityPeriod)\n\t\t_ = b.WriteByte(c.RegisteredDelivery)\n\t\t_ = b.WriteByte(c.ReplaceIfPresentFlag)\n\t\tc.Message.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *SubmitMulti) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.ServiceType, err = b.ReadCString(); err == nil {\n\t\t\tif err = c.SourceAddr.Unmarshal(b); err == nil {\n\t\t\t\tif err = c.DestAddrs.Unmarshal(b); err == nil {\n\t\t\t\t\tif c.EsmClass, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\tif c.ProtocolID, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\tif c.PriorityFlag, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\tif c.ScheduleDeliveryTime, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\t\t\t\tif c.ValidityPeriod, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\t\t\t\t\tif c.RegisteredDelivery, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\t\t\t\tif c.ReplaceIfPresentFlag, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\t\t\t\t\terr = c.Message.Unmarshal(b, (c.EsmClass&data.SM_UDH_GSM) > 0)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/SubmitMultiResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// SubmitMultiResp PDU.\ntype SubmitMultiResp struct {\n\tbase\n\tMessageID     string\n\tUnsuccessSMEs UnsuccessSMEs\n}\n\n// NewSubmitMultiResp returns new SubmitMultiResp.\nfunc NewSubmitMultiResp() PDU {\n\tc := &SubmitMultiResp{\n\t\tbase:          newBase(),\n\t\tMessageID:     data.DFLT_MSGID,\n\t\tUnsuccessSMEs: NewUnsuccessSMEs(),\n\t}\n\tc.CommandID = data.SUBMIT_MULTI_RESP\n\treturn c\n}\n\n// NewSubmitMultiRespFromReq returns new SubmitMultiResp.\nfunc NewSubmitMultiRespFromReq(req *SubmitMulti) PDU {\n\tc := NewSubmitMultiResp().(*SubmitMultiResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *SubmitMultiResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *SubmitMultiResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *SubmitMultiResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + 1)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t\tc.UnsuccessSMEs.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *SubmitMultiResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.MessageID, err = b.ReadCString(); err == nil {\n\t\t\terr = c.UnsuccessSMEs.Unmarshal(b)\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/SubmitMultiResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSubmitMultiResp(t *testing.T) {\n\treq := NewSubmitMulti().(*SubmitMulti)\n\treq.SequenceNumber = 13\n\n\tv := NewSubmitMultiRespFromReq(req).(*SubmitMultiResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tv.MessageID = \"football\"\n\n\taddr1 := NewUnsuccessSMEWithTonNpi(38, 33, 19)\n\trequire.Nil(t, addr1.SetAddress(\"Bob1\"))\n\trequire.EqualValues(t, 19, addr1.ErrorStatusCode())\n\n\taddr2, err := NewUnsuccessSMEWithAddr(\"Bob2\", 20)\n\trequire.Nil(t, err)\n\trequire.EqualValues(t, 20, addr2.ErrorStatusCode())\n\n\tv.UnsuccessSMEs.Add(addr1, addr2)\n\trequire.Equal(t, []UnsuccessSME{addr1, addr2}, v.UnsuccessSMEs.Get())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000003080000021000000000000000d666f6f7462616c6c00022621426f623100000000130000426f62320000000014\",\n\t\tdata.SUBMIT_MULTI_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/SubmitMulti_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSubmitMulti(t *testing.T) {\n\tv := NewSubmitMulti().(*SubmitMulti)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001280000021000000000000000d0000\",\n\t\tdata.SUBMIT_MULTI_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\n\taddr := NewAddress()\n\trequire.Nil(t, addr.SetAddress(\"Bob1\"))\n\td1 := NewDestinationAddress()\n\td1.SetAddress(addr)\n\n\tdl, err := NewDistributionList(\"List1\")\n\trequire.Nil(t, err)\n\td2 := NewDestinationAddress()\n\td2.SetDistributionList(dl)\n\n\tdl, err = NewDistributionList(\"List2\")\n\trequire.Nil(t, err)\n\td3 := NewDestinationAddress()\n\td3.SetDistributionList(dl)\n\n\tv.DestAddrs.Add(d1, d2, d3)\n\trequire.Equal(t, []DestinationAddress{d1, d2, d3}, v.DestAddrs.Get())\n\n\tv.EsmClass = 13\n\tv.ProtocolID = 99\n\tv.PriorityFlag = 61\n\tv.RegisteredDelivery = 83\n\n\tv.Message, err = NewShortMessageWithEncoding(\"nghắ nghiêng nghiễng ngả\", data.UCS2)\n\trequire.Nil(t, err)\n\tv.Message.message = \"\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000006e00000021000000000000000d616263001c1d416c696365720003010000426f623100024c6973743100024c69737432000d633d00005300080030006e006700681eaf0020006e00670068006900ea006e00670020006e0067006800691ec5006e00670020006e00671ea3\",\n\t\tdata.SUBMIT_MULTI,\n\t)\n}\n\nfunc TestSubmitMultiwithUDH(t *testing.T) {\n\tv := NewSubmitMulti().(*SubmitMulti)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001280000021000000000000000d0000\",\n\t\tdata.SUBMIT_MULTI_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\n\taddr := NewAddress()\n\trequire.Nil(t, addr.SetAddress(\"Bob1\"))\n\td1 := NewDestinationAddress()\n\td1.SetAddress(addr)\n\n\tdl, err := NewDistributionList(\"List1\")\n\trequire.Nil(t, err)\n\td2 := NewDestinationAddress()\n\td2.SetDistributionList(dl)\n\n\tdl, err = NewDistributionList(\"List2\")\n\trequire.Nil(t, err)\n\td3 := NewDestinationAddress()\n\td3.SetDistributionList(dl)\n\n\tv.DestAddrs.Add(d1, d2, d3)\n\trequire.Equal(t, []DestinationAddress{d1, d2, d3}, v.DestAddrs.Get())\n\n\tv.EsmClass = 77\n\tv.ProtocolID = 99\n\tv.PriorityFlag = 61\n\tv.RegisteredDelivery = 83\n\n\tv.Message, err = NewShortMessageWithEncoding(\"nghắ nghiêng nghiễng ngả\", data.UCS2)\n\trequire.Nil(t, err)\n\tv.Message.message = \"\"\n\tv.Message.SetUDH(UDH{NewIEConcatMessage(2, 1, 254)})\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000007400000021000000000000000d616263001c1d416c696365720003010000426f623100024c6973743100024c69737432004d633d00005300080036050003fe0201006e006700681eaf0020006e00670068006900ea006e00670020006e0067006800691ec5006e00670020006e00671ea3\",\n\t\tdata.SUBMIT_MULTI,\n\t)\n}\n"
  },
  {
    "path": "pdu/SubmitSM.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// SubmitSM PDU is used by an ESME to submit a short message to the SMSC for onward\n// transmission to a specified short message entity (SME). The submit_sm PDU does\n// not support the transaction message mode.\ntype SubmitSM struct {\n\tbase\n\tServiceType          string\n\tSourceAddr           Address\n\tDestAddr             Address\n\tEsmClass             byte\n\tProtocolID           byte\n\tPriorityFlag         byte\n\tScheduleDeliveryTime string // not used\n\tValidityPeriod       string // not used\n\tRegisteredDelivery   byte\n\tReplaceIfPresentFlag byte // not used\n\tMessage              ShortMessage\n}\n\n// NewSubmitSM returns SubmitSM PDU.\nfunc NewSubmitSM() PDU {\n\tmessage, _ := NewShortMessage(\"\")\n\tc := &SubmitSM{\n\t\tbase:                 newBase(),\n\t\tServiceType:          data.DFLT_SRVTYPE,\n\t\tSourceAddr:           NewAddress(),\n\t\tDestAddr:             NewAddress(),\n\t\tEsmClass:             data.DFLT_ESM_CLASS,\n\t\tProtocolID:           data.DFLT_PROTOCOLID,\n\t\tPriorityFlag:         data.DFLT_PRIORITY_FLAG,\n\t\tScheduleDeliveryTime: data.DFLT_SCHEDULE,\n\t\tValidityPeriod:       data.DFLT_VALIDITY,\n\t\tRegisteredDelivery:   data.DFLT_REG_DELIVERY,\n\t\tReplaceIfPresentFlag: data.DFTL_REPLACE_IFP,\n\t\tMessage:              message,\n\t}\n\tc.CommandID = data.SUBMIT_SM\n\treturn c\n}\n\n// ShouldSplit check if this the user data of submitSM PDU\nfunc (c *SubmitSM) ShouldSplit() bool {\n\t// GSM standard mandates that User Data must be no longer than 140 octet\n\treturn len(c.Message.messageData) > data.SM_GSM_MSG_LEN\n}\n\n// CanResponse implements PDU interface.\nfunc (c *SubmitSM) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *SubmitSM) GetResponse() PDU {\n\treturn NewSubmitSMRespFromReq(c)\n}\n\n// Split split a single long text message into multiple SubmitSM PDU,\n// Each have the TPUD within the GSM's User Data limit of 140 octet\n// If the message is short enough and doesn't need splitting,\n// Split() returns an array of length 1\nfunc (c *SubmitSM) Split() (multiSubSM []*SubmitSM, err error) {\n\tmultiSubSM = []*SubmitSM{}\n\n\tmultiMsg, err := c.Message.split()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tesmClass := c.EsmClass // no need to \"or\" with SM_UDH_GSM when a message has a single part\n\tif len(multiMsg) > 1 {\n\t\tesmClass = c.EsmClass | data.SM_UDH_GSM // must set to indicate UDH\n\t}\n\n\tfor _, msg := range multiMsg {\n\t\tmultiSubSM = append(multiSubSM, &SubmitSM{\n\t\t\tbase:                 c.base,\n\t\t\tServiceType:          c.ServiceType,\n\t\t\tSourceAddr:           c.SourceAddr,\n\t\t\tDestAddr:             c.DestAddr,\n\t\t\tEsmClass:             esmClass,\n\t\t\tProtocolID:           c.ProtocolID,\n\t\t\tPriorityFlag:         c.PriorityFlag,\n\t\t\tScheduleDeliveryTime: c.ScheduleDeliveryTime,\n\t\t\tValidityPeriod:       c.ValidityPeriod,\n\t\t\tRegisteredDelivery:   c.RegisteredDelivery,\n\t\t\tReplaceIfPresentFlag: c.ReplaceIfPresentFlag,\n\t\t\tMessage:              *msg,\n\t\t})\n\t}\n\treturn\n}\n\n// Marshal implements PDU interface.\nfunc (c *SubmitSM) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.ServiceType) + len(c.ScheduleDeliveryTime) + len(c.ValidityPeriod) + 10)\n\n\t\t_ = b.WriteCString(c.ServiceType)\n\t\tc.SourceAddr.Marshal(b)\n\t\tc.DestAddr.Marshal(b)\n\t\t_ = b.WriteByte(c.EsmClass)\n\t\t_ = b.WriteByte(c.ProtocolID)\n\t\t_ = b.WriteByte(c.PriorityFlag)\n\t\t_ = b.WriteCString(c.ScheduleDeliveryTime)\n\t\t_ = b.WriteCString(c.ValidityPeriod)\n\t\t_ = b.WriteByte(c.RegisteredDelivery)\n\t\t_ = b.WriteByte(c.ReplaceIfPresentFlag)\n\t\tc.Message.Marshal(b)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *SubmitSM) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tif c.ServiceType, err = b.ReadCString(); err == nil {\n\t\t\tif err = c.SourceAddr.Unmarshal(b); err == nil {\n\t\t\t\tif err = c.DestAddr.Unmarshal(b); err == nil {\n\t\t\t\t\tif c.EsmClass, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\tif c.ProtocolID, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\tif c.PriorityFlag, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\tif c.ScheduleDeliveryTime, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\t\t\t\tif c.ValidityPeriod, err = b.ReadCString(); err == nil {\n\t\t\t\t\t\t\t\t\t\tif c.RegisteredDelivery, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\t\t\t\tif c.ReplaceIfPresentFlag, err = b.ReadByte(); err == nil {\n\t\t\t\t\t\t\t\t\t\t\t\terr = c.Message.Unmarshal(b, (c.EsmClass&data.SM_UDH_GSM) > 0)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/SubmitSMResp.go",
    "content": "package pdu\n\nimport (\n\t\"errors\"\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"io\"\n)\n\n// SubmitSMResp PDU.\ntype SubmitSMResp struct {\n\tbase\n\tMessageID string\n}\n\n// NewSubmitSMResp returns new SubmitSMResp.\nfunc NewSubmitSMResp() PDU {\n\tc := &SubmitSMResp{\n\t\tbase:      newBase(),\n\t\tMessageID: data.DFLT_MSGID,\n\t}\n\tc.CommandID = data.SUBMIT_SM_RESP\n\treturn c\n}\n\n// NewSubmitSMRespFromReq returns new SubmitSMResp.\nfunc NewSubmitSMRespFromReq(req *SubmitSM) PDU {\n\tc := NewSubmitSMResp().(*SubmitSMResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *SubmitSMResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *SubmitSMResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *SubmitSMResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, func(b *ByteBuffer) {\n\t\tb.Grow(len(c.MessageID) + 1)\n\n\t\t_ = b.WriteCString(c.MessageID)\n\t})\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *SubmitSMResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, func(b *ByteBuffer) (err error) {\n\t\tc.MessageID, err = b.ReadCString()\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn nil\n\t\t}\n\t\treturn\n\t})\n}\n"
  },
  {
    "path": "pdu/SubmitSMResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSubmitSMResp(t *testing.T) {\n\treq := NewSubmitSM().(*SubmitSM)\n\treq.SequenceNumber = 13\n\n\tv := NewSubmitSMRespFromReq(req).(*SubmitSMResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tv.MessageID = \"football\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001980000004000000000000000d666f6f7462616c6c00\",\n\t\tdata.SUBMIT_SM_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/SubmitSM_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSubmitSM(t *testing.T) {\n\tv := NewSubmitSM().(*SubmitSM)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001180000004000000000000000d00\",\n\t\tdata.SUBMIT_SM_RESP,\n\t)\n\n\tv.ServiceType = \"abc\"\n\t_ = v.SourceAddr.SetAddress(\"Alicer\")\n\tv.SourceAddr.SetTon(28)\n\tv.SourceAddr.SetNpi(29)\n\n\t_ = v.DestAddr.SetAddress(\"Bob\")\n\tv.DestAddr.SetTon(79)\n\tv.DestAddr.SetNpi(80)\n\n\tv.EsmClass = 77 ^ data.SM_UDH_GSM\n\tv.ProtocolID = 99\n\tv.PriorityFlag = 61\n\tv.RegisteredDelivery = 83\n\t_ = v.Message.SetMessageWithEncoding(\"nghắ nghiêng nghiễng ngả\", data.UCS2)\n\tv.Message.message = \"\"\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000005d00000004000000000000000d616263001c1d416c69636572004f50426f62000d633d00005300080030006e006700681eaf0020006e00670068006900ea006e00670020006e0067006800691ec5006e00670020006e00671ea3\",\n\t\tdata.SUBMIT_SM,\n\t)\n}\n"
  },
  {
    "path": "pdu/TLV.go",
    "content": "package pdu\n\n// Source code in this file is copied from: https://github.com/fiorix\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n)\n\n// Tag is the tag of a Tag-Length-Value (TLV) field.\ntype Tag uint16\n\n// Hex returns hexadecimal representation of tag\nfunc (t Tag) Hex() string {\n\tvar bin [2]byte\n\tbinary.BigEndian.PutUint16(bin[:], uint16(t))\n\treturn hex.EncodeToString(bin[:])\n}\n\n// Common Tag-Length-Value (TLV) tags.\nconst (\n\tTagDestAddrSubunit          Tag = 0x0005\n\tTagDestNetworkType          Tag = 0x0006\n\tTagDestBearerType           Tag = 0x0007\n\tTagDestTelematicsID         Tag = 0x0008\n\tTagSourceAddrSubunit        Tag = 0x000D\n\tTagSourceNetworkType        Tag = 0x000E\n\tTagSourceBearerType         Tag = 0x000F\n\tTagSourceTelematicsID       Tag = 0x0010\n\tTagQosTimeToLive            Tag = 0x0017\n\tTagPayloadType              Tag = 0x0019\n\tTagAdditionalStatusInfoText Tag = 0x001D\n\tTagReceiptedMessageID       Tag = 0x001E\n\tTagMsMsgWaitFacilities      Tag = 0x0030\n\tTagPrivacyIndicator         Tag = 0x0201\n\tTagSourceSubaddress         Tag = 0x0202\n\tTagDestSubaddress           Tag = 0x0203\n\tTagUserMessageReference     Tag = 0x0204\n\tTagUserResponseCode         Tag = 0x0205\n\tTagSourcePort               Tag = 0x020A\n\tTagDestinationPort          Tag = 0x020B\n\tTagSarMsgRefNum             Tag = 0x020C\n\tTagLanguageIndicator        Tag = 0x020D\n\tTagSarTotalSegments         Tag = 0x020E\n\tTagSarSegmentSeqnum         Tag = 0x020F\n\tTagCallbackNumPresInd       Tag = 0x0302\n\tTagCallbackNumAtag          Tag = 0x0303\n\tTagNumberOfMessages         Tag = 0x0304\n\tTagCallbackNum              Tag = 0x0381\n\tTagDpfResult                Tag = 0x0420\n\tTagSetDpf                   Tag = 0x0421\n\tTagMsAvailabilityStatus     Tag = 0x0422\n\tTagNetworkErrorCode         Tag = 0x0423\n\tTagMessagePayload           Tag = 0x0424\n\tTagDeliveryFailureReason    Tag = 0x0425\n\tTagMoreMessagesToSend       Tag = 0x0426\n\tTagMessageStateOption       Tag = 0x0427\n\tTagUssdServiceOp            Tag = 0x0501\n\tTagDisplayTime              Tag = 0x1201\n\tTagSmsSignal                Tag = 0x1203\n\tTagMsValidity               Tag = 0x1204\n\tTagAlertOnMessageDelivery   Tag = 0x130C\n\tTagItsReplyType             Tag = 0x1380\n\tTagItsSessionInfo           Tag = 0x1383\n)\n\n// Field is a PDU Tag-Length-Value (TLV) field\ntype Field struct {\n\tTag  Tag\n\tData []byte\n}\n\n// String implements the Data interface.\nfunc (t *Field) String() string {\n\tif l := len(t.Data); l > 0 && t.Data[l-1] == 0x00 {\n\t\treturn string(t.Data[:l-1])\n\t}\n\treturn string(t.Data)\n}\n\n// Marshal to writer.\nfunc (t *Field) Marshal(w *ByteBuffer) {\n\tif len(t.Data) > 0 {\n\t\tw.Grow(4 + len(t.Data))\n\n\t\tw.WriteShort(int16(t.Tag))\n\t\tw.WriteShort(int16(len(t.Data)))\n\t\t_, _ = w.Write(t.Data)\n\t}\n}\n\n// Unmarshal from reader.\nfunc (t *Field) Unmarshal(b *ByteBuffer) (err error) {\n\tvar tag, ln int16\n\tif tag, err = b.ReadShort(); err == nil {\n\t\tt.Tag = Tag(tag)\n\t\tif ln, err = b.ReadShort(); err == nil {\n\t\t\tt.Data, err = b.ReadN(int(ln))\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "pdu/UDH.go",
    "content": "package pdu\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// For now, this package only support message uses of UDH for message concatenation\n// No plan for supporting other Enhanced Messaging Service\n// Credit to https://github.com/warthog618/sms\n\n// UDH represent User Data Header\n// as defined in 3GPP TS 23.040 Section 9.2.3.24.\ntype UDH []InfoElement\n\n// UDHL returns length (octets) of encoded UDH, including the UDHL byte.\n//\n// If there is no InfoElement (IE), returns 0. If total length exceed 255, return -1.\nfunc (u UDH) UDHL() (l int) {\n\tif len(u) == 0 {\n\t\treturn\n\t}\n\n\tfor i := range u {\n\t\tif len(u[i].Data) > 255 {\n\t\t\treturn -1\n\t\t}\n\n\t\t// to account for id and type bytes\n\t\tif l += 2 + len(u[i].Data); l > 255 {\n\t\t\treturn -1\n\t\t}\n\t}\n\n\t// include the udhlength byte itself\n\tl++\n\n\treturn\n}\n\n// MarshalBinary marshal UDH into bytes array\n// The first byte is UDHL\n// MarshalBinary preserve InformationElement order as they appears in the UDH\n//\n// If the total UDHL is larger than what length(byte) can specified,\n// this will truncate IE until total length fit within 256, if you want to\n// check if any IE has been truncated, see if UDHL() < -1, notes on UDHL()\nfunc (u UDH) MarshalBinary() (b []byte, err error) {\n\treservedLength := u.UDHL()\n\tif reservedLength == -1 {\n\t\terr = fmt.Errorf(\"header limit (255 in marshal size) exceeds\")\n\t\treturn\n\t}\n\tif reservedLength == 0 {\n\t\treturn\n\t}\n\n\t// reserve the first byte for UDHL\n\tbuf := bytes.NewBuffer(make([]byte, 1, reservedLength))\n\n\t// marshalling elements\n\tlength := 0\n\tfor i := 0; i < len(u); i++ {\n\t\t// Begin marshaling UDH data, each IE is composed of 3 parts:\n\t\t//\t\t[ ID_1, LENGTH_1, DATA_N ]\n\t\t// When adding a new IE, if total length ID + LEN + DATA\n\t\t// exceed 256, we skip that IE altogether\n\t\taddition := 2 + len(u[i].Data)\n\n\t\t// limit exceeded, break loop?\n\t\tif length += addition; length > 255 {\n\t\t\tlength -= addition\n\t\t\tbreak\n\t\t}\n\n\t\tbuf.WriteByte(u[i].ID)\n\t\tbuf.WriteByte(byte(len(u[i].Data)))\n\t\tbuf.Write(u[i].Data)\n\t}\n\n\t// only set buffer when UDHL length is not zero\n\tif length > 0 {\n\t\t// final assignment and encode length\n\t\tb = buf.Bytes()\n\t\tb[0] = byte(length)\n\t}\n\n\treturn\n}\n\n// UnmarshalBinary reads the InformationElements from raw binary UDH.\n// Unmarshal preserve InfoElement order as they appears in the raw data\n// The src contains the complete UDH, including the UDHL and all IEs.\n// Returns the number of bytes read from src, and the first error\n// detected while unmarshalling.\n//\n// Since UDHL can only represented in 1 byte, UnmarshalBinary\n// will only read up to a maximum of 256 byte regardless of src length\nfunc (u *UDH) UnmarshalBinary(src []byte) (int, error) {\n\tif len(src) < 1 {\n\t\treturn 0, fmt.Errorf(\"decode error UDHL %d underflow\", 0)\n\t}\n\n\tudhl := int(src[0])\n\tif udhl == 0 {\n\t\treturn 0, fmt.Errorf(\"error: UDHL length is 0, probably sender mistake forgot to include UDH but still set UDH flag in ESME_CLASS\")\n\t}\n\n\t// check length, excluding first UDHL byte\n\tif len(src)-1 < udhl {\n\t\treturn 0, fmt.Errorf(\"decode error UDH underflow, expect len %d got %d\", udhl, len(src))\n\t}\n\n\t// count number of bytes which are read\n\tvar (\n\t\tread = 1 // UDHL byte\n\t)\n\n\ties := []InfoElement{}\n\tfor read < udhl { // loop until we still have data to read\n\t\tie := InfoElement{}\n\n\t\tr, err := ie.UnmarshalBinary(src[read:])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\t// moving forward\n\t\tread += r\n\n\t\t// add info elements\n\t\ties = append(ies, ie)\n\t}\n\n\t*u = UDH(ies)\n\treturn read, nil\n}\n\n// FindInfoElement find the first occurrence of the Information Element with id\nfunc (u UDH) FindInfoElement(id byte) (ie *InfoElement, found bool) {\n\tfor i := range u {\n\t\tif u[i].ID == id {\n\t\t\treturn &u[i], true\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// GetConcatInfo return the FIRST concatenated message IE,\nfunc (u UDH) GetConcatInfo() (totalParts, partNum, mref byte, found bool) {\n\tif len(u) == 0 {\n\t\tfound = false\n\t\treturn\n\t}\n\n\tif ie, ok := u.FindInfoElement(data.UDH_CONCAT_MSG_8_BIT_REF); ok && len(ie.Data) == 3 {\n\t\tmref = ie.Data[0]\n\t\ttotalParts = ie.Data[1]\n\t\tpartNum = ie.Data[2]\n\t\tfound = ok\n\t}\n\n\treturn\n}\n\n// InfoElement represent a 3 parts Information-Element\n// as defined in 3GPP TS 23.040 Section 9.2.3.24\n// Each InfoElement is comprised of it's identifier and data\ntype InfoElement struct {\n\tID   byte\n\tData []byte\n}\n\n// NewIEConcatMessage  turn a new IE element for concat message info\n// IE.Data is populated at time of object creation\nfunc NewIEConcatMessage(totalParts, partNum, mref byte) InfoElement {\n\treturn InfoElement{\n\t\tID:   data.UDH_CONCAT_MSG_8_BIT_REF,\n\t\tData: []byte{byte(mref), byte(totalParts), byte(partNum)},\n\t}\n}\n\n// UnmarshalBinary unmarshal IE from binary in src, only read a single IE,\n// expect src at least of length 2 with correct IE format:\n//\n//\t[ ID_1, LENGTH_1, DATA_N ]\nfunc (ie *InfoElement) UnmarshalBinary(src []byte) (int, error) {\n\tif len(src) < 2 {\n\t\treturn 0, fmt.Errorf(\"decode error InfoElement underflow, len = %d\", len(src))\n\t}\n\n\t// second byte is len\n\tieLen := int(src[1])\n\n\t// check length, excluding first 2 bytes\n\tif len(src)-2 < ieLen {\n\t\treturn 0, fmt.Errorf(\"decode error InfoElement underflow, expect length %d, got %d\", ieLen, len(src))\n\t}\n\n\tie.ID = src[0]               // first byte is ID\n\tie.Data = src[2:(ieLen + 2)] // 3rd byte onward is data\n\n\treturn 2 + ieLen, nil\n}\n"
  },
  {
    "path": "pdu/UDH_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestUserDataHeader(t *testing.T) {\n\tt.Run(\"marshalBinaryUDHConcatMessage\", func(t *testing.T) {\n\t\tu := UDH{NewIEConcatMessage(2, 1, 12)}\n\t\tb, err := u.MarshalBinary()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"0500030c0201\", toHex(b))\n\t})\n\n\tt.Run(\"marshalBinaryUDHConcatMessage (8 bit)\", func(t *testing.T) {\n\t\tu := UDH{NewIEConcatMessage(2, 1, 12)}\n\t\tb, err := u.MarshalBinary()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"0500030c0201\", toHex(b))\n\n\t\ttotalParts, sequence, reference, found := u.GetConcatInfo()\n\t\trequire.True(t, found)\n\t\trequire.Equal(t, totalParts, byte(2))\n\t\trequire.Equal(t, sequence, byte(1))\n\t\trequire.Equal(t, reference, uint8(12))\n\t})\n\n\tt.Run(\"unmarshalBinaryUDHConcatMessage\", func(t *testing.T) {\n\t\tu, rd := new(UDH), []byte{0x05, 0x00, 0x03, 0x0c, 0x02, 0x01}\n\t\tread, err := u.UnmarshalBinary(rd)\n\t\trequire.False(t, read <= 0)\n\n\t\trequire.NoError(t, err)\n\n\t\tb, err := u.MarshalBinary()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"0500030c0201\", toHex(b))\n\t})\n\n\tt.Run(\"unmarshalBinaryUDHConcatMessage failed\", func(t *testing.T) {\n\t\tfailedList := [][]byte{\n\t\t\t{0x04, 0x00, 0x02, 0x02, 0x01},\n\t\t\t{0x04, 0x08, 0x02, 0x02, 0x01},\n\t\t}\n\t\tu := new(UDH)\n\t\tfor _, data := range failedList {\n\t\t\t_, _ = u.UnmarshalBinary(data)\n\t\t\t_, _, _, found := u.GetConcatInfo()\n\t\t\trequire.False(t, found, data)\n\t\t}\n\t})\n\tt.Run(\"marshalBinaryTruncateLongIE\", func(t *testing.T) {\n\t\tu := UDH{NewIEConcatMessage(2, 1, 12)}\n\t\tfor i := 0; i < 255; i++ {\n\t\t\tu = append(u, NewIEConcatMessage(2, 1, 12))\n\t\t}\n\n\t\trequire.LessOrEqual(t, u.UDHL(), 256) // UDHL must not exceed 256 ( including UDHL byte )\n\n\t\t_, err := u.MarshalBinary()\n\t\trequire.Error(t, err)\n\t})\n}\n"
  },
  {
    "path": "pdu/Unbind.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// Unbind PDU is to deregister an instance of an ESME from the SMSC and inform the SMSC\n// that the ESME no longer wishes to use this network connection for the submission or\n// delivery of messages.\ntype Unbind struct {\n\tbase\n}\n\n// NewUnbind returns Unbind PDU.\nfunc NewUnbind() PDU {\n\tc := &Unbind{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.UNBIND\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *Unbind) CanResponse() bool {\n\treturn true\n}\n\n// GetResponse implements PDU interface.\nfunc (c *Unbind) GetResponse() PDU {\n\treturn NewUnbindRespFromReq(c)\n}\n\n// Marshal implements PDU interface.\nfunc (c *Unbind) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *Unbind) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/UnbindResp.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// UnbindResp PDU.\ntype UnbindResp struct {\n\tbase\n}\n\n// NewUnbindResp returns UnbindResp.\nfunc NewUnbindResp() PDU {\n\tc := &UnbindResp{\n\t\tbase: newBase(),\n\t}\n\tc.CommandID = data.UNBIND_RESP\n\treturn c\n}\n\n// NewUnbindRespFromReq returns UnbindResp.\nfunc NewUnbindRespFromReq(req *Unbind) PDU {\n\tc := NewUnbindResp().(*UnbindResp)\n\tif req != nil {\n\t\tc.SequenceNumber = req.SequenceNumber\n\t}\n\treturn c\n}\n\n// CanResponse implements PDU interface.\nfunc (c *UnbindResp) CanResponse() bool {\n\treturn false\n}\n\n// GetResponse implements PDU interface.\nfunc (c *UnbindResp) GetResponse() PDU {\n\treturn nil\n}\n\n// Marshal implements PDU interface.\nfunc (c *UnbindResp) Marshal(b *ByteBuffer) {\n\tc.base.marshal(b, nil)\n}\n\n// Unmarshal implements PDU interface.\nfunc (c *UnbindResp) Unmarshal(b *ByteBuffer) error {\n\treturn c.base.unmarshal(b, nil)\n}\n"
  },
  {
    "path": "pdu/UnbindResp_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestUnbindResp(t *testing.T) {\n\treq := NewUnbind().(*Unbind)\n\treq.SequenceNumber = 13\n\n\tv := NewUnbindRespFromReq(req).(*UnbindResp)\n\trequire.False(t, v.CanResponse())\n\trequire.Nil(t, v.GetResponse())\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001080000006000000000000000d\",\n\t\tdata.UNBIND_RESP,\n\t)\n}\n"
  },
  {
    "path": "pdu/Unbind_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestUnbind(t *testing.T) {\n\tv := NewUnbind().(*Unbind)\n\trequire.True(t, v.CanResponse())\n\tv.SequenceNumber = 13\n\n\tvalidate(t,\n\t\tv.GetResponse(),\n\t\t\"0000001080000006000000000000000d\",\n\t\tdata.UNBIND_RESP,\n\t)\n\n\tvalidate(t,\n\t\tv,\n\t\t\"0000001000000006000000000000000d\",\n\t\tdata.UNBIND,\n\t)\n}\n"
  },
  {
    "path": "pdu/UnsuccessSME.go",
    "content": "package pdu\n\nimport (\n\t\"github.com/linxGnu/gosmpp/data\"\n)\n\n// UnsuccessSME indicates submission was unsuccessful and the respective errors.\ntype UnsuccessSME struct {\n\tAddress\n\terrorStatusCode data.CommandStatusType\n}\n\n// NewUnsuccessSME returns new UnsuccessSME\nfunc NewUnsuccessSME() (c UnsuccessSME) {\n\tc = UnsuccessSME{\n\t\tAddress:         NewAddress(),\n\t\terrorStatusCode: data.ESME_ROK,\n\t}\n\treturn\n}\n\n// NewUnsuccessSMEWithAddr returns new UnsuccessSME with address.\nfunc NewUnsuccessSMEWithAddr(addr string, status data.CommandStatusType) (c UnsuccessSME, err error) {\n\tc = NewUnsuccessSME()\n\tif err = c.SetAddress(addr); err == nil {\n\t\tc.SetErrorStatusCode(status)\n\t}\n\treturn\n}\n\n// NewUnsuccessSMEWithTonNpi create new address with ton, npi and error code.\nfunc NewUnsuccessSMEWithTonNpi(ton, npi byte, status data.CommandStatusType) UnsuccessSME {\n\treturn UnsuccessSME{\n\t\tAddress:         NewAddressWithTonNpi(ton, npi),\n\t\terrorStatusCode: status,\n\t}\n}\n\n// Unmarshal from buffer.\nfunc (c *UnsuccessSME) Unmarshal(b *ByteBuffer) (err error) {\n\tvar st int32\n\tif err = c.Address.Unmarshal(b); err == nil {\n\t\tst, err = b.ReadInt()\n\t\tif err == nil {\n\t\t\tc.errorStatusCode = data.CommandStatusType(st)\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *UnsuccessSME) Marshal(b *ByteBuffer) {\n\tc.Address.Marshal(b)\n\tb.WriteInt(int32(c.errorStatusCode))\n}\n\n// SetErrorStatusCode sets error status code.\nfunc (c *UnsuccessSME) SetErrorStatusCode(v data.CommandStatusType) {\n\tc.errorStatusCode = v\n}\n\n// ErrorStatusCode returns assigned status code.\nfunc (c *UnsuccessSME) ErrorStatusCode() data.CommandStatusType {\n\treturn c.errorStatusCode\n}\n\n// UnsuccessSMEs represents list of UnsuccessSME.\ntype UnsuccessSMEs struct {\n\tl []UnsuccessSME\n}\n\n// NewUnsuccessSMEs returns list of UnsuccessSME.\nfunc NewUnsuccessSMEs() (u UnsuccessSMEs) {\n\tu.l = make([]UnsuccessSME, 0, 8)\n\treturn\n}\n\n// Add to list.\nfunc (c *UnsuccessSMEs) Add(us ...UnsuccessSME) {\n\tc.l = append(c.l, us...)\n}\n\n// Get list.\nfunc (c *UnsuccessSMEs) Get() []UnsuccessSME {\n\treturn c.l\n}\n\n// Unmarshal from buffer.\nfunc (c *UnsuccessSMEs) Unmarshal(b *ByteBuffer) (err error) {\n\tvar n byte\n\tif n, err = b.ReadByte(); err == nil {\n\t\tc.l = make([]UnsuccessSME, n)\n\n\t\tvar i byte\n\t\tfor ; i < n; i++ {\n\t\t\tif err = c.l[i].Unmarshal(b); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// Marshal to buffer.\nfunc (c *UnsuccessSMEs) Marshal(b *ByteBuffer) {\n\tn := byte(len(c.l))\n\t_ = b.WriteByte(n)\n\n\tvar i byte\n\tfor ; i < n; i++ {\n\t\tc.l[i].Marshal(b)\n\t}\n}\n"
  },
  {
    "path": "pdu/UnsuccessSME_test.go",
    "content": "package pdu\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMalformUSME(t *testing.T) {\n\tt.Run(\"malformSME\", func(t *testing.T) {\n\t\tb := NewBuffer(nil)\n\t\tvar u UnsuccessSME\n\t\trequire.NotNil(t, u.Unmarshal(b))\n\t})\n\n\tt.Run(\"malformSMEs\", func(t *testing.T) {\n\t\tb := NewBuffer(nil)\n\t\t_ = b.WriteByte(1)\n\t\tvar u UnsuccessSMEs\n\t\trequire.NotNil(t, u.Unmarshal(b))\n\t})\n}\n"
  },
  {
    "path": "pdu/helper_test.go",
    "content": "package pdu\n\nimport (\n\t\"encoding/hex\"\n\t\"log\"\n\t\"testing\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc fromHex(h string) (v []byte) {\n\tvar err error\n\tv, err = hex.DecodeString(h)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn\n}\n\nfunc toHex(v []byte) (h string) {\n\th = hex.EncodeToString(v)\n\treturn\n}\n\nfunc validate(t *testing.T, p PDU, hexValue string, expectCommandID data.CommandIDType) {\n\tbuf := NewBuffer(nil)\n\tp.Marshal(buf)\n\trequire.Equal(t, fromHex(hexValue), buf.Bytes())\n\n\texpectAfterParse(t, buf, p, expectCommandID)\n}\n\nfunc expectAfterParse(t *testing.T, b *ByteBuffer, expect PDU, expectCommandID data.CommandIDType) {\n\tc, err := Parse(b)\n\trequire.Nil(t, err)\n\trequire.Equal(t, expect, c)\n\trequire.EqualValues(t, expectCommandID, c.GetHeader().CommandID)\n\trequire.Zero(t, b.Len())\n}\n"
  },
  {
    "path": "pkg.go",
    "content": "package gosmpp\n\nimport (\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\n// Transceiver interface.\ntype Transceiver interface {\n\tio.Closer\n\tSubmit(pdu.PDU) error\n\tSystemID() string\n}\n\n// Transmitter interface.\ntype Transmitter interface {\n\tio.Closer\n\tSubmit(pdu.PDU) error\n\tSystemID() string\n}\n\n// Receiver interface.\ntype Receiver interface {\n\tio.Closer\n\tSystemID() string\n}\n\n// Settings for TX (transmitter), RX (receiver), TRX (transceiver).\ntype Settings struct {\n\t// ReadTimeout is timeout for reading PDU from SMSC.\n\t// Underlying net.Conn will be stricted with ReadDeadline(now + timeout).\n\t// This setting is very important to detect connection failure.\n\t//\n\t// Must: ReadTimeout > max(0, EnquireLink)\n\tReadTimeout time.Duration\n\n\t// WriteTimeout is timeout for submitting PDU.\n\tWriteTimeout time.Duration\n\n\t// EnquireLink periodically sends EnquireLink to SMSC.\n\t// The duration must not be smaller than 1 minute.\n\t//\n\t// Zero duration disables auto enquire link.\n\tEnquireLink time.Duration\n\n\t// OnPDU handles received PDU from SMSC.\n\t//\n\t// `Responded` flag indicates this pdu is responded automatically,\n\t// no manual respond needed.\n\t//\n\t// Will be ignored if OnAllPDU or WindowedRequestTracking is set\n\tOnPDU PDUCallback\n\n\t// OnAllPDU handles all received PDU from SMSC.\n\t//\n\t// This pdu is NOT responded to automatically,\n\t// manual response/handling is needed\n\t//\n\t// User can also decide to close bind by retuning true, default is false\n\t//\n\t// Will be ignored if WindowedRequestTracking is set\n\tOnAllPDU AllPDUCallback\n\n\t// OnReceivingError notifies happened error while reading PDU\n\t// from SMSC.\n\tOnReceivingError ErrorCallback\n\n\t// OnSubmitError notifies fail-to-submit PDU with along error.\n\tOnSubmitError PDUErrorCallback\n\n\t// OnRebindingError notifies error while rebinding.\n\tOnRebindingError ErrorCallback\n\n\t// OnClosed notifies `closed` event due to State.\n\tOnClosed ClosedCallback\n\n\t// OnRebind notifies `rebind` event due to State.\n\tOnRebind RebindCallback\n\n\t// SMPP Bind Window tracking feature config\n\t*WindowedRequestTracking\n\n\tresponse func(pdu.PDU)\n}\n\n// WindowedRequestTracking settings for TX (transmitter) and TRX (transceiver) request store.\ntype WindowedRequestTracking struct {\n\n\t// OnReceivedPduRequest handles received PDU request from SMSC.\n\t//\n\t// User can also decide to close bind by retuning true, default is false\n\tOnReceivedPduRequest AllPDUCallback\n\n\t// OnExpectedPduResponse handles expected PDU response from SMSC.\n\t// Only triggered when the original request is found in the window cache\n\t//\n\t// Handle is optional\n\t// If not set, response will be dropped\n\tOnExpectedPduResponse func(Response)\n\n\t// OnUnexpectedPduResponse handles unexpected PDU response from SMSC.\n\t// Only triggered if the original request is not found in the window cache\n\t//\n\t// Handle is optional\n\t// If not set, response will be dropped\n\tOnUnexpectedPduResponse func(pdu.PDU)\n\n\t// OnExpiredPduRequest handles expired PDU request with no response received\n\t//\n\t// Mandatory: the PduExpireTimeOut must be set\n\t// Handle is optional\n\t// If not set, expired PDU will be removed from cache\n\t// the bind can be closed by retuning true on closeBind.\n\tOnExpiredPduRequest func(pdu.PDU) (closeBind bool)\n\n\t// OnClosePduRequest will return all PDU request found in the store when the bind closes\n\tOnClosePduRequest func(pdu.PDU)\n\n\t// Set the time duration to expire a request sent to the SMSC\n\t//\n\t// Zero duration disables pdu expire check, and the cache may fill up over time with expired PDU request\n\t// Recommended: equal or less to the value set in ReadTimeout + EnquireLink\n\tPduExpireTimeOut time.Duration\n\n\t// The time period between each check of the expired PDU in the cache\n\t//\n\t// Zero duration disables pdu expire check and the cache may fill up over time with expired PDU request\n\t// Recommended: Less or half the time set in for PduExpireTimeOut\n\t// Don't be too aggressive, there is a performance hit if the check is done often\n\tExpireCheckTimer time.Duration\n\n\t// The maximum number of pending requests sent to the SMSC\n\t//\n\t// Maximum value is 255\n\tMaxWindowSize uint8\n\n\t// if enabled, EnquireLink and Unbind request will be responded to automatically\n\tEnableAutoRespond bool\n\n\t// Set the time duration to expire a request for storing or retrieving data from request store\n\t//\n\t// Value must be greater than 0\n\t// 200 to 1000 milliseconds is a good starting point\n\tStoreAccessTimeOut time.Duration\n}\n"
  },
  {
    "path": "receivable.go",
    "content": "package gosmpp\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\ntype receivable struct {\n\tctx          context.Context\n\tcancel       context.CancelFunc\n\twg           sync.WaitGroup\n\tsettings     Settings\n\tconn         *Connection\n\taliveState   int32\n\trequestStore RequestStore\n}\n\nfunc newReceivable(conn *Connection, settings Settings, requestStore RequestStore) *receivable {\n\tr := &receivable{\n\t\tsettings:     settings,\n\t\tconn:         conn,\n\t\trequestStore: requestStore,\n\t}\n\tr.ctx, r.cancel = context.WithCancel(context.Background())\n\n\treturn r\n}\n\nfunc (t *receivable) close(state State) (err error) {\n\tif atomic.CompareAndSwapInt32(&t.aliveState, Alive, Closed) {\n\t\t// cancel to notify stop\n\t\tt.cancel()\n\n\t\t// set read deadline for current blocking read\n\t\t_ = t.conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond))\n\n\t\t// wait daemons\n\t\tt.wg.Wait()\n\n\t\t// close connection to notify daemons to stop\n\t\tif state != StoppingProcessOnly {\n\t\t\terr = t.conn.Close()\n\t\t}\n\n\t\t// notify receiver closed\n\t\tif t.settings.OnClosed != nil {\n\t\t\tt.settings.OnClosed(state)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (t *receivable) closing(state State) {\n\tgo func() {\n\t\t_ = t.close(state)\n\t}()\n}\n\nfunc (t *receivable) start() {\n\tt.wg.Add(1)\n\tgo func() {\n\t\tdefer t.wg.Done()\n\t\tt.loop()\n\t}()\n}\n\nfunc (t *receivable) loop() {\n\tvar err error\n\tfor {\n\t\tselect {\n\t\tcase <-t.ctx.Done():\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\t// read pdu from conn\n\t\tvar p pdu.PDU\n\t\tif err = t.conn.SetReadTimeout(t.settings.ReadTimeout); err == nil {\n\t\t\tp, err = pdu.Parse(t.conn)\n\t\t}\n\t\tif err != nil {\n\t\t\tif atomic.LoadInt32(&t.aliveState) == Alive {\n\t\t\t\tif t.settings.OnReceivingError != nil {\n\t\t\t\t\tt.settings.OnReceivingError(err)\n\t\t\t\t}\n\t\t\t\tt.closing(InvalidStreaming)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tvar closeOnUnbind bool\n\t\tif p != nil {\n\t\t\tif t.settings.WindowedRequestTracking != nil && t.settings.OnExpectedPduResponse != nil {\n\t\t\t\tcloseOnUnbind = t.handleWindowPdu(p)\n\t\t\t} else if t.settings.OnAllPDU != nil {\n\t\t\t\tcloseOnUnbind = t.handleAllPdu(p)\n\t\t\t} else {\n\t\t\t\tcloseOnUnbind = t.handleOrClose(p)\n\t\t\t}\n\t\t\tif closeOnUnbind {\n\t\t\t\tt.closing(UnbindClosing)\n\t\t\t}\n\t\t}\n\n\t}\n}\n\nfunc (t *receivable) handleWindowPdu(p pdu.PDU) (closing bool) {\n\tif t.settings.WindowedRequestTracking != nil && t.settings.OnExpectedPduResponse != nil && p != nil {\n\t\t// This case must match the same request item list in transmittable write func\n\t\tswitch pp := p.(type) {\n\t\tcase *pdu.CancelSMResp,\n\t\t\t*pdu.DataSMResp,\n\t\t\t*pdu.DeliverSMResp,\n\t\t\t*pdu.EnquireLinkResp,\n\t\t\t*pdu.QuerySMResp,\n\t\t\t*pdu.ReplaceSMResp,\n\t\t\t*pdu.SubmitMultiResp,\n\t\t\t*pdu.SubmitSMResp:\n\t\t\tif t.settings.OnExpectedPduResponse != nil {\n\t\t\t\tctx, cancelFunc := context.WithTimeout(context.Background(), t.settings.StoreAccessTimeOut)\n\t\t\t\tdefer cancelFunc()\n\t\t\t\trequest, ok := t.requestStore.Get(ctx, p.GetSequenceNumber())\n\t\t\t\tif ok {\n\t\t\t\t\t_ = t.requestStore.Delete(ctx, p.GetSequenceNumber())\n\t\t\t\t\tresponse := Response{\n\t\t\t\t\t\tPDU:             p,\n\t\t\t\t\t\tOriginalRequest: request,\n\t\t\t\t\t}\n\t\t\t\t\tt.settings.OnExpectedPduResponse(response)\n\t\t\t\t} else if t.settings.OnUnexpectedPduResponse != nil {\n\t\t\t\t\tt.settings.OnUnexpectedPduResponse(p)\n\t\t\t\t}\n\t\t\t}\n\t\tcase *pdu.EnquireLink:\n\t\t\tif t.settings.EnableAutoRespond {\n\t\t\t\tt.settings.response(pp.GetResponse())\n\t\t\t} else if t.settings.OnReceivedPduRequest != nil {\n\t\t\t\tr, _ := t.settings.OnReceivedPduRequest(p)\n\t\t\t\tt.settings.response(r)\n\n\t\t\t}\n\t\tcase *pdu.Unbind:\n\t\t\tif t.settings.EnableAutoRespond {\n\t\t\t\tt.settings.response(pp.GetResponse())\n\n\t\t\t\t// wait to send response before closing\n\t\t\t\ttime.Sleep(50 * time.Millisecond)\n\t\t\t\tclosing = true\n\t\t\t} else if t.settings.OnReceivedPduRequest != nil {\n\t\t\t\tr, closeBind := t.settings.OnReceivedPduRequest(p)\n\t\t\t\tt.settings.response(r)\n\t\t\t\tif closeBind {\n\t\t\t\t\ttime.Sleep(50 * time.Millisecond)\n\t\t\t\t\tclosing = true\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tif t.settings.OnReceivedPduRequest != nil {\n\t\t\t\tr, closeBind := t.settings.OnReceivedPduRequest(p)\n\t\t\t\tt.settings.response(r)\n\t\t\t\tif closeBind {\n\t\t\t\t\ttime.Sleep(50 * time.Millisecond)\n\t\t\t\t\tclosing = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (t *receivable) handleAllPdu(p pdu.PDU) (closing bool) {\n\tif t.settings.OnAllPDU != nil && p != nil {\n\t\tr, closeBind := t.settings.OnAllPDU(p)\n\t\tt.settings.response(r)\n\t\tif closeBind {\n\t\t\ttime.Sleep(50 * time.Millisecond)\n\t\t\tclosing = true\n\t\t}\n\t}\n\treturn\n}\n\nfunc (t *receivable) handleOrClose(p pdu.PDU) (closing bool) {\n\tif p != nil {\n\t\tswitch pp := p.(type) {\n\t\tcase *pdu.EnquireLink:\n\t\t\tt.settings.response(pp.GetResponse())\n\n\t\tcase *pdu.Unbind:\n\t\t\tt.settings.response(pp.GetResponse())\n\t\t\t// wait to send response before closing\n\t\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\t\tclosing = true\n\n\t\tdefault:\n\t\t\tvar responded bool\n\t\t\tif p.CanResponse() {\n\t\t\t\tt.settings.response(p.GetResponse())\n\t\t\t\tresponded = true\n\t\t\t}\n\n\t\t\tif t.settings.OnPDU != nil {\n\t\t\t\tt.settings.OnPDU(p, responded)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "receivable_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"fmt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestReceive(t *testing.T) {\n\tauth := nextAuth()\n\treceiver, err := NewSession(\n\t\tRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnPDU: func(p pdu.PDU, _ bool) {\n\t\t\t\tt.Log(p)\n\t\t\t},\n\n\t\t\tOnClosed: func(state State) {\n\t\t\t\tt.Log(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\trequire.Nil(t, err)\n\trequire.NotNil(t, receiver)\n\tdefer func() {\n\t\t_ = receiver.Close()\n\t}()\n\n\trequire.Equal(t, \"MelroseLabsSMSC\", receiver.Receiver().SystemID())\n\n\ttime.Sleep(time.Second)\n\treceiver.rebind()\n}\n\nfunc Test_receivable_handleAllPdu(t1 *testing.T) {\n\ttype fields struct {\n\t\tsettings Settings\n\t}\n\ttype args struct {\n\t\tp pdu.PDU\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\tfields      fields\n\t\targs        args\n\t\twantClosing bool\n\t}{\n\t\t{\n\t\t\tname:        \"nil setting\",\n\t\t\tfields:      fields{},\n\t\t\targs:        args{},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:        \"nil pdu\",\n\t\t\tfields:      fields{},\n\t\t\targs:        args{},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname: \"DeliverSM pdu\",\n\t\t\tfields: fields{\n\t\t\t\tsettings: newTransceivable(nil, Settings{\n\t\t\t\t\tOnAllPDU: receivableHandleAllPDU(t1),\n\t\t\t\t}, nil).in.settings,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewDeliverSM(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname: \"EnquireLink pdu\",\n\t\t\tfields: fields{\n\t\t\t\tsettings: newTransceivable(nil, Settings{\n\t\t\t\t\tOnAllPDU: receivableHandleAllPDU(t1),\n\t\t\t\t}, nil).in.settings,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewEnquireLink(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t\t/*{\n\t\t\tname: \"Undind pdu\", // run this as the last test case\n\t\t\tfields: fields{\n\t\t\t\tsettings: newTransceivable(nil, Settings{\n\t\t\t\t\tOnAllPDU: receivableHandleAllPDU(t1),\n\t\t\t\t}).in.settings,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewUnbind(),\n\t\t\t},\n\t\t\twantClosing: true,\n\t\t},*/\n\t}\n\tfor _, tt := range tests {\n\t\tt1.Run(tt.name, func(t1 *testing.T) {\n\t\t\tt := &receivable{\n\t\t\t\tsettings: tt.fields.settings,\n\t\t\t}\n\t\t\tassert.Equalf(t1, tt.wantClosing, t.handleAllPdu(tt.args.p), \"handleAllPdu(%v)\", tt.args.p)\n\t\t})\n\t}\n}\n\nfunc receivableHandleAllPDU(t1 *testing.T) func(pdu.PDU) (pdu.PDU, bool) {\n\treturn func(p pdu.PDU) (pdu.PDU, bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\tfmt.Println(\"Unbind Received\")\n\t\t\treturn pd.GetResponse(), true\n\n\t\tcase *pdu.UnbindResp:\n\t\t\tt1.Log(\"UnbindResp Received\")\n\n\t\tcase *pdu.SubmitSMResp:\n\t\t\tt1.Log(\"SubmitSMResp Received\")\n\n\t\tcase *pdu.GenericNack:\n\t\t\tt1.Log(\"GenericNack Received\")\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tt1.Log(\"EnquireLinkResp Received\")\n\n\t\tcase *pdu.EnquireLink:\n\t\t\tt1.Log(\"EnquireLink Received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DataSM:\n\t\t\tt1.Log(\"DataSM received\")\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DeliverSM:\n\t\t\tt1.Log(\"DeliverSM received\")\n\t\t\treturn pd.GetResponse(), false\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\nfunc Test_receivable_handleOrClose(t1 *testing.T) {\n\ttype fields struct {\n\t\tsettings Settings\n\t}\n\ttype args struct {\n\t\tp pdu.PDU\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\tfields      fields\n\t\targs        args\n\t\twantClosing bool\n\t}{\n\t\t{\n\t\t\tname:        \"nil setting\",\n\t\t\tfields:      fields{},\n\t\t\targs:        args{},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:        \"nil pdu\",\n\t\t\tfields:      fields{},\n\t\t\targs:        args{},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname: \"EnquireLink pdu\",\n\t\t\tfields: fields{\n\t\t\t\tsettings: newTransceivable(nil, Settings{}, nil).in.settings,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewEnquireLink(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t\t/*{\n\t\t\tname: \"Undind pdu\", // run this as the last test case\n\t\t\tfields: fields{\n\t\t\t\tsettings: newTransceivable(nil, Settings{}).in.settings,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewUnbind(),\n\t\t\t},\n\t\t\twantClosing: true,\n\t\t},*/\n\t}\n\tfor _, tt := range tests {\n\t\tt1.Run(tt.name, func(t1 *testing.T) {\n\t\t\tt := &receivable{\n\t\t\t\tsettings: tt.fields.settings,\n\t\t\t}\n\t\t\tassert.Equalf(t1, tt.wantClosing, t.handleOrClose(tt.args.p), \"handleOrClose(%v)\", tt.args.p)\n\t\t})\n\t}\n}\n\nfunc Test_receivable_handleWindowPdu(t1 *testing.T) {\n\ttype fields struct {\n\t\tsettings Settings\n\t}\n\ttype args struct {\n\t\tp pdu.PDU\n\t}\n\ttests := []struct {\n\t\tname        string\n\t\tfields      fields\n\t\targs        args\n\t\twantClosing bool\n\t}{\n\t\t{\n\t\t\tname:        \"nil setting\",\n\t\t\tfields:      fields{},\n\t\t\targs:        args{},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:        \"nil pdu\",\n\t\t\tfields:      fields{},\n\t\t\targs:        args{},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:   \"EnquireLink pdu\",\n\t\t\tfields: fields{},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewEnquireLink(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:   \"EnquireLinkResp pdu\",\n\t\t\tfields: fields{},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewEnquireLink().GetResponse(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:   \"DeliverSM pdu\",\n\t\t\tfields: fields{},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewDeliverSM(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t\t{\n\t\t\tname:   \"SubmitSMResp pdu\",\n\t\t\tfields: fields{},\n\t\t\targs: args{\n\t\t\t\tp: pdu.NewSubmitSM().GetResponse(),\n\t\t\t},\n\t\t\twantClosing: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt1.Run(tt.name, func(t1 *testing.T) {\n\t\t\tt := &receivable{\n\t\t\t\tsettings: tt.fields.settings,\n\t\t\t}\n\t\t\tassert.Equalf(t1, tt.wantClosing, t.handleWindowPdu(tt.args.p), \"handleWindowPdu(%v)\", tt.args.p)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "request_store.go",
    "content": "package gosmpp\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\tcmap \"github.com/orcaman/concurrent-map/v2\"\n\t\"golang.org/x/exp/maps\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// Request represent a request tracked by the RequestStore\ntype Request struct {\n\tpdu.PDU\n\tTimeSent time.Time\n}\n\n// Response represents a response from a Request in the RequestStore\ntype Response struct {\n\tpdu.PDU\n\tOriginalRequest Request\n}\n\n// RequestStore interface used for WindowedRequestTracking\ntype RequestStore interface {\n\tSet(ctx context.Context, request Request) error\n\tGet(ctx context.Context, sequenceNumber int32) (Request, bool)\n\tList(ctx context.Context) []Request\n\tDelete(ctx context.Context, sequenceNumber int32) error\n\tClear(ctx context.Context) error\n\tLength(ctx context.Context) (int, error)\n}\n\ntype DefaultStore struct {\n\tstore cmap.ConcurrentMap[string, Request]\n}\n\nfunc NewDefaultStore() DefaultStore {\n\treturn DefaultStore{\n\t\tstore: cmap.New[Request](),\n\t}\n}\n\nfunc (s DefaultStore) Set(ctx context.Context, request Request) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\tfmt.Println(\"Task cancelled\")\n\t\treturn ctx.Err()\n\tdefault:\n\t\ts.store.Set(strconv.Itoa(int(request.PDU.GetSequenceNumber())), request)\n\t\treturn nil\n\t}\n}\n\nfunc (s DefaultStore) Get(ctx context.Context, sequenceNumber int32) (Request, bool) {\n\tselect {\n\tcase <-ctx.Done():\n\t\tfmt.Println(\"Task cancelled\")\n\t\treturn Request{}, false\n\tdefault:\n\t\treturn s.store.Get(strconv.Itoa(int(sequenceNumber)))\n\t}\n}\n\nfunc (s DefaultStore) List(ctx context.Context) []Request {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn []Request{}\n\tdefault:\n\t\treturn maps.Values(s.store.Items())\n\t}\n}\n\nfunc (s DefaultStore) Delete(ctx context.Context, sequenceNumber int32) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tdefault:\n\t\ts.store.Remove(strconv.Itoa(int(sequenceNumber)))\n\t\treturn nil\n\t}\n}\n\nfunc (s DefaultStore) Clear(ctx context.Context) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tdefault:\n\t\ts.store.Clear()\n\t\treturn nil\n\t}\n}\n\nfunc (s DefaultStore) Length(ctx context.Context) (int, error) {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn 0, ctx.Err()\n\tdefault:\n\t\treturn s.store.Count(), nil\n\t}\n}\n"
  },
  {
    "path": "session.go",
    "content": "package gosmpp\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nvar (\n\tErrWindowSizeEqualZero                   = errors.New(\"request window size cannot be 0\")\n\tErrExpireCheckTimerNotSet                = errors.New(\"ExpireCheckTimer cannot be 0 if PduExpireTimeOut is set\")\n\tErrStoreAccessTimeOutEqualZero           = errors.New(\"StoreAccessTimeOut window size cannot be 0\")\n\tErrWindowSizeNotAvailableOnReceiverBinds = errors.New(\"window size not available on receiver binds\")\n)\n\n// Session represents session for TX, RX, TRX.\ntype Session struct {\n\tc Connector\n\n\toriginalOnClosed func(State)\n\tsettings         Settings\n\n\trebindingInterval time.Duration\n\n\ttrx atomic.Value // transceivable\n\n\tstate        int32\n\trebinding    int32\n\trequestStore RequestStore\n}\n\ntype SessionOption func(session *Session)\n\n// NewSession creates new session for TX, RX, TRX.\n//\n// Session will `non-stop`, automatically rebind (create new and authenticate connection with SMSC) when\n// unexpected error happened.\n//\n// `rebindingInterval` indicates duration that Session has to wait before rebinding again.\n//\n// Setting `rebindingInterval <= 0` will disable `auto-rebind` functionality.\nfunc NewSession(c Connector, settings Settings, rebindingInterval time.Duration, opts ...SessionOption) (session *Session, err error) {\n\t// Loop through each option\n\n\tif settings.ReadTimeout <= 0 || settings.ReadTimeout <= settings.EnquireLink {\n\t\treturn nil, fmt.Errorf(\"invalid settings: ReadTimeout must greater than max(0, EnquireLink)\")\n\t}\n\tvar requestStore RequestStore = nil\n\tif settings.WindowedRequestTracking != nil {\n\t\trequestStore = NewDefaultStore()\n\t\tif settings.MaxWindowSize == 0 {\n\t\t\treturn nil, ErrWindowSizeEqualZero\n\t\t}\n\t\tif settings.StoreAccessTimeOut == 0 {\n\t\t\treturn nil, ErrStoreAccessTimeOutEqualZero\n\t\t}\n\t\tif settings.PduExpireTimeOut > 0 && settings.ExpireCheckTimer == 0 {\n\t\t\treturn nil, ErrExpireCheckTimerNotSet\n\t\t}\n\t}\n\n\tconn, err := c.Connect()\n\tif err == nil {\n\t\tsession = &Session{\n\t\t\tc:                 c,\n\t\t\trebindingInterval: rebindingInterval,\n\t\t\toriginalOnClosed:  settings.OnClosed,\n\t\t\trequestStore:      requestStore,\n\t\t}\n\n\t\tfor _, opt := range opts {\n\t\t\topt(session)\n\t\t}\n\n\t\tif rebindingInterval > 0 {\n\t\t\tnewSettings := settings\n\t\t\tnewSettings.OnClosed = func(state State) {\n\t\t\t\tswitch state {\n\t\t\t\tcase ExplicitClosing:\n\t\t\t\t\treturn\n\n\t\t\t\tdefault:\n\t\t\t\t\tif session.originalOnClosed != nil {\n\t\t\t\t\t\tsession.originalOnClosed(state)\n\t\t\t\t\t}\n\t\t\t\t\tsession.rebind()\n\t\t\t\t}\n\t\t\t}\n\t\t\tsession.settings = newSettings\n\t\t} else {\n\t\t\tsession.settings = settings\n\t\t}\n\n\t\t// bind to session\n\t\ttrans := newTransceivable(conn, session.settings, session.requestStore)\n\t\ttrans.start()\n\t\tsession.trx.Store(trans)\n\t}\n\treturn\n}\n\nfunc WithRequestStore(store RequestStore) SessionOption {\n\treturn func(s *Session) {\n\t\ts.requestStore = store\n\t}\n}\n\nfunc (s *Session) bound() *transceivable {\n\tr, _ := s.trx.Load().(*transceivable)\n\treturn r\n}\n\n// Transmitter returns bound Transmitter.\nfunc (s *Session) Transmitter() Transmitter {\n\treturn s.bound()\n}\n\n// Receiver returns bound Receiver.\nfunc (s *Session) Receiver() Receiver {\n\treturn s.bound()\n}\n\n// Transceiver returns bound Transceiver.\nfunc (s *Session) Transceiver() Transceiver {\n\treturn s.bound()\n}\n\nfunc (s *Session) GetWindowSize() (int, error) {\n\tif s.c.GetBindType() == pdu.Transmitter || s.c.GetBindType() == pdu.Transceiver {\n\t\tsize, err := s.bound().GetWindowSize()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn size, nil\n\t}\n\treturn 0, ErrWindowSizeNotAvailableOnReceiverBinds\n}\n\n// Close session.\nfunc (s *Session) Close() (err error) {\n\tif atomic.CompareAndSwapInt32(&s.state, Alive, Closed) {\n\t\terr = s.close()\n\t}\n\treturn\n}\n\nfunc (s *Session) close() (err error) {\n\tif b := s.bound(); b != nil {\n\t\terr = b.Close()\n\t}\n\treturn\n}\n\nfunc (s *Session) rebind() {\n\tif atomic.CompareAndSwapInt32(&s.rebinding, 0, 1) {\n\t\t_ = s.close()\n\n\t\tfor atomic.LoadInt32(&s.state) == Alive {\n\t\t\tconn, err := s.c.Connect()\n\t\t\tif err != nil {\n\t\t\t\tif s.settings.OnRebindingError != nil {\n\t\t\t\t\ts.settings.OnRebindingError(err)\n\t\t\t\t}\n\t\t\t\ttime.Sleep(s.rebindingInterval)\n\t\t\t} else {\n\t\t\t\t// bind to session\n\t\t\t\ttrans := newTransceivable(conn, s.settings, s.requestStore)\n\t\t\t\ttrans.start()\n\t\t\t\ts.trx.Store(trans)\n\n\t\t\t\t// reset rebinding state\n\t\t\t\tatomic.StoreInt32(&s.rebinding, 0)\n\t\t\t\tif s.settings.OnRebind != nil {\n\t\t\t\t\ts.settings.OnRebind()\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "session_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestInvalidSessionSettings(t *testing.T) {\n\tauth := nextAuth()\n\n\t_, err := NewSession(\n\t\tTXConnector(NonTLSDialer, auth),\n\t\tSettings{}, 2*time.Second)\n\trequire.Error(t, err)\n\n\t_, err = NewSession(\n\t\tRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 200 * time.Millisecond,\n\t\t\tEnquireLink: 333 * time.Millisecond,\n\t\t}, 2*time.Second)\n\trequire.Error(t, err)\n}\n\nfunc TestGetWindowSize(t *testing.T) {\n\n\tauth := nextAuth()\n\n\ts, err := NewSession(\n\t\tTXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tEnquireLink: 5 * time.Second,\n\t\t\tReadTimeout: 10 * time.Second,\n\t\t\tWindowedRequestTracking: &WindowedRequestTracking{\n\t\t\t\tOnReceivedPduRequest: handleReceivedPduRequest(t),\n\t\t\t\tMaxWindowSize:        10,\n\t\t\t\tStoreAccessTimeOut:   100 * time.Millisecond,\n\t\t\t},\n\t\t}, 2*time.Second)\n\trequire.Nil(t, err)\n\tsize, err := s.GetWindowSize()\n\tif err != nil {\n\t\treturn\n\t}\n\trequire.Nil(t, err)\n\trequire.Equal(t, 0, size)\n\terr = s.Close()\n\trequire.Nil(t, err)\n\n\ts, err = NewSession(\n\t\tRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tEnquireLink: 5 * time.Second,\n\t\t\tReadTimeout: 10 * time.Second,\n\t\t\tWindowedRequestTracking: &WindowedRequestTracking{\n\t\t\t\tOnReceivedPduRequest: handleReceivedPduRequest(t),\n\t\t\t\tMaxWindowSize:        10,\n\t\t\t\tStoreAccessTimeOut:   100 * time.Millisecond,\n\t\t\t},\n\t\t}, 2*time.Second)\n\trequire.Nil(t, err)\n\tsize, err = s.GetWindowSize()\n\tif err != nil {\n\t\treturn\n\t}\n\trequire.Nil(t, err)\n\trequire.Equal(t, -1, size)\n\terr = s.Close()\n\trequire.Nil(t, err)\n\n\ts, err = NewSession(\n\t\tTRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tEnquireLink: 5 * time.Second,\n\t\t\tReadTimeout: 10 * time.Second,\n\t\t\tWindowedRequestTracking: &WindowedRequestTracking{\n\t\t\t\tMaxWindowSize:      10,\n\t\t\t\tStoreAccessTimeOut: 100 * time.Millisecond,\n\t\t\t},\n\t\t}, 2*time.Second)\n\trequire.NoError(t, err)\n\tsize, err = s.GetWindowSize()\n\tif err != nil {\n\t\treturn\n\t}\n\trequire.Nil(t, err)\n\trequire.Equal(t, 0, size)\n\terr = s.Close()\n\trequire.Nil(t, err)\n\n\ts, err = NewSession(\n\t\tTRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tEnquireLink: 5 * time.Second,\n\t\t\tReadTimeout: 10 * time.Second,\n\t\t\tWindowedRequestTracking: &WindowedRequestTracking{\n\t\t\t\tExpireCheckTimer:   5,\n\t\t\t\tPduExpireTimeOut:   10,\n\t\t\t\tMaxWindowSize:      10,\n\t\t\t\tStoreAccessTimeOut: 100 * time.Millisecond,\n\t\t\t},\n\t\t}, 2*time.Second)\n\trequire.NoError(t, err)\n\tsize, err = s.GetWindowSize()\n\tif err != nil {\n\t\treturn\n\t}\n\trequire.Nil(t, err)\n\trequire.Equal(t, -1, size)\n\terr = s.Close()\n\trequire.Nil(t, err)\n}\n"
  },
  {
    "path": "state.go",
    "content": "package gosmpp\n\nconst (\n\tAlive int32 = iota\n\tClosed\n)\n\n// State represents Transmitter/Receiver/Transceiver state.\ntype State byte\n\nconst (\n\t// ExplicitClosing indicates that Transmitter/Receiver/Transceiver is closed\n\t// explicitly (from outside).\n\tExplicitClosing State = iota\n\n\t// StoppingProcessOnly stops daemons but does not close underlying net conn.\n\tStoppingProcessOnly\n\n\t// InvalidStreaming indicates Transceiver/Receiver data reading state is\n\t// invalid due to network connection or SMSC responsed with an invalid PDU\n\t// which potentially damages other following PDU(s).\n\t//\n\t// In both cases, Transceiver/Receiver is closed implicitly.\n\tInvalidStreaming\n\n\t// ConnectionIssue indicates that Transmitter/Receiver/Transceiver is closed\n\t// due to network connection issue or SMSC is not available anymore.\n\tConnectionIssue\n\n\t// UnbindClosing indicates Receiver got unbind request from SMSC and closed due to this request.\n\tUnbindClosing\n)\n\n// String interface.\nfunc (s *State) String() string {\n\tswitch *s {\n\tcase ExplicitClosing:\n\t\treturn \"ExplicitClosing\"\n\n\tcase StoppingProcessOnly:\n\t\treturn \"StoppingProcessOnly\"\n\n\tcase InvalidStreaming:\n\t\treturn \"InvalidStreaming\"\n\n\tcase ConnectionIssue:\n\t\treturn \"ConnectionIssue\"\n\n\tcase UnbindClosing:\n\t\treturn \"UnbindClosing\"\n\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "state_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestState_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\ts    State\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"ExplicitClosing\",\n\t\t\ts:    ExplicitClosing,\n\t\t\twant: \"ExplicitClosing\",\n\t\t},\n\t\t{\n\t\t\tname: \"StoppingProcessOnly\",\n\t\t\ts:    StoppingProcessOnly,\n\t\t\twant: \"StoppingProcessOnly\",\n\t\t},\n\t\t{\n\t\t\tname: \"InvalidStreaming\",\n\t\t\ts:    InvalidStreaming,\n\t\t\twant: \"InvalidStreaming\",\n\t\t},\n\t\t{\n\t\t\tname: \"ConnectionIssue\",\n\t\t\ts:    ConnectionIssue,\n\t\t\twant: \"ConnectionIssue\",\n\t\t},\n\t\t{\n\t\t\tname: \"UnbindClosing\",\n\t\t\ts:    UnbindClosing,\n\t\t\twant: \"UnbindClosing\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tassert.Equalf(t, tt.want, tt.s.String(), \"String()\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "transceivable.go",
    "content": "package gosmpp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nvar (\n\tErrWindowNotConfigured = errors.New(\"window settings not configured\")\n)\n\ntype transceivable struct {\n\tsettings Settings\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\twg     sync.WaitGroup\n\tconn   *Connection\n\tin     *receivable\n\tout    *transmittable\n\n\taliveState   int32\n\trequestStore RequestStore\n}\ntype TransceivableOption func(session *Session)\n\nfunc newTransceivable(conn *Connection, settings Settings, requestStore RequestStore) *transceivable {\n\n\tt := &transceivable{\n\t\tsettings:     settings,\n\t\tconn:         conn,\n\t\trequestStore: requestStore,\n\t}\n\tt.ctx, t.cancel = context.WithCancel(context.Background())\n\n\tt.out = newTransmittable(conn, Settings{\n\t\tWriteTimeout: settings.WriteTimeout,\n\n\t\tEnquireLink: settings.EnquireLink,\n\n\t\tOnSubmitError: settings.OnSubmitError,\n\n\t\tOnClosed: func(state State) {\n\t\t\tswitch state {\n\t\t\tcase ConnectionIssue:\n\t\t\t\t// also close input\n\t\t\t\t_ = t.in.close(ExplicitClosing)\n\n\t\t\t\tif t.settings.OnClosed != nil {\n\t\t\t\t\tt.settings.OnClosed(ConnectionIssue)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t\t}\n\t\t},\n\n\t\tWindowedRequestTracking: settings.WindowedRequestTracking,\n\t}, requestStore)\n\n\tt.in = newReceivable(conn, Settings{\n\t\tReadTimeout: settings.ReadTimeout,\n\n\t\tOnPDU: settings.OnPDU,\n\n\t\tOnAllPDU: settings.OnAllPDU,\n\n\t\tOnReceivingError: settings.OnReceivingError,\n\n\t\tOnClosed: func(state State) {\n\t\t\tswitch state {\n\t\t\tcase InvalidStreaming, UnbindClosing:\n\t\t\t\t// also close output\n\t\t\t\t_ = t.out.close(ExplicitClosing)\n\n\t\t\t\tif t.settings.OnClosed != nil {\n\t\t\t\t\tt.settings.OnClosed(state)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t\t}\n\t\t},\n\n\t\tWindowedRequestTracking: settings.WindowedRequestTracking,\n\n\t\tresponse: func(p pdu.PDU) {\n\t\t\t_ = t.Submit(p)\n\t\t},\n\t},\n\t\trequestStore,\n\t)\n\treturn t\n}\n\nfunc (t *transceivable) start() {\n\tif t.settings.WindowedRequestTracking != nil && t.settings.ExpireCheckTimer > 0 {\n\t\tt.wg.Add(1)\n\t\tgo func() {\n\t\t\tdefer t.wg.Done()\n\t\t\tt.windowCleanup()\n\t\t}()\n\n\t}\n\tt.out.start()\n\tt.in.start()\n}\n\n// SystemID returns tagged SystemID which is attached with bind_resp from SMSC.\nfunc (t *transceivable) SystemID() string {\n\treturn t.conn.systemID\n}\n\n// Close transceiver and stop underlying daemons.\nfunc (t *transceivable) Close() (err error) {\n\treturn t.closing(ExplicitClosing)\n}\n\n// Submit a PDU.\nfunc (t *transceivable) Submit(p pdu.PDU) error {\n\treturn t.out.Submit(p)\n}\n\nfunc (t *transceivable) GetWindowSize() (int, error) {\n\tif t.settings.WindowedRequestTracking != nil {\n\t\tctx, cancelFunc := context.WithTimeout(context.Background(), t.settings.StoreAccessTimeOut)\n\t\tdefer cancelFunc()\n\t\treturn t.requestStore.Length(ctx)\n\t}\n\treturn 0, ErrWindowNotConfigured\n\n}\n\nfunc (t *transceivable) windowCleanup() {\n\tticker := time.NewTicker(t.settings.ExpireCheckTimer)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-t.ctx.Done():\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t\tctx, cancelFunc := context.WithTimeout(context.Background(), t.settings.StoreAccessTimeOut)\n\t\t\tfor _, request := range t.requestStore.List(ctx) {\n\t\t\t\tif time.Since(request.TimeSent) > t.settings.PduExpireTimeOut {\n\t\t\t\t\t_ = t.requestStore.Delete(ctx, request.GetSequenceNumber())\n\t\t\t\t\tif t.settings.OnExpiredPduRequest != nil {\n\t\t\t\t\t\tif t.settings.OnExpiredPduRequest(request.PDU) {\n\t\t\t\t\t\t\t_ = t.closing(ConnectionIssue)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcancelFunc() //defer should not be used because we are inside loop\n\t\t}\n\t}\n}\n\nfunc (t *transceivable) closing(state State) (err error) {\n\tif atomic.CompareAndSwapInt32(&t.aliveState, Alive, Closed) {\n\t\tt.cancel()\n\n\t\t// closing input and output\n\t\t_ = t.out.close(StoppingProcessOnly)\n\t\t_ = t.in.close(StoppingProcessOnly)\n\n\t\t// close underlying conn\n\t\terr = t.conn.Close()\n\n\t\t// notify transceiver closed\n\t\tif t.settings.OnClosed != nil {\n\t\t\tt.settings.OnClosed(state)\n\t\t}\n\n\t\tt.wg.Wait()\n\t}\n\treturn\n}\n"
  },
  {
    "path": "transceivable_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/data\"\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar (\n\tcountSubmitSMResp, countDeliverSM int32\n)\n\nfunc handlePDU(t *testing.T) func(pdu.PDU, bool) {\n\treturn func(p pdu.PDU, responded bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.SubmitSMResp:\n\t\t\trequire.False(t, responded)\n\t\t\trequire.EqualValues(t, data.ESME_ROK, pd.CommandStatus)\n\t\t\trequire.NotZero(t, len(pd.MessageID))\n\t\t\tatomic.AddInt32(&countSubmitSMResp, 1)\n\n\t\tcase *pdu.GenericNack:\n\t\t\trequire.False(t, responded)\n\t\t\tt.Fatal(pd)\n\n\t\tcase *pdu.DataSM:\n\t\t\trequire.True(t, responded)\n\t\t\tt.Logf(\"%+v\\n\", pd)\n\n\t\tcase *pdu.DeliverSM:\n\t\t\trequire.True(t, responded)\n\t\t\trequire.EqualValues(t, data.ESME_ROK, pd.CommandStatus)\n\n\t\t\t_mess, err := pd.Message.GetMessageWithEncoding(data.UCS2)\n\t\t\tassert.Nil(t, err)\n\t\t\tif mess == _mess {\n\t\t\t\tatomic.AddInt32(&countDeliverSM, 1)\n\t\t\t}\n\n\t\t}\n\t}\n}\n\nfunc transceivableHandleAllPDU(t *testing.T) func(pdu.PDU) (pdu.PDU, bool) {\n\treturn func(p pdu.PDU) (pdu.PDU, bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.SubmitSMResp:\n\n\t\t\trequire.EqualValues(t, data.ESME_ROK, pd.CommandStatus)\n\t\t\trequire.NotZero(t, len(pd.MessageID))\n\t\t\tatomic.AddInt32(&countSubmitSMResp, 1)\n\t\t\treturn nil, false\n\n\t\tcase *pdu.GenericNack:\n\t\t\tt.Fatal(pd)\n\t\t\treturn nil, false\n\t\tcase *pdu.DataSM:\n\t\t\tt.Logf(\"%+v\\n\", pd)\n\t\t\treturn p.GetResponse(), false\n\t\tcase *pdu.Unbind:\n\t\t\tt.Logf(\"%+v\\n\", pd)\n\t\t\treturn p.GetResponse(), true\n\n\t\tcase *pdu.DeliverSM:\n\t\t\trequire.EqualValues(t, data.ESME_ROK, pd.CommandStatus)\n\n\t\t\t_mess, err := pd.Message.GetMessageWithEncoding(data.UCS2)\n\t\t\tassert.Nil(t, err)\n\t\t\tif mess == _mess {\n\t\t\t\tatomic.AddInt32(&countDeliverSM, 1)\n\t\t\t}\n\t\t\treturn p.GetResponse(), false\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\nfunc TestTRXSubmitSM(t *testing.T) {\n\tauth := nextAuth()\n\ttrans, err := NewSession(\n\t\tTRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\tWriteTimeout: 3 * time.Second,\n\n\t\t\tEnquireLink: 200 * time.Millisecond,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tt.Fatal(err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnPDU: handlePDU(t),\n\n\t\t\tOnClosed: func(state State) {\n\t\t\t\tt.Log(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\trequire.Nil(t, err)\n\trequire.NotNil(t, trans)\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\trequire.Equal(t, \"MelroseLabsSMSC\", trans.Transceiver().SystemID())\n\n\t// sending 20 SMS\n\tfor i := 0; i < 20; i++ {\n\t\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\t\trequire.Nil(t, err)\n\t\ttime.Sleep(50 * time.Millisecond)\n\t}\n\n\ttime.Sleep(5 * time.Second)\n\n\t// wait response received\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 15)\n\n\t// rebind and submit again\n\ttrans.rebind()\n\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\trequire.Nil(t, err)\n\ttime.Sleep(time.Second)\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 16)\n}\n\nfunc TestTRXSubmitSM_with_OnAllPDU(t *testing.T) {\n\tauth := nextAuth()\n\ttrans, err := NewSession(\n\t\tTRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\tWriteTimeout: 3 * time.Second,\n\n\t\t\tEnquireLink: 200 * time.Millisecond,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tt.Fatal(err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnAllPDU: transceivableHandleAllPDU(t),\n\n\t\t\tOnClosed: func(state State) {\n\t\t\t\tt.Log(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\trequire.Nil(t, err)\n\trequire.NotNil(t, trans)\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\trequire.Equal(t, \"MelroseLabsSMSC\", trans.Transceiver().SystemID())\n\n\t// sending 20 SMS\n\tfor i := 0; i < 20; i++ {\n\t\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\t\trequire.Nil(t, err)\n\t\ttime.Sleep(50 * time.Millisecond)\n\t}\n\n\ttime.Sleep(5 * time.Second)\n\n\t// wait response received\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 15)\n\n\t// rebind and submit again\n\ttrans.rebind()\n\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\trequire.Nil(t, err)\n\ttime.Sleep(time.Second)\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 16)\n}\n\nfunc newSubmitSM(systemID string) *pdu.SubmitSM {\n\t// build up submitSM\n\tsrcAddr := pdu.NewAddress()\n\tsrcAddr.SetTon(5)\n\tsrcAddr.SetNpi(0)\n\t_ = srcAddr.SetAddress(systemID)\n\n\tdestAddr := pdu.NewAddress()\n\tdestAddr.SetTon(1)\n\tdestAddr.SetNpi(1)\n\t_ = destAddr.SetAddress(\"12\" + systemID)\n\n\tsubmitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)\n\tsubmitSM.SourceAddr = srcAddr\n\tsubmitSM.DestAddr = destAddr\n\t_ = submitSM.Message.SetMessageWithEncoding(mess, data.UCS2)\n\tsubmitSM.ProtocolID = 0\n\tsubmitSM.RegisteredDelivery = 1\n\tsubmitSM.ReplaceIfPresentFlag = 0\n\tsubmitSM.EsmClass = 0\n\n\treturn submitSM\n}\n\nfunc TestTRXSubmitSM_with_WindowConfig(t *testing.T) {\n\tauth := nextAuth()\n\ttrans, err := NewSession(\n\t\tTRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\tWriteTimeout: 3 * time.Second,\n\n\t\t\tEnquireLink: 200 * time.Millisecond,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tt.Fatal(err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tWindowedRequestTracking: &WindowedRequestTracking{\n\t\t\t\tOnReceivedPduRequest:  handleReceivedPduRequest(t),\n\t\t\t\tOnExpectedPduResponse: handleExpectedPduResponse(t),\n\t\t\t\tOnExpiredPduRequest:   nil,\n\t\t\t\tPduExpireTimeOut:      30 * time.Second,\n\t\t\t\tExpireCheckTimer:      10 * time.Second,\n\t\t\t\tMaxWindowSize:         30,\n\t\t\t\tEnableAutoRespond:     false,\n\t\t\t\tStoreAccessTimeOut:    100 * time.Millisecond,\n\t\t\t},\n\n\t\t\tOnClosed: func(state State) {\n\t\t\t\tt.Log(state)\n\t\t\t},\n\t\t}, 5*time.Second)\n\trequire.Nil(t, err)\n\trequire.NotNil(t, trans)\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\trequire.Equal(t, \"MelroseLabsSMSC\", trans.Transceiver().SystemID())\n\n\t// sending 20 SMS\n\tfor i := 0; i < 50; i++ {\n\t\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\t\trequire.Nil(t, err)\n\t\ttime.Sleep(50 * time.Millisecond)\n\t}\n\n\ttime.Sleep(5 * time.Second)\n\n\t// wait response received\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 15)\n\n\t// rebind and submit again\n\ttrans.rebind()\n\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\trequire.Nil(t, err)\n\ttime.Sleep(time.Second)\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 16)\n}\n\nfunc TestTRXSubmitSM_with_WindowConfig_and_AutoRespond(t *testing.T) {\n\tauth := nextAuth()\n\ttrans, err := NewSession(\n\t\tTRXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\tWriteTimeout: 3 * time.Second,\n\n\t\t\tEnquireLink: 200 * time.Millisecond,\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tt.Fatal(err)\n\t\t\t},\n\n\t\t\tOnReceivingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tt.Log(err)\n\t\t\t},\n\n\t\t\tWindowedRequestTracking: &WindowedRequestTracking{\n\t\t\t\tOnReceivedPduRequest:  handleReceivedPduRequest(t),\n\t\t\t\tOnExpectedPduResponse: handleExpectedPduResponse(t),\n\t\t\t\tOnExpiredPduRequest:   nil,\n\t\t\t\tPduExpireTimeOut:      30 * time.Second,\n\t\t\t\tExpireCheckTimer:      10 * time.Second,\n\t\t\t\tMaxWindowSize:         30,\n\t\t\t\tEnableAutoRespond:     true,\n\t\t\t\tStoreAccessTimeOut:    100 * time.Millisecond,\n\t\t\t},\n\n\t\t\tOnClosed: func(state State) {\n\t\t\t\tt.Log(\"rebinded\")\n\t\t\t},\n\t\t}, 5*time.Second)\n\trequire.Nil(t, err)\n\trequire.NotNil(t, trans)\n\tdefer func() {\n\t\t_ = trans.Close()\n\t}()\n\n\trequire.Equal(t, \"MelroseLabsSMSC\", trans.Transceiver().SystemID())\n\n\t// sending 20 SMS\n\tfor i := 0; i < 20; i++ {\n\t\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\t\trequire.Nil(t, err)\n\t\ttime.Sleep(50 * time.Millisecond)\n\t}\n\n\ttime.Sleep(5 * time.Second)\n\n\t// wait response received\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 15)\n\n\t// rebind and submit again\n\ttrans.rebind()\n\terr = trans.Transceiver().Submit(newSubmitSM(auth.SystemID))\n\trequire.Nil(t, err)\n\ttime.Sleep(time.Second)\n\trequire.True(t, atomic.LoadInt32(&countSubmitSMResp) >= 16)\n}\n\nfunc handleReceivedPduRequest(t *testing.T) func(pdu.PDU) (pdu.PDU, bool) {\n\treturn func(p pdu.PDU) (pdu.PDU, bool) {\n\t\tswitch pd := p.(type) {\n\t\tcase *pdu.Unbind:\n\t\t\treturn pd.GetResponse(), true\n\n\t\tcase *pdu.GenericNack:\n\t\t\tt.Fatal(pd)\n\n\t\tcase *pdu.EnquireLink:\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DataSM:\n\t\t\tt.Logf(\"%+v\\n\", pd)\n\t\t\treturn pd.GetResponse(), false\n\n\t\tcase *pdu.DeliverSM:\n\t\t\trequire.EqualValues(t, data.ESME_ROK, pd.CommandStatus)\n\n\t\t\t_mess, err := pd.Message.GetMessageWithEncoding(data.UCS2)\n\t\t\tassert.Nil(t, err)\n\t\t\tif mess == _mess {\n\t\t\t\tatomic.AddInt32(&countDeliverSM, 1)\n\t\t\t}\n\t\t\treturn pd.GetResponse(), false\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\nfunc handleExpectedPduResponse(t *testing.T) func(response Response) {\n\treturn func(response Response) {\n\n\t\tswitch pp := response.PDU.(type) {\n\t\tcase *pdu.UnbindResp:\n\t\t\t//t.Logf(\"%+v\\n\", pp)\n\n\t\tcase *pdu.SubmitSMResp:\n\t\t\trequire.NotZero(t, len(pp.MessageID))\n\t\t\tatomic.AddInt32(&countSubmitSMResp, 1)\n\t\t\tt.Logf(\"%+v with original %+v\\n\", pp, response.OriginalRequest.PDU)\n\n\t\tcase *pdu.EnquireLinkResp:\n\t\t\tt.Logf(\"%+v\\n\", pp)\n\t\t}\n\t}\n}\n\nfunc Test_newTransceivable(t *testing.T) {\n\tt.Run(\"always receive a non nil response\", func(t *testing.T) {\n\t\ttrans := newTransceivable(nil, Settings{}, nil)\n\t\tassert.NotNil(t, trans.in.settings.response)\n\t})\n}\n"
  },
  {
    "path": "transmittable.go",
    "content": "package gosmpp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\nvar (\n\t// ErrConnectionClosing indicates transmitter is closing. Can not send any PDU.\n\tErrConnectionClosing = errors.New(\"connection is closing, can not send PDU to SMSC\")\n\tErrWindowsFull       = errors.New(\"window full\")\n)\n\ntype transmittable struct {\n\tsettings Settings\n\n\twg    sync.WaitGroup\n\tinput chan pdu.PDU\n\n\tconn *Connection\n\n\taliveState   int32\n\tpendingWrite int32\n\trequestStore RequestStore\n}\n\nfunc newTransmittable(conn *Connection, settings Settings, requestStore RequestStore) *transmittable {\n\tt := &transmittable{\n\t\tsettings:     settings,\n\t\tconn:         conn,\n\t\tinput:        make(chan pdu.PDU, 1),\n\t\taliveState:   Alive,\n\t\tpendingWrite: 0,\n\t\trequestStore: requestStore,\n\t}\n\n\treturn t\n}\n\nfunc (t *transmittable) close(state State) (err error) {\n\tif atomic.CompareAndSwapInt32(&t.aliveState, Alive, Closed) {\n\t\tfor atomic.LoadInt32(&t.pendingWrite) != 0 {\n\t\t\truntime.Gosched()\n\t\t}\n\n\t\t// notify daemon\n\t\tclose(t.input)\n\n\t\t// wait daemon\n\t\tt.wg.Wait()\n\n\t\t// try to send unbind\n\t\t_, _ = t.write(pdu.NewUnbind())\n\n\t\t// close connection\n\t\tif state != StoppingProcessOnly {\n\t\t\terr = t.conn.Close()\n\t\t}\n\n\t\t// notify transmitter closed\n\t\tif t.settings.OnClosed != nil {\n\t\t\tt.settings.OnClosed(state)\n\t\t}\n\n\t\t// concurrent-map has no func to verify initialization\n\t\t// we need to do the same check in\n\t\tif t.settings.WindowedRequestTracking != nil {\n\t\t\tctx, cancelFunc := context.WithTimeout(context.Background(), t.settings.StoreAccessTimeOut)\n\t\t\tdefer cancelFunc()\n\t\t\tvar size int\n\t\t\tsize, err = t.requestStore.Length(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif size > 0 {\n\t\t\t\tfor _, request := range t.requestStore.List(ctx) {\n\t\t\t\t\tif t.settings.OnClosePduRequest != nil {\n\t\t\t\t\t\tt.settings.OnClosePduRequest(request.PDU)\n\t\t\t\t\t}\n\t\t\t\t\terr = t.requestStore.Delete(ctx, request.GetSequenceNumber())\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (t *transmittable) closing(state State) {\n\tgo func() {\n\t\t_ = t.close(state)\n\t}()\n}\n\n// Submit a PDU.\nfunc (t *transmittable) Submit(p pdu.PDU) (err error) {\n\tatomic.AddInt32(&t.pendingWrite, 1)\n\n\tif atomic.LoadInt32(&t.aliveState) == Alive {\n\t\tt.input <- p\n\t} else {\n\t\terr = ErrConnectionClosing\n\t}\n\n\tatomic.AddInt32(&t.pendingWrite, -1)\n\treturn\n}\n\nfunc (t *transmittable) start() {\n\tt.wg.Add(1)\n\tif t.settings.EnquireLink > 0 {\n\t\tgo func() {\n\t\t\tdefer t.wg.Done()\n\t\t\tt.loopWithEnquireLink()\n\t\t}()\n\t} else {\n\t\tgo func() {\n\t\t\tdefer t.wg.Done()\n\t\t\tt.loop()\n\t\t}()\n\t}\n}\n\nfunc (t *transmittable) drain() {\n\tfor range t.input {\n\t}\n}\n\nfunc (t *transmittable) loop() {\n\tdefer t.drain()\n\n\tfor p := range t.input {\n\t\tif p != nil {\n\t\t\tn, err := t.write(p)\n\t\t\tif t.check(p, n, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (t *transmittable) loopWithEnquireLink() {\n\tticker := time.NewTicker(t.settings.EnquireLink)\n\tdefer func() {\n\t\tticker.Stop()\n\t\tt.drain()\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\teqp := pdu.NewEnquireLink()\n\t\t\tn, err := t.write(eqp)\n\t\t\tif t.check(eqp, n, err) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase p, ok := <-t.input:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif p != nil {\n\t\t\t\tn, err := t.write(p)\n\t\t\t\tif t.check(p, n, err) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// check error and do closing if need\nfunc (t *transmittable) check(p pdu.PDU, n int, err error) (closing bool) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tif t.settings.OnSubmitError != nil {\n\t\tt.settings.OnSubmitError(p, err)\n\t}\n\n\tif n == 0 {\n\t\tif errors.Is(err, ErrWindowsFull) {\n\t\t\tclosing = false\n\t\t} else if nErr, ok := err.(net.Error); ok {\n\t\t\tclosing = nErr.Timeout()\n\t\t} else {\n\t\t\tclosing = true\n\t\t}\n\t} else {\n\t\tclosing = true // force closing\n\t}\n\n\tif closing {\n\t\tt.closing(ConnectionIssue) // start closing\n\t}\n\n\treturn\n}\n\n// low level writing\nfunc (t *transmittable) write(p pdu.PDU) (n int, err error) {\n\tif t.settings.WriteTimeout > 0 {\n\t\terr = t.conn.SetWriteTimeout(t.settings.WriteTimeout)\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif t.settings.WindowedRequestTracking != nil && t.settings.MaxWindowSize > 0 && isAllowPDU(p) {\n\t\tctx, cancelFunc := context.WithTimeout(context.Background(), t.settings.StoreAccessTimeOut)\n\t\tdefer cancelFunc()\n\t\tvar length int\n\t\tlength, err = t.requestStore.Length(ctx)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif length < int(t.settings.MaxWindowSize) {\n\t\t\tn, err = t.conn.WritePDU(p)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\trequest := Request{\n\t\t\t\tPDU:      p,\n\t\t\t\tTimeSent: time.Now(),\n\t\t\t}\n\t\t\terr = t.requestStore.Set(ctx, request)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t} else {\n\t\t\treturn 0, ErrWindowsFull\n\t\t}\n\t} else {\n\t\tn, err = t.conn.WritePDU(p)\n\t}\n\n\treturn\n}\n\nfunc isAllowPDU(p pdu.PDU) bool {\n\tif p.CanResponse() {\n\t\tswitch p.(type) {\n\t\tcase *pdu.BindRequest, *pdu.Unbind:\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "transmittable_test.go",
    "content": "package gosmpp\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/linxGnu/gosmpp/pdu\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTransmit(t *testing.T) {\n\tt.Run(\"Binding\", func(t *testing.T) {\n\t\tauth := nextAuth()\n\t\ttransmitter, err := NewSession(\n\t\t\tTXConnector(NonTLSDialer, auth),\n\t\t\tSettings{\n\t\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\t\tOnPDU: func(p pdu.PDU, _ bool) {\n\t\t\t\t\tt.Logf(\"%+v\\n\", p)\n\t\t\t\t},\n\n\t\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t},\n\n\t\t\t\tOnRebindingError: func(err error) {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t},\n\n\t\t\t\tOnClosed: func(state State) {\n\t\t\t\t\tt.Log(state)\n\t\t\t\t},\n\t\t\t}, -1)\n\t\trequire.Nil(t, err)\n\t\trequire.NotNil(t, transmitter)\n\t\tdefer func() {\n\t\t\t_ = transmitter.Close()\n\t\t}()\n\n\t\trequire.Equal(t, \"MelroseLabsSMSC\", transmitter.Transmitter().SystemID())\n\n\t\terr = transmitter.Transmitter().Submit(newSubmitSM(auth.SystemID))\n\t\trequire.Nil(t, err)\n\n\t\ttime.Sleep(400 * time.Millisecond)\n\n\t\ttransmitter.rebind()\n\t\terr = transmitter.Transmitter().Submit(newSubmitSM(auth.SystemID))\n\t\trequire.Nil(t, err)\n\t})\n\n\terrorHandling := func(t *testing.T, trigger func(*transmittable)) {\n\t\tconn, err := net.Dial(\"tcp\", smscAddr)\n\t\trequire.NoError(t, err)\n\n\t\tvar tr transmittable\n\t\ttr.input = make(chan pdu.PDU, 1)\n\n\t\tc := NewConnection(conn)\n\t\tdefer func() {\n\t\t\t_ = c.Close()\n\n\t\t\t// write on closed conn?\n\t\t\tn, err := tr.write(pdu.NewEnquireLink())\n\t\t\trequire.NotNil(t, err)\n\t\t\trequire.Zero(t, n)\n\t\t}()\n\n\t\t// fake settings\n\t\ttr.conn = c\n\n\t\tvar count int32\n\t\ttr.settings.OnClosed = func(State) {\n\t\t\tatomic.AddInt32(&count, 1)\n\t\t}\n\n\t\ttr.settings.OnSubmitError = func(p pdu.PDU, err error) {\n\t\t\trequire.NotNil(t, err)\n\t\t\t_, ok := p.(*pdu.CancelSM)\n\t\t\trequire.True(t, ok)\n\t\t}\n\n\t\t// do trigger\n\t\ttrigger(&tr)\n\n\t\ttime.Sleep(300 * time.Millisecond)\n\t\trequire.NotZero(t, atomic.LoadInt32(&count))\n\t}\n\n\tt.Run(\"ErrorHandling\", func(t *testing.T) {\n\t\terrorHandling(t, func(tr *transmittable) {\n\t\t\tvar p pdu.CancelSM\n\t\t\ttr.check(&p, 100, fmt.Errorf(\"fake error\"))\n\t\t})\n\n\t\terrorHandling(t, func(tr *transmittable) {\n\t\t\tvar p pdu.CancelSM\n\t\t\ttr.check(&p, 0, fmt.Errorf(\"fake error\"))\n\t\t})\n\t})\n\n\tt.Run(\"SubmitErr\", func(t *testing.T) {\n\t\tvar tr transmittable\n\t\ttr.input = make(chan pdu.PDU, 1)\n\n\t\ttr.aliveState = 1\n\t\terr := tr.Submit(nil)\n\t\trequire.Error(t, err)\n\n\t\ttr.aliveState = 0\n\t\terr = tr.Submit(nil)\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConcurrentSubmitClose(t *testing.T) {\n\tauth := nextAuth()\n\ttransmitter, err := NewSession(\n\t\tTXConnector(NonTLSDialer, auth),\n\t\tSettings{\n\t\t\tReadTimeout: 2 * time.Second,\n\n\t\t\tOnPDU: func(p pdu.PDU, _ bool) {\n\t\t\t\tt.Logf(\"%+v\\n\", p)\n\t\t\t},\n\n\t\t\tOnSubmitError: func(_ pdu.PDU, err error) {\n\t\t\t\tt.Fatal(err)\n\t\t\t},\n\n\t\t\tOnRebindingError: func(err error) {\n\t\t\t\tt.Fatal(err)\n\t\t\t},\n\n\t\t\tOnClosed: func(state State) {\n\t\t\t\tt.Log(state)\n\t\t\t},\n\t\t}, -1)\n\trequire.Nil(t, err)\n\trequire.NotNil(t, transmitter)\n\tdefer func() {\n\t\t_ = transmitter.Close()\n\t}()\n\n\trequire.Equal(t, \"MelroseLabsSMSC\", transmitter.Transmitter().SystemID())\n\n\tvar wg sync.WaitGroup\n\n\twg.Add(1)\n\n\ttoStart := make(chan struct{}, 1)\n\tgo func() {\n\t\tdefer wg.Done()\n\n\t\ttime.Sleep(time.Millisecond)\n\t\tfor i := 0; i < 100; i++ {\n\t\t\terr := transmitter.Transmitter().Submit(newSubmitSM(auth.SystemID))\n\t\t\trequire.True(t, err == nil || err == ErrConnectionClosing)\n\n\t\t\tif i == 10 {\n\t\t\t\ttoStart <- struct{}{}\n\t\t\t}\n\t\t}\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\n\t\t<-toStart\n\t\t_ = transmitter.close()\n\t}()\n\n\twg.Wait()\n}\n"
  },
  {
    "path": "types.go",
    "content": "package gosmpp\n\nimport (\n\t\"github.com/linxGnu/gosmpp/pdu\"\n)\n\n// PDUCallback handles received PDU.\ntype PDUCallback func(pdu pdu.PDU, responded bool)\n\n// AllPDUCallback handles all received PDU.\n//\n// This pdu is NOT responded to automatically, manual response/handling is needed\n// and the bind can be closed by retuning true on closeBind.\ntype AllPDUCallback func(pdu pdu.PDU) (responsePdu pdu.PDU, closeBind bool)\n\n// PDUErrorCallback notifies fail-to-submit PDU with along error.\ntype PDUErrorCallback func(pdu pdu.PDU, err error)\n\n// ErrorCallback notifies happened error while reading PDU.\ntype ErrorCallback func(error)\n\n// ClosedCallback notifies closed event due to State.\ntype ClosedCallback func(State)\n\n// RebindCallback notifies rebind event due to State.\ntype RebindCallback func()\n"
  }
]