Esempio n. 1
0
def form_submissions(hashid):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)

    if not form.controlled_by(current_user):
        if request_wants_json():
            return jsonerror(403, {'error': "You do not control this form."})
        else:
            return redirect(url_for('dashboard'))

    submissions = form.submissions

    if request_wants_json():
        return jsonify({'submissions': [s.data for s in submissions]})
    else:
        fields = set()
        for s in submissions:
            fields.update(s.data.keys())
        fields -= set(EXCLUDE_KEYS)

        return render_template('forms/submissions.html',
                               form=form,
                               fields=sorted(fields),
                               submissions=submissions)
Esempio n. 2
0
def form_submissions(hashid):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)

    if not form.controlled_by(current_user):
        if request_wants_json():
            return jsonerror(403, {'error': "You do not control this form."})
        else:
            return redirect(url_for('dashboard'))

    submissions = form.submissions

    if request_wants_json():
        return jsonify({
            'submissions': [s.data for s in submissions]
        })
    else:
        fields = set()
        for s in submissions:
            fields.update(s.data.keys())
        fields -= set(EXCLUDE_KEYS)

        return render_template('forms/submissions.html',
            form=form,
            fields=sorted(fields),
            submissions=submissions
        )
Esempio n. 3
0
def custom_template_set(hashid):
    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(404, {'error': "Form not found."})

    if not form.controlled_by(current_user):
        return jsonerror(401, {'error': "You do not control this form."})

    template = form.template
    if not template:
        template = EmailTemplate(form_id=form.id)

    template.from_name = request.get_json()['from_name']
    template.subject = request.get_json()['subject']
    template.style = request.get_json()['style']
    template.body = request.get_json()['body']

    try:
        template.sample()
    except Exception as e:
        print(e)
        return jsonerror(406, {'error': "Failed to render. The template has errors."})

    DB.session.add(template)
    DB.session.commit()
    return jsonify({'ok': True})
Esempio n. 4
0
def custom_template_set(hashid):
    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(404, {'error': "Form not found."})

    if not form.controlled_by(current_user):
        return jsonerror(401, {'error': "You do not control this form."})

    template = form.template
    if not template:
        template = EmailTemplate(form_id=form.id)

    template.from_name = request.get_json()['from_name']
    template.subject = request.get_json()['subject']
    template.style = request.get_json()['style']
    template.body = request.get_json()['body']

    try:
        template.sample()
    except Exception as e:
        print(e)
        return jsonerror(
            406, {'error': "Failed to render. The template has errors."})

    DB.session.add(template)
    DB.session.commit()
    return jsonify({'ok': True})
Esempio n. 5
0
def update(hashid):
    # check that this request came from user dashboard to prevent XSS and CSRF
    referrer = referrer_to_baseurl(request.referrer)
    service = referrer_to_baseurl(settings.SERVICE_URL)
    if referrer != service:
        return jsonerror(400, {'error': 'Improper request.'})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(400, {'error': 'Not a valid form.'})

    if form.owner_id != current_user.id and form not in current_user.forms:
        return jsonerror(401, {'error': 'Wrong user.'})

    patch = request.get_json()

    for attr in [
            'disable_storage', 'disabled', 'disable_email', 'captcha_disabled'
    ]:
        if attr in patch:
            setattr(form, attr, patch[attr])

    DB.session.add(form)
    DB.session.commit()
    return jsonify({'ok': True})
Esempio n. 6
0
def form_submissions(random_like_string):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_form_by_random_like_string(random_like_string)
    submissions = form.submissions

    if request_wants_json():
        if current_user.id != form.owner_id:
            return jsonerror(403,
                             {'error': "You're not the owner of this form."})

        return jsonify({'submissions': [s.data for s in submissions]})
    else:
        if current_user.id != form.owner_id:
            return redirect(url_for('dashboard'))

        fields = set()
        for s in submissions:
            fields.update(s.data.keys())
        fields -= set(EXCLUDE_KEYS)

        return render_template('forms/submissions.html',
                               form=form,
                               fields=sorted(fields),
                               submissions=submissions)
Esempio n. 7
0
def form_submissions(random_like_string):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_form_by_random_like_string(random_like_string)
    submissions = form.submissions

    if request_wants_json():
        if current_user.id != form.owner_id:
            return jsonerror(403, {'error': "You're not the owner of this form."})

        return jsonify({
            'submissions': [s.data for s in submissions]
        })
    else:
        if current_user.id != form.owner_id:
            return redirect(url_for('dashboard'))

        fields = set()
        for s in submissions:
            fields.update(s.data.keys())
        fields -= set(EXCLUDE_KEYS)

        return render_template('forms/submissions.html',
            form=form,
            fields=sorted(fields),
            submissions=submissions
        )
Esempio n. 8
0
def forms():
    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(
                501, {
                    'error':
                    "This endpoint may return the list of forms for the logged user."
                })
        else:
            return redirect(url_for('dashboard'))

    # Create a new form
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    if request.get_json():
        email = request.get_json().get('email')
    else:
        email = request.form.get('email')

    if not IS_VALID_EMAIL(email):
        if request_wants_json():
            return jsonerror(
                400, {'error': "The email you sent is not a valid email."})
        else:
            flash('The email you sent is not a valid email.', 'error')
            return redirect(url_for('dashboard'))

    form = Form(email, owner=current_user)
    DB.session.add(form)
    DB.session.commit()

    # A unique identifier for the form that maps to its id,
    # but doesn't seem like a sequential integer
    random_like_string = form.get_random_like_string()

    if request_wants_json():
        return jsonify({
            'ok':
            True,
            'random_like_string':
            random_like_string,
            'submission_url':
            settings.API_ROOT + '/' + random_like_string
        })
    else:
        return redirect(url_for('dashboard'))
Esempio n. 9
0
def resend_confirmation(email):
    g.log = g.log.bind(email=email, host=request.form.get('host'))
    g.log.info('Resending confirmation.')

    # the first thing to do is to check the captcha
    r = requests.post('https://www.google.com/recaptcha/api/siteverify', data={
        'secret': settings.RECAPTCHA_SECRET,
        'response': request.form['g-recaptcha-response'],
        'remoteip': request.remote_addr
    })
    if r.ok and r.json().get('success'):
        # then proceed to check if this email is listed on SendGrid's bounces
        r = requests.get('https://api.sendgrid.com/api/bounces.get.json',
            params={
                'email': email,
                'api_user': settings.SENDGRID_USERNAME,
                'api_key': settings.SENDGRID_PASSWORD
            }
        )
        if r.ok and len(r.json()) and 'reason' in r.json()[0]:
            # tell the user to verify his mailbox
            reason = r.json()[0]['reason']
            g.log.info('Email is blocked on SendGrid. Telling the user.')
            if request_wants_json():
                resp = jsonify({'error': "Verify your mailbox, we can't reach it.", 'reason': reason})
            else:
                resp = make_response(render_template('info.html',
                    title='Verify the availability of your mailbox',
                    text="We encountered an error when trying to deliver the confirmation message to <b>" + email + "</b> at the first time we tried. For spam reasons, we will not try again until we are sure the problem is fixed. Here's the reason:</p><p><center><i>" + reason + "</i></center></p><p>Please make sure this problem is not happening still, then go to <a href='/unblock/" + email + "'>this page</a> to unblock your address.</p><p>After you have unblocked the address, please try to resend the confirmation again.</p>"
                ))
            return resp
        # ~~~
        # if there's no bounce, we proceed to resend the confirmation.

        form = Form.query.filter_by(hash=HASH(email, request.form['host'])).first()
        if not form:
            if request_wants_json():
                return jsonerror(400, {'error': "This form does not exist."})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='This form does not exist.'), 400
        form.confirm_sent = False
        status = form.send_confirmation()
        if status['code'] == Form.STATUS_CONFIRMATION_SENT:
            if request_wants_json():
                return jsonify({'success': "confirmation email sent"})
            else:
                return render_template('forms/confirmation_sent.html',
                    email=email,
                    host=request.form['host'],
                    resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
                )

    # fallback response -- should happen only when the recaptcha is failed.
    g.log.warning('Failed to resend confirmation.')
    return render_template('error.html',
                           title='Unable to send email',
                           text='Please make sure you pass the <i>reCaptcha</i> test before submitting.'), 500
Esempio n. 10
0
def resend_confirmation(email):
    g.log = g.log.bind(email=email, host=request.form.get('host'))
    g.log.info('Resending confirmation.')

    # the first thing to do is to check the captcha
    r = requests.post('https://www.google.com/recaptcha/api/siteverify', data={
        'secret': settings.RECAPTCHA_SECRET,
        'response': request.form['g-recaptcha-response'],
        'remoteip': request.remote_addr
    })
    if r.ok and r.json().get('success'):
        # then proceed to check if this email is listed on SendGrid's bounces
        r = requests.get('https://api.sendgrid.com/api/bounces.get.json',
            params={
                'email': email,
                'api_user': settings.SENDGRID_USERNAME,
                'api_key': settings.SENDGRID_PASSWORD
            }
        )
        if r.ok and len(r.json()) and 'reason' in r.json()[0]:
            # tell the user to verify his mailbox
            reason = r.json()[0]['reason']
            g.log.info('Email is blocked on SendGrid. Telling the user.')
            if request_wants_json():
                resp = jsonify({'error': "Verify your mailbox, we can't reach it.", 'reason': reason})
            else:
                resp = make_response(render_template('info.html',
                    title='Verify the availability of your mailbox',
                    text="We encountered an error when trying to deliver the confirmation message to <b>" + email + "</b> at the first time we tried. For spam reasons, we will not try again until we are sure the problem is fixed. Here's the reason:</p><p><center><i>" + reason + "</i></center></p><p>Please make sure this problem is not happening still, then go to <a href='/unblock/" + email + "'>this page</a> to unblock your address.</p><p>After you have unblocked the address, please try to resend the confirmation again.</p>"
                ))
            return resp
        # ~~~
        # if there's no bounce, we proceed to resend the confirmation.

        form = Form.query.filter_by(hash=HASH(email, request.form['host'])).first()
        if not form:
            if request_wants_json():
                return jsonerror(400, {'error': "This form does not exist."})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='This form does not exist.'), 400
        form.confirm_sent = False
        status = form.send_confirmation()
        if status['code'] == Form.STATUS_CONFIRMATION_SENT:
            if request_wants_json():
                return jsonify({'success': "confirmation email sent"})
            else:
                return render_template('forms/confirmation_sent.html',
                    email=email,
                    host=request.form['host'],
                    resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
                )

    # fallback response -- should happen only when the recaptcha is failed.
    g.log.warning('Failed to resend confirmation.')
    return render_template('error.html',
                           title='Unable to send email',
                           text='Please make sure you pass the <i>reCaptcha</i> test before submitting.'), 500
Esempio n. 11
0
def get(hashid):
    if not current_user.has_feature('dashboard'):
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(404, {'error': "Form not found."})

    if not form.controlled_by(current_user):
        return jsonerror(401, {'error': "You do not control this form."})

    submissions, fields = form.submissions_with_fields()

    ret = form.serialize()
    ret['submissions'] = submissions
    ret['fields'] = fields

    return jsonify(ret)
Esempio n. 12
0
def malformed_replyto_error(status):
    if request_wants_json():
        return jsonerror(500, {'error': "_replyto or email field has not been sent correctly"})

    return render_template(
        'error.html',
        title='Invalid email address',
        text=u'You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>'''.format(address=status['address'], back=status['referrer'])
    ), 400
Esempio n. 13
0
def bad_method_error():
    if request_wants_json():
        return jsonerror(405, {'error': "Please submit POST request."})

    return render_template('info.html',
                           title='Form should POST',
                           text='Make sure your form has the <span '
                           'class="code"><strong>method="POST"'
                           '</strong></span> attribute'), 405
Esempio n. 14
0
def get(hashid):
    if not current_user.has_feature('dashboard'):
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(404, {'error': "Form not found."})

    if not form.controlled_by(current_user):
        return jsonerror(401, {'error': "You do not control this form."})

    submissions, fields = form.submissions_with_fields()

    ret = form.serialize()
    ret['submissions'] = submissions
    ret['fields'] = fields

    return jsonify(ret)
Esempio n. 15
0
def empty_form_error(referrer):
    if request_wants_json():
        return jsonerror(400, {'error': "Can't send an empty form"})

    return render_template(
        'error.html',
        title='Can\'t send an empty form',
        text=u'<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank"><code>"name"</code> attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank"><code>"required"</code> property</a>.</p><p>This error also happens when you have an <code>"enctype"</code> attribute set in your <code>&lt;form&gt;</code>, so make sure you don\'t.</p><p><a href="{}">Return to form</a></p>'.format(referrer)
    ), 400
Esempio n. 16
0
def empty_form_error(referrer):
    if request_wants_json():
        return jsonerror(400, {'error': "Can't send an empty form"})

    return render_template(
        'error.html',
        title='Can\'t send an empty form',
        text=
        u'<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank"><code>"name"</code> attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank"><code>"required"</code> property</a>.</p><p>This error also happens when you have an <code>"enctype"</code> attribute set in your <code>&lt;form&gt;</code>, so make sure you don\'t.</p><p><a href="{}">Return to form</a></p>'
        .format(referrer)), 400
Esempio n. 17
0
def generic_send_error(status):
    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})

    return render_template(
        'error.html',
        title='Unable to send email',
        text=u'Unable to send email. If you can, please send the link to your form and the error information to  <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>'.format(message=json.dumps(status), email=settings.CONTACT_EMAIL)
    ), 500
Esempio n. 18
0
def delete(hashid):
    # check that this request came from user dashboard to prevent XSS and CSRF
    referrer = referrer_to_baseurl(request.referrer)
    service = referrer_to_baseurl(settings.SERVICE_URL)
    if referrer != service:
        return jsonerror(400, {'error': 'Improper request.'})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(400, {'error': 'Not a valid form.'})

    if form.owner_id != current_user.id and form not in current_user.forms:
        return jsonerror(401, {'error': 'Wrong user.'})

    for submission in form.submissions:
        DB.session.delete(submission)
    DB.session.delete(form)
    DB.session.commit()

    return jsonify({'ok': True})
Esempio n. 19
0
def generic_send_error(status):
    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})

    return render_template(
        'error.html',
        title='Unable to send email',
        text=
        u'Unable to send email. If you can, please send the link to your form and the error information to  <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>'
        .format(message=json.dumps(status), email=settings.CONTACT_EMAIL)), 500
Esempio n. 20
0
def disabled_error():
    # owner has disabled the form, so it should not receive any submissions
    g.log.info('submission rejected. Form is disabled.')
    if request_wants_json():
        return jsonerror(403, {'error': 'Form not active'})

    return render_template(
        'error.html',
        title='Form not active',
        text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'
    ), 403
Esempio n. 21
0
def bad_method_error():
    if request_wants_json():
        return jsonerror(405, {'error': "Please submit POST request."})

    return render_template(
        'info.html',
        title='Form should POST',
        text='Make sure your form has the <span '
             'class="code"><strong>method="POST"'
             '</strong></span> attribute'
    ), 405
Esempio n. 22
0
def delete(hashid):
    # check that this request came from user dashboard to prevent XSS and CSRF
    referrer = referrer_to_baseurl(request.referrer)
    service = referrer_to_baseurl(settings.SERVICE_URL)
    if referrer != service:
        return jsonerror(400, {'error': 'Improper request.'})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(400, {'error': 'Not a valid form.'})

    if form.owner_id != current_user.id and form not in current_user.forms:
        return jsonerror(401, {'error': 'Wrong user.'})

    for submission in form.submissions:
        DB.session.delete(submission)
    DB.session.delete(form)
    DB.session.commit()

    return jsonify({'ok': True})
Esempio n. 23
0
def get(hashid):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(404, {'error': "Form not found."})

    for cont in form.controllers:
        if cont.id == current_user.id: break
    else:
        return jsonerror(401, {'error': "You do not control this form."})

    submissions, fields = form.submissions_with_fields()

    ret = form.serialize()
    ret['submissions'] = submissions
    ret['fields'] = fields

    return jsonify(ret)
Esempio n. 24
0
def bad_hashid_error(email_or_string):
    # no form row found. it is an error.
    g.log.info('Submission rejected. No form found for this target.')
    if request_wants_json():
        return jsonerror(400, {'error': "Invalid email address"})

    return render_template(
        'error.html',
        title='Check email address',
        text='Email address %s is not formatted correctly' \
             % str(email_or_string)
    ), 400    
Esempio n. 25
0
def mismatched_host_error(host, form):
    g.log.info('Submission rejected. From a different host than confirmed.')
    if request_wants_json():
        return jsonerror(403, {'error': "Submission from different host than confirmed",
                               'submitted': host, 
                               'confirmed': form.host})
    return render_template(
        'error.html',
        title='Check form address',
        text='This submission came from "%s" but the form was\
                confirmed for address "%s"' % (host, form.host)
    ), 403
Esempio n. 26
0
def disabled_error():
    # owner has disabled the form, so it should not receive any submissions
    g.log.info('submission rejected. Form is disabled.')
    if request_wants_json():
        return jsonerror(403, {'error': 'Form not active'})

    return render_template(
        'error.html',
        title='Form not active',
        text=
        'The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'
    ), 403
Esempio n. 27
0
def bad_hashid_error(email_or_string):
    # no form row found. it is an error.
    g.log.info('Submission rejected. No form found for this target.')
    if request_wants_json():
        return jsonerror(400, {'error': "Invalid email address"})

    return render_template(
        'error.html',
        title='Check email address',
        text='Email address %s is not formatted correctly' \
             % str(email_or_string)
    ), 400
Esempio n. 28
0
def malformed_replyto_error(status):
    if request_wants_json():
        return jsonerror(
            500,
            {'error': "_replyto or email field has not been sent correctly"})

    return render_template(
        'error.html',
        title='Invalid email address',
        text=
        u'You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>'
        ''.format(address=status['address'], back=status['referrer'])), 400
Esempio n. 29
0
def forms():
    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(501, {'error': "This endpoint may return the list of forms for the logged user."})
        else:
            return redirect(url_for('dashboard'))

    # Create a new form
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    if request.get_json():
        email = request.get_json().get('email')
    else:
        email = request.form.get('email')

    if not IS_VALID_EMAIL(email):
        if request_wants_json():
            return jsonerror(400, {'error': "The email you sent is not a valid email."})
        else:
            flash('The email you sent is not a valid email.', 'error')
            return redirect(url_for('dashboard'))

    form = Form(email, owner=current_user)
    DB.session.add(form)
    DB.session.commit()

    # A unique identifier for the form that maps to its id,
    # but doesn't seem like a sequential integer
    random_like_string = form.get_random_like_string()

    if request_wants_json():
        return jsonify({
            'ok': True,
            'random_like_string': random_like_string,
            'submission_url': settings.API_ROOT + '/' + random_like_string
        })
    else:
        return redirect(url_for('dashboard'))
Esempio n. 30
0
def mismatched_host_error(host, form):
    g.log.info('Submission rejected. From a different host than confirmed.')
    if request_wants_json():
        return jsonerror(
            403, {
                'error': "Submission from different host than confirmed",
                'submitted': host,
                'confirmed': form.host
            })
    return render_template(
        'error.html',
        title='Check form address',
        text='This submission came from "%s" but the form was\
                confirmed for address "%s"' % (host, form.host)), 403
Esempio n. 31
0
def update(hashid):
    # check that this request came from user dashboard to prevent XSS and CSRF
    referrer = referrer_to_baseurl(request.referrer)
    service = referrer_to_baseurl(settings.SERVICE_URL)
    if referrer != service:
        return jsonerror(400, {'error': 'Improper request.'})

    form = Form.get_with_hashid(hashid)
    if not form:
        return jsonerror(400, {'error': 'Not a valid form.'})

    if form.owner_id != current_user.id and form not in current_user.forms:
        return jsonerror(401, {'error': 'Wrong user.'})

    patch = request.get_json()

    for attr in ['disable_storage', 'disabled', 'disable_email', 'captcha_disabled']:
        if attr in patch:
            setattr(form, attr, patch[attr])

    DB.session.add(form)
    DB.session.commit()
    return jsonify({'ok': True})
Esempio n. 32
0
def no_referrer_error():
    g.log.info('Invalid Referrer.')

    if request_wants_json():
        return jsonerror(400, {'error': "Invalid \"Referer\" header"})

    return render_template(
        'error.html',
        title='Unable to submit form',
        text='<p>Make sure you open this page through a web server, '
        'Formspree will not work in pages browsed as HTML files. '
        'Also make sure that you\'re posting to <b>{host}{path}</b>.</p>'
        '<p>For geeks: could not find the "Referer" header.</p>'.format(
            host=settings.SERVICE_URL, path=request.path)), 400
Esempio n. 33
0
def resend_confirmation(email):
    # I'm not sure if this should be available for forms created on the dashboard.
    form = Form.query.filter_by(hash=HASH(email, request.form['host'])).first()
    if not form:
        if request_wants_json():
            return jsonerror(400, {'error': "This form does not exists"})
        else:
            return render_template('error.html',
                                   title='Check email address',
                                   text='This form does not exists'), 400

    r = requests.post('https://www.google.com/recaptcha/api/siteverify',
                      data={
                          'secret': settings.RECAPTCHA_SECRET,
                          'response': request.form['g-recaptcha-response'],
                          'remoteip': request.remote_addr
                      })
    if r.ok and r.json()['success']:
        form.confirm_sent = False
        status = form.send_confirmation()
        if status['code'] == Form.STATUS_CONFIRMATION_SENT:
            if request_wants_json():
                return jsonify({'success': "confirmation email sent"})
            else:
                return render_template('forms/confirmation_sent.html',
                                       email=email,
                                       host=request.form['host'],
                                       resend=status['code'] ==
                                       Form.STATUS_CONFIRMATION_DUPLICATED)

    # fallback response -- should never happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email'), 500
Esempio n. 34
0
def resend_confirmation(email):
    # I'm not sure if this should be available for forms created on the dashboard.
    form = Form.query.filter_by(hash=HASH(email, request.form['host'])).first()
    if not form:
        if request_wants_json():
            return jsonerror(400, {'error': "This form does not exists"})
        else:
            return render_template('error.html',
                                   title='Check email address',
                                   text='This form does not exists'), 400

    r = requests.post('https://www.google.com/recaptcha/api/siteverify', data={
        'secret': settings.RECAPTCHA_SECRET,
        'response': request.form['g-recaptcha-response'],
        'remoteip': request.remote_addr
    })
    if r.ok and r.json()['success']:
        form.confirm_sent = False
        status = form.send_confirmation()
        if status['code'] == Form.STATUS_CONFIRMATION_SENT:
            if request_wants_json():
                return jsonify({'success': "confirmation email sent"})
            else:
                return render_template('forms/confirmation_sent.html',
                    email=email,
                    host=request.form['host'],
                    resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
                )
        
    # fallback response -- should never happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email'), 500
Esempio n. 35
0
def no_referrer_error():
    g.log.info('Invalid Referrer.')

    if request_wants_json():
        return jsonerror(400, {'error': "Invalid \"Referer\" header"})

    return render_template(
        'error.html',
        title='Unable to submit form',
        text='<p>Make sure you open this page through a web server, '
             'Formspree will not work in pages browsed as HTML files. '
             'Also make sure that you\'re posting to <b>{host}{path}</b>.</p>'
             '<p>For geeks: could not find the "Referer" header.</p>'.format(
                host=settings.SERVICE_URL,
                path=request.path
             )
    ), 400
Esempio n. 36
0
def export_submissions(hashid, format=None):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)
    for cont in form.controllers:
        if cont.id == current_user.id: break
    else:
        return abort(401)

    submissions, fields = form.submissions_with_fields()

    if format == 'json':
        return Response(
            json.dumps({
                'host': form.host,
                'email': form.email,
                'fields': fields,
                'submissions': submissions
            }, sort_keys=True, indent=2),
            mimetype='application/json',
            headers={
                'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.json' \
                            % (hashid, datetime.datetime.now().isoformat().split('.')[0])
            }
        )
    elif format == 'csv':
        out = io.BytesIO()

        w = csv.DictWriter(out, fieldnames=['id'] + fields, encoding='utf-8')
        w.writeheader()
        for sub in submissions:
            w.writerow(sub)

        return Response(
            out.getvalue(),
            mimetype='text/csv',
            headers={
                'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.csv' \
                            % (hashid, datetime.datetime.now().isoformat().split('.')[0])
            }
        )
Esempio n. 37
0
def export_submissions(hashid, format=None):
    if not current_user.has_feature('dashboard'):
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)
    if not form.controlled_by(current_user):
        return abort(401)

    submissions, fields = form.submissions_with_fields()

    if format == 'json':
        return Response(
            json.dumps({
                'host': form.host,
                'email': form.email,
                'fields': fields,
                'submissions': submissions
            }, sort_keys=True, indent=2),
            mimetype='application/json',
            headers={
                'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.json' \
                            % (hashid, datetime.datetime.now().isoformat().split('.')[0])
            }
        )
    elif format == 'csv':
        out = io.BytesIO()
        
        w = csv.DictWriter(out, fieldnames=['id'] + fields, encoding='utf-8')
        w.writeheader()
        for sub in submissions:
            w.writerow(sub)

        return Response(
            out.getvalue(),
            mimetype='text/csv',
            headers={
                'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.csv' \
                            % (hashid, datetime.datetime.now().isoformat().split('.')[0])
            }
        )
Esempio n. 38
0
def get_or_create_form(email, host):
    '''
    Gets the form if it already exits, otherwise checks to ensure
    that this is a valid new form submission. If so, creates a
    new form.
    '''

    form = Form.query.filter_by(hash=HASH(email, host)).first()

    if not form:

        if request_wants_json():
            # Can't create a new ajax form unless from the dashboard
            ajax_error_str = "To prevent spam, only " + \
                                settings.UPGRADED_PLAN_NAME + \
                                " accounts may create AJAX forms."
            raise SubmitFormError(jsonerror(400, {'error': ajax_error_str}))

        if url_domain(settings.SERVICE_URL) in host:
            # Bad user is trying to submit a form spoofing formspree.io
            g.log.info(
                'User attempting to create new form spoofing SERVICE_URL. Ignoring.'
            )
            raise SubmitFormError(
                (render_template('error.html',
                                 title='Unable to submit form',
                                 text='Sorry'), 400))

        # all good, create form
        form = Form(email, host)

    # Check if it has been assigned using AJAX or not
    assign_ajax(form, request_wants_json())

    if form.disabled:
        raise SubmitFormError(errors.disabled_error())

    return form
Esempio n. 39
0
def get_or_create_form(email, host):
    '''
    Gets the form if it already exits, otherwise checks to ensure
    that this is a valid new form submission. If so, creates a
    new form.
    '''

    form = Form.query.filter_by(hash=HASH(email, host)).first()

    if not form:

        if request_wants_json():
            # Can't create a new ajax form unless from the dashboard
            ajax_error_str = "To prevent spam, only " + \
                                settings.UPGRADED_PLAN_NAME + \
                                " accounts may create AJAX forms."
            raise SubmitFormError(jsonerror(400, {'error': ajax_error_str}))

        if url_domain(settings.SERVICE_URL) in host:
            # Bad user is trying to submit a form spoofing formspree.io
            g.log.info('User attempting to create new form spoofing SERVICE_URL. Ignoring.')
            raise SubmitFormError((render_template(
                'error.html',
                title='Unable to submit form',
                text='Sorry'), 400))

        # all good, create form
        form = Form(email, host)

    # Check if it has been assigned using AJAX or not
    assign_ajax(form, request_wants_json())

    if form.disabled:
        raise SubmitFormError(errors.disabled_error())

    return form
Esempio n. 40
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template('info.html',
                                   title='Form should POST',
                                   text='Make sure your form has the <span class="code"><strong>method="POST"</strong></span> attribute'), 405

    host = referrer_to_path(flask.request.referrer)
    if not host:
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template('error.html',
                                   title='Unable to submit form',
                                   text='Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. For geeks: could not find the "Referrer" header.'), 400

    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()
            elif form.host != host:
                # if the form submission came from a different host, it is an error
                if request_wants_json():
                    return jsonerror(403, {'error': "Submission from different host than confirmed",
                                           'submitted': host, 'confirmed': form.host})
                else:
                    return render_template('error.html',
                                           title='Check form address',
                                           text='This submission came from "%s" but the form was\
                                                 confirmed for the address "%s"' % (host, form.host)), 403
        else:
            # no form row found. it is an error.
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first() \
               or Form(email, host) # or create it if it doesn't exists

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        status = form.send(request.form, request.referrer)
    else:
        status = form.send_confirmation(with_data=request.form)

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({ 'success': "email sent", 'next': status['next'] })
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template('error.html',
                                   title='Can\'t send an empty form',
                                   text=str('<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank">"name" attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank">"required" property</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" target="_blank">see more HTML form customization info</a>.</p><p><a href="%s">Return to form</a></p>' % request.referrer)), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:

        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template('forms/confirmation_sent.html',
                email=email,
                host=host,
                resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
            )
    elif status['code'] == Form.STATUS_OVERLIMIT:

        if request_wants_json():
            return jsonify({'error': "form over quota"})
        else:
            return render_template('error.html',
                                   title='Form over quota',
                                   text='It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.'
            )

    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email. If you can, please report this immediately to <b>[email protected]</b>. And send them the following: <p><pre><code>' + json.dumps(status) + '</code></pre></p>'), 500
Esempio n. 41
0
def forms():
    if request.method == 'GET':
        '''
        A reminder: this is the /forms endpoint, but for GET requests
        it is also the /dashboard endpoint.

        The /dashboard endpoint, the address gave by url_for('dashboard'),
        is the target of a lot of redirects around the app, but it can
        be changed later to point to somewhere else.
        '''

        # grab all the forms this user controls
        if current_user.upgraded:
            forms = current_user.forms.order_by(Form.id.desc()).all()
        else:
            forms = []

        if request_wants_json():
            return jsonify({
                'ok': True,
                'forms': [{
                    'email': f.email,
                    'host': f.host,
                    'confirm_sent': f.confirm_sent,
                    'confirmed': f.confirmed,
                    'is_public': bool(f.hash),
                    'url': '{S}/{E}'.format(
                        S=settings.SERVICE_URL,
                        E=f.hashid
                    )
                } for f in forms]
            })
        else:
            return render_template('forms/list.html', forms=forms)

    elif request.method == 'POST':
        # create a new form
        if not current_user.upgraded:
            return jsonerror(402, {'error': "Please upgrade your account."})

        if request.get_json():
            email = request.get_json().get('email')
        else:
            email = request.form.get('email')

        if not IS_VALID_EMAIL(email):
            if request_wants_json():
                return jsonerror(400, {'error': "The email you sent is not a valid email."})
            else:
                flash('The email you provided is not a valid email.', 'error')
                return redirect(url_for('dashboard'))

        form = Form(email, owner=current_user)
        DB.session.add(form)
        DB.session.commit()

        if request_wants_json():
            return jsonify({
                'ok': True,
                'hashid': form.hashid,
                'submission_url': settings.API_ROOT + '/' + form.hashid
            })
        else:
            flash('Your new form endpoint was created!', 'success')
            return redirect(url_for('dashboard') + '#view-code-' + form.hashid)
Esempio n. 42
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    g.log = g.log.bind(target=email_or_string)

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template('info.html',
                                   title='Form should POST',
                                   text='Make sure your form has the <span '
                                        'class="code"><strong>method="POST"'
                                        '</strong></span> attribute'), 405

    if request.form:
        received_data, sorted_keys = http_form_to_dict(request.form)
    else:
        received_data = request.get_json() or {}
        sorted_keys = received_data.keys()

    sorted_keys = [k for k in sorted_keys if k not in KEYS_EXCLUDED_FROM_EMAIL]

    # NOTE: host in this function generally refers to the referrer hostname.

    try:
        # Get stored hostname from redis (from captcha)
        host, referrer = get_temp_hostname(received_data['_host_nonce'])
    except KeyError:
        host, referrer = referrer_to_path(request.referrer), request.referrer
    except ValueError as err:
        g.log.error('Invalid hostname stored on Redis.', err=err)
        return render_template(
            'error.html',
            title='Unable to submit form',
            text='<p>We had a problem identifying to whom we should have submitted this form. Please try submitting again. If it fails once more, please let us know at {email}</p>'.format(
                email=settings.CONTACT_EMAIL,
            )
        ), 500

    if not host:
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template(
                'error.html',
                title='Unable to submit form',
                text='<p>Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. Also make sure that you\'re posting to <b>{host}{path}</b>.</p><p>For geeks: could not find the "Referrer" header.</p>'.format(
                    host=settings.SERVICE_URL,
                    path=request.path
                )
            ), 400

    g.log = g.log.bind(host=host, wants='json' if request_wants_json() else 'html')

    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            # Check if it has been assigned about using AJAX or not
            assign_ajax(form, request_wants_json())

            if form.disabled:
                # owner has disabled the form, so it should not receive any submissions
                if request_wants_json():
                    return jsonerror(403, {'error': 'Form not active'})
                else:
                    return render_template('error.html',
                                           title='Form not active',
                                           text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'), 403
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()

                # it is an error when
                #   form is not sitewide, and submission came from a different host
                #   form is sitewide, but submission came from a host rooted somewhere else, or
            elif (not form.sitewide and
                  # ending slashes can be safely ignored here:
                  form.host.rstrip('/') != host.rstrip('/')) or \
                 (form.sitewide and \
                  # removing www from both sides makes this a neutral operation:
                  not remove_www(host).startswith(remove_www(form.host))
                 ):
                g.log.info('Submission rejected. From a different host than confirmed.')
                if request_wants_json():
                    return jsonerror(403, {
                       'error': "Submission from different host than confirmed",
                       'submitted': host, 'confirmed': form.host
                    })
                else:
                    return render_template('error.html',
                                           title='Check form address',
                                           text='This submission came from "%s" but the form was\
                                                 confirmed for address "%s"' % (host, form.host)), 403
        else:
            # no form row found. it is an error.
            g.log.info('Submission rejected. No form found for this target.')
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string.lower()

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first()

        # or create it if it doesn't exist
        if not form:
            if request_wants_json():
                # Can't create a new ajax form unless from the dashboard
                ajax_error_str = "To prevent spam, only " + \
                                 settings.UPGRADED_PLAN_NAME + \
                                 " accounts may create AJAX forms."
                return jsonerror(400, {'error': ajax_error_str})
            elif url_domain(settings.SERVICE_URL) in host:
                # Bad user is trying to submit a form spoofing formspree.io
                g.log.info('User attempting to create new form spoofing SERVICE_URL. Ignoring.')
                return render_template(
                    'error.html',
                    title='Unable to submit form',
                    text='Sorry'), 400
            else:
                # all good, create form
                form = Form(email, host)

        # Check if it has been assigned using AJAX or not
        assign_ajax(form, request_wants_json())

        if form.disabled:
            g.log.info('submission rejected. Form is disabled.')
            if request_wants_json():
                return jsonerror(403, {'error': 'Form not active'})
            else:
                return render_template('error.html',
                                       title='Form not active',
                                       text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'), 403

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        captcha_verified = verify_captcha(received_data, request)
        needs_captcha = not (request_wants_json() or
                             captcha_verified or
                             settings.TESTING)

        # if form is upgraded check if captcha is disabled
        if form.upgraded:
            needs_captcha = needs_captcha and not form.captcha_disabled

        if needs_captcha:
            data_copy = received_data.copy()
            # Temporarily store hostname in redis while doing captcha
            nonce = temp_store_hostname(form.host, request.referrer)
            data_copy['_host_nonce'] = nonce
            action = urljoin(settings.API_ROOT, email_or_string)
            try:
                if '_language' in received_data:
                    return render_template(
                        'forms/captcha_lang/{}.html'.format(received_data['_language']),
                        data=data_copy,
                        sorted_keys=sorted_keys,
                        action=action,
                        lang=received_data['_language']
                    )
            except TemplateNotFound:
                g.log.error('Requested language not found for reCAPTCHA page, defaulting to English', referrer=request.referrer, lang=received_data['_language'])
                pass

            return render_template('forms/captcha.html',
                                           data=data_copy,
                                           sorted_keys=sorted_keys,
                                           action=action,
                                           lang=None)

        status = form.send(received_data, sorted_keys, referrer)
    else:
        status = form.send_confirmation(store_data=received_data)

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({'success': "email sent", 'next': status['next']})
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_NO_EMAIL:
        if request_wants_json():
            return jsonify({'success': "no email sent, access submission archive on {} dashboard".format(settings.SERVICE_NAME), 'next': status['next']})
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template(
                'error.html',
                title='Can\'t send an empty form',
                text=u'<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank"><code>"name"</code> attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank"><code>"required"</code> property</a>.</p><p>This error also happens when you have an <code>"enctype"</code> attribute set in your <code>&lt;form&gt;</code>, so make sure you don\'t.</p><p><a href="{}">Return to form</a></p>'.format(referrer)
            ), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:

        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template('forms/confirmation_sent.html',
                email=email,
                host=host,
                resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
            )
    elif status['code'] == Form.STATUS_OVERLIMIT:
        if request_wants_json():
            return jsonify({'error': "form over quota"})
        else:
            return render_template('error.html', title='Form over quota', text='It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.'), 402

    elif status['code'] == Form.STATUS_REPLYTO_ERROR:
        if request_wants_json():
            return jsonerror(500, {'error': "_replyto or email field has not been sent correctly"})
        else:
            return render_template(
                'error.html',
                title='Invalid email address',
                text=u'You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>'''.format(address=status['address'], back=status['referrer'])
            ), 400

    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template(
            'error.html',
            title='Unable to send email',
            text=u'Unable to send email. If you can, please send the link to your form and the error information to  <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>'.format(message=json.dumps(status), email=settings.CONTACT_EMAIL)
        ), 500
Esempio n. 43
0
def create_form():
    # create a new form

    if not current_user.upgraded:
        g.log.info('Failed to create form from dashboard. User is not upgraded.')
        return jsonerror(402, {'error': "Please upgrade your account."})

    if request.get_json():
        email = request.get_json().get('email')
        url = request.get_json().get('url')
        sitewide = request.get_json().get('sitewide')
    else:
        email = request.form.get('email')
        url = request.form.get('url')
        sitewide = request.form.get('sitewide')

    g.log = g.log.bind(email=email, url=url, sitewide=sitewide)

    if not IS_VALID_EMAIL(email):
        g.log.info('Failed to create form from dashboard. Invalid address.')
        if request_wants_json():
            return jsonerror(400, {'error': "The provided email address is not valid."})
        else:
            flash(u'The provided email address is not valid.', 'error')
            return redirect(url_for('dashboard'))

    g.log.info('Creating a new form from the dashboard.')

    email = email.lower() # case-insensitive
    form = Form(email, owner=current_user)
    if url:
        url = 'http://' + url if not url.startswith('http') else url
        form.host = referrer_to_path(url)

        # sitewide forms, verified with a file at the root of the target domain
        if sitewide:
            if sitewide_file_check(url, email):
                form.host = remove_www(referrer_to_path(urljoin(url, '/'))[:-1])
                form.sitewide = True
            else:
                return jsonerror(403, {
                    'error': u"Couldn't verify the file at {}.".format(url)
                })

    DB.session.add(form)
    DB.session.commit()

    if form.host:
        # when the email and url are provided, we can automatically confirm the form
        # but only if the email is registered for this account
        for email in current_user.emails:
            if email.address == form.email:
                g.log.info('No need for email confirmation.')
                form.confirmed = True
                DB.session.add(form)
                DB.session.commit()
                break
        else:
            # in case the email isn't registered for this user
            # we automatically send the email confirmation
            form.send_confirmation()

    if request_wants_json():
        return jsonify({
            'ok': True,
            'hashid': form.hashid,
            'submission_url': settings.API_ROOT + '/' + form.hashid,
            'confirmed': form.confirmed
        })
    else:
        flash(u'Your new form endpoint was created!', 'success')
        return redirect(url_for('dashboard', new=form.hashid) + '#form-' + form.hashid)
Esempio n. 44
0
def forms():
    if request.method == 'GET':
        '''
        A reminder: this is the /forms endpoint, but for GET requests
        it is also the /dashboard endpoint.

        The /dashboard endpoint, the address gave by url_for('dashboard'),
        is the target of a lot of redirects around the app, but it can
        be changed later to point to somewhere else.
        '''

        # grab all the forms this user controls
        if current_user.upgraded:
            forms = current_user.forms.order_by(Form.id.desc()).all()
        else:
            forms = []

        if request_wants_json():
            return jsonify({
                'ok':
                True,
                'forms': [{
                    'email':
                    f.email,
                    'host':
                    f.host,
                    'confirm_sent':
                    f.confirm_sent,
                    'confirmed':
                    f.confirmed,
                    'is_public':
                    bool(f.hash),
                    'url':
                    '{S}/{E}'.format(S=settings.SERVICE_URL, E=f.hashid)
                } for f in forms]
            })
        else:
            return render_template('forms/list.html', forms=forms)

    elif request.method == 'POST':
        # create a new form
        if not current_user.upgraded:
            return jsonerror(402, {'error': "Please upgrade your account."})

        if request.get_json():
            email = request.get_json().get('email')
        else:
            email = request.form.get('email')

        if not IS_VALID_EMAIL(email):
            if request_wants_json():
                return jsonerror(
                    400, {'error': "The email you sent is not a valid email."})
            else:
                flash('The email you provided is not a valid email.', 'error')
                return redirect(url_for('dashboard'))

        form = Form(email, owner=current_user)
        DB.session.add(form)
        DB.session.commit()

        if request_wants_json():
            return jsonify({
                'ok':
                True,
                'hashid':
                form.hashid,
                'submission_url':
                settings.API_ROOT + '/' + form.hashid
            })
        else:
            flash('Your new form endpoint was created!', 'success')
            return redirect(url_for('dashboard') + '#view-code-' + form.hashid)
Esempio n. 45
0
def resend_confirmation(email):
    # the first thing to do is to check the captcha
    r = requests.post('https://www.google.com/recaptcha/api/siteverify', data={
        'secret': settings.RECAPTCHA_SECRET,
        'response': request.form['g-recaptcha-response'],
        'remoteip': request.remote_addr
    })
    if r.ok and r.json().get('success'):
        # then proceed to check for bounced addresses

        if request.form.get('bounce_problem_solved') == 'true':
            # delete bounce from SendGrid then proceed
            requests.delete('https://api.sendgrid.com/v3/suppression/bounces/',
                            auth=(settings.SENDGRID_USERNAME, settings.SENDGRID_PASSWORD),
                            data={'emails': [email]})
        else:
            # check if this email is listed on SendGrid's bounces
            r = requests.get('https://api.sendgrid.com/v3/suppression/bounces/' + email,
                             auth=(settings.SENDGRID_USERNAME, settings.SENDGRID_PASSWORD))
            if r.ok and len(r.json()) and 'reason' in r.json()[0]:
                # tell the user to verify his mailbox
                # and, at the same time, let him mark the checkbox that says he has already
                #      made his mailbox available in the next time he tries to resend
                #      confirmation.
                reason = r.json()[0]['reason']
                if request_wants_json():
                    resp = jsonify({'error': "Verify your mailbox, we can't reach it.", 'reason': reason})
                else:
                    resp = make_response(render_template('info.html',
                        title='Verify the availability of your mailbox',
                        text="We encountered an error when trying to deliver the confirmation message to <b>" + email + "</b> at the first time we tried. For spam reasons, we will not try again until we are sure the problem is fixed. Here's the reason:</p><p><center><i>" + reason + "</i></center></p><p>Please make sure this problem is not happening still, then come here and try to resend your confirmation message again."
                    ))
                resp.set_cookie('has_been_notified_of_bounce', 'true', max_age=345600)
                return resp
        # ~~~

        # if there's no bounce or the bounce problem has been solved, we proceed to resend
        # the confirmation.

        # I'm not sure if this should be available for forms created on the dashboard.
        form = Form.query.filter_by(hash=HASH(email, request.form['host'])).first()
        if not form:
            if request_wants_json():
                return jsonerror(400, {'error': "This form does not exists"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='This form does not exists'), 400
        form.confirm_sent = False
        status = form.send_confirmation()
        if status['code'] == Form.STATUS_CONFIRMATION_SENT:
            if request_wants_json():
                return jsonify({'success': "confirmation email sent"})
            else:
                return render_template('forms/confirmation_sent.html',
                    email=email,
                    host=request.form['host'],
                    resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
                )
        
    # fallback response -- should never happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email'), 500
Esempio n. 46
0
def create():
    # check that this request came from user dashboard to prevent XSS and CSRF
    referrer = referrer_to_baseurl(request.referrer)
    service = referrer_to_baseurl(settings.SERVICE_URL)
    if referrer != service:
        return jsonerror(400, {'error': 'Improper request.'})

    if not current_user.has_feature('dashboard'):
        g.log.info('Failed to create form from dashboard. Forbidden.')
        return jsonerror(402, {'error': "Please upgrade your account."})

    email = request.get_json().get('email')
    url = request.get_json().get('url')
    sitewide = request.get_json().get('sitewide')

    g.log = g.log.bind(email=email, url=url, sitewide=sitewide)

    if not IS_VALID_EMAIL(email):
        g.log.info('Failed to create form from dashboard. Invalid address.')
        return jsonerror(400,
                         {'error': "The provided email address is not valid."})

    g.log.info('Creating a new form from the dashboard.')

    email = email.lower().strip()  # case-insensitive
    form = Form(email, owner=current_user)
    if url:
        url = 'http://' + url if not url.startswith('http') else url
        form.host = referrer_to_path(url)

        # sitewide forms, verified with a file at the root of the target domain
        if sitewide:
            if sitewide_file_check(url, email):
                form.host = remove_www(
                    referrer_to_path(urljoin(url, '/'))[:-1])
                form.sitewide = True
            else:
                return jsonerror(
                    403,
                    {'error': u"Couldn't verify the file at {}.".format(url)})

    DB.session.add(form)
    DB.session.commit()

    if form.host:
        # when the email and url are provided, we can automatically confirm the form
        # but only if the email is registered for this account
        for email in current_user.emails:
            if email.address == form.email:
                g.log.info('No need for email confirmation.')
                form.confirmed = True
                DB.session.add(form)
                DB.session.commit()
                break
        else:
            # in case the email isn't registered for this user
            # we automatically send the email confirmation
            form.send_confirmation()

    return jsonify({
        'ok': True,
        'hashid': form.hashid,
        'submission_url': settings.API_ROOT + '/' + form.hashid,
        'confirmed': form.confirmed
    })
Esempio n. 47
0
def create():
    # check that this request came from user dashboard to prevent XSS and CSRF
    referrer = referrer_to_baseurl(request.referrer)
    service = referrer_to_baseurl(settings.SERVICE_URL)
    if referrer != service:
        return jsonerror(400, {'error': 'Improper request.'})

    if not current_user.has_feature('dashboard'):
        g.log.info('Failed to create form from dashboard. Forbidden.')
        return jsonerror(402, {'error': "Please upgrade your account."})

    email = request.get_json().get('email')
    url = request.get_json().get('url')
    sitewide = request.get_json().get('sitewide')

    g.log = g.log.bind(email=email, url=url, sitewide=sitewide)

    if not IS_VALID_EMAIL(email):
        g.log.info('Failed to create form from dashboard. Invalid address.')
        return jsonerror(400, {'error': "The provided email address is not valid."})

    g.log.info('Creating a new form from the dashboard.')

    email = email.lower().strip() # case-insensitive
    form = Form(email, owner=current_user)
    if url:
        url = 'http://' + url if not url.startswith('http') else url
        form.host = referrer_to_path(url)

        # sitewide forms, verified with a file at the root of the target domain
        if sitewide:
            if sitewide_file_check(url, email):
                form.host = remove_www(referrer_to_path(urljoin(url, '/'))[:-1])
                form.sitewide = True
            else:
                return jsonerror(403, {
                    'error': u"Couldn't verify the file at {}.".format(url)
                })

    DB.session.add(form)
    DB.session.commit()

    if form.host:
        # when the email and url are provided, we can automatically confirm the form
        # but only if the email is registered for this account
        for email in current_user.emails:
            if email.address == form.email:
                g.log.info('No need for email confirmation.')
                form.confirmed = True
                DB.session.add(form)
                DB.session.commit()
                break
        else:
            # in case the email isn't registered for this user
            # we automatically send the email confirmation
            form.send_confirmation()

    return jsonify({
        'ok': True,
        'hashid': form.hashid,
        'submission_url': settings.API_ROOT + '/' + form.hashid,
        'confirmed': form.confirmed
    })
Esempio n. 48
0
def create_form():
    # create a new form

    if not current_user.upgraded:
        g.log.info(
            'Failed to create form from dashboard. User is not upgraded.')
        return jsonerror(402, {'error': "Please upgrade your account."})

    if request.get_json():
        email = request.get_json().get('email')
        url = request.get_json().get('url')
        sitewide = request.get_json().get('sitewide')
    else:
        email = request.form.get('email')
        url = request.form.get('url')
        sitewide = request.form.get('sitewide')

    g.log = g.log.bind(email=email, url=url, sitewide=sitewide)

    if not IS_VALID_EMAIL(email):
        g.log.info('Failed to create form from dashboard. Invalid address.')
        if request_wants_json():
            return jsonerror(
                400, {'error': "The provided email address is not valid."})
        else:
            flash(u'The provided email address is not valid.', 'error')
            return redirect(url_for('dashboard'))

    g.log.info('Creating a new form from the dashboard.')

    email = email.lower()  # case-insensitive
    form = Form(email, owner=current_user)
    if url:
        url = 'http://' + url if not url.startswith('http') else url
        form.host = referrer_to_path(url)

        # sitewide forms, verified with a file at the root of the target domain
        if sitewide:
            if sitewide_file_check(url, email):
                form.host = remove_www(
                    referrer_to_path(urljoin(url, '/'))[:-1])
                form.sitewide = True
            else:
                return jsonerror(
                    403,
                    {'error': u"Couldn't verify the file at {}.".format(url)})

    DB.session.add(form)
    DB.session.commit()

    if form.host:
        # when the email and url are provided, we can automatically confirm the form
        # but only if the email is registered for this account
        for email in current_user.emails:
            if email.address == form.email:
                g.log.info('No need for email confirmation.')
                form.confirmed = True
                DB.session.add(form)
                DB.session.commit()
                break
        else:
            # in case the email isn't registered for this user
            # we automatically send the email confirmation
            form.send_confirmation()

    if request_wants_json():
        return jsonify({
            'ok': True,
            'hashid': form.hashid,
            'submission_url': settings.API_ROOT + '/' + form.hashid,
            'confirmed': form.confirmed
        })
    else:
        flash(u'Your new form endpoint was created!', 'success')
        return redirect(
            url_for('dashboard', new=form.hashid) + '#form-' + form.hashid)
Esempio n. 49
0
def resend_confirmation(email):
    # the first thing to do is to check the captcha
    r = requests.post('https://www.google.com/recaptcha/api/siteverify',
                      data={
                          'secret': settings.RECAPTCHA_SECRET,
                          'response': request.form['g-recaptcha-response'],
                          'remoteip': request.remote_addr
                      })
    if r.ok and r.json().get('success'):
        # then proceed to check for bounced addresses

        if request.form.get('bounce_problem_solved') == 'true':
            # delete bounce from SendGrid then proceed
            requests.delete('https://api.sendgrid.com/v3/suppression/bounces/',
                            auth=(settings.SENDGRID_USERNAME,
                                  settings.SENDGRID_PASSWORD),
                            data={'emails': [email]})
        else:
            # check if this email is listed on SendGrid's bounces
            r = requests.get(
                'https://api.sendgrid.com/v3/suppression/bounces/' + email,
                auth=(settings.SENDGRID_USERNAME, settings.SENDGRID_PASSWORD))
            if r.ok and len(r.json()) and 'reason' in r.json()[0]:
                # tell the user to verify his mailbox
                # and, at the same time, let him mark the checkbox that says he has already
                #      made his mailbox available in the next time he tries to resend
                #      confirmation.
                reason = r.json()[0]['reason']
                if request_wants_json():
                    resp = jsonify({
                        'error': "Verify your mailbox, we can't reach it.",
                        'reason': reason
                    })
                else:
                    resp = make_response(
                        render_template(
                            'info.html',
                            title='Verify the availability of your mailbox',
                            text=
                            "We encountered an error when trying to deliver the confirmation message to <b>"
                            + email +
                            "</b> at the first time we tried. For spam reasons, we will not try again until we are sure the problem is fixed. Here's the reason:</p><p><center><i>"
                            + reason +
                            "</i></center></p><p>Please make sure this problem is not happening still, then come here and try to resend your confirmation message again."
                        ))
                resp.set_cookie('has_been_notified_of_bounce',
                                'true',
                                max_age=345600)
                return resp
        # ~~~

        # if there's no bounce or the bounce problem has been solved, we proceed to resend
        # the confirmation.

        # I'm not sure if this should be available for forms created on the dashboard.
        form = Form.query.filter_by(
            hash=HASH(email, request.form['host'])).first()
        if not form:
            if request_wants_json():
                return jsonerror(400, {'error': "This form does not exists"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='This form does not exists'), 400
        form.confirm_sent = False
        status = form.send_confirmation()
        if status['code'] == Form.STATUS_CONFIRMATION_SENT:
            if request_wants_json():
                return jsonify({'success': "confirmation email sent"})
            else:
                return render_template('forms/confirmation_sent.html',
                                       email=email,
                                       host=request.form['host'],
                                       resend=status['code'] ==
                                       Form.STATUS_CONFIRMATION_DUPLICATED)

    # fallback response -- should never happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email'), 500
Esempio n. 50
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template(
                'info.html',
                title='Form should POST',
                text=
                'Make sure your form has the <span class="code"><strong>method="POST"</strong></span> attribute'
            ), 405

    host = referrer_to_path(flask.request.referrer)
    if not host:
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template(
                'error.html',
                title='Unable to submit form',
                text=
                'Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. For geeks: could not find the "Referrer" header.'
            ), 400

    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()
            elif form.host != host:
                # if the form submission came from a different host, it is an error
                if request_wants_json():
                    return jsonerror(
                        403, {
                            'error':
                            "Submission from different host than confirmed",
                            'submitted': host,
                            'confirmed': form.host
                        })
                else:
                    return render_template(
                        'error.html',
                        title='Check form address',
                        text='This submission came from "%s" but the form was\
                                                 confirmed for the address "%s"'
                        % (host, form.host)), 403
        else:
            # no form row found. it is an error.
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first() \
               or Form(email, host) # or create it if it doesn't exists

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        status = form.send(request.form, request.referrer)
    else:
        status = form.send_confirmation(with_data=request.form)

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({'success': "email sent", 'next': status['next']})
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template(
                'error.html',
                title='Can\'t send an empty form',
                text=str(
                    '<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank">"name" attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank">"required" property</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" target="_blank">see more HTML form customization info</a>.</p><p><a href="%s">Return to form</a></p>'
                    % request.referrer)), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:

        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template(
                'forms/confirmation_sent.html',
                email=email,
                host=host,
                resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED)
    elif status['code'] == Form.STATUS_OVERLIMIT:

        if request_wants_json():
            return jsonify({'error': "form over quota"})
        else:
            return render_template(
                'error.html',
                title='Form over quota',
                text=
                'It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.'
            )

    elif status['code'] == Form.STATUS_REPLYTO_ERROR:
        if request_wants_json():
            return jsonerror(500, {
                'error':
                "_replyto or email field has not been sent correctly"
            })
        else:
            return render_template(
                'error.html',
                title='Unable to send email',
                text=
                'Unable to send email. The field with a name attribute _replyto or email was not set correctly. This may be the result of you have multiple _replyto or email fields. If you cannot find your error, please contact <b>[email protected]</b> with a link to your form and this error message: <p><pre><code>'
                + status['error-message'] + '</code></pre></p>'), 500

    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template(
            'error.html',
            title='Unable to send email',
            text=
            'Unable to send email. If you can, please send the link to your form and the error information to  <b>[email protected]</b>. And send them the following: <p><pre><code>'
            + json.dumps(status) + '</code></pre></p>'), 500
Esempio n. 51
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template('info.html',
                                   title='Form should POST',
                                   text='Make sure your form has the <span class="code"><strong>method="POST"</strong></span> attribute'), 405

    host = referrer_to_path(flask.request.referrer)
    if not host:
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template('error.html',
                                   title='Unable to submit form',
                                   text='Make sure your form is running on a proper server. For geeks: could not find the "Referrer" header.'), 400

    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()
            elif form.host != host:
                # if the form submission came from a different host, it is an error
                if request_wants_json():
                    return jsonerror(403, {'error': "Submission from different host than confirmed",
                                           'submitted': host, 'confirmed': form.host})
                else:
                    return render_template('error.html',
                                           title='Check form address',
                                           text='This submission came from "%s" but the form was\
                                                 confirmed for the address "%s"' % (host, form.host)), 403
        else:
            # no form row found. it is an error.
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first() \
               or Form(email, host) # or create it if it doesn't exists

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        status = form.send(request.form, request.referrer)
    else:
        status = form.send_confirmation()

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({ 'success': "email sent", 'next': status['next'] })
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template('error.html',
                                   title='Can\'t send an empty form',
                                   text=str('<a href="%s">Return to form</a>' % request.referrer)), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:
        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template('forms/confirmation_sent.html', email=email, host=host)

    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email'), 500
Esempio n. 52
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    g.log = g.log.bind(target=email_or_string)

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template('info.html',
                                   title='Form should POST',
                                   text='Make sure your form has the <span class="code"><strong>method="POST"</strong></span> attribute'), 405

    host = referrer_to_path(request.referrer)
    if not host:
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template('error.html',
                                   title='Unable to submit form',
                                   text='<p>Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. Also make sure that you\'re posting to <b>https://</b>{host}.</p><p>For geeks: could not find the "Referrer" header.</p>'.format(host=request.url.split('//')[1])), 400

    g.log = g.log.bind(host=host, wants='json' if request_wants_json() else 'html')

    g.log.info('Received submission.')
    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            if form.disabled:
                # owner has disabled the form, so it should not receive any submissions
                if request_wants_json():
                    return jsonerror(403, {'error': 'Form not active'})
                else:
                    return render_template('error.html',
                                           title='Form not active',
                                           text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'), 403
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()

                # it is an error when
                #   form is sitewide, but submission came from a host rooted somewhere else, or
                #   form is not sitewide, and submission came from a different host
            elif (not form.sitewide and form.host != host) or (
                   form.sitewide and (
                     not host.startswith(form.host) and \
                     not remove_www(host).startswith(form.host)
                   )
                 ):
                g.log.info('Submission rejected. From a different host than confirmed.')
                if request_wants_json():
                    return jsonerror(403, {
                       'error': "Submission from different host than confirmed",
                       'submitted': host, 'confirmed': form.host
                    })
                else:
                    return render_template('error.html',
                                           title='Check form address',
                                           text='This submission came from "%s" but the form was\
                                                 confirmed for address "%s"' % (host, form.host)), 403
        else:
            # no form row found. it is an error.
            g.log.info('Submission rejected. No form found for this target.')
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string.lower()

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first() \
               or Form(email, host) # or create it if it doesn't exists
        if form.disabled:
            g.log.info('submission rejected. Form is disabled.')
            if request_wants_json():
                return jsonerror(403, {'error': 'Form not active'})
            else:
                return render_template('error.html',
                                       title='Form not active',
                                       text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'), 403

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    received_data = request.form or request.get_json() or {}
    if form.confirmed:
        status = form.send(received_data, request.referrer)
    else:
        status = form.send_confirmation(received_data)

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({'success': "email sent", 'next': status['next']})
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template('error.html',
                                   title='Can\'t send an empty form',
                                   text=str('<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank">"name" attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank">"required" property</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" target="_blank">see more HTML form customization info</a>.</p><p><a href="%s">Return to form</a></p>' % request.referrer)), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:

        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template('forms/confirmation_sent.html',
                email=email,
                host=host,
                resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
            )
    elif status['code'] == Form.STATUS_OVERLIMIT:
        if request_wants_json():
            return jsonify({'error': "form over quota"})
        else:
            return render_template('error.html', title='Form over quota', text='It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.'), 402

    elif status['code'] == Form.STATUS_REPLYTO_ERROR:
        if request_wants_json():
            return jsonerror(500, {'error': "_replyto or email field has not been sent correctly"})
        else:
            return render_template('error.html', title='Invalid email address', text='You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>'''.format(address=status['address'], back=status['referrer'])), 400

    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email. If you can, please send the link to your form and the error information to  <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>'.format(message=json.dumps(status), email=settings.CONTACT_EMAIL)), 500
Esempio n. 53
0
def form_submissions(hashid, format=None):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)

    for cont in form.controllers:
        if cont.id == current_user.id: break
    else:
        if request_wants_json():
            return jsonerror(403, {'error': "You do not control this form."})
        else:
            return redirect(url_for('dashboard'))

    submissions = form.submissions

    if not format:
        # normal request.
        if request_wants_json():
            return jsonify({
                'host': form.host,
                'email': form.email,
                'submissions': [dict(s.data, date=s.submitted_at.isoformat()) for s in submissions]
            })
        else:
            fields = set()
            for s in submissions:
                fields.update(s.data.keys())
            fields -= set(EXCLUDE_KEYS)

            return render_template('forms/submissions.html',
                form=form,
                fields=sorted(fields),
                submissions=submissions
            )
    elif format:
        # an export request, format can be json or csv
        if format == 'json':
            return Response(
                json.dumps({
                    'host': form.host,
                    'email': form.email,
                    'submissions': [dict(s.data, date=s.submitted_at.isoformat()) for s in submissions]
                }, sort_keys=True, indent=2),
                mimetype='application/json',
                headers={
                    'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.json' \
                                % (hashid, datetime.datetime.now().isoformat().split('.')[0])
                }
            )
        elif format == 'csv':
            out = io.BytesIO()
            fieldnames = set(field for sub in submissions for field in sub.data.keys())
            fieldnames = ['date'] + sorted(fieldnames)
            
            w = csv.DictWriter(out, fieldnames=fieldnames, encoding='utf-8')
            w.writeheader()
            for sub in submissions:
                w.writerow(dict(sub.data, date=sub.submitted_at.isoformat()))

            return Response(
                out.getvalue(),
                mimetype='text/csv',
                headers={
                    'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.csv' \
                                % (hashid, datetime.datetime.now().isoformat().split('.')[0])
                }
            )
Esempio n. 54
0
def create_form():
    # create a new form
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    if request.get_json():
        email = request.get_json().get('email')
        url = request.get_json().get('url')
        sitewide = request.get_json().get('sitewide')
    else:
        email = request.form.get('email')
        url = request.form.get('url')
        sitewide = request.form.get('sitewide')

    if not IS_VALID_EMAIL(email):
        if request_wants_json():
            return jsonerror(400, {'error': "The provided email address is not valid."})
        else:
            flash('The provided email address is not valid.', 'error')
            return redirect(url_for('dashboard'))

    form = Form(email, owner=current_user)
    if url:
        url = 'http://' + url if not url.startswith('http') else url
        form.host = referrer_to_path(url)

        # sitewide forms, verified with a file at the root of the target domain
        if sitewide:
            if sitewide_file_exists(url, email):
                form.host = remove_www(referrer_to_path(urljoin(url, '/'))[:-1])
                form.sitewide = True
            else:
                return jsonerror(403, {'error': "Couldn't find the verification file."})

    DB.session.add(form)
    DB.session.commit()

    if form.host:
        # when the email and url are provided, we can automatically confirm the form
        # but only if the email is registered for this account
        for email in current_user.emails:
            if email.address == form.email:
                form.confirmed = True
                DB.session.add(form)
                DB.session.commit()
                break
        else:
            # in case the email isn't registered for this user
            # we automatically send the email confirmation
            form.send_confirmation()

    if request_wants_json():
        return jsonify({
            'ok': True,
            'hashid': form.hashid,
            'submission_url': settings.API_ROOT + '/' + form.hashid,
            'confirmed': form.confirmed
        })
    else:
        flash('Your new form endpoint was created!', 'success')
        return redirect(url_for('dashboard') + '#view-code-' + form.hashid)
Esempio n. 55
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    g.log = g.log.bind(target=email_or_string)

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template('info.html',
                                   title='Form should POST',
                                   text='Make sure your form has the <span '
                                   'class="code"><strong>method="POST"'
                                   '</strong></span> attribute'), 405

    if request.form:
        received_data, sorted_keys = http_form_to_dict(request.form)
    else:
        received_data = request.get_json() or {}
        sorted_keys = received_data.keys()

    try:
        # Get stored hostname from redis (from captcha)
        host, referrer = get_temp_hostname(received_data['_host_nonce'])
    except KeyError:
        host, referrer = referrer_to_path(request.referrer), request.referrer

    if not host or host == 'www.google.com':
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template(
                'error.html',
                title='Unable to submit form',
                text=
                '<p>Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. Also make sure that you\'re posting to <b>{host}{path}</b>.</p><p>For geeks: could not find the "Referrer" header.</p>'
                .format(host=settings.SERVICE_URL, path=request.path)), 400

    g.log = g.log.bind(host=host,
                       wants='json' if request_wants_json() else 'html')

    g.log.info('Received submission.')
    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            # Check if it has been assigned about using AJAX or not
            assign_ajax(form, request_wants_json())

            if form.disabled:
                # owner has disabled the form, so it should not receive any submissions
                if request_wants_json():
                    return jsonerror(403, {'error': 'Form not active'})
                else:
                    return render_template(
                        'error.html',
                        title='Form not active',
                        text=
                        'The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'
                    ), 403
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()

                # it is an error when
                #   form is sitewide, but submission came from a host rooted somewhere else, or
                #   form is not sitewide, and submission came from a different host
            elif (not form.sitewide and form.host != host) or (
                   form.sitewide and (
                     not host.startswith(form.host) and \
                     not remove_www(host).startswith(form.host)
                   )
                 ):
                g.log.info(
                    'Submission rejected. From a different host than confirmed.'
                )
                if request_wants_json():
                    return jsonerror(
                        403, {
                            'error':
                            "Submission from different host than confirmed",
                            'submitted': host,
                            'confirmed': form.host
                        })
                else:
                    return render_template(
                        'error.html',
                        title='Check form address',
                        text='This submission came from "%s" but the form was\
                                                 confirmed for address "%s"' %
                        (host, form.host)), 403
        else:
            # no form row found. it is an error.
            g.log.info('Submission rejected. No form found for this target.')
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string.lower()

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first() \
               or Form(email, host) # or create it if it doesn't exists

        # Check if it has been assigned about using AJAX or not
        assign_ajax(form, request_wants_json())

        if form.disabled:
            g.log.info('submission rejected. Form is disabled.')
            if request_wants_json():
                return jsonerror(403, {'error': 'Form not active'})
            else:
                return render_template(
                    'error.html',
                    title='Form not active',
                    text=
                    'The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'
                ), 403

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        captcha_verified = verify_captcha(received_data, request)
        needs_captcha = not (request_wants_json() or captcha_verified
                             or settings.TESTING)

        # if form is upgraded check if captcha is disabled
        if form.upgraded:
            needs_captcha = needs_captcha and not form.captcha_disabled

        if needs_captcha:
            data_copy = received_data.copy()
            # Temporarily store hostname in redis while doing captcha
            nonce = temp_store_hostname(form.host, request.referrer)
            data_copy['_host_nonce'] = nonce
            action = urljoin(settings.API_ROOT, email_or_string)
            return render_template('forms/captcha.html',
                                   data=data_copy,
                                   sorted_keys=sorted_keys,
                                   action=action)

        status = form.send(received_data, sorted_keys, referrer)
    else:
        status = form.send_confirmation()

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({'success': "email sent", 'next': status['next']})
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template(
                'error.html',
                title='Can\'t send an empty form',
                text=
                u'<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank"><code>"name"</code> attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank"><code>"required"</code> property</a>.</p><p>This error also happens when you have an <code>"enctype"</code> attribute set in your <code>&lt;form&gt;</code>, so make sure you don\'t.</p><p><a href="{}">Return to form</a></p>'
                .format(referrer)), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:

        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template(
                'forms/confirmation_sent.html',
                email=email,
                host=host,
                resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED)
    elif status['code'] == Form.STATUS_OVERLIMIT:
        if request_wants_json():
            return jsonify({'error': "form over quota"})
        else:
            return render_template(
                'error.html',
                title='Form over quota',
                text=
                'It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.'
            ), 402

    elif status['code'] == Form.STATUS_REPLYTO_ERROR:
        if request_wants_json():
            return jsonerror(500, {
                'error':
                "_replyto or email field has not been sent correctly"
            })
        else:
            return render_template(
                'error.html',
                title='Invalid email address',
                text=
                u'You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>'
                ''.format(address=status['address'],
                          back=status['referrer'])), 400

    # error fallback -- shouldn't happen
    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template(
            'error.html',
            title='Unable to send email',
            text=
            u'Unable to send email. If you can, please send the link to your form and the error information to  <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>'
            .format(message=json.dumps(status),
                    email=settings.CONTACT_EMAIL)), 500
Esempio n. 56
0
def send(email_or_string):
    '''
    Main endpoint, finds or creates the form row from the database,
    checks validity and state of the form and sends either form data
    or verification to email.
    '''

    if request.method == 'GET':
        if request_wants_json():
            return jsonerror(405, {'error': "Please submit POST request."})
        else:
            return render_template(
                'info.html',
                title='Form should POST',
                text=
                'Make sure your form has the <span class="code"><strong>method="POST"</strong></span> attribute'
            ), 405

    host = referrer_to_path(flask.request.referrer)
    if not host:
        if request_wants_json():
            return jsonerror(400, {'error': "Invalid \"Referrer\" header"})
        else:
            return render_template(
                'error.html',
                title='Unable to submit form',
                text=
                'Make sure your form is running on a proper server. For geeks: could not find the "Referrer" header.'
            ), 400

    if not IS_VALID_EMAIL(email_or_string):
        # in this case it can be a hashid identifying a
        # form generated from the dashboard
        hashid = email_or_string
        form = Form.get_with_hashid(hashid)

        if form:
            email = form.email

            if not form.host:
                # add the host to the form
                form.host = host
                DB.session.add(form)
                DB.session.commit()
            elif form.host != host:
                # if the form submission came from a different host, it is an error
                if request_wants_json():
                    return jsonerror(
                        403, {
                            'error':
                            "Submission from different host than confirmed",
                            'submitted': host,
                            'confirmed': form.host
                        })
                else:
                    return render_template(
                        'error.html',
                        title='Check form address',
                        text='This submission came from "%s" but the form was\
                                                 confirmed for the address "%s"'
                        % (host, form.host)), 403
        else:
            # no form row found. it is an error.
            if request_wants_json():
                return jsonerror(400, {'error': "Invalid email address"})
            else:
                return render_template('error.html',
                                       title='Check email address',
                                       text='Email address %s is not formatted correctly' \
                                            % str(email_or_string)), 400
    else:
        # in this case, it is a normal email
        email = email_or_string

        # get the form for this request
        form = Form.query.filter_by(hash=HASH(email, host)).first() \
               or Form(email, host) # or create it if it doesn't exists

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        status = form.send(request.form, request.referrer)
    else:
        status = form.send_confirmation()

    # Respond to the request accordingly to the status code
    if status['code'] == Form.STATUS_EMAIL_SENT:
        if request_wants_json():
            return jsonify({'success': "email sent", 'next': status['next']})
        else:
            return redirect(status['next'], code=302)
    elif status['code'] == Form.STATUS_EMAIL_EMPTY:
        if request_wants_json():
            return jsonerror(400, {'error': "Can't send an empty form"})
        else:
            return render_template('error.html',
                                   title='Can\'t send an empty form',
                                   text=str('<a href="%s">Return to form</a>' %
                                            request.referrer)), 400
    elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \
         status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED:
        if request_wants_json():
            return jsonify({'success': "confirmation email sent"})
        else:
            return render_template('forms/confirmation_sent.html',
                                   email=email,
                                   host=host)

    if request_wants_json():
        return jsonerror(500, {'error': "Unable to send email"})
    else:
        return render_template('error.html',
                               title='Unable to send email',
                               text='Unable to send email'), 500
Esempio n. 57
0
def form_submissions(hashid, format=None):
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    form = Form.get_with_hashid(hashid)

    for cont in form.controllers:
        if cont.id == current_user.id: break
    else:
        if request_wants_json():
            return jsonerror(403, {'error': "You do not control this form."})
        else:
            return redirect(url_for('dashboard'))

    submissions = form.submissions

    if not format:
        # normal request.
        if request_wants_json():
            return jsonify({
                'host':
                form.host,
                'email':
                form.email,
                'submissions': [
                    dict(s.data, date=s.submitted_at.isoformat())
                    for s in submissions
                ]
            })
        else:
            fields = set()
            for s in submissions:
                fields.update(s.data.keys())
            fields -= EXCLUDE_KEYS

            return render_template('forms/submissions.html',
                                   form=form,
                                   fields=sorted(fields),
                                   submissions=submissions)
    elif format:
        # an export request, format can be json or csv
        if format == 'json':
            return Response(
                json.dumps({
                    'host': form.host,
                    'email': form.email,
                    'submissions': [dict(s.data, date=s.submitted_at.isoformat()) for s in submissions]
                }, sort_keys=True, indent=2),
                mimetype='application/json',
                headers={
                    'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.json' \
                                % (hashid, datetime.datetime.now().isoformat().split('.')[0])
                }
            )
        elif format == 'csv':
            out = io.BytesIO()
            fieldnames = set(field for sub in submissions
                             for field in sub.data.keys())
            fieldnames = ['date'] + sorted(fieldnames)

            w = csv.DictWriter(out, fieldnames=fieldnames, encoding='utf-8')
            w.writeheader()
            for sub in submissions:
                w.writerow(dict(sub.data, date=sub.submitted_at.isoformat()))

            return Response(
                out.getvalue(),
                mimetype='text/csv',
                headers={
                    'Content-Disposition': 'attachment; filename=form-%s-submissions-%s.csv' \
                                % (hashid, datetime.datetime.now().isoformat().split('.')[0])
                }
            )
Esempio n. 58
0
def create_form():
    # create a new form
    if not current_user.upgraded:
        return jsonerror(402, {'error': "Please upgrade your account."})

    if request.get_json():
        email = request.get_json().get('email')
        url = request.get_json().get('url')
        sitewide = request.get_json().get('sitewide')
    else:
        email = request.form.get('email')
        url = request.form.get('url')
        sitewide = request.form.get('sitewide')

    if not IS_VALID_EMAIL(email):
        if request_wants_json():
            return jsonerror(400, {'error': "The provided email address is not valid."})
        else:
            flash('The provided email address is not valid.', 'error')
            return redirect(url_for('dashboard'))

    form = Form(email, owner=current_user)
    if url:
        url = 'http://' + url if not url.startswith('http') else url
        form.host = referrer_to_path(url)

        # sitewide forms, verified with a file at the root of the target domain
        if sitewide:
            if sitewide_file_exists(url, email):
                form.host = remove_www(referrer_to_path(urljoin(url, '/'))[:-1])
                form.sitewide = True
            else:
                return jsonerror(403, {'error': "Couldn't find the verification file."})

    DB.session.add(form)
    DB.session.commit()

    if form.host:
        # when the email and url are provided, we can automatically confirm the form
        # but only if the email is registered for this account
        for email in current_user.emails:
            if email.address == form.email:
                form.confirmed = True
                DB.session.add(form)
                DB.session.commit()
                break
        else:
            # in case the email isn't registered for this user
            # we automatically send the email confirmation
            form.send_confirmation()

    if request_wants_json():
        return jsonify({
            'ok': True,
            'hashid': form.hashid,
            'submission_url': settings.API_ROOT + '/' + form.hashid,
            'confirmed': form.confirmed
        })
    else:
        flash('Your new form endpoint was created!', 'success')
        return redirect(url_for('dashboard') + '#view-code-' + form.hashid)