Esempio n. 1
0
def referrer_to_path(r):
    if not r:
        return ''
    parsed = urlparse.urlparse(r)
    n = parsed.netloc + parsed.path
    log.debug('Referrer was %s, now %s' % (str(r), n))
    return n
Esempio n. 2
0
def referrer_to_baseurl(r):
    if not r:
        return ''
    parsed = urlparse.urlparse(r)
    n = parsed.netloc
    log.debug('Referrer was %s, now %s' % (str(r), n))
    return n
Esempio n. 3
0
def sitewide_file_check(url, email):
    url = urljoin(url, '/formspree-verify.txt')
    log.debug('Checking sitewide file: %s' % url)
    res = requests.get(url, timeout=2)
    if not res.ok: return False

    for line in res.text.splitlines():
        if line.strip() == email:
            return True

    return False
Esempio n. 4
0
    def send_confirmation(self, with_data=None):
        '''
        Helper that actually creates confirmation nonce
        and sends the email to associated email. Renders
        different templates depending on the result
        '''

        log.debug('Sending confirmation')
        if self.confirm_sent:
            return {'code': Form.STATUS_CONFIRMATION_DUPLICATED}

        # the nonce for email confirmation will be the hash when it exists
        # (whenever the form was created from a simple submission) or
        # a concatenation of HASH(email, id) + ':' + hashid
        # (whenever the form was created from the dashboard)
        id = str(self.id)
        nonce = self.hash or '%s:%s' % (HASH(self.email, id), self.hashid)
        link = url_for('confirm_email', nonce=nonce, _external=True)

        def render_content(ext):
            data, keys = None, None
            if with_data:
                if type(with_data) in (ImmutableMultiDict,
                                       ImmutableOrderedMultiDict):
                    data, keys = http_form_to_dict(with_data)
                else:
                    data, keys = with_data, with_data.keys()

            return render_template('email/confirm.%s' % ext,
                                   email=self.email,
                                   host=self.host,
                                   nonce_link=link,
                                   data=data,
                                   keys=keys)

        log.debug('Sending email')

        result = send_email(to=self.email,
                            subject='Confirm email for %s' %
                            settings.SERVICE_NAME,
                            text=render_content('txt'),
                            html=render_content('html'),
                            sender=settings.DEFAULT_SENDER)

        log.debug('Sent')

        if not result[0]:
            return {'code': Form.STATUS_CONFIRMATION_FAILED}

        self.confirm_sent = True
        DB.session.add(self)
        DB.session.commit()

        return {'code': Form.STATUS_CONFIRMATION_SENT}
Esempio n. 5
0
    def send_confirmation(self, with_data=None):
        '''
        Helper that actually creates confirmation nonce
        and sends the email to associated email. Renders
        different templates depending on the result
        '''

        log.debug('Sending confirmation')
        if self.confirm_sent:
            return { 'code': Form.STATUS_CONFIRMATION_DUPLICATED }

        # the nonce for email confirmation will be the hash when it exists
        # (whenever the form was created from a simple submission) or
        # a concatenation of HASH(email, id) + ':' + hashid
        # (whenever the form was created from the dashboard)
        id = str(self.id)
        nonce = self.hash or '%s:%s' % (HASH(self.email, id), self.hashid)
        link = url_for('confirm_email', nonce=nonce, _external=True)

        def render_content(ext):
            data, keys = None, None
            if with_data:
                if type(with_data) in (ImmutableMultiDict, ImmutableOrderedMultiDict):
                    data, keys = http_form_to_dict(with_data)
                else:
                    data, keys = with_data, with_data.keys()

            return render_template('email/confirm.%s' % ext,
                                      email=self.email,
                                      host=self.host,
                                      nonce_link=link,
                                      data=data,
                                      keys=keys)

        log.debug('Sending email')

        result = send_email(to=self.email,
                            subject='Confirm email for %s' % settings.SERVICE_NAME,
                            text=render_content('txt'),
                            html=render_content('html'),
                            sender=settings.DEFAULT_SENDER)

        log.debug('Sent')

        if not result[0]:
            return { 'code': Form.STATUS_CONFIRMATION_FAILED }

        self.confirm_sent = True
        DB.session.add(self)
        DB.session.commit()

        return { 'code': Form.STATUS_CONFIRMATION_SENT }
Esempio n. 6
0
def sitewide_file_check(url, email):
    if not url.startswith('http://') and not url.startswith('https://'):
        url = 'http://' + url
    url = urljoin(url, '/formspree-verify.txt')
    log.debug('Checking sitewide file: %s' % url)
    res = requests.get(url, timeout=2)
    if not res.ok:
        log.debug('Nothing found on address.')
        return False

    for line in res.text.splitlines():
        line = line.strip(u'\xef\xbb\xbf ')
        if line == email:
            return True

    log.debug('%s not found in %s' % (email, res.text[:200]))
    return False
Esempio n. 7
0
    def send_confirmation(self):
        '''
        Helper that actually creates confirmation nonce
        and sends the email to associated email. Renders
        different templates depending on the result
        '''

        log.debug('Sending confirmation')
        if self.confirm_sent:
            return {'code': Form.STATUS_CONFIRMATION_DUPLICATED}

        # the nonce for email confirmation will be the hash when it exists
        # (whenever the form was created from a simple submission) or
        # a concatenation of HASH(email, id) + ':' + random_like_string
        # (whenever the form was created from the dashboard)
        id = str(self.id)
        nonce = self.hash or '%s:%s' % (HASH(
            self.email, id), self.get_random_like_string())
        link = url_for('confirm_email', nonce=nonce, _external=True)

        def render_content(type):
            return render_template('email/confirm.%s' % type,
                                   email=self.email,
                                   host=self.host,
                                   nonce_link=link)

        log.debug('Sending email')

        result = send_email(to=self.email,
                            subject='Confirm email for %s' %
                            settings.SERVICE_NAME,
                            text=render_content('txt'),
                            html=render_content('html'),
                            sender=settings.DEFAULT_SENDER)

        log.debug('Sent')

        if not result[0]:
            return {'code': Form.STATUS_CONFIRMATION_FAILED}

        self.confirm_sent = True
        DB.session.add(self)
        DB.session.commit()

        return {'code': Form.STATUS_CONFIRMATION_SENT}
Esempio n. 8
0
    def send_confirmation(self):
        '''
        Helper that actually creates confirmation nonce
        and sends the email to associated email. Renders
        different templates depending on the result
        '''

        log.debug('Sending confirmation')
        if self.confirm_sent:
            return { 'code': Form.STATUS_CONFIRMATION_DUPLICATED }

        # the nonce for email confirmation will be the hash when it exists
        # (whenever the form was created from a simple submission) or
        # a concatenation of HASH(email, id) + ':' + random_like_string
        # (whenever the form was created from the dashboard)
        id = str(self.id)
        nonce = self.hash or '%s:%s' % (HASH(self.email, id), self.get_random_like_string())
        link = url_for('confirm_email', nonce=nonce, _external=True)

        def render_content(type):
            return render_template('email/confirm.%s' % type,
                                      email=self.email,
                                      host=self.host,
                                      nonce_link=link)

        log.debug('Sending email')

        result = send_email(to=self.email,
                            subject='Confirm email for %s' % settings.SERVICE_NAME,
                            text=render_content('txt'),
                            html=render_content('html'),
                            sender=settings.DEFAULT_SENDER)

        log.debug('Sent')

        if not result[0]:
            return { 'code': Form.STATUS_CONFIRMATION_FAILED }

        self.confirm_sent = True
        DB.session.add(self)
        DB.session.commit()

        return { 'code': Form.STATUS_CONFIRMATION_SENT }
Esempio n. 9
0
def sitewide_file_exists (url, email):
    url = urljoin(url, '/' + 'formspree_verify_{0}.txt'.format(email))
    log.debug('Checking sitewide file: %s' % url)
    res = requests.get(url, timeout=2)
    return res.ok
Esempio n. 10
0
def referrer_to_path(r):
    log.debug('Referrer was %s' % str(r))
    if not r:
        return ''
    parsed = urlparse.urlparse(r)
    return parsed.netloc + parsed.path
Esempio n. 11
0
def referrer_to_path(r):
    log.debug("Referrer was %s" % str(r))
    if not r:
        return ""
    parsed = urlparse.urlparse(r)
    return parsed.netloc + parsed.path
Esempio n. 12
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=
                '<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

    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)
                   )
                 ):
                log.debug('Submission rejected from %s to %s' % (host, email))
                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.
            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:
            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='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 having multiple _replyto or email fields in the form. If you cannot find your error, please contact <b>{email}</b> with a link to your form and this error message: <p><pre><code>{message}</code></pre></p>'
                .format(message=status['error-message'],
                        email=settings.CONTACT_EMAIL)), 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. 13
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='<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

    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)
                   )
                 ):
                log.debug('Submission rejected from %s to %s' % (host, email))
                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.
            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:
            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='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 having multiple _replyto or email fields in the form. If you cannot find your error, please contact <b>{email}</b> with a link to your form and this error message: <p><pre><code>{message}</code></pre></p>'.format(message=status['error-message'], email=settings.CONTACT_EMAIL)), 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