示例#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
        )
示例#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)
示例#3
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
示例#4
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
示例#5
0
文件: views.py 项目: mba811/formspree
def forms():
    '''
    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)
示例#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)
示例#7
0
文件: views.py 项目: jean/formspree
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
        )
示例#8
0
def forms():
    '''
    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)
示例#9
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'))
示例#10
0
def no_email_sent_success(status):
    if request_wants_json():
        return jsonify({
            'success': "no email sent, access submission archive on {} dashboard".format(settings.SERVICE_NAME), 
            'next': status['next']
        })

    return redirect(status['next'], code=302)    
示例#11
0
def over_limit_error():
    if request_wants_json():
        return jsonify({'error': "form over quota"})

    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
示例#12
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
示例#13
0
def confirmation_sent_success(form, host, status):
    if request_wants_json():
        return jsonify({'success': "confirmation email sent"})

    return render_template(
        'forms/confirmation_sent.html',
        email=form.email,
        host=host,
        resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED)
示例#14
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
示例#15
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
示例#16
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':
        return errors.bad_method_error()

    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]

    try:
        # NOTE: host in this function generally refers to the referrer hostname.
        host, referrer = get_host_and_referrer(received_data)
    except SubmitFormError as vfe:
        return vfe.response

    if not host:
        return errors.no_referrer_error()

    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
        try:
            form = validate_user_form(email_or_string, host)
        except SubmitFormError as vfe:
            return vfe.response
    else:
        # in this case, it is a normal email
        try:
            form = get_or_create_form(email_or_string.lower(), host)
        except SubmitFormError as vfe:
            return vfe.response

    # If form exists and is confirmed, send email
    # otherwise send a confirmation email
    if form.confirmed:
        captcha_page = check_captcha(form, email_or_string, received_data, sorted_keys)
        if captcha_page:
            return captcha_page
        status = form.send(received_data, sorted_keys, referrer)
    else:
        status = form.send_confirmation(store_data=received_data)

    return response_for_status(form, host, referrer, status)
示例#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
示例#18
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
示例#19
0
def confirmation_sent_success(form, host, status):
    if request_wants_json():
        return jsonify({'success': "confirmation email sent"})

    return render_template(
        'forms/confirmation_sent.html',
        email=form.email,
        host=host,
        resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED
    )
示例#20
0
def over_limit_error():
    if request_wants_json():
        return jsonify({'error': "form over quota"})

    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
示例#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
示例#22
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
示例#23
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
示例#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
示例#25
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
示例#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
示例#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    
示例#28
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
示例#29
0
文件: views.py 项目: jean/formspree
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'))
示例#30
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
示例#31
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 jsonify({"error": "Invalid email address"}), 400

    return (
        render_template(
            "error.html",
            title="Check email address",
            text="Email address %s is not formatted correctly" % str(email_or_string),
        ),
        400,
    )
示例#32
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
示例#33
0
def bad_method_error(e=None):
    if request_wants_json():
        return jsonify({"error": "Please submit POST request."}), 405

    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,
    )
示例#34
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 jsonify({"error": "Form not active"}), 403

    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,
    )
示例#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
示例#36
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
示例#37
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
示例#38
0
文件: models.py 项目: ajabo/formspree
 def __init__(self, email, host=None, owner=None):
     if host:
         self.hash = HASH(email, host)
     elif owner:
         self.owner_id = owner.id
     else:
         raise Exception('cannot create form without a host and a owner. provide one of these.')
     self.email = email
     self.host = host
     self.confirm_sent = False
     self.confirmed = False
     self.counter = 0
     self.disabled = False
     self.uses_ajax = request_wants_json()
     self.captcha_disabled = False
示例#39
0
 def __init__(self, email, host=None, owner=None):
     if host:
         self.hash = HASH(email, host)
     elif owner:
         self.owner_id = owner.id
     else:
         raise Exception('cannot create form without a host and a owner. provide one of these.')
     self.email = email
     self.host = host
     self.confirm_sent = False
     self.confirmed = False
     self.counter = 0
     self.disabled = False
     self.uses_ajax = request_wants_json()
     self.captcha_disabled = False
示例#40
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
示例#41
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
示例#42
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.get_with(email=email, host=host)

    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((jsonify({"error": ajax_error_str}), 400))

        if (
            url_domain(settings.SERVICE_URL) in host
            and host.rstrip("/") != settings.TEST_URL
        ):
            # 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=host, confirmed=False, normalize=True)

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

    return form
示例#43
0
def no_referrer_error():
    g.log.info("Invalid Referrer.")

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

    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,
    )
示例#44
0
def check_captcha(form, email_or_string, received_data, sorted_keys):
    '''
    Checks to see if a captcha page is required, if so renders it.
    '''

    captcha_verified = verify_captcha(received_data, request)
    needs_captcha = not (request_wants_json() or captcha_verified
                         or settings.TESTING)

    # check if captcha is disabled
    if form.has_feature('dashboard'):
        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)
示例#45
0
def check_captcha(form, email_or_string, received_data, sorted_keys):
    '''
    Checks to see if a captcha page is required, if so renders it.
    '''
    
    captcha_verified = verify_captcha(received_data, request)
    needs_captcha = not (request_wants_json() or
                            captcha_verified or
                            settings.TESTING)

    # check if captcha is disabled
    if form.has_feature('dashboard'):
        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)
示例#46
0
def mismatched_host_error(host, form):
    g.log.info("Submission rejected. From a different host than confirmed.")
    if request_wants_json():
        return (
            jsonify(
                {
                    "error": "Submission from different host than confirmed",
                    "submitted": host,
                    "confirmed": form.host,
                }
            ),
            403,
        )
    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,
    )
示例#47
0
def validate_user_form(hashid, host):
    '''
    Gets a form from a hashid, created on the dashboard. 
    Checks to make sure the submission can be accepted by this form.
    '''

    form = Form.get_with_hashid(hashid)

    if not form:
        raise SubmitFormError(errors.bad_hashid_error(hashid))

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

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

    if not form.host:
        # add the host to the form
        # ALERT: As a side effect, sets the form's host if not already set
        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))):
        raise SubmitFormError(errors.mismatched_host_error(host, form))

    return form
示例#48
0
def validate_user_form(hashid, host):
    '''
    Gets a form from a hashid, created on the dashboard. 
    Checks to make sure the submission can be accepted by this form.
    '''

    form = Form.get_with_hashid(hashid)

    if not form:
        raise SubmitFormError(errors.bad_hashid_error(hashid))

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

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

    if not form.host:
        # add the host to the form
        # ALERT: As a side effect, sets the form's host if not already set
        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))):
        raise SubmitFormError(errors.mismatched_host_error(host, form))

    return form
示例#49
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])
                }
            )
示例#50
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)
示例#51
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])
                }
            )
示例#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.
    '''

    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