Пример #1
0
def PUT_users(request, format):
    """ Respond to a "PUT users" request.

        We let the caller update the details of the currently logged-in user.
    """
    params = apiHelper.get_params(request, resource_name="user")
    error  = apiHelper.check_params(params, required_params=["token"],
                                    optional_params=["latitude", "longitude",
                                                     "username", "password"])
    if error != None: return error

    token = params['token']

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    if "latitude" in params:
        user.latitude = params['latitude']
    if "longitude" in params:
        user.longitude = params['longitude']
    if "username" in params:
        user.username = params['username']
    if "password" in params:
        user.set_password(params['password'])
    user.updated_at = datetime.datetime.now()
    user.save()

    return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #2
0
def GET_messages_count(request, format):
    """ Respond to the "GET messages/count" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="message")

    error = apiHelper.check_params(params,
                                   required_params=["token"],
                                   optional_params=[])
    if error != None: return error

    token = params['token']

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Calculate the total number of messages for all profiles belonging to this
    # user.  Note that we have to do this separately for the sending and
    # receiving profiles.

    sender_total    = Message.objects.filter(sender__user=user).count()
    recipient_total = Message.objects.filter(recipient__user=user).count()

    count = sender_total + recipient_total

    # Finally, return the total back to the caller.

    return apiHelper.response(count,
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #3
0
def GET_user_id(request, format):
    """ Respond to the "GET user_id" request.

        This "hidden" API call translates from a profile ID to a user ID.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name=None)

    error = apiHelper.check_params(params,
                                   required_params=["token", "profile_id"],
                                   optional_params=[])
    if error != None: return error

    token      = params['token']
    profile_id = params['profile_id']

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Get the requested Profile object.

    try:
        profile = Profile.objects.get(id=profile_id)
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such profile")

    # Return the user associated with that profile.

    return apiHelper.response(profile.user.id,
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #4
0
def POST_user_numbers(request, format):
    """ Respond to a "POST user_numbers" request.
    """
    params = apiHelper.get_params(request, "user_number")

    error = apiHelper.check_params(params,
                                   required_params=["token", "number"],
                                   optional_params=["country"])
    if error != None: return error

    token   = params['token']
    number  = params['number']

    if "country" in params:
        country = params['country']
    else:
        country = "US"

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Ask the "phonenumbers" library to format the phone number into E.164
    # format.

    phone_number = phonenumbers.parse(number, country)
    phone_format = phonenumbers.PhoneNumberFormat.E164
    formatted_number = phonenumbers.format_number(phone_number, phone_format)

    # If we already have a UserNumber record for this phone number, delete the
    # old one.

    try:
        user_num = UserNumber.objects.get(number=formatted_number)
    except UserNumber.DoesNotExist:
        user_num = None

    if user_num != None:
        user_num.delete() # ??? What if the number is owned by someone else?

    # Create the UserNumber record for this phone number.

    user_num = UserNumber()
    user_num.user       = user
    user_num.number     = formatted_number
    user_num.code       = utils.calc_random_digits(num_digits=4)
    user_num.verified   =  False
    user_num.created_at = datetime.datetime.now()
    user_num.updated_at = datetime.datetime.now()
    user_num.save()

    # Ask our SMS gateway to send a verification message to the phone.

    _send_verification_code(user_num)

    # Finally, return the newly-created user number back to the caller.

    return apiHelper.response({'user_number' : user_num.to_dict()},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #5
0
def POST_attachments(request, format):
    """ Respond to the "POST attachments" request.

        We let the caller add an attachment to an existing message.
    """
    # Get our request parameters.

    params = apiHelper.get_params(request, resource_name=None)

    error = apiHelper.check_params(params,
                                   required_params=["token", "message_id",
                                                    "attachment"],
                                   optional_params=[])
    if error != None: return error

    token      = params['token']
    message_id = params['message_id']
    file       = params['attachment']

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Get the Message to add the attachment to.

    try:
        message = Message.objects.get(id=message_id)
    except Message.DoesNotExist:
        message = None

    if message == None:
        return HttpResponseBadRequest("No such message")

    # Add the attachment to this message.

    attachment = Attachment()
    attachment.message = message
    attachment.created_at = datetime.datetime.now()
    attachment.updated_at = datetime.datetime.now()
    attachment.save()
    attachment.attachment.save(file.name, file)

    # Send out a notification for the message.

    message.send_notifications()

    # Update the message.

    message.updated_at = datetime.datetime.now()
    message.save()

    # Finally, return a copy of the newly-created attachment back to the
    # caller.

    return apiHelper.response({'attachment' : attachment.to_dict()},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #6
0
def PUT_user_numbers(request, format):
    """ Respond to a "PUT user_numbers" request.
    """
    params = apiHelper.get_params(request, "user_number")

    error = apiHelper.check_params(params,
                                   required_params=["token", "id"],
                                   optional_params=["code"])
    if error != None: return error

    token = params['token']
    id    = params['id']

    if "code" in params:
        code = params['code']
    else:
        code = None

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Get the UserNumber record with the specified ID.

    try:
        user_num = UserNumber.objects.get(id=id)
    except UserNumber.DoesNotExist:
        return HttpResponseBadRequest("No such number")

    if user_num.user != user:
        return HttpResponseBadRequest("Not your number")

    # If the caller didn't specify a code, or the code is incorrect, send
    # another code.

    if code == None or user_num.code != code:

        user_num.code       = utils.calc_random_digits(num_digits=4)
        user_num.verified   = False
        user_num.updated_at = datetime.datetime.now()
        user_num.save()

        _send_verification_code(user_num)

    else:

        # If we get here, the verification code is correct -> verify the
        # number.

        user_num.verified   = True
        user_num.updated_at = datetime.datetime.now()
        user_num.save()

    # Finally, return an empty response back to the caller.

    return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #7
0
def PUT_conversations_unhide(request, format):
    """ Respond to the "PUT conversations/unhide" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="conversation")

    error = apiHelper.check_params(params,
                                   required_params=["token",
                                                    "profile_id",
                                                    "conversation_id"],
                                   optional_params=[])
    if error != None: return error

    token = params['token']

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Check that the profile and conversation both exist, that the profile is
    # part of the conversation and that the currently logged-in user owns the
    # profile.

    try:
        profile = Profile.objects.get(id=params['profile_id'])
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such profile")

    try:
        conversation = Conversation.objects.get(id=params['conversation_id'])
    except Conversation.DoesNotExist:
        return HttpResponseBadRequest("No such conversation")

    if conversation.profile1 != profile and conversation.profile2 != profile:
        return HttpResponseBadRequest("Profile not in conversation")

    if profile.user != user:
        return HttpResponseBadRequest("Not your profile")

    # Unhide this conversation for the given profile.

    if profile == conversation.profile1:
        conversation.hidden1 = False
    elif profile == conversation.profile2:
        conversation.hidden2 = False
    conversation.updated_at = datetime.datetime.now()
    conversation.save()

    # Finally, return an empty response back to the caller.

    return apiHelper.response(None,
                              format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #8
0
def PUT_revelations_unreveal(request, format):
    """ Respond to the "PUT revelations/unreveal" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="revelation")

    error = apiHelper.check_params(
        params, required_params=["token", "profile_id", "recipient_id", "field_name"], optional_params=[]
    )
    if error != None:
        return error

    token = params["token"]
    field_name = params["field_name"]

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Check that the source and recipient profiles both exist, and that the
    # currently logged-in user owns the source profile.

    try:
        profile = Profile.objects.get(id=params["profile_id"])
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such profile")

    try:
        recipient = Profile.objects.get(id=params["recipient_id"])
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such recipient profile")

    if profile.user != user:
        return HttpResponseBadRequest("Not your profile")

    # Check that the field name is valid.

    if field_name not in Revelation.VALID_FIELD_NAMES:
        return HttpResponseBadRequest("Invalid field_name")

    # Remove the existing revelation for this field, if any.

    Revelation.objects.filter(profile=profile, recipient=recipient, field_name=field_name).delete()

    # Tell the push gateway that the profile has changed.

    push_gateway.send_profile(profile, recipient)

    # Finally, return an empty response back to the caller.

    return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #9
0
def GET_conversations(request, format):
    """ Respond to the "GET /conversations" API call.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="conversation")

    error = apiHelper.check_params(params,
                                   required_params=["token"],
                                   optional_params=["since"])
    if error != None: return error

    token = params['token']
    since = params.get("since")

    # Check that the user's session is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Attempt to parse the "since" parameter, if it was supplied.

    if since != None:
        try:
            since = datetime.datetime.strptime(since, "%Y-%m-%dT%H:%M:%SZ") 
        except ValueError:
            return HttpResponseBadRequest("Invalid 'since' value")

    # Search for all conversations in which the given user is a participant.

    conversations = []

    query = {'profile1__user' : user}
    if since != None:
        query['updated_at__gte'] = since

    for c in Conversation.objects.filter(**query):
        cdict = c.to_dict_for_profile(c.profile1)
        conversations.append({'conversation' : cdict})

    query = {'profile2__user' : user}
    if since != None:
        query['updated_at__gte'] = since

    for c in Conversation.objects.filter(**query):
        cdict = c.to_dict_for_profile(c.profile2)
        conversations.append({'conversation' : cdict})

    # Finally, return the list of matching conversations back to the caller.

    return apiHelper.response(conversations,
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #10
0
def POST_users(request, format):
    """ Respond to the "POST users" request.

        We create a new user and log them in.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="user")

    error = apiHelper.check_params(params,
                                   required_params=[],
                                   optional_params=["username", "password"])
    if error != None: return error

    username = params.get("username")
    password = params.get("password")

    if username == "": username = None
    if password == "": password = None

    # Check that the given username doesn't already exist.

    if username != None:
        if User.objects.filter(username=username).exists():
            return HttpResponseBadRequest("Duplicate User")

    # Create a new User object for this user.

    user = User()
    user.username = username
    if password != None:
        user.set_password(password)
    user.created_at = datetime.datetime.now()
    user.updated_at = datetime.datetime.now()
    user.save()

    # Open up a session for this new user.

    token = session.create(user)

    # Finally, return the newly-created user and the login token back to the
    # caller.

    return apiHelper.response({'user'  : user.to_dict(),
                               'token' : token},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #11
0
def PUT_messages_read(request, format):
    """ Respond to the "PUT messages/read" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="message")

    error = apiHelper.check_params(params,
                                   required_params=["token", "ids"],
                                   optional_params=[])
    if error != None: return error

    token = params['token']

    msg_ids = []
    for msg_id in params['ids'].split(","):
        msg_ids.append(msg_id)

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Mark each of the messages as read.

    for msg_id in msg_ids:
        try:
            message = Message.objects.get(id=msg_id)
        except Message.DoesNotExist:
            return HttpResponseBadRequest("No message with ID %s" % msg_id)
        
        if message.recipient.user != user:
            return HttpResponseBadRequest("You're not the recipient of the " +
                                          "message with ID %s" % msg_id)

        message.read       = True
        message.updated_at = datetime.datetime.now()
        message.save()

    # Finally, return an empty response back to the caller.

    return apiHelper.response(None,
                              format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #12
0
def GET_version(request, format):
    """ Respond to the "GET version" request.

        We return a dictionary mapping client OS names to the minimum supported
        version number for that client.  Note that the actual version numbers
        are stored in the api.client_versions module.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name=None)

    error = apiHelper.check_params(params,
                                   required_params=[],
                                   optional_params=[])
    if error != None: return error

    return apiHelper.response(CLIENT_VERSIONS,
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #13
0
def PUT_sessions(request, format):
    """ Respond to a "PUT sessions" request.
    """
    params = apiHelper.get_params(request, "session")
    error = apiHelper.check_params(params, required_params=["token", "code"])
    if error != None: return error

    token = params['token']
    code  = params['code']

    if not session.check_verification_code(token, code):
        return HttpResponseForbidden("Incorrect verification code")

    user = session.get_user(token)

    return apiHelper.response({'user'  : user.to_dict(),
                               'token' : token},
                              format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #14
0
def GET_users(request, format):
    """ Respond to a "GET users" request.

        We return the details of the currently logged-in user.
    """
    params = apiHelper.get_params(request, resource_name="user")
    error  = apiHelper.check_params(params, required_params=["token"])
    if error != None: return error

    token = params['token']

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    return apiHelper.response({'user' : user.to_dict()},
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #15
0
def POST_devices(request, format):
    """ Respond to the "POST devices" request.

        We create a new device in the system, overwriting the existing device
        if it exists.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="device")

    error = apiHelper.check_params(params,
                                   required_params=["token",
                                                    "notification_type",
                                                    "notification_token"])
    if error != None: return error

    token              = params['token']
    notification_type  = params['notification_type']
    notification_token = params['notification_token']

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Delete any existing devices with the given notification type and token.

    Device.objects.filter(notification_type=notification_type,
                          notification_token=notification_token).delete()

    # Create the new device.

    device = Device()
    device.user               = user
    device.notification_type  = notification_type
    device.notification_token = notification_token
    device.created_at         = datetime.datetime.now()
    device.updated_at         = datetime.datetime.now()
    device.save()

    # Finally, return the device back to our caller.

    return apiHelper.response({'device' : device.to_dict()},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #16
0
def POST_messages_sms(request, format):
    """ Respond to the "POST messages/sms" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name=None)

    error = apiHelper.check_params(params,
                                   required_params=["To", "From", "Body"],
                                   optional_params=[])
    if error != None: return error

    to_number    = params['To']
    from_number  = params['From']
    message_body = params['Body']

    # Ask our SMS gateway to do all the work.

    sms_reply = sms_gateway.receive_message(to_number,
                                            from_number,
                                            message_body)

    # Finally, return the response back to the caller.

    response = []
    response.append('<?xml version="1.0"?>')
    response.append('<Response>')
    if sms_reply != None:
        response.append('<Sms>')
        response.append(sms_reply)
        response.append('</Sms>')
    response.append('</Response>')

    return HttpResponse("\n".join(response),
                        mimetype="text/xml",
                        status=HTTP_RESPONSE_POST_OK)
Пример #17
0
def GET_profiles(request, format):
    """ Respond to the "GET profiles" request.

        We return a list of other people's profiles that have been revealed to
        the currently logged-in user.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="profile")

    error = apiHelper.check_params(params,
                                   required_params=["token"],
                                   optional_params=[])
    if error != None: return error

    token = params['token']

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Get the profiles that have been revealed to this user.

    profiles = []
    for revelation in Revelation.objects.filter(recipient__user=user) \
                                        .distinct("profile"):
        profile = revelation.profile.to_dict_revealed_to(revelation.recipient)
        profiles.append({'profile' : profile})

    # Finally, return the list of revealed profiles back to the caller.

    return apiHelper.response(profiles,
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #18
0
def POST_messages(request, format):
    """ Respond to a "POST /messages" request.
    """
    # Get our request parameters.

    params = apiHelper.get_params(request, "message")

    error = apiHelper.check_params(params,
                                   required_params=["token", "sender_id",
                                                    "body"],
                                   optional_params=["guid", "recipient_id",
                                                    "link_id", "message_type",
                                                    "latitude", "longitude",
                                                    "speed", "course",
                                       "message[attachments][][attachment]"])
    if error != None: return error

    if "recipient_id" in params:
        has_link = False
        recipient_id = params['recipient_id']
    elif "link_id" in params:
        has_link = True
        link_id = params['link_id']
    else:
        return HttpResponseBadRequest("recipient_id or link_id required.")

    token     = params['token']
    sender_id = params['sender_id']
    body      = params['body']

    if "guid" in params:
        guid = params['guid']
    else:
        guid = None

    if "message_type" in params:
        message_type = params['message_type']
    else:
        message_type = None # Maybe default to "text"???

    attachments = []
    if "message[attachments][][attachment]" in params:
        for attachment in params.getlist("message[attachments][][attachment]"):
            attachments.append(attachment)

    values = {} # Maps parameter name to decimal value or None.

    for param in ["latitude", "longitude", "speed", "course"]:
        if param in params:
            try:
                value = decimal.Decimal(params[param])
            except ValueError:
                return HttpResponseBadRequest("Invalid '%s' value", param)
        else:
            value = None

        values[param] = value

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # See if there is already a message with that GUID value.  If so, we return
    # that message rather than creating a new one.

    if guid not in ["", None]:
        try:
            existing_message = Message.objects.get(guid=guid)
        except Message.DoesNotExist:
            existing_message = None

        if existing_message != None:
            return apiHelper.response({'message' : existing_message.to_dict()},
                                      format=format,
                                      status=HTTP_RESPONSE_POST_OK)

    # If we've been supplied a link ID, convert the link to a profile so we can
    # send the message to that profile.

    if has_link:
        try:
            link = Link.objects.get(id=link_id)
        except Link.DoesNotExist:
            return HttpResponseBadRequest("Nonexistent link")

        recipient_id = link.profile.id

    # Get the sending and receiving profiles.  Note that the recipient ID might
    # be specified using a link ID or a profile ID.

    try:
        sender_profile = Profile.objects.get(id=sender_id)
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("Nonexistent sending profile")

    if sender_profile.user != user:
        return HttpResponseBadRequest("sender_id not your profile")

    try:
        recipient_profile = Profile.objects.get(id=recipient_id)
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("Nonexistent recipient profile")

    # See if we already have a conversation for this sender and recipient.

    try:
        conversation = Conversation.objects.get(profile1=sender_profile,
                                                profile2=recipient_profile)
    except Conversation.DoesNotExist:
        try:
            conversation = Conversation.objects.get(profile1=recipient_profile,
                                                    profile2=sender_profile)
        except Conversation.DoesNotExist:
            conversation = None

    if conversation != None:
        # Unhide the existing conversation for both parties.
        conversation.hidden1    = False
        conversation.hidden2    = False
        conversation.updated_at = datetime.datetime.now()
        conversation.save()
    else:
        # There's no existing conversation for these two profiles -> create
        # one.
        conversation = Conversation()
        conversation.profile1   = sender_profile
        conversation.profile2   = recipient_profile
        conversation.hidden1    = False
        conversation.hidden2    = False
        conversation.created_at = datetime.datetime.now()
        conversation.updated_at = datetime.datetime.now()
        conversation.save()

        if has_link:
            # Copy the tags from the link across to the conversation.
            for link_tag in LinkTag.objects.filter(link=link):
                conversation_tag = ConversationTag()
                conversation_tag.conversation = conversation
                conversation_tag.profile      = link.profile
                conversation_tag.tag          = link_tag.tag
                conversation_tag.save()

    # If the message had a lat/long coordinate, update the associated user's
    # last-known latitude and longitude.

    if values['latitude'] != None and values['longitude'] != None:
        user.latitude   = values['latitude']
        user.longitude  = values['longitude']
        user.updated_at = datetime.datetime.now()
        user.save()

    # Create the new Message object.

    message = Message()
    message.guid         = guid
    message.conversation = conversation
    message.sender       = sender_profile
    message.recipient    = recipient_profile
    message.read         = False
    message.message_type = message_type
    message.body         = body
    message.sent_apn     = False
    message.sent_android = False
    message.sent_sms     = False
    message.latitude     = values['latitude']
    message.longitude    = values['longitude']
    message.speed        = values['speed']
    message.course       = values['course']
    message.created_at   = datetime.datetime.now()
    message.updated_at   = datetime.datetime.now()
    message.save()

    # Create Attachment objects for each of the message's attachments (if any).

    for file in attachments:
        attachment = Attachment()
        attachment.message = message
        attachment.created_at = datetime.datetime.now()
        attachment.updated_at = datetime.datetime.now()
        attachment.save()
        attachment.attachment.save(file.name, file)

    # Send out notifications for the newly-created message, if appropriate.

    send_notification = True # initially.
    if message_type == "image" and len(attachments) == 0:
        # We have to delay sending out the notification until after the
        # attachment has been received.
        send_notification = False

    if send_notification:
        message.send_notifications()

    # Finally, return a copy of the newly-created message back to the caller.

    return apiHelper.response({'message' : message.to_dict()},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #19
0
def POST_links(request, format):
    """ Respond to the "POST links" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="link")

    if "number" in params:
        create_from_phone = True
        error = apiHelper.check_params(params,
                                       required_params=["number", "tag"],
                                       optional_params=[])
        if error != None: return error
    elif "profile_id" in params:
        create_from_phone = False
        error = apiHelper.check_params(params,
                                       required_params=["token",
                                                        "profile_id",
                                                        "tag"],
                                       optional_params=[])
        if error != None: return error
    else:
        return HttpResponseBadRequest("A phone number or a profile ID " +
                                      "must be supplied.")


    if create_from_phone:

        # Get the User, User Number and Profile for the supplied phone number.
        # Note that we automatically create these records if they don't already
        # exist.

        phone_number = params['number']

        try:
            user_num = UserNumber.objects.get(number=phone_number)
        except UserNumber.DoesNotExist:
            user_num = None

        if user_num == None:
            # We don't know this phone number -> create new User and UserNumber
            # records for this phone number.
            user = User()
            user.created_at = datetime.datetime.now()
            user.updated_at = datetime.datetime.now()
            user.save()

            user_num = UserNumber()
            user_num.user       = user
            user_num.number     = phone_number
            user_num.verified   = True
            user_num.created_at = datetime.datetime.now()
            user_num.updated_at = datetime.datetime.now()
            user_num.save()
        else:
            user = user_num.user

        # See if this user has a default profile.  If not, create one.

        if Profile.objects.filter(user=user).exists():
            profiles = Profile.objects.filter(user=user).order_by("id")
            if user.default_profile < 0 or user.default_profile >= len(profiles):
                # Set the default.
                profile = profiles[0]
                user.default_profile = 0
                user.created_at      = datetime.datetime.now()
                user.updated_at      = datetime.datetime.now()
                user.save()
            else:
                # Use the default.
                profile = profiles[user.default_profile]
        else:
            # Create a new profile for this user.
            profile = Profile()
            profile.user       = user
            profile.created_at = datetime.datetime.now()
            profile.updated_at = datetime.datetime.now()
            profile.save()

            user.default_profile = 0
            user.updated_at      = datetime.datetime.now()
            user.save()

    else:

        # We've been supplied a profile ID.  Use that for the new Link object.

        token      = params['token']
        profile_id = params['profile_id']

        if not session.validate(token):
            return HttpResponseBadRequest("Invalid token")

        user = session.get_user(token)

        try:
            profile = Profile.objects.get(id=profile_id)
        except Profile.DoesNotExist:
            return HttpResponseBadRequest("Invalid profile ID")

        if profile.user != user:
            return HttpResponseBadRequest("Not your profile")

    link = Link()
    link.profile    = profile
    link.created_at = datetime.datetime.now()
    link.updated_at = datetime.datetime.now()
    link.save()

    # Create the tags for this link.

    for tag in params.getlist("tag"):
        link_tag = LinkTag()
        link_tag.link = link
        link_tag.tag  = tag
        link_tag.save()

    # Finally, return the newly-created Link back to the caller.

    return apiHelper.response({'link' : link.to_dict()},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #20
0
def POST_revelations(request, format):
    """ Respond to the "POST revelations" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="revelation")

    error = apiHelper.check_params(params,
                                   required_params=["token", "profile_id",
                                                    "recipient_id"],
                                   optional_params=[
                                                "field_names",
                                                "revelations[][recipient_id]",
                                                "revelations[][profile_id]",
                                                "revelations[][field_name]"])
    if error != None: return error

    token = params['token']

    if params.get("field_names") != None:
        param_style = "NEW"
    elif params.get("revelations[][field_name]") != None:
        param_style = "OLD"
    else:
        return HttpResponseBadRequest("Invalid parameters")

    if param_style == "NEW":
        required_params   = ["field_names"]
        prohibited_params = ["revelations[][recipient_id]",
                             "revelations[][profile_id]",
                             "revelations[][field_name]"]
    elif param_style == "OLD":
        required_params   = ["revelations[][recipient_id]",
                             "revelations[][profile_id]",
                             "revelations[][field_name]"]
        prohibited_params = ["field_names"]
    else:
        required_params   = []
        prohibited_params = []

    for param in required_params:
        if param not in params:
            return HttpResponseBadRequest("Missing '%s' parameter" % param)
    for param in prohibited_params:
        if param in params:
            return HttpResponseBadRequest("Unexpected '%s' parameter" % param)

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Check that the source and recipient profiles both exist, and that the
    # currently logged-in user owns the source profile.

    try:
        profile = Profile.objects.get(id=params['profile_id'])
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such profile")

    try:
        recipient = Profile.objects.get(id=params['recipient_id'])
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such recipient profile")

    if profile.user != user:
        return HttpResponseBadRequest("Not your profile")

    # Extract the list of fields to reveal.  Note that how we do this depends
    # on the parameter style being used.

    if param_style == "NEW":
        fields_to_reveal = params['field_names'].split(",")
    else:
        fields_to_reveal = []
        for field_name in params.getlist("revelations[][field_name]"):
            fields_to_reveal.append(field_name)

    # Check that the field names are all correct.

    for field_name in fields_to_reveal:
        if field_name not in Revelation.VALID_FIELD_NAMES:
            return HttpResponseBadRequest("Invalid field: %s" % field_name)

    # Remove the existing revelations for these source and destination
    # profiles.

    Revelation.objects.filter(profile=profile, recipient=recipient).delete()

    # Now add the new revelations to the database.

    revelations = []
    for field_name in fields_to_reveal:
        revelation = Revelation()
        revelation.profile    = profile
        revelation.recipient  = recipient
        revelation.field_name = field_name
        revelation.created_at = datetime.datetime.now()
        revelation.updated_at = datetime.datetime.now()
        revelation.save()
        revelations.append({'revelation' : revelation.to_dict()})

    # Tell the push gateway that the profile has changed.

    push_gateway.send_profile(profile, recipient)

    # Finally, return the newly-created revelations back to the caller.

    return apiHelper.response(revelations,
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #21
0
def GET_revelations(request, format):
    """ Respond to the "GET revelations" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="revelation")

    error = apiHelper.check_params(params,
                                   required_params=["token"],
                                   optional_params=["profile_id",
                                                    "recipient_id"])
    if error != None: return error

    token        = params['token']
    profile_id   = params.get("profile_id")
    recipient_id = params.get("recipient_id")

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Check that the source and recipient profiles exist (if specified), and
    # that the recipient profile (if any) is owned by the currently logged-in
    # user.

    if profile_id != None:
        try:
            profile = Profile.objects.get(id=profile_id)
        except Profile.DoesNotExist:
            return HttpResponseBadRequest("No such profile")
    else:
        profile = None

    if recipient_id != None:
        try:
            recipient = Profile.objects.get(id=recipient_id)
        except Profile.DoesNotExist:
            return HttpResponseBadRequest("No such recipient profile")
    else:
        recipient = None

    if recipient != None and recipient.user != user:
        return HttpResponseBadRequest("Not your profile")

    # Find the matching set of revelations, filtering by the profile and
    # recipient, if any.

    kwargs = {}
    if profile != None:
        kwargs['profile'] = profile
    if recipient != None:
        # Find revelations made to the given recipient profile.
        kwargs['recipient'] = recipient
    else:
        # Find all revelations made to us.
        kwargs['recipient__user'] = user

    revelations = []
    for revelation in Revelation.objects.filter(**kwargs):
        revelations.append({'revelation' : revelation.to_dict()})

    # Finally, return the revelations back to the caller.

    return apiHelper.response(revelations,
                              format=format, status=HTTP_RESPONSE_GET_OK)
Пример #22
0
def POST_profiles(request, format):
    """ Respond to the "POST profiles" request.

        We create a new profile for the currently logged-in user.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="profile")

    error = apiHelper.check_params(params,
                                   required_params=["token"],
                                   optional_params=["alias",  "avatar",
                                                    "image1", "image2",
                                                    "image3", "image4",
                                                    "url",    "email",
                                                    "location", "bio"])
    if error != None: return error

    token    = params['token']
    alias    = params.get("alias")
    avatar   = params.get("avatar")
    image1   = params.get("image1")
    image2   = params.get("image2")
    image3   = params.get("image3")
    image4   = params.get("image4")
    url      = params.get("url")
    email    = params.get("email")
    location = params.get("location")
    bio      = params.get("bio")

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Create a Profile object with the supplied parameters.

    profile = Profile()
    profile.user     = user
    profile.alias    = alias
    profile.url      = url
    profile.email    = email
    profile.location = location
    profile.bio      = bio
    profile.created_at = datetime.datetime.now()
    profile.updated_at = datetime.datetime.now()
    profile.save()

    if avatar != None:
        profile.avatar_orig.save(avatar.name, avatar)
    if image1 != None:
        profile.image1_orig.save(image1.name, image1)
    if image2 != None:
        profile.image2_orig.save(image2.name, image2)
    if image3 != None:
        profile.image3_orig.save(image3.name, image3)
    if image4 != None:
        profile.image4_orig.save(image4.name, image4)

    # Finally, return the newly-created profile back to the caller.

    return apiHelper.response({'profile' : profile.own_to_dict()},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #23
0
def POST_conversations(request, format):
    """ Respond to the "POST conversations" request.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="conversation")

    error = apiHelper.check_params(params,
                                   required_params=["token", "sender_id"],
                                   optional_params=["number", "country",
                                                    "recipient_id", "aka",
                                                    "tag"])
    if error != None: return error

    token        = params['token']
    sender_id    = params['sender_id']
    number       = params.get("number")
    country      = params.get("country")
    recipient_id = params.get("recipient_id")
    aka          = params.get("aka")

    if "tag" in params:
        tags = params.getlist("tag")
    else:
        tags = []

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # If the caller supplied a phone number and not a recipient ID, try to
    # match the phone number against an existing user.  If the phone number is
    # new, we create a new user for that phone number on-the-fly.

    if number != None and recipient_id == None:
        # Normalize the phone number into E.164 format.

        if country == None: country = "US"

        phone_number     = phonenumbers.parse(number, country)
        phone_format     = phonenumbers.PhoneNumberFormat.E164
        formatted_number = phonenumbers.format_number(phone_number,
                                                      phone_format)

        # See if the phone number already exists in the system.

        try:
            recipient_number = UserNumber.objects.get(number=formatted_number)
        except UserNumber.DoesNotExist:
            recipient_number = None

        if recipient_number == None:

            # This is the first time we've encountered this phone number ->
            # create a new User record for this user, and attach it to the
            # phone number.

            recipient_user = User()
            recipient_user.created_at = datetime.datetime.now()
            recipient_user.updated_at = datetime.datetime.now()
            recipient_user.save()

            recipient_number = UserNumber()
            recipient_number.user       = recipient_user
            recipient_number.number     = formatted_number
            recipient_number.verified   = True # Number is already validated.
            recipient_number.created_at = datetime.datetime.now()
            recipient_number.updated_at = datetime.datetime.now()
            recipient_number.save()
        else:
            recipient_user = recipient_number.user

        # We now have a User and UserNumber record for this phone number.
        # Check that we have a profile for that user; if not, we have to create
        # one on the fly.

        profiles = []
        for profile in recipient_user.profile_set.order_by("id"):
            profiles.append(profile)

        if len(profiles) == 0:
            recipient_profile = Profile()
            recipient_profile.user       = recipient_user
            recipient_profile.created_at = datetime.datetime.now()
            recipient_profile.updated_at = datetime.datetime.now()
            recipient_profile.save()
        else:
            recipient_profile = profiles[recipient_user.default_profile]

        # Finally, use the recipient profile for this conversation.

        recipient_id = recipient_profile.id

    else:

        # Check that the caller specified a recipient ID.

        if recipient_id == None:
            return HttpResponseBadRequest("Missing recipient_id parameter")

    # Get the sender and recipient profiles.

    try:
        sender_profile = Profile.objects.get(id=sender_id)
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("Nonexistent sending profile")

    if sender_profile.user != user:
        return HttpResponseBadRequest("sender_id not your profile")

    try:
        recipient_profile = Profile.objects.get(id=recipient_id)
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("Nonexistent recipient profile")

    # See if we already have a conversation for this sender and recipient.

    try:
        conversation = Conversation.objects.get(profile1=sender_profile,
                                                profile2=recipient_profile)
    except Conversation.DoesNotExist:
        try:
            conversation = Conversation.objects.get(profile1=recipient_profile,
                                                    profile2=sender_profile)
        except Conversation.DoesNotExist:
            conversation = None

    if conversation != None:
        # Unhide the existing conversation for both parties.
        conversation.hidden1    = False
        conversation.hidden2    = False
        conversation.updated_at = datetime.datetime.now()
        conversation.save()
    else:
        # There's no existing conversation for these two profiles -> create
        # one.
        conversation = Conversation()
        conversation.profile1   = sender_profile
        conversation.profile2   = recipient_profile
        conversation.hidden1    = False
        conversation.hidden2    = False
        conversation.created_at = datetime.datetime.now()
        conversation.updated_at = datetime.datetime.now()
        conversation.save()

        # If either party has made revelations to the "PUBLIC" profile, copy
        # those revelations and make them directly to the other party.  In this
        # way, changing the public revelations won't affect the revelations
        # made in this conversation.

        _copy_public_revelations(sender_profile, recipient_profile)
        _copy_public_revelations(recipient_profile, sender_profile)

    # Store the "aka" value for this conversation, if one was supplied.

    if aka != None:
        if sender_profile == conversation.profile1:
            conversation.aka1 = aka
        else:
            conversation.aka2 = aka
        conversation.updated_at = datetime.datetime.now()
        conversation.save()

    # If the caller supplied any "tag" values, use these to replace the
    # existing tags (if any).

    if len(tags) > 0:
        ConversationTag.objects.filter(conversation=conversation,
                                       profile=sender_profile).delete()
        for tag in tags:
            conversation_tag = ConversationTag()
            conversation_tag.conversation = conversation
            conversation_tag.profile      = sender_profile
            conversation_tag.tag          = tag
            conversation_tag.created_at   = datetime.datetime.now()
            conversation_tag.updated_at   = datetime.datetime.now()
            conversation_tag.save()

    # Finally, return the conversation back to the caller.

    cdict = conversation.to_dict_for_profile(sender_profile,
                                             include_revelations=True)
    return apiHelper.response({'conversation' : cdict},
                              format=format, status=HTTP_RESPONSE_POST_OK)
Пример #24
0
def PUT_profiles(request, format):
    """ Respond to the "PUT profiles" request.

        We let the caller update the details of an existing profile.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name="profile")

    error = apiHelper.check_params(params,
                                   required_params=["token", "id"],
                                   optional_params=["alias",  "avatar",
                                                    "image1", "image2",
                                                    "image3", "image4",
                                                    "url",    "email",
                                                    "location", "bio"])
    if error != None: return error

    token      = params['token']
    profile_id = params['id']
    alias      = params.get("alias")
    avatar     = params.get("avatar")
    image1     = params.get("image1")
    image2     = params.get("image2")
    image3     = params.get("image3")
    image4     = params.get("image4")
    url        = params.get("url")
    email      = params.get("email")
    location   = params.get("location")
    bio        = params.get("bio")

    # Check that the login token is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Get the Profile object we're updating.

    try:
        profile = Profile.objects.get(id=profile_id)
    except Profile.DoesNotExist:
        return HttpResponseBadRequest("No such profile")

    if profile.user != user:
        return HttpResponseBadRequest("Not your profile")

    # Update the Profile with the new values.

    if alias != None:
        profile.alias = alias
    if url != None:
        profile.url = url
    if email != None:
        profile.email = email
    if location != None:
        profile.location = location
    if bio != None:
        profile.bio = bio

    if avatar != None:
        if profile.avatar_orig != None:
            profile.avatar_orig.delete(save=False)
        profile.avatar_orig.save(avatar.name, avatar, save=False)

    if image1 != None:
        if profile.image1_orig != None:
            profile.image1_orig.delete(save=False)
        profile.image1_orig.save(image1.name, image, save=False)

    if image2 != None:
        if profile.image2_orig != None:
            profile.image2_orig.delete(save=False)
        profile.image2_orig.save(image2.name, image, save=False)

    if image3 != None:
        if profile.image3_orig != None:
            profile.image3_orig.delete(save=False)
        profile.image3_orig.save(image3.name, image, save=False)

    if image4 != None:
        if profile.image4_orig != None:
            profile.image4_orig.delete(save=False)
        profile.image4_orig.save(image4.name, image, save=False)

    profile.updated_at = datetime.datetime.now()
    profile.save()

    # Finally, return the updated profile back to the caller.

    return apiHelper.response({'profile' : profile.own_to_dict()},
                              format=format, status=HTTP_RESPONSE_PUT_OK)
Пример #25
0
def POST_sessions(request, format):
    """ Respond to a "POST sessions" request.
    """
    params = apiHelper.get_params(request, "session")

    if "username" in params:
        error = apiHelper.check_params(params,
                                       required_params=["username",
                                                        "password"])
        login_via_username = True
    elif "number" in params:
        error = apiHelper.check_params(params,
                                       required_params=["number"],
                                       optional_params=["country"])
        login_via_username = False
    else:
        return HttpResponseBadRequest("username/password or " +
                                      "phone number required")

    if error != None: return error

    if login_via_username:

        # Log the caller in using the supplied username and password.

        username = params['username']
        password = params['password']

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            user = None

        if user == None or not user.check_password(password):
            return HttpResponseForbidden("Incorrect username or password")

        # Open up a session for this user.

        token = session.create(user)

        # Finally, return the user and the login token back to the caller.

        return apiHelper.response({'user'  : user.to_dict(),
                                   'token' : token},
                                  format=format, status=HTTP_RESPONSE_POST_OK)

    else:

        # Log the caller in using the supplied phone number.

        number = params['number']

        if "country" in params:
            country = params['country']
        else:
            country = "US"

        # Ask the "phonenumbers" library to format the phone number into E.164
        # format.

        phone_number     = phonenumbers.parse(number, country)
        phone_format     = phonenumbers.PhoneNumberFormat.E164
        formatted_number = phonenumbers.format_number(phone_number, phone_format)

        # Open up a session for this phone number.

        token,verification_code = session.create_for_phone(formatted_number)

        # Send an SMS message to the supplied phone number.

        sms_gateway.send_message(text="code: " + verification_code +
                                      " -- " + settings.SYSTEM_NAME,
                                 from_user=None,
                                 from_profile=None,
                                 to_user=None,
                                 to_profile=None,
                                 to_phone_number=formatted_number,
                                 message_id=None)

        # Finally, return the formatted phone number and login token back to
        # the caller.

        return apiHelper.response({'number'  : formatted_number,
                                   'token'   : token},
                                  format=format, status=HTTP_RESPONSE_POST_OK)
Пример #26
0
def PUT_conversations(request, format):
    """ Respond to the "PUT /conversations" API call.
    """
    # Get the request parameters.

    params = apiHelper.get_params(request, resource_name=None)

    error = apiHelper.check_params(params,
                                   required_params=["token", "conversation_id"],
                                   optional_params=["aka", "tag"])
    if error != None: return error

    token = params['token']
    conversation_id = params['conversation_id']

    if "aka" in params:
        aka = params['aka']
    else:
        aka = None

    if "tag" in params:
        tags = params.getlist("tag")
    else:
        tags = []

    # Check that the user's session is still valid.

    if not session.validate(token):
        return HttpResponseBadRequest("Invalid token")

    user = session.get_user(token)

    # Get the conversation to update.

    try:
        conversation = Conversation.objects.get(id=conversation_id)
    except Conversation.DoesNotExist:
        return HttpResponseBadRequest("No such conversation")

    # Check that the current user is involved in the conversation, and remember
    # the associated profile.

    if conversation.profile1.user == user:
        profile = conversation.profile1
    elif conversation.profile2.user == user:
        profile = conversation.profile2
    else:
        return HttpResponseBadRequest("Not your conversation")

    # Update the conversation.

    if aka != None:
        if profile == conversation.profile1:
            conversation.aka1 = aka
        else:
            conversation.aka2 = aka
        conversation.updated_at = datetime.datetime.now()
        conversation.save()

    if len(tags) > 0:
        ConversationTag.objects.filter(conversation=conversation,
                                       profile=profile).delete()
        for tag in tags:
            conversation_tag = ConversationTag()
            conversation_tag.conversation = conversation
            conversation_tag.profile      = profile
            conversation_tag.tag          = tag
            conversation_tag.created_at   = datetime.datetime.now()
            conversation_tag.updated_at   = datetime.datetime.now()
            conversation_tag.save()

    # Finally, return updated conversation back to the caller.

    cdict = conversation.to_dict_for_profile(profile,
                                             include_revelations=True)
    return apiHelper.response({'conversation' : cdict},
                              format=format, status=HTTP_RESPONSE_PUT_OK)