Ejemplo n.º 1
0
    def post(self, request, format=None):
        needed_fields = ["imsi", "trans", "dest"]
        if not all(i in request.POST for i in needed_fields):
            return Response("Missing Args", status=status.HTTP_400_BAD_REQUEST)

        imsi = request.data['imsi']
        ret = transaction = request.data['trans']
        dest = request.data['dest']

        # Kludge!
        try:
            endaga_sub.get_imsi_from_number(dest)
            local = True
        except BaseException:
            local = False

        if not local and is_dest_globe(dest):
            # replace first word with globe keyword
            transaction = 'globe_' + transaction.split('_')[1]

        # check first if subscriber has subscribed to U/B/D promos
        # follows priority order

        # we also have to check that the subscriber has allocation
        # for the transaction. For example: if a user wants to do
        # outside sms but only has promo allocation for unli local sms, then
        # we should apply regular rates
        for promo_type in ['U', 'B', 'D', 'G']:
            query = transaction + '__gt'
            listed_promos = PromoSubscription.objects.filter(
                contact__imsi__exact=imsi,
                promo__promo_type=promo_type,
                **{
                    query: 0
                }).order_by('date_expiration')
            if listed_promos:
                if promo_type == 'G':  # check destination number
                    try:
                        GroupMembers.objects.get(group__owner__exact=imsi,
                                                 user__callerid=dest)
                        break
                    except BaseException:
                        pass  # destination not a group member, do nothing
                else:
                    break
        # No promo subscriptions
        else:
            return Response(ret, status=status.HTTP_200_OK)

        # NOTE: get the first item from queryset since that would expire first
        key = 'listed_promos[0].%s' % transaction
        if eval(key) > 0:
            ret = '%s_%s' % (promo_type, transaction)

        return Response(ret, status=status.HTTP_200_OK)
def fsapi(session, stream, env, msisdn):
    """Handle FS API requests.

    Args:
      msisdn: a subscriber's number
    """
    try:
        imsi = str(subscriber.get_imsi_from_number(msisdn, False))
    except SubscriberNotFound:
        imsi = ''
    consoleLog('info', "Returned FSAPI: " + imsi + "\n")
    stream.write(imsi)
def chat(message, msisdn):
    """Handle chat requests.

    Args:
      msisdn: a subscriber's number
    """
    try:
        imsi = str(subscriber.get_imsi_from_number(msisdn, False))
    except SubscriberNotFound:
        imsi = ''
    consoleLog('info', "Returned Chat: " + imsi + "\n")
    message.chat_execute('set', '_openbts_ret=%s' % imsi)
Ejemplo n.º 4
0
 def bill(self, to_number, from_number):
     try:
         if from_number == DASHBOARD_FROM_NUMBER:
             self.tariff_type = 'free_sms'
         tariff = billing.get_sms_cost(self.tariff_type,
                                       destination_number=to_number)
         username = subscriber.get_imsi_from_number(to_number)
         if username:
             reason = 'SMS from %s to %s (incoming_sms)' % (
                 from_number, username)
             old_balance = subscriber.get_account_balance(username)
             subscriber.subtract_credit(username, str(int(tariff)))
             events.create_sms_event(username, old_balance, tariff, reason,
                                     to_number, from_number=from_number)
     except Exception as e:
         logger.error("Endaga bill error:" + traceback.format_exc(e))
Ejemplo n.º 5
0
def fsapi(session, stream, env, msisdn):
    """Handle FS API requests.

    Args:
      msisdn: a subscriber's number
    """
    try:
        imsi = str(subscriber.get_imsi_from_number(msisdn))
    except SubscriberNotFound:
        # If the MSISDN isn't even in the sub registry, it's not camped
        consoleLog('info', "Returned FSAPI: FALSE\n")
        stream.write('FALSE')
        return
    camped = [str(_['imsi']) for _ in bts.active_subscribers()]
    res = "TRUE" if imsi in camped else "FALSE"
    consoleLog('info', "Returned FSAPI: %s\n" % res)
    stream.write(res)
Ejemplo n.º 6
0
def chat(message, msisdn):
    """Handle chat requests.

    Args:
      msisdn: a subscriber's number
    """
    try:
        imsi = str(subscriber.get_imsi_from_number(msisdn))
    except SubscriberNotFound:
        # If the MSISDN isn't even in the sub registry, it's not camped
        consoleLog('info', "Returned Chat: FALSE\n")
        message.chat_execute('set', '_openbts_ret=FALSE')
        return
    camped = [str(_['imsi']) for _ in bts.active_subscribers()]
    res = "TRUE" if imsi in camped else "FALSE"
    consoleLog('info', "Returned Chat: %s\n" % res)
    message.chat_execute('set', '_openbts_ret=%s' % res)
Ejemplo n.º 7
0
 def POST(self, command):
     """Handles certain POST commands."""
     # Always send back these headers.
     headers = {'Content-type': 'text/plain'}
     # Validate the exact endpoint.
     valid_post_commands = ('deactivate_number', 'deactivate_subscriber')
     if command not in valid_post_commands:
         return web.NotFound()
     # Get the posted data and validate.  There should be a 'jwt' key with
     # signed data.  That dict should contain a 'number' key -- the one we
     # want to deactivate.
     data = web.input()
     jwt = data.get('jwt', None)
     if not jwt:
         return web.BadRequest()
     serializer = itsdangerous.JSONWebSignatureSerializer(
         self.conf['bts_secret'])
     try:
         jwt = serializer.loads(jwt)
     except itsdangerous.BadSignature:
         return web.BadRequest()
     if command == 'deactivate_number':
         if 'number' not in jwt:
             return web.BadRequest()
         # The params validated, deactivate the number.  ValueError is
         # raised if this is the subscriber's last number.
         # The number should correspond to an IMSI or give a 404
         try:
             imsi = subscriber.get_imsi_from_number(jwt['number'])
             subscriber.delete_number(imsi, jwt['number'])
         except SubscriberNotFound:
             return web.NotFound()
         except ValueError:
             return web.BadRequest()
         return web.ok(None, headers)
     elif command == 'deactivate_subscriber':
         if 'imsi' not in jwt:
             return web.BadRequest()
         # The number should correspond to an IMSI.
         try:
             subscriber.delete_subscriber(jwt['imsi'])
         except SubscriberNotFound:
             return web.NotFound()
         return web.ok(None, headers)
Ejemplo n.º 8
0
def handle_incoming(from_imsi, request):
    """Called externally by an FS script.

    Args:
      from_imsi: sender's IMSI
      request: a credit transfer or credit transfer confirmation request
    """
    request = request.strip()

    # This parses a to_number (length 1 or more) and an amount that can
    # be formatted using a comma for a thousands seperator and a period for
    # the decimal place
    transfer_command = re.compile(
        r'^(?P<to_number>[0-9]+)'
        r'\*'
        r'(?P<amount>[0-9]*(?:,[0-9]{3})*(?:\.[0-9]*)?)$')
    transfer = transfer_command.match(request)

    confirm_command = re.compile(r'^(?P<confirm_code>[0-9]{%d})$' %
                                 int(config_db['code_length']))
    confirm = confirm_command.match(request)
    _init_pending_transfer_db()
    if transfer:
        to_number, amount = transfer.groups()
        amount = freeswitch_strings.parse_credits(amount).amount_raw
        # Translate everything into IMSIs.
        try:
            to_imsi = subscriber.get_imsi_from_number(to_number)
            _, resp = process_transfer(from_imsi, to_imsi, amount)
        except SubscriberNotFound:
            resp = gt("Invalid phone number: %(number)s" %
                      {'number': to_number})
    elif confirm:
        # The code is the whole request, so no need for groups.
        code = request.strip()
        _, resp = process_confirm(from_imsi, code)
    else:
        # NOTE: Sent when the user tries to transfer credit with the wrong
        #       format message.
        resp = gt("To transfer credit, reply with a message in the"
                  " format 'NUMBER*AMOUNT'.")
    from_number = subscriber.get_numbers_from_imsi(from_imsi)[0]
    sms.send(str(from_number), str(config_db['app_number']), str(resp))
Ejemplo n.º 9
0
 def POST(self):
     """Handles POST requests."""
     data = web.input()
     if ('from_name' not in data or 'service_type' not in data or
             'destination' not in data):
         raise web.BadRequest()
     # Process the CDR data.
     cost_in_credits = billing.get_sms_cost(
         data.service_type, destination_number=data.destination)
     try:
         old_balance = subscriber.get_account_balance(data.from_name)
         subscriber.subtract_credit(data.from_name, str(cost_in_credits))
     except SubscriberNotFound:
         # The subscriber does not exist yet but has sent an SMS
         if data.service_type == 'free_sms':
             # But that is OK for a free service like provisioning
             old_balance = 0
         else:
             raise
     reason = "SMS sent to %s (%s)" % (data.destination, data.service_type)
     events.create_sms_event(
         data.from_name, old_balance, cost_in_credits, reason,
         data.destination, from_imsi=data.from_name,
         from_number=data.from_number)
     # If this was an in-network event, find the cost for the recipient.  We
     # can lookup the recipient by data.destination (the "to number").
     if 'local' in data.service_type:
         recipient_imsi = subscriber.get_imsi_from_number(data.destination)
         old_balance = subscriber.get_account_balance(recipient_imsi)
         cost_in_credits = billing.get_sms_cost(
             'local_recv_sms', destination_number=data.destination)
         subscriber.subtract_credit(recipient_imsi, str(cost_in_credits))
         reason = "SMS received from %s (local_recv_sms)" % data.from_name
         events.create_sms_event(
             recipient_imsi, old_balance, cost_in_credits, reason,
             data.destination, from_imsi=data.from_name,
             from_number=data.from_number)
     # Return 200 OK.
     headers = {
         'Content-type': 'text/plain'
     }
     raise web.OK(None, headers)
Ejemplo n.º 10
0
    def process_cdr(self, cdr_xml):
        """Processes the XML CDR for the caller."""
        cdr_dom = xml.parseString(cdr_xml)
        # Handle only b-legs for billing.
        origin = cdr_dom.getElementsByTagName("origination")
        if origin:
            return
        # Handle only b-legs for billing.
        # For our purposes, billsec is how long the call lasted. call_duration
        # captures the amount of time spent ringing, which we don't charge for,
        # so don't include here. Caller and callee are just used for logging
        # and reason statements.
        # TODO(matt): what happens if the tag does not exist?
        call_duration = int(
            get_tag_text(
                cdr_dom.getElementsByTagName("duration")[0].childNodes))
        billsec = int(
            get_tag_text(
                cdr_dom.getElementsByTagName("billsec")[0].childNodes))
        # In b-leg cdrs, there are multiple destinations -- the sip one (IMSI)
        # and the dialed one (MSISDN).  We want the latter.
        callees = cdr_dom.getElementsByTagName("destination_number")
        callee = ''
        for c in callees:
            c = get_tag_text(c.childNodes)
            # NOT THE IMSI
            if c[0:4] != IMSI_PREFIX:
                callee = c
                break
        if callee[0] == "+":
            callee = callee[1:]
        hangupcause = get_hangup_cause(cdr_dom)
        # This is where we get the info we need to do billing.
        if len(cdr_dom.getElementsByTagName("service_type")) > 0:
            service_type = get_tag_text(
                cdr_dom.getElementsByTagName("service_type")[0].childNodes)
            # Get caller / callee info.  See the 'CDR notes' doc in drive for
            # more info.
            from_imsi, from_number, to_imsi, to_number = 4 * [None]
            # We always get 'from_imsi' from the <username> tag with the
            # <caller_profile> parent element. If it's a BTS-originated call,
            # this will be an IMSI; otherwise, it'll be an MSISDN.
            if service_type not in ['incoming_call']:
                elements = cdr_dom.getElementsByTagName('username')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        username = get_tag_text(element.childNodes)
                        from_imsi = subscriber.get_imsi_from_username(username)
                        break
            # Get 'from_number' (only available for outside and local calls).
            if service_type in ['outside_call', 'local_call', 'incoming_call']:
                elements = cdr_dom.getElementsByTagName('caller_id_name')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        from_number = get_tag_text(element.childNodes)
                        break
            # Get 'to_imsi' (only available for local/incoming calls).
            if service_type in ['local_call', 'incoming_call']:
                elements = cdr_dom.getElementsByTagName('callee_id_number')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        callee_id = get_tag_text(element.childNodes)
                        if callee_id[0:4] == IMSI_PREFIX:
                            to_imsi = callee_id
                        else:
                            # callee_id_number in the CDR is MSISDN.
                            to_imsi = subscriber.get_imsi_from_number(
                                callee_id)
                        break

            # Get 'to_number' (slightly different for local/incoming calls).
            if service_type in ['outside_call', 'free_call', 'error_call']:
                elements = cdr_dom.getElementsByTagName('destination_number')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        to_number = get_tag_text(element.childNodes)
                        break
            elif service_type in ['local_call', 'incoming_call']:
                elements = cdr_dom.getElementsByTagName('destination_number')
                for element in elements:
                    if (element.parentNode.nodeName ==
                            'originator_caller_profile'):
                        to_number = get_tag_text(element.childNodes)
                        break
            # Generate billing information for the caller, if the caller is
            # local to the BTS.
            if service_type != 'incoming_call':
                tariff = billing.get_service_tariff(
                    service_type, 'call', destination_number=to_number)
                cost = billing.get_call_cost(billsec,
                                             service_type,
                                             destination_number=to_number)
                reason = "%s sec call to %s (%s/%s)" % (
                    billsec, to_number, service_type, hangupcause)
                old_balance = subscriber.get_account_balance(from_imsi)
                subscriber.subtract_credit(from_imsi, str(cost))
                owner_imsi = from_imsi
                kind = service_type
                events.create_call_event(owner_imsi, from_imsi, from_number,
                                         to_imsi, to_number, old_balance, cost,
                                         kind, reason, tariff, call_duration,
                                         billsec)

            # Create a call record for the callee, if applicable.
            if service_type in ['local_call', 'incoming_call']:
                if service_type == 'local_call':
                    service_type = 'local_recv_call'
                tariff = billing.get_service_tariff(service_type, 'call')
                cost = billing.get_call_cost(billsec, service_type)
                reason = "%d sec call from %s (%s/%s)" % (
                    billsec, from_number, service_type, hangupcause)
                # Note! This is different than how we bill for a caller --
                # we're deducting from the 'to_imsi' (the callee) instead.
                old_balance = subscriber.get_account_balance(to_imsi)
                subscriber.subtract_credit(to_imsi, str(cost))
                owner_imsi = to_imsi
                kind = service_type
                events.create_call_event(owner_imsi, from_imsi, from_number,
                                         to_imsi, to_number, old_balance, cost,
                                         kind, reason, tariff, call_duration,
                                         billsec)

        else:
            username = get_tag_text(
                cdr_dom.getElementsByTagName("username")[0].childNodes)
            from_imsi = subscriber.get_imsi_from_username(username)
            message = "No rate info for this call. (from: %s, billsec: %s)" % (
                from_imsi, billsec)
            logger.error(message)