Пример #1
0
def message_api(public_id):
    try:
        valid_public_id(public_id)
        message = g.db_session.query(Message).filter(
            Message.public_id == public_id).one()
        assert int(message.namespace.id) == int(g.namespace.id)
    except InputError:
        return err(400, 'Invalid message id {}'.format(public_id))
    except NoResultFound:
        return err(
            404, "Couldn't find message with id {0} "
            "on namespace {1}".format(public_id, g.namespace_public_id))
    if request.method == 'GET':
        return g.encoder.jsonify(message)
    elif request.method == 'PUT':
        data = request.get_json(force=True)
        if data.keys() != ['unread'] or not isinstance(data['unread'], bool):
            return err(400,
                       'Can only change the unread attribute of a message')

        # TODO(emfree): Shouldn't allow this on messages that are actually
        # drafts.

        unread_tag = message.namespace.tags['unread']
        unseen_tag = message.namespace.tags['unseen']
        if data['unread']:
            message.is_read = False
            message.thread.apply_tag(unread_tag)
        else:
            message.is_read = True
            message.thread.remove_tag(unseen_tag)
            if all(m.is_read for m in message.thread.messages):
                message.thread.remove_tag(unread_tag)
        return g.encoder.jsonify(message)
Пример #2
0
def draft_update_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid draft id {}'.format(public_id))
    parent_draft = g.db_session.query(Message). \
        filter(Message.public_id == public_id).first()
    if parent_draft is None or not parent_draft.is_draft or \
            parent_draft.namespace.id != g.namespace.id:
        return err(404, 'No draft with public id {}'.format(public_id))
    if not parent_draft.is_latest:
        return err(409, 'Draft {} has already been updated to {}'.format(
            public_id, g.encoder.cereal(parent_draft.most_recent_revision)))

    # TODO(emfree): what if you try to update a draft on a *thread* that's been
    # deleted?

    data = request.get_json(force=True)

    to = data.get('to')
    cc = data.get('cc')
    bcc = data.get('bcc')
    subject = data.get('subject')
    body = data.get('body')
    try:
        tags = get_tags(data.get('tags'), g.namespace.id, g.db_session)
        files = get_attachments(data.get('files'), g.namespace.id,
                                g.db_session)
    except InputError as e:
        return err(404, e.message)

    draft = sendmail.update_draft(g.db_session, g.namespace.account,
                                  parent_draft, to, subject, body,
                                  files, cc, bcc, tags)
    return g.encoder.jsonify(draft)
Пример #3
0
def webhooks_read_update_api(public_id):
    if request.method == 'GET':
        try:
            hook = g.db_session.query(Webhook).filter(
                Webhook.public_id == public_id,
                Webhook.namespace_id == g.namespace.id).one()
            return jsonify(hook)
        except NoResultFound:
            return err(404,
                       "Couldn't find webhook with id {}".format(public_id))

    if request.method == 'PUT':
        data = request.get_json(force=True)
        # We only support updates to the 'active' flag.
        if data.keys() != ['active']:
            return err(400, 'Malformed webhook request')

        try:
            if data['active']:
                get_webhook_client().start_hook(public_id)
            else:
                get_webhook_client().stop_hook(public_id)
            return jsonify({"success": True})
        except zerorpc.RemoteError:
            return err(404,
                       "Couldn't find webhook with id {}".format(public_id))
Пример #4
0
def event_create_api():
    # Handle ical uploads
    if request.headers['content-type'] == 'text/calendar':
        ics_str = request.data
        new_events = events.crud.create_from_ics(g.namespace, g.db_session,
                                                 ics_str)
        if not new_events:
            return err(400, "Couldn't parse .ics file.")

        return g.encoder.jsonify(new_events)

    data = request.get_json(force=True)

    try:
        valid_event(data)
    except InputError as e:
        return err(404, e.message)

    subject = data.get('subject', '')
    body = data.get('body')
    location = data.get('location')
    reminders = data.get('reminders')
    recurrence = data.get('recurrence')
    when = data.get('when')

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    new_contact = events.crud.create(g.namespace, g.db_session, subject, body,
                                     location, reminders, recurrence, when,
                                     participants)
    return g.encoder.jsonify(new_contact)
Пример #5
0
def event_update_api(public_id):
    data = request.get_json(force=True)

    try:
        valid_event_update(data)
    except InputError as e:
        return err(404, e.message)

    if 'start' in data:
        data['start'] = datetime.utcfromtimestamp(int(data.get('start')))

    if 'end' in data:
        data['end'] = datetime.utcfromtimestamp(int(data.get('end')))

    if 'busy' in data:
        data['busy'] = int(data.get('busy'))

    if 'all_day' in data:
        data['all_day'] = int(data.get('all_day'))

    if 'participants' in data:
        data['participant_list'] = data['participants']
        del data['participants']
        for p in data['participant_list']:
            if 'status' not in p:
                p['status'] = 'awaiting'

    result = events.crud.update(g.namespace, g.db_session,
                                public_id, data)
    if result is None:
        return err(404, "Couldn't find event with id {0}".
                   format(public_id))
    return g.encoder.jsonify(result)
Пример #6
0
def start():
    g.db_session = InboxSession()

    g.log = current_app.logger
    try:
        g.namespace = g.db_session.query(Namespace) \
            .filter(Namespace.public_id == g.namespace_public_id).one()
    except NoResultFound:
        return err(
            404, "Couldn't find namespace with id `{0}` ".format(
                g.namespace_public_id))

    try:
        g.lens = Lens(
            namespace_id=g.namespace.id,
            subject=request.args.get('subject'),
            thread_public_id=request.args.get('thread'),
            to_addr=request.args.get('to'),
            from_addr=request.args.get('from'),
            cc_addr=request.args.get('cc'),
            bcc_addr=request.args.get('bcc'),
            any_email=request.args.get('any_email'),
            started_before=request.args.get('started_before'),
            started_after=request.args.get('started_after'),
            last_message_before=request.args.get('last_message_before'),
            last_message_after=request.args.get('last_message_after'),
            filename=request.args.get('filename'),
            tag=request.args.get('tag'),
            detached=True)
        g.lens_limit = request.args.get('limit')
        g.lens_offset = request.args.get('offset')
    except ValueError as e:
        return err(400, e.message)
Пример #7
0
def event_update_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid event id {}'.format(public_id))
    data = request.get_json(force=True)

    try:
        valid_event_update(data, g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    # Convert the data into our types where necessary
    # e.g. timestamps, participant_list
    if 'start' in data:
        data['start'] = datetime.utcfromtimestamp(int(data.get('start')))
    if 'end' in data:
        data['end'] = datetime.utcfromtimestamp(int(data.get('end')))
    if 'participants' in data:
        data['participant_list'] = []
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'
            data['participant_list'].append(p)
        del data['participants']

    try:
        result = events.crud.update(g.namespace, g.db_session, public_id, data)
    except InputError as e:
        return err(400, e.message)

    if result is None:
        return err(404, "Couldn't find event with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #8
0
def start():
    g.db_session = InboxSession()

    g.log = current_app.logger
    try:
        g.namespace = g.db_session.query(Namespace) \
            .filter(Namespace.public_id == g.namespace_public_id).one()
    except NoResultFound:
        return err(404, "Couldn't find namespace with id `{0}` ".format(
            g.namespace_public_id))

    try:
        g.lens = Lens(
            namespace_id=g.namespace.id,
            subject=request.args.get('subject'),
            thread_public_id=request.args.get('thread'),
            to_addr=request.args.get('to'),
            from_addr=request.args.get('from'),
            cc_addr=request.args.get('cc'),
            bcc_addr=request.args.get('bcc'),
            any_email=request.args.get('any_email'),
            started_before=request.args.get('started_before'),
            started_after=request.args.get('started_after'),
            last_message_before=request.args.get('last_message_before'),
            last_message_after=request.args.get('last_message_after'),
            filename=request.args.get('filename'),
            tag=request.args.get('tag'),
            detached=True)
        g.lens_limit = request.args.get('limit')
        g.lens_offset = request.args.get('offset')
    except ValueError as e:
        return err(400, e.message)
Пример #9
0
def contact_search_api():
    filter = request.args.get('filter', '')
    try:
        limit = int(request.args.get('limit', 10))
        offset = int(request.args.get('offset', 0))
    except ValueError:
        return err(400, 'limit and offset parameters must be integers')
    if limit < 0 or offset < 0:
        return err(
            400, 'limit and offset parameters must be nonnegative '
            'integers')
    if limit > 1000:
        return err(400, 'cannot request more than 1000 contacts at once.')
    order = request.args.get('order')
    if order == 'rank':
        results = contacts.search_util.search(g.db_session,
                                              g.namespace.account_id, filter,
                                              limit, offset)
    else:
        results = g.db_session.query(Contact). \
            filter(Contact.account_id == g.namespace.account_id,
                   Contact.source == 'local'). \
            order_by(asc(Contact.id)).limit(limit).offset(offset).all()

    return jsonify(results)
Пример #10
0
def draft_update_api(public_id):
    parent_draft = g.db_session.query(Message). \
        filter(Message.public_id == public_id).first()
    if parent_draft is None or not parent_draft.is_draft or \
            parent_draft.namespace.id != g.namespace.id:
        return err(404, 'No draft with public id {}'.format(public_id))
    if not parent_draft.is_latest:
        return err(409, 'Draft {} has already been updated to {}'.format(
            public_id, g.encoder.cereal(parent_draft.most_recent_revision)))

    # TODO(emfree): what if you try to update a draft on a *thread* that's been
    # deleted?

    data = request.get_json(force=True)

    to = data.get('to')
    cc = data.get('cc')
    bcc = data.get('bcc')
    subject = data.get('subject')
    body = data.get('body')
    try:
        tags = get_tags(data.get('tags'), g.namespace.id, g.db_session)
        files = get_attachments(data.get('files'), g.namespace.id,
                                g.db_session)
    except InputError as e:
        return err(404, e.message)

    draft = sendmail.update_draft(g.db_session, g.namespace.account,
                                  parent_draft, to, subject, body,
                                  files, cc, bcc, tags)
    return g.encoder.jsonify(draft)
Пример #11
0
def draft_create_api():
    data = request.get_json(force=True)

    to = get_recipients(data.get('to'), 'to')
    cc = get_recipients(data.get('cc'), 'cc')
    bcc = get_recipients(data.get('bcc'), 'bcc')
    subject = data.get('subject')
    body = data.get('body')
    try:
        tags = get_tags(data.get('tags'), g.namespace.id, g.db_session)
        files = get_attachments(data.get('file_ids'), g.namespace.id,
                                g.db_session)
        replyto_thread = get_thread(data.get('thread_id'),
                                    g.namespace.id, g.db_session)
    except InputError as e:
        return err(404, e.message)

    try:
        draft = sendmail.create_draft(g.db_session, g.namespace.account, to,
                                      subject, body, files, cc, bcc,
                                      tags, replyto_thread)
    except ActionError as e:
        return err(e.error, str(e))

    return g.encoder.jsonify(draft)
Пример #12
0
def draft_create_api():
    data = request.get_json(force=True)

    to = get_recipients(data.get('to'), 'to')
    cc = get_recipients(data.get('cc'), 'cc')
    bcc = get_recipients(data.get('bcc'), 'bcc')
    subject = data.get('subject')
    body = data.get('body')
    try:
        tags = get_tags(data.get('tags'), g.namespace.id, g.db_session)
        files = get_attachments(data.get('file_ids'), g.namespace.id,
                                g.db_session)
        replyto_thread = get_thread(data.get('thread_id'), g.namespace.id,
                                    g.db_session)
    except InputError as e:
        return err(404, e.message)

    try:
        draft = sendmail.create_draft(g.db_session, g.namespace.account, to,
                                      subject, body, files, cc, bcc, tags,
                                      replyto_thread)
    except ActionError as e:
        return err(e.error, str(e))

    return g.encoder.jsonify(draft)
Пример #13
0
def webhooks_read_update_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid webhook id {}'.format(public_id))
    if request.method == 'GET':
        try:
            hook = g.db_session.query(Webhook).filter(
                Webhook.public_id == public_id,
                Webhook.namespace_id == g.namespace.id).one()
            return g.encoder.jsonify(hook)
        except NoResultFound:
            return err(404, "Couldn't find webhook with id {}"
                       .format(public_id))

    if request.method == 'PUT':
        data = request.get_json(force=True)
        # We only support updates to the 'active' flag.
        if data.keys() != ['active']:
            return err(400, 'Malformed webhook request')

        try:
            if data['active']:
                get_webhook_client().start_hook(public_id)
            else:
                get_webhook_client().stop_hook(public_id)
            return g.encoder.jsonify({"success": True})
        except zerorpc.RemoteError:
            return err(404, "Couldn't find webhook with id {}"
                       .format(public_id))
Пример #14
0
def event_update_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, "Invalid event id {}".format(public_id))
    data = request.get_json(force=True)

    try:
        valid_event_update(data)
    except InputError as e:
        return err(404, e.message)

    # Convert the data into our types where necessary
    # e.g. timestamps, participant_list
    if "start" in data:
        data["start"] = datetime.utcfromtimestamp(int(data.get("start")))
    if "end" in data:
        data["end"] = datetime.utcfromtimestamp(int(data.get("end")))
    if "participants" in data:
        data["participant_list"] = []
        for p in data["participants"]:
            if "status" not in p:
                p["status"] = "noreply"
            data["participant_list"].append(p)
        del data["participants"]

    try:
        result = events.crud.update(g.namespace, g.db_session, public_id, data)
    except InputError as e:
        return err(404, e.message)

    if result is None:
        return err(404, "Couldn't find event with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #15
0
def event_update_api(public_id):
    data = request.get_json(force=True)

    try:
        valid_event_update(data)
    except InputError as e:
        return err(404, e.message)

    # Convert the data into our types where necessary
    # e.g. timestamps, participant_list
    if 'start' in data:
        data['start'] = datetime.utcfromtimestamp(int(data.get('start')))
    if 'end' in data:
        data['end'] = datetime.utcfromtimestamp(int(data.get('end')))
    if 'participants' in data:
        data['participant_list'] = []
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'
            data['participant_list'].append(p)
        del data['participants']

    try:
        result = events.crud.update(g.namespace, g.db_session,
                                    public_id, data)
    except InputError as e:
        return err(404, e.message)

    if result is None:
        return err(404, "Couldn't find event with id {0}".
                   format(public_id))
    return g.encoder.jsonify(result)
Пример #16
0
def message_api(public_id):
    try:
        valid_public_id(public_id)
        message = g.db_session.query(Message).filter(
            Message.public_id == public_id).one()
        assert int(message.namespace.id) == int(g.namespace.id)
    except InputError:
        return err(400, 'Invalid message id {}'.format(public_id))
    except NoResultFound:
        return err(404,
                   "Couldn't find message with id {0} "
                   "on namespace {1}".format(public_id, g.namespace_public_id))
    if request.method == 'GET':
        return g.encoder.jsonify(message)
    elif request.method == 'PUT':
        data = request.get_json(force=True)
        if data.keys() != ['unread'] or not isinstance(data['unread'], bool):
            return err(400,
                       'Can only change the unread attribute of a message')

        # TODO(emfree): Shouldn't allow this on messages that are actually
        # drafts.

        unread_tag = message.namespace.tags['unread']
        unseen_tag = message.namespace.tags['unseen']
        if data['unread']:
            message.is_read = False
            message.thread.apply_tag(unread_tag)
        else:
            message.is_read = True
            message.thread.remove_tag(unseen_tag)
            if all(m.is_read for m in message.thread.messages):
                message.thread.remove_tag(unread_tag)
        return g.encoder.jsonify(message)
Пример #17
0
def draft_get_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid draft id {}'.format(public_id))
    draft = sendmail.get_draft(g.db_session, g.namespace.account, public_id)
    if draft is None:
        return err(404, 'No draft found with id {}'.format(public_id))
    return g.encoder.jsonify(draft)
Пример #18
0
def do(path=[]):
    if len(path) != 3:
        return err.err("Error")
    db = DB.DB()
    table = db.key2table(path[2])
    if table == None:
        return err.err("Error Data is not exist.\n" + path[2])
    res = {"status": "success", "result": db.select(table)}
    return json.dumps(res)
Пример #19
0
def draft_get_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid draft id {}'.format(public_id))
    draft = sendmail.get_draft(g.db_session, g.namespace.account, public_id)
    if draft is None:
        return err(404, 'No draft found with id {}'.format(public_id))
    return g.encoder.jsonify(draft)
Пример #20
0
def tag_create_api():
    data = request.get_json(force=True)
    if data.keys() != ['name']:
        return err(400, 'Malformed tag request')
    tag_name = data['name']
    if not UserTag.name_available(tag_name, g.namespace.id, g.db_session):
        return err(409, 'Tag name not available')

    tag = UserTag(name=tag_name, namespace=g.namespace)
    g.db_session.commit()
    return jsonify(tag)
Пример #21
0
def contact_read_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, "Invalid contact id {}".format(public_id))
    # TODO auth with account object
    # Get all data for an existing contact.
    result = contacts.crud.read(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find contact with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #22
0
def contact_read_api(public_id):
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid contact id {}'.format(public_id))
    # TODO auth with account object
    # Get all data for an existing contact.
    result = contacts.crud.read(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find contact with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #23
0
def file_read_api(public_id):
    try:
        valid_public_id(public_id)
        f = g.db_session.query(Block).filter(
            Block.public_id == public_id).one()
        return g.encoder.jsonify(f)
    except InputError:
        return err(400, 'Invalid file id {}'.format(public_id))
    except NoResultFound:
        return err(404, "Couldn't find file with id {0} "
                   "on namespace {1}".format(public_id, g.namespace_public_id))
Пример #24
0
def calendar_read_api(public_id):
    """Get all data for an existing calendar."""
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid calendar id {}'.format(public_id))

    result = events.crud.read_calendar(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find calendar with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #25
0
def tag_create_api():
    data = request.get_json(force=True)
    if data.keys() != ['name']:
        return err(400, 'Malformed tag request')
    tag_name = data['name']
    if not UserTag.name_available(tag_name, g.namespace.id, g.db_session):
        return err(409, 'Tag name not available')

    tag = UserTag(name=tag_name, namespace=g.namespace)
    g.db_session.commit()
    return jsonify(tag)
Пример #26
0
def tag_read_api(public_id):
    try:
        valid_public_id(public_id)
        tag = g.db_session.query(Tag).filter(
            Tag.public_id == public_id,
            Tag.namespace_id == g.namespace.id).one()
    except InputError:
        return err(400, '{} is not a valid id'.format(public_id))
    except NoResultFound:
        return err(404, 'No tag found')

    return g.encoder.jsonify(tag)
Пример #27
0
def thread_api(public_id):
    try:
        valid_public_id(public_id)
        thread = g.db_session.query(Thread).filter(
            Thread.public_id == public_id,
            Thread.namespace_id == g.namespace.id).one()
        return g.encoder.jsonify(thread)
    except InputError:
        return err(400, 'Invalid thread id {}'.format(public_id))
    except NoResultFound:
        return err(404, "Couldn't find thread with id `{0}` "
                   "on namespace {1}".format(public_id, g.namespace_public_id))
Пример #28
0
def file_read_api(public_id):
    try:
        valid_public_id(public_id)
        f = g.db_session.query(Block).filter(
            Block.public_id == public_id,
            Block.namespace_id == g.namespace.id).one()
        return g.encoder.jsonify(f)
    except InputError:
        return err(400, 'Invalid file id {}'.format(public_id))
    except NoResultFound:
        return err(404, "Couldn't find file with id {0} "
                   "on namespace {1}".format(public_id, g.namespace_public_id))
Пример #29
0
def tag_read_api(public_id):
    try:
        valid_public_id(public_id)
        tag = g.db_session.query(Tag).filter(
            Tag.public_id == public_id,
            Tag.namespace_id == g.namespace.id).one()
    except InputError:
        return err(400, '{} is not a valid id'.format(public_id))
    except NoResultFound:
        return err(404, 'No tag found')

    return g.encoder.jsonify(tag)
Пример #30
0
def calendar_delete_api(public_id):
    try:
        calendar = get_calendar(public_id, g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    if calendar.read_only:
        return err(400, "Cannot delete a read_only calendar.")

    result = events.crud.delete_calendar(g.namespace, g.db_session, public_id)

    return g.encoder.jsonify(result)
Пример #31
0
def thread_api(public_id):
    try:
        valid_public_id(public_id)
        thread = g.db_session.query(Thread).filter(
            Thread.public_id == public_id,
            Thread.namespace_id == g.namespace.id).one()
        return g.encoder.jsonify(thread)
    except InputError:
        return err(400, 'Invalid thread id {}'.format(public_id))
    except NoResultFound:
        return err(404, "Couldn't find thread with id `{0}` "
                   "on namespace {1}".format(public_id, g.namespace_public_id))
Пример #32
0
def calendar_read_api(public_id):
    """Get all data for an existing calendar."""
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid calendar id {}'.format(public_id))

    result = events.crud.read_calendar(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find calendar with id {0}".
                   format(public_id))
    return g.encoder.jsonify(result)
Пример #33
0
def tag_create_api():
    data = request.get_json(force=True)
    if data.keys() != ['name']:
        return err(400, 'Malformed tag request')
    tag_name = data['name']
    if not Tag.name_available(tag_name, g.namespace.id, g.db_session):
        return err(409, 'Tag name not available')
    if len(tag_name) > MAX_INDEXABLE_LENGTH:
        return err(400, 'Tag name is too long.')

    tag = Tag(name=tag_name, namespace=g.namespace, user_created=True)
    g.db_session.commit()
    return g.encoder.jsonify(tag)
Пример #34
0
def calendar_delete_api(public_id):
    try:
        calendar = get_calendar(public_id, g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    if calendar.read_only:
        return err(400, "Cannot delete a read_only calendar.")

    result = events.crud.delete_calendar(g.namespace, g.db_session,
                                         public_id)

    return g.encoder.jsonify(result)
Пример #35
0
def tag_create_api():
    data = request.get_json(force=True)
    if data.keys() != ['name']:
        return err(400, 'Malformed tag request')
    tag_name = data['name']
    if not Tag.name_available(tag_name, g.namespace.id, g.db_session):
        return err(409, 'Tag name not available')
    if len(tag_name) > MAX_INDEXABLE_LENGTH:
        return err(400, 'Tag name is too long.')

    tag = Tag(name=tag_name, namespace=g.namespace, user_created=True)
    g.db_session.commit()
    return g.encoder.jsonify(tag)
Пример #36
0
def draft_send_api():
    data = request.get_json(force=True)
    if data.get("draft_id") is None:
        if data.get("to") is None:
            return err(400, "Must specify either draft id + version or " "message recipients.")
    else:
        if data.get("version") is None:
            return err(400, "Must specify version to send")

    draft_public_id = data.get("draft_id")
    version = data.get("version")
    if draft_public_id is not None:
        try:
            valid_public_id(draft_public_id)
            draft = g.db_session.query(Message).filter(Message.public_id == draft_public_id).one()
        except InputError:
            return err(400, "Invalid public id {}".format(draft_public_id))
        except NoResultFound:
            return err(404, "No draft found with id {}".format(draft_public_id))

        if draft.namespace != g.namespace:
            return err(404, "No draft found with id {}".format(draft_public_id))

        if draft.is_sent or not draft.is_draft:
            return err(400, "Message with id {} is not a draft".format(draft_public_id))

        if not draft.to_addr:
            return err(400, "No 'to:' recipients specified")

        if draft.version != version:
            return err(
                409,
                "Draft {0}.{1} has already been updated to version {2}".format(draft_public_id, version, draft.version),
            )

        schedule_action("send_draft", draft, g.namespace.id, g.db_session)
    else:
        to = data.get("to")
        cc = data.get("cc")
        bcc = data.get("bcc")
        subject = data.get("subject")
        body = data.get("body")
        try:
            tags = get_tags(data.get("tags"), g.namespace.id, g.db_session)
            files = get_attachments(data.get("files"), g.namespace.id, g.db_session)
            replyto_thread = get_thread(data.get("reply_to_thread"), g.namespace.id, g.db_session)
        except InputError as e:
            return err(404, e.message)

        draft = sendmail.create_draft(
            g.db_session, g.namespace.account, to, subject, body, files, cc, bcc, tags, replyto_thread, syncback=False
        )
        schedule_action("send_directly", draft, g.namespace.id, g.db_session)

    draft.state = "sending"
    return g.encoder.jsonify(draft)
Пример #37
0
def event_create_api():
    # Handle ical uploads
    if request.headers.get('content-type') == 'text/calendar':
        ics_str = request.data
        new_events = events.crud.create_from_ics(g.namespace, g.db_session,
                                                 ics_str)
        if not new_events:
            return err(400, "Couldn't parse .ics file.")

        return g.encoder.jsonify(new_events)

    data = request.get_json(force=True)
    try:
        calendar = get_calendar(data.get('calendar_id'),
                                g.namespace, g.db_session)
    except InputError as e:
        return err(404, str(e))

    if calendar.read_only:
        return err(400, "Can't create events on read_only calendar.")

    try:
        valid_event(data)
    except InputError as e:
        return err(404, str(e))

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    reminders = data.get('reminders')
    recurrence = data.get('recurrence')
    when = data.get('when')

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    new_event = events.crud.create(g.namespace, g.db_session,
                                     calendar,
                                     title,
                                     description,
                                     location,
                                     reminders,
                                     recurrence,
                                     when,
                                     participants)

    schedule_action('create_event', new_event, g.namespace.id, g.db_session)
    return g.encoder.jsonify(new_event)
Пример #38
0
def event_create_api():
    # Handle ical uploads
    if request.headers['content-type'] == 'text/calendar':
        ics_str = request.data
        new_events = events.crud.create_from_ics(g.namespace, g.db_session,
                                                 ics_str)
        if not new_events:
            return err(400, "Couldn't parse .ics file.")

        return g.encoder.jsonify(new_events)

    data = request.get_json(force=True)
    try:
        calendar = get_calendar(data.get('calendar_id'),
                                g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    if calendar.read_only:
        return err(400, "Can't create events on read_only calendar.")

    try:
        valid_event(data)
    except InputError as e:
        return err(404, e.message)

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    reminders = data.get('reminders')
    recurrence = data.get('recurrence')
    when = data.get('when')

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    new_event = events.crud.create(g.namespace, g.db_session,
                                     calendar,
                                     title,
                                     description,
                                     location,
                                     reminders,
                                     recurrence,
                                     when,
                                     participants)

    schedule_action('create_event', new_event, g.namespace.id, g.db_session)
    return g.encoder.jsonify(new_event)
Пример #39
0
def file_read_api(public_id):
    try:
        valid_public_id(public_id)
        f = g.db_session.query(Block).filter(Block.public_id == public_id).one()
        if hasattr(f, "message"):
            assert int(f.message.namespace.id) == int(g.namespace.id)
            g.log.info("block's message namespace matches api context namespace")
        else:
            # Block was likely uploaded via file API and not yet sent in a msg
            g.log.debug("This block doesn't have a corresponding message: {}".format(f.public_id))
        return g.encoder.jsonify(f)
    except InputError:
        return err(400, "Invalid file id {}".format(public_id))
    except NoResultFound:
        return err(404, "Couldn't find file with id {0} " "on namespace {1}".format(public_id, g.namespace_public_id))
Пример #40
0
def stream_changes():
    g.parser.add_argument('timeout', type=float, location='args')
    g.parser.add_argument('cursor', type=valid_public_id, location='args',
                          required=True)
    args = strict_parse_args(g.parser, request.args)
    timeout = args['timeout'] or 3600
    transaction_pointer = None
    cursor = args['cursor']
    if cursor == '0':
        transaction_pointer = 0
    else:
        query_result = g.db_session.query(Transaction.id).filter(
            Transaction.namespace_id == g.namespace.id,
            Transaction.public_id == cursor).first()
        if query_result is None:
            return err(400, 'Invalid cursor {}'.format(args['cursor']))
        transaction_pointer = query_result[0]

    # Hack to not keep a database session open for the entire (long) request
    # duration.
    g.db_session.close()
    generator = delta_sync.streaming_change_generator(
        g.namespace.id, transaction_pointer=transaction_pointer,
        poll_interval=1, timeout=timeout)
    return Response(generator, mimetype='text/event-stream')
Пример #41
0
def sync_deltas():
    g.parser.add_argument('cursor', type=valid_public_id, location='args',
                          required=True)
    args = strict_parse_args(g.parser, request.args)
    cursor = args['cursor']
    if cursor == '0':
        start_pointer = 0
    else:
        try:
            start_pointer, = g.db_session.query(Transaction.id). \
                filter(Transaction.public_id == cursor,
                       Transaction.namespace_id == g.namespace.id).one()
        except NoResultFound:
            return err(404, 'Invalid cursor parameter')
    deltas, _ = delta_sync.format_transactions_after_pointer(
        g.namespace.id, start_pointer, g.db_session, args['limit'])
    response = {
        'cursor_start': cursor,
        'deltas': deltas,
    }
    if deltas:
        response['cursor_end'] = deltas[-1]['cursor']
    else:
        # No changes.
        response['cursor_end'] = cursor
    return g.encoder.jsonify(response)
Пример #42
0
def calendar_update_api(public_id):
    try:
        calendar = get_calendar(public_id, g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    if calendar.read_only:
        return err(400, "Cannot update a read_only calendar.")

    data = request.get_json(force=True)
    result = events.crud.update_calendar(g.namespace, g.db_session, public_id,
                                         data)

    if result is None:
        return err(404, "Couldn't find calendar with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #43
0
def contact_read_api(public_id):
    # TODO auth with account object
    # Get all data for an existing contact.
    result = contacts.crud.read(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find contact with id {0}".format(public_id))
    return jsonify(result)
Пример #44
0
def webhooks_create_api():
    try:
        parameters = request.get_json(force=True)
        result = get_webhook_client().register_hook(g.namespace.id, parameters)
        return Response(result, mimetype='application/json')
    except zerorpc.RemoteError:
        return err(400, 'Malformed webhook request')
Пример #45
0
def sync_deltas():
    g.parser.add_argument('cursor', type=valid_public_id, location='args',
                          required=True)
    g.parser.add_argument('exclude_types', type=valid_delta_object_types,
                          location='args')
    args = strict_parse_args(g.parser, request.args)
    cursor = args['cursor']
    if cursor == '0':
        start_pointer = 0
    else:
        try:
            start_pointer, = g.db_session.query(Transaction.id). \
                filter(Transaction.public_id == cursor,
                       Transaction.namespace_id == g.namespace.id).one()
        except NoResultFound:
            return err(404, 'Invalid cursor parameter')
    exclude_types = args.get('exclude_types')
    deltas, _ = delta_sync.format_transactions_after_pointer(
        g.namespace.id, start_pointer, g.db_session, args['limit'],
        delta_sync._format_transaction_for_delta_sync, exclude_types)
    response = {
        'cursor_start': cursor,
        'deltas': deltas,
    }
    if deltas:
        response['cursor_end'] = deltas[-1]['cursor']
    else:
        # No changes.
        response['cursor_end'] = cursor
    return g.encoder.jsonify(response)
Пример #46
0
def stream_changes():
    g.parser.add_argument('timeout', type=float, location='args')
    g.parser.add_argument('cursor', type=valid_public_id, location='args',
                          required=True)
    g.parser.add_argument('exclude_types', type=valid_delta_object_types,
                          location='args')
    args = strict_parse_args(g.parser, request.args)
    timeout = args['timeout'] or 3600
    transaction_pointer = None
    cursor = args['cursor']
    if cursor == '0':
        transaction_pointer = 0
    else:
        query_result = g.db_session.query(Transaction.id).filter(
            Transaction.namespace_id == g.namespace.id,
            Transaction.public_id == cursor).first()
        if query_result is None:
            return err(400, 'Invalid cursor {}'.format(args['cursor']))
        transaction_pointer = query_result[0]
    exclude_types = args.get('exclude_types')

    # Hack to not keep a database session open for the entire (long) request
    # duration.
    g.db_session.close()
    generator = delta_sync.streaming_change_generator(
        g.namespace.id, transaction_pointer=transaction_pointer,
        poll_interval=1, timeout=timeout, exclude_types=exclude_types)
    return Response(generator, mimetype='text/event-stream')
Пример #47
0
def webhooks_create_api():
    try:
        parameters = request.get_json(force=True)
        result = get_webhook_client().register_hook(g.namespace.id, parameters)
        return Response(result, mimetype='application/json')
    except zerorpc.RemoteError:
        return err(400, 'Malformed webhook request')
Пример #48
0
def event_create_api():
    # Handle ical uploads
    if request.headers["content-type"] == "text/calendar":
        ics_str = request.data
        new_events = events.crud.create_from_ics(g.namespace, g.db_session, ics_str)
        if not new_events:
            return err(400, "Couldn't parse .ics file.")

        return g.encoder.jsonify(new_events)

    data = request.get_json(force=True)

    try:
        valid_event(data)
    except InputError as e:
        return err(404, e.message)

    start = datetime.utcfromtimestamp(int(data.get("start")))
    end = datetime.utcfromtimestamp(int(data.get("end")))
    subject = data.get("subject", "")
    body = data.get("body")
    location = data.get("location")
    reminders = data.get("reminders")
    recurrence = data.get("recurrence")
    busy = int(data.get("busy"))
    all_day = int(data.get("all_day"))
    participants = data.get("participants", [])
    for p in participants:
        if "status" not in p:
            p["status"] = "noreply"

    new_contact = events.crud.create(
        g.namespace,
        g.db_session,
        subject,
        body,
        location,
        reminders,
        recurrence,
        start,
        end,
        busy,
        all_day,
        participants,
    )
    return g.encoder.jsonify(new_contact)
Пример #49
0
def sync_deltas():
    g.parser.add_argument("cursor", type=valid_public_id, location="args", required=True)
    args = strict_parse_args(g.parser, request.args)
    try:
        results = delta_sync.get_entries_from_public_id(g.namespace.id, args["cursor"], g.db_session, args["limit"])
        return g.encoder.jsonify(results)
    except ValueError:
        return err(404, "Invalid cursor parameter")
Пример #50
0
def generate_cursor():
    data = request.get_json(force=True)
    if data.keys() != ["start"] or not isinstance(data["start"], int):
        return err(400, "generate_cursor request body must have the format " '{"start": <Unix timestamp>}')

    timestamp = int(data["start"])
    cursor = delta_sync.get_public_id_from_ts(g.namespace.id, timestamp, g.db_session)
    return g.encoder.jsonify({"cursor": cursor})
Пример #51
0
def event_read_api(public_id):
    # TODO auth with account object
    # Get all data for an existing event.
    result = events.crud.read(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find event with id {0}".
                   format(public_id))
    return g.encoder.jsonify(result)
Пример #52
0
def event_read_api(public_id):
    # TODO auth with account object
    # Get all data for an existing event.
    result = events.crud.read(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find event with id {0}".
                   format(public_id))
    return g.encoder.jsonify(result)
Пример #53
0
def thread_api_update(public_id):
    try:
        valid_public_id(public_id)
        thread = g.db_session.query(Thread).filter(
            Thread.public_id == public_id,
            Thread.namespace_id == g.namespace.id).one()
    except InputError:
        return err(400, 'Invalid thread id {}'.format(public_id))
    except NoResultFound:
        return err(
            404, "Couldn't find thread with id `{0}` "
            "on namespace {1}".format(public_id, g.namespace_public_id))
    data = request.get_json(force=True)
    if not set(data).issubset({'add_tags', 'remove_tags'}):
        return err(400, 'Can only add or remove tags from thread.')

    removals = data.get('remove_tags', [])

    for tag_identifier in removals:
        tag = g.db_session.query(Tag).filter(
            Tag.namespace_id == g.namespace.id,
            or_(Tag.public_id == tag_identifier,
                Tag.name == tag_identifier)).first()
        if tag is None:
            return err(404, 'No tag found with name {}'.format(tag_identifier))
        if not tag.user_removable:
            return err(400, 'Cannot remove tag {}'.format(tag_identifier))

        try:
            thread.remove_tag(tag, execute_action=True)
        except ActionError as e:
            return err(e.error, str(e))

    additions = data.get('add_tags', [])
    for tag_identifier in additions:
        tag = g.db_session.query(Tag).filter(
            Tag.namespace_id == g.namespace.id,
            or_(Tag.public_id == tag_identifier,
                Tag.name == tag_identifier)).first()
        if tag is None:
            return err(404, 'No tag found with name {}'.format(tag_identifier))
        if not tag.user_addable:
            return err(400, 'Cannot add tag {}'.format(tag_identifier))

        try:
            thread.apply_tag(tag, execute_action=True)
        except ActionError as e:
            return err(e.error, str(e))

    g.db_session.commit()
    return g.encoder.jsonify(thread)
Пример #54
0
def sync_events():
    start_stamp = request.args.get('stamp')
    try:
        limit = int(request.args.get('limit', 100))
    except ValueError:
        return err(400, 'Invalid limit parameter')
    if limit <= 0:
        return err(400, 'Invalid limit parameter')
    if start_stamp is None:
        return err(400, 'No stamp parameter in sync request.')

    try:
        results = client_sync.get_entries_from_public_id(
            g.namespace.id, start_stamp, g.db_session, limit)
        return g.encoder.jsonify(results)
    except ValueError:
        return err(404, 'Invalid stamp parameter')
Пример #55
0
def start():
    g.db_session = InboxSession(engine)

    g.log = current_app.logger
    try:
        g.namespace = g.db_session.query(Namespace) \
            .filter(Namespace.public_id == g.namespace_public_id).one()

        g.encoder = APIEncoder(g.namespace.public_id)
    except NoResultFound:
        return err(404, "Couldn't find namespace with id `{0}` ".format(
            g.namespace_public_id))

    try:
        g.limit = int(request.args.get('limit', 10))
        g.offset = int(request.args.get('offset', 0))
    except ValueError:
        return err(400, 'limit and offset parameters must be integers')
    if g.limit < 0 or g.offset < 0:
        return err(400, 'limit and offset parameters must be nonnegative '
                        'integers')
    if g.limit > MAX_LIMIT:
        return err(400, 'cannot request more than {} resources at once.'.
                   format(MAX_LIMIT))
    try:
        g.api_filter = Filter(
            namespace_id=g.namespace.id,
            subject=request.args.get('subject'),
            thread_public_id=request.args.get('thread'),
            to_addr=request.args.get('to'),
            from_addr=request.args.get('from'),
            cc_addr=request.args.get('cc'),
            bcc_addr=request.args.get('bcc'),
            any_email=request.args.get('any_email'),
            started_before=request.args.get('started_before'),
            started_after=request.args.get('started_after'),
            last_message_before=request.args.get('last_message_before'),
            last_message_after=request.args.get('last_message_after'),
            filename=request.args.get('filename'),
            tag=request.args.get('tag'),
            limit=g.limit,
            offset=g.offset,
            order_by=request.args.get('order_by'),
            db_session=g.db_session)
    except ValueError as e:
        return err(400, e.message)
Пример #56
0
def event_read_api(public_id):
    """Get all data for an existing event."""
    try:
        valid_public_id(public_id)
    except InputError:
        return err(400, 'Invalid event id {}'.format(public_id))
    g.parser.add_argument('participant_id',
                          type=valid_public_id,
                          location='args')
    g.parser.add_argument('action', type=valid_event_action, location='args')
    g.parser.add_argument('rsvp', type=valid_rsvp, location='args')
    args = strict_parse_args(g.parser, request.args)

    if 'action' in args:
        # Participants are able to RSVP to events by clicking on links (e.g.
        # that are emailed to them). Therefore, the RSVP action is invoked via
        # a GET.
        if args['action'] == 'rsvp':
            try:
                participant_id = args.get('participant_id')
                if not participant_id:
                    return err(404, "Must specify a participant_id with rsvp")

                participant = g.db_session.query(Participant).filter_by(
                    public_id=participant_id).one()

                participant.status = args['rsvp']
                g.db_session.commit()

                result = events.crud.read(g.namespace, g.db_session, public_id)

                if result is None:
                    return err(
                        404,
                        "Couldn't find event with id {0}".format(public_id))

                return g.encoder.jsonify(result)
            except NoResultFound:
                return err(
                    404, "Couldn't find participant with id `{0}` ".format(
                        participant_id))

    result = events.crud.read(g.namespace, g.db_session, public_id)
    if result is None:
        return err(404, "Couldn't find event with id {0}".format(public_id))
    return g.encoder.jsonify(result)
Пример #57
0
def calendar_update_api(public_id):
    try:
        calendar = get_calendar(public_id, g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    if calendar.read_only:
        return err(400, "Cannot update a read_only calendar.")

    data = request.get_json(force=True)
    result = events.crud.update_calendar(g.namespace, g.db_session,
                                         public_id, data)

    if result is None:
        return err(404, "Couldn't find calendar with id {0}".
                   format(public_id))
    return g.encoder.jsonify(result)
Пример #58
0
def draft_delete_api(public_id):
    data = request.get_json(force=True)
    if data.get('version') is None:
        return err(400, 'Must specify version to delete')
    version = data.get('version')

    try:
        valid_public_id(public_id)
        draft = g.db_session.query(Message).filter(
            Message.public_id == public_id).one()
    except InputError:
        return err(400, 'Invalid public id {}'.format(public_id))
    except NoResultFound:
        return err(404, 'No draft found with public_id {}'.format(public_id))

    if draft.namespace != g.namespace:
        return err(404, 'No draft found with public_id {}'.format(public_id))

    if not draft.is_draft:
        return err(
            400, 'Message with public id {} is not a draft'.format(public_id))

    if draft.version != version:
        return err(
            409, 'Draft {0}.{1} has already been updated to version '
            '{2}'.format(public_id, version, draft.version))

    try:
        result = sendmail.delete_draft(g.db_session, g.namespace.account,
                                       public_id)
    except ActionError as e:
        return err(e.error, str(e))

    return g.encoder.jsonify(result)
Пример #59
0
def file_download_api(public_id):
    try:
        valid_public_id(public_id)
        f = g.db_session.query(Block).filter(
            Block.public_id == public_id,
            Block.namespace_id == g.namespace.id).one()
    except InputError:
        return err(400, 'Invalid file id {}'.format(public_id))
    except NoResultFound:
        return err(
            404, "Couldn't find file with id {0} "
            "on namespace {1}".format(public_id, g.namespace_public_id))

    # Here we figure out the filename.extension given the
    # properties which were set on the original attachment
    # TODO consider using werkzeug.secure_filename to sanitize?

    if f.content_type:
        ct = f.content_type.lower()
    else:
        # TODO Detect the content-type using the magic library
        # and set ct = the content type, which is used below
        g.log.error("Content type not set! Defaulting to text/plain")
        ct = 'text/plain'

    if f.filename:
        name = f.filename
    else:
        g.log.debug("No filename. Generating...")
        if ct in common_extensions:
            name = 'attachment.{0}'.format(common_extensions[ct])
        else:
            g.log.error("Unknown extension for content-type: {0}".format(ct))
            # HACK just append the major part of the content type
            name = 'attachment.{0}'.format(ct.split('/')[0])

    # TODO the part.data object should really behave like a stream we can read
    # & write to
    response = make_response(f.data)

    response.headers['Content-Type'] = 'application/octet-stream'  # ct
    response.headers[
        'Content-Disposition'] = "attachment; filename={0}".format(name)
    g.log.info(response.headers)
    return response
Пример #60
0
def contact_create_api():
    # TODO(emfree) Detect attempts at duplicate insertions.
    data = request.get_json(force=True)
    name = data.get('name')
    email = data.get('email')
    if not any((name, email)):
        return err(400, 'Contact name and email cannot both be null.')
    new_contact = contacts.crud.create(g.namespace, g.db_session, name, email)
    return g.encoder.jsonify(new_contact)