Skip to content

Commit

Permalink
Merge pull request #571 from jookies/0.9rc17
Browse files Browse the repository at this point in the history
0.9rc17
  • Loading branch information
farirat committed May 6, 2017
2 parents 514d4a9 + a781ac7 commit 4189c2b
Show file tree
Hide file tree
Showing 69 changed files with 10,086 additions and 519 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ language: python
python:
- '2.7'
env:
- ROOT_PATH=~/jasmin JASMIN_RELEASE=0.9rc16
- ROOT_PATH=~/jasmin JASMIN_RELEASE=0.9rc17
# Command to install dependencies
install:
- python setup.py sdist
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ MAINTAINER Jookies LTD <jasmin@jookies.net>
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r jasmin && useradd -r -g jasmin jasmin

ENV JASMIN_VERSION 0.9rc16
ENV JASMIN_VERSION 0.9rc17

# Install requirements
RUN apt-get update && apt-get install -y \
Expand Down
1 change: 1 addition & 0 deletions install-requirements
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ lockfile>=0.10.0
celery>=4.0.0
redis>=2.10
falcon>=1.1.0
requests>=2.13.0
2 changes: 1 addition & 1 deletion jasmin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

MAJOR = 0
MINOR = 9
PATCH = 16
PATCH = 17
META = 'rc'

def get_version():
Expand Down
18 changes: 15 additions & 3 deletions jasmin/protocols/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET

from jasmin.protocols.smpp.operations import SMPPOperationFactory
from jasmin.protocols.smpp.operations import SMPPOperationFactory, gsm_encode
from jasmin.routing.Routables import RoutableSubmitSm
from jasmin.vendor.smpp.pdu.constants import priority_flag_value_map
from jasmin.vendor.smpp.pdu.pdu_types import RegisteredDeliveryReceipt, RegisteredDelivery
Expand Down Expand Up @@ -96,11 +96,17 @@ def route_routable(self, updated_request):
user.getCnxStatus().httpapi['submit_sm_request_count'] += 1
user.getCnxStatus().httpapi['last_activity_at'] = datetime.now()

# Convert utf8 to GSM 03.38
if updated_request.args['coding'][0] == '0':
short_message = gsm_encode(updated_request.args['content'][0].decode('utf-8'))
else:
short_message = updated_request.args['content'][0]

# Build SubmitSmPDU
SubmitSmPDU = self.opFactory.SubmitSM(
source_addr=None if 'from' not in updated_request.args else updated_request.args['from'][0],
destination_addr=updated_request.args['to'][0],
short_message=updated_request.args['content'][0],
short_message=short_message,
data_coding=int(updated_request.args['coding'][0]))
self.log.debug("Built base SubmitSmPDU: %s", SubmitSmPDU)

Expand Down Expand Up @@ -480,11 +486,17 @@ def route_routable(self, request):
user.getCnxStatus().httpapi['rate_request_count'] += 1
user.getCnxStatus().httpapi['last_activity_at'] = datetime.now()

# Convert utf8 to GSM 03.38
if request.args['coding'][0] == '0':
short_message = gsm_encode(request.args['content'][0].decode('utf-8'))
else:
short_message = request.args['content'][0]

# Build SubmitSmPDU
SubmitSmPDU = self.opFactory.SubmitSM(
source_add=None if 'from' not in request.args else request.args['from'][0],
destination_addr=request.args['to'][0],
short_message=request.args['content'][0],
short_message=short_message,
data_coding=int(request.args['coding'][0]),
)
self.log.debug("Built base SubmitSmPDU: %s", SubmitSmPDU)
Expand Down
9 changes: 6 additions & 3 deletions jasmin/protocols/rest/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import base64
import jasmin
import json
import os
import sys

import falcon

import jasmin
from .api import PingResource, BalanceResource, RateResource, SendResource, SendBatchResource
from .config import *

sys.path.append("%s/vendor" % os.path.dirname(os.path.abspath(jasmin.__file__)))
import falcon

# @TODO: make configuration loadable from /etc/jasmin/restapi.conf
logger = logging.getLogger('jasmin-restapi')
if len(logger.handlers) == 0:
Expand Down
46 changes: 37 additions & 9 deletions jasmin/protocols/rest/api.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import json
import os
import sys
import uuid

import falcon
import requests

import jasmin
from .config import *
from .tasks import httpapi_send

sys.path.append("%s/vendor" % os.path.dirname(os.path.abspath(jasmin.__file__)))
import falcon
import binascii


class JasminHttpApiProxy(object):
"""Provides a WS caller for old Jasmin http api"""
Expand Down Expand Up @@ -112,22 +118,34 @@ def on_post(self, request, response):
Note: Calls Jasmin http api /send resource
"""

request_args = dict(
self.decode_request_data(request).items() + {
'username': request.context['username'],
'password': request.context['password']
}.items()
)

# If we have a hex_content then convert it back to content:
if 'hex_content' in request_args:
try:
request_args['content'] = binascii.unhexlify(request_args['hex_content'])
except Exception:
raise falcon.HTTPPreconditionFailed('Cannot parse hex_content value',
'Got unparseable hex_content value: %s' % request_args[
'hex_content'])
else:
del (request_args['hex_content'])

self.build_response_from_proxy_result(
response,
self.call_jasmin(
'send',
params=dict(
self.decode_request_data(request).items() + {
'username': request.context['username'],
'password': request.context['password']
}.items()
)
params=request_args
)
)


class SendBatchResource(JasminRestApi, JasminHttpApiProxy):

def on_post(self, request, response):
"""
POST /secure/sendbatch request processing
Expand All @@ -146,9 +164,19 @@ def on_post(self, request, response):
message_params.update(_message_params)

# Ignore message if these args are not found
if 'to' not in message_params or 'content' not in message_params:
if 'to' not in message_params or ('content' not in message_params and 'hex_content' not in message_params):
continue

# If we have a hex_content then convert it back to content:
if 'hex_content' in message_params:
try:
message_params['content'] = binascii.unhexlify(message_params['hex_content'])
except Exception:
# Ignore message on any error that may occur when unhexlifying hex_content
continue
else:
del (message_params['hex_content'])

# Do we have multiple destinations for this message ?
if isinstance(message_params.get('to', ''), list):
to_list = message_params.get('to')
Expand Down
15 changes: 9 additions & 6 deletions jasmin/protocols/rest/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,35 @@ def httpapi_send(self, batch_id, batch_config, message_params):
except requests.exceptions.ConnectionError as e:
logger.error('[%s] Jasmin httpapi connection error: %s' % (batch_id, e))
if batch_config.get('errback_url', None):
batch_callback.delay(batch_config.get('errback_url'), batch_id, 0, 'HTTPAPI Connection error: %s' % e)
batch_callback.delay(batch_config.get('errback_url'), batch_id, message_params['to'], 0,
'HTTPAPI Connection error: %s' % e)
except Exception as e:
logger.error('[%s] Unknown error (%s): %s' % (batch_id, type(e), e))
if batch_config.get('errback_url', None):
batch_callback.delay(batch_config.get('errback_url'), batch_id, 0, 'Unknown error: %s' % e)
batch_callback.delay(batch_config.get('errback_url'), batch_id, message_params['to'], 0,
'Unknown error: %s' % e)
else:
if r.status_code != 200:
logger.error('[%s] %s' % (batch_id, r.content.strip('"')))
if batch_config.get('errback_url', None):
batch_callback.delay(
batch_config.get('errback_url'), batch_id, 0, 'HTTPAPI error: %s' % r.content.strip('"'))
batch_config.get('errback_url'), batch_id, message_params['to'], 0,
'HTTPAPI error: %s' % r.content.strip('"'))
else:
if batch_config.get('callback_url', None):
batch_callback.delay(
batch_config.get('callback_url'), batch_id, 1, r.content)
batch_config.get('callback_url'), batch_id, message_params['to'], 1, r.content)


@task(bind=True, base=JasminTask)
def batch_callback(self, url, batch_id, status, status_text):
def batch_callback(self, url, batch_id, to, status, status_text):
try:
if status == 0:
operation_name = 'Errback'
else:
operation_name = 'Callback'

requests.get(url, params={'batchId': batch_id, 'status': status, 'statusText': status_text})
requests.get(url, params={'batchId': batch_id, 'to': to, 'status': status, 'statusText': status_text})
except Exception as e:
logger.error('(%s) of batch %s to %s failed (%s): %s.' % (operation_name, batch_id, url, type(e), e))
else:
Expand Down
39 changes: 33 additions & 6 deletions jasmin/protocols/smpp/operations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- test-case-name: jasmin.test.test_operations -*-
# -*- coding: utf-8 -*-

import datetime
import math
import re
Expand All @@ -11,6 +13,29 @@
from jasmin.vendor.smpp.pdu.pdu_types import (EsmClass, EsmClassMode, EsmClassType, EsmClassGsmFeatures,
MoreMessagesToSend, MessageState, AddrTon, AddrNpi)

gsm_chars = (u"@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>"
u"?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà")
gsm_chars_ext = (u"````````````````````^```````````````````{}`````\\````````````[~]`"
u"|````````````````````````````````````€``````````````````````````")


def gsm_encode(plaintext):
"""Will encode plaintext to gsm 338
Taken from
http://stackoverflow.com/questions/2452861/python-library-for-converting-plain-text-ascii-into-gsm-7-bit-character-set
"""
res = ""
for c in plaintext:
idx = gsm_chars.find(c)
if idx != -1:
res += chr(idx)
continue
idx = gsm_chars_ext.find(c)
if idx != -1:
res += chr(27) + chr(idx)
return res


message_state_map = {
'%s' % MessageState.ACCEPTED: 'ACCEPTD',
'%s' % MessageState.UNDELIVERABLE: 'UNDELIV',
Expand All @@ -22,18 +47,20 @@
'%s' % MessageState.UNKNOWN: 'UNKNOWN',
}


class UnknownMessageStatusError(Exception):
"""Raised when message_status is not recognized
"""


class SMPPOperationFactory(object):
lastLongMsgRefNum = 0

def __init__(self, config=None, long_content_max_parts=5, long_content_split='sar'):
if config is not None:
self.config = config
else:
self.config = SMPPClientConfig(**{'id':'anyid'})
self.config = SMPPClientConfig(**{'id': 'anyid'})

self.long_content_max_parts = long_content_max_parts
self.long_content_split = long_content_split
Expand Down Expand Up @@ -103,8 +130,8 @@ def isDeliveryReceipt(self, pdu):
if m:
key = m.groupdict().keys()[0]
if (key not in ['id', 'stat']
or (key == 'id' and 'id' not in ret)
or (key == 'stat' and 'stat' not in ret)):
or (key == 'id' and 'id' not in ret)
or (key == 'stat' and 'stat' not in ret)):
ret.update(m.groupdict())

# Should we consider this as a DLR ?
Expand Down Expand Up @@ -166,7 +193,7 @@ def SubmitSM(self, short_message, data_coding=0, **kwargs):
msg_ref_num = self.claimLongMsgRefNum()

for i in range(total_segments):
segment_seqnum = i+1
segment_seqnum = i + 1

# Keep in memory previous PDU in order to set nextPdu in it later
try:
Expand All @@ -176,9 +203,9 @@ def SubmitSM(self, short_message, data_coding=0, **kwargs):
previousPdu = None

if bits == 16:
kwargs['short_message'] = longMessage[slicedMaxSmLength*i*2:slicedMaxSmLength*(i+1)*2]
kwargs['short_message'] = longMessage[slicedMaxSmLength * i * 2:slicedMaxSmLength * (i + 1) * 2]
else:
kwargs['short_message'] = longMessage[slicedMaxSmLength*i:slicedMaxSmLength*(i+1)]
kwargs['short_message'] = longMessage[slicedMaxSmLength * i:slicedMaxSmLength * (i + 1)]
tmpPdu = self._setConfigParamsInPDU(SubmitSM(**kwargs), kwargs)
if self.long_content_split == 'sar':
# Slice short_message and create the PDU using SAR options
Expand Down

0 comments on commit 4189c2b

Please sign in to comment.