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
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
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
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}
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 }
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
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}
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 }
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
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
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
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
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