#!/usr/bin/env python3

# Simple tool for sending arbitrary (e.g. binary) SMS via SMPP
# based on https://github.com/python-smpplib/python-smpplib.
#
# Copyright (c) 2022  Vadim Yanitskiy <fixeria@osmocom.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import argparse
import logging
import sys

import smpplib.gsm
import smpplib.client
import smpplib.consts

logging.basicConfig(level = logging.INFO,
                    format = "%(levelname)s %(filename)s:%(lineno)d %(message)s")


class SmppClient:
    def __init__(self, addr: str, port: int):
        self._client = smpplib.client.Client(addr, port)

        msg_tx_cb = lambda pdu: logging.info('Tx %s', pdu)
        self._client.set_message_sent_handler(msg_tx_cb)

        msg_rx_cb = lambda pdu: logging.info('Rx %s', pdu)
        self._client.set_message_received_handler(msg_rx_cb)

    def connect(self, sysid: str, password: str) -> None:
        self._client.connect()
        self._client.bind_transceiver(system_id=sysid, password=password)

    def listen(self) -> None:
        self._client.listen()

    # **params: see smpplib.command.SubmitSM.params
    def send_msg(self, sender: str, dest: str, data: bytes, **params) -> None:
        logging.info('Sending SMS from "%s" to "%s"', sender, dest)
        pdu = self._client.send_message(source_addr_ton=smpplib.consts.SMPP_TON_INTL,
                                        source_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
                                        source_addr=sender,
                                        dest_addr_ton=smpplib.consts.SMPP_TON_INTL,
                                        dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
                                        destination_addr=dest,
                                        short_message=data,
                                        esm_class=smpplib.consts.SMPP_MSGMODE_FORWARD,
                                        registered_delivery=False,
                                        **params)


ap = argparse.ArgumentParser(prog='smpp_tool', description='Simple SMPP tool',
                             formatter_class=argparse.ArgumentDefaultsHelpFormatter)

ap.add_argument('-a', '--smpp-server-addr', metavar='ADDR',
                type=str, dest='addr', default='127.0.0.1',
                help='SMPP server host (default %(default)s)')
ap.add_argument('-p', '--smpp-server-port', metavar='PORT',
                type=int, dest='port', default=2775,
                help='SMPP server port (default %(default)s)')
ap.add_argument('-s', '--smpp-system-id', metavar='SYSID',
                type=str, dest='sysid', default='test',
                help='SMPP system ID (default "%(default)s")')
ap.add_argument('-P', '--smpp-password', metavar='PASS',
                type=str, dest='password', default='test',
                help='SMPP password (default "%(default)s")')

ap.add_argument('--tp-pid', metavar='PID',
                type=int, dest='tp_pid', default=127,
                help='TP-Protocol-Identifier (default %(default)s)')
ap.add_argument('--tp-dcs', metavar='DCS',
                type=int, dest='tp_dcs', default=246,
                help='TP-Data-Coding-Scheme (default %(default)s)')

ap.add_argument(metavar='MSISDN', type=str, dest='msisdn',
                help='Destination MSISDN (also used as sender)')
ap.add_argument(metavar='HEXPDU', type=str, dest='pdu',
                help='SMS TPDU to be delivered (in HEX)')

if __name__ == '__main__':
    argv = ap.parse_args()

    client = SmppClient(argv.addr, argv.port)
    client.connect(argv.sysid, argv.password)
    client.send_msg(argv.msisdn, argv.msisdn, bytes.fromhex(argv.pdu),
                    protocol_id=argv.tp_pid, data_coding=argv.tp_dcs)
    client.listen()
