mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-13 11:46:22 +00:00
First preparations for Django >= 4.x
This commit is contained in:
@@ -6,7 +6,7 @@ Configuration overrides for settings.py
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.messages import constants as message_constants
|
from django.contrib.messages import constants as message_constants
|
||||||
from ivatar.settings import BASE_DIR
|
from ivatar.settings import BASE_DIR
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Import the whole libravatar export
|
Import the whole libravatar export
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from os.path import isfile, isdir, join
|
from os.path import isfile, isdir, join
|
||||||
@@ -9,13 +10,18 @@ import sys
|
|||||||
import base64
|
import base64
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import django
|
import django
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ivatar.settings") # pylint: disable=wrong-import-position
|
|
||||||
|
os.environ.setdefault(
|
||||||
|
"DJANGO_SETTINGS_MODULE", "ivatar.settings"
|
||||||
|
) # pylint: disable=wrong-import-position
|
||||||
django.setup() # pylint: disable=wrong-import-position
|
django.setup() # pylint: disable=wrong-import-position
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from django_openid_auth.models import UserOpenID
|
from django_openid_auth.models import UserOpenID
|
||||||
from ivatar.settings import JPEG_QUALITY
|
from ivatar.settings import JPEG_QUALITY
|
||||||
from ivatar.ivataraccount.read_libravatar_export import read_gzdata as libravatar_read_gzdata
|
from ivatar.ivataraccount.read_libravatar_export import (
|
||||||
|
read_gzdata as libravatar_read_gzdata,
|
||||||
|
)
|
||||||
from ivatar.ivataraccount.models import ConfirmedEmail
|
from ivatar.ivataraccount.models import ConfirmedEmail
|
||||||
from ivatar.ivataraccount.models import ConfirmedOpenId
|
from ivatar.ivataraccount.models import ConfirmedOpenId
|
||||||
from ivatar.ivataraccount.models import Photo
|
from ivatar.ivataraccount.models import Photo
|
||||||
@@ -26,54 +32,63 @@ if len(sys.argv) < 2:
|
|||||||
exit(-255)
|
exit(-255)
|
||||||
|
|
||||||
if not isdir(sys.argv[1]):
|
if not isdir(sys.argv[1]):
|
||||||
print("First argument to '%s' must be a directory containing the exports" % sys.argv[0])
|
print(
|
||||||
|
"First argument to '%s' must be a directory containing the exports"
|
||||||
|
% sys.argv[0]
|
||||||
|
)
|
||||||
exit(-255)
|
exit(-255)
|
||||||
|
|
||||||
PATH = sys.argv[1]
|
PATH = sys.argv[1]
|
||||||
for file in os.listdir(PATH):
|
for file in os.listdir(PATH):
|
||||||
if not file.endswith('.xml.gz'):
|
if not file.endswith(".xml.gz"):
|
||||||
continue
|
continue
|
||||||
if isfile(join(PATH, file)):
|
if isfile(join(PATH, file)):
|
||||||
fh = open(join(PATH, file), 'rb')
|
fh = open(join(PATH, file), "rb")
|
||||||
items = libravatar_read_gzdata(fh.read())
|
items = libravatar_read_gzdata(fh.read())
|
||||||
print('Adding user "%s"' % items['username'])
|
print('Adding user "%s"' % items["username"])
|
||||||
(user, created) = User.objects.get_or_create(username=items['username'])
|
(user, created) = User.objects.get_or_create(username=items["username"])
|
||||||
user.password = items['password']
|
user.password = items["password"]
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
saved_photos = {}
|
saved_photos = {}
|
||||||
for photo in items['photos']:
|
for photo in items["photos"]:
|
||||||
photo_id = photo['id']
|
photo_id = photo["id"]
|
||||||
data = base64.decodebytes(bytes(photo['data'], 'utf-8'))
|
data = base64.decodebytes(bytes(photo["data"], "utf-8"))
|
||||||
pilobj = Image.open(BytesIO(data))
|
pilobj = Image.open(BytesIO(data))
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
pilobj.save(out, pilobj.format, quality=JPEG_QUALITY)
|
pilobj.save(out, pilobj.format, quality=JPEG_QUALITY)
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
photo = Photo()
|
photo = Photo()
|
||||||
photo.user = user
|
photo.user = user
|
||||||
photo.ip_address = '0.0.0.0'
|
photo.ip_address = "0.0.0.0"
|
||||||
photo.format = file_format(pilobj.format)
|
photo.format = file_format(pilobj.format)
|
||||||
photo.data = out.read()
|
photo.data = out.read()
|
||||||
photo.save()
|
photo.save()
|
||||||
saved_photos[photo_id] = photo
|
saved_photos[photo_id] = photo
|
||||||
|
|
||||||
for email in items['emails']:
|
for email in items["emails"]:
|
||||||
try:
|
try:
|
||||||
ConfirmedEmail.objects.get_or_create(email=email['email'], user=user,
|
ConfirmedEmail.objects.get_or_create(
|
||||||
photo=saved_photos.get(email['photo_id']))
|
email=email["email"],
|
||||||
except django.db.utils.IntegrityError:
|
user=user,
|
||||||
print('%s not unique?' % email['email'])
|
photo=saved_photos.get(email["photo_id"]),
|
||||||
|
|
||||||
for openid in items['openids']:
|
|
||||||
try:
|
|
||||||
ConfirmedOpenId.objects.get_or_create(openid=openid['openid'], user=user,
|
|
||||||
photo=saved_photos.get(openid['photo_id'])) # pylint: disable=no-member
|
|
||||||
UserOpenID.objects.get_or_create(
|
|
||||||
user_id=user.id,
|
|
||||||
claimed_id=openid['openid'],
|
|
||||||
display_id=openid['openid'],
|
|
||||||
)
|
)
|
||||||
except django.db.utils.IntegrityError:
|
except django.db.utils.IntegrityError:
|
||||||
print('%s not unique?' % openid['openid'])
|
print("%s not unique?" % email["email"])
|
||||||
|
|
||||||
|
for openid in items["openids"]:
|
||||||
|
try:
|
||||||
|
ConfirmedOpenId.objects.get_or_create(
|
||||||
|
openid=openid["openid"],
|
||||||
|
user=user,
|
||||||
|
photo=saved_photos.get(openid["photo_id"]),
|
||||||
|
) # pylint: disable=no-member
|
||||||
|
UserOpenID.objects.get_or_create(
|
||||||
|
user_id=user.id,
|
||||||
|
claimed_id=openid["openid"],
|
||||||
|
display_id=openid["openid"],
|
||||||
|
)
|
||||||
|
except django.db.utils.IntegrityError:
|
||||||
|
print("%s not unique?" % openid["openid"])
|
||||||
|
|
||||||
fh.close()
|
fh.close()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Module init
|
Module init
|
||||||
'''
|
"""
|
||||||
app_label = __name__ # pylint: disable=invalid-name
|
app_label = __name__ # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Register models in admin
|
Register models in admin
|
||||||
'''
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Photo, ConfirmedEmail, UnconfirmedEmail
|
from .models import Photo, ConfirmedEmail, UnconfirmedEmail
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Classes for our ivatar.ivataraccount.forms
|
Classes for our ivatar.ivataraccount.forms
|
||||||
'''
|
"""
|
||||||
from urllib.parse import urlsplit, urlunsplit
|
from urllib.parse import urlsplit, urlunsplit
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from ipware import get_client_ip
|
from ipware import get_client_ip
|
||||||
|
|
||||||
@@ -20,93 +21,97 @@ MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT = 5
|
|||||||
|
|
||||||
|
|
||||||
class AddEmailForm(forms.Form):
|
class AddEmailForm(forms.Form):
|
||||||
'''
|
"""
|
||||||
Form to handle adding email addresses
|
Form to handle adding email addresses
|
||||||
'''
|
"""
|
||||||
|
|
||||||
email = forms.EmailField(
|
email = forms.EmailField(
|
||||||
label=_('Email'),
|
label=_("Email"),
|
||||||
min_length=MIN_LENGTH_EMAIL,
|
min_length=MIN_LENGTH_EMAIL,
|
||||||
max_length=MAX_LENGTH_EMAIL,
|
max_length=MAX_LENGTH_EMAIL,
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean_email(self):
|
def clean_email(self):
|
||||||
'''
|
"""
|
||||||
Enforce lowercase email
|
Enforce lowercase email
|
||||||
'''
|
"""
|
||||||
# TODO: Domain restriction as in libravatar?
|
# TODO: Domain restriction as in libravatar?
|
||||||
return self.cleaned_data['email'].lower()
|
return self.cleaned_data["email"].lower()
|
||||||
|
|
||||||
def save(self, request):
|
def save(self, request):
|
||||||
'''
|
"""
|
||||||
Save the model, ensuring some safety
|
Save the model, ensuring some safety
|
||||||
'''
|
"""
|
||||||
user = request.user
|
user = request.user
|
||||||
# Enforce the maximum number of unconfirmed emails a user can have
|
# Enforce the maximum number of unconfirmed emails a user can have
|
||||||
num_unconfirmed = user.unconfirmedemail_set.count()
|
num_unconfirmed = user.unconfirmedemail_set.count()
|
||||||
|
|
||||||
max_num_unconfirmed_emails = getattr(
|
max_num_unconfirmed_emails = getattr(
|
||||||
settings,
|
settings, "MAX_NUM_UNCONFIRMED_EMAILS", MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
|
||||||
'MAX_NUM_UNCONFIRMED_EMAILS',
|
)
|
||||||
MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT)
|
|
||||||
|
|
||||||
if num_unconfirmed >= max_num_unconfirmed_emails:
|
if num_unconfirmed >= max_num_unconfirmed_emails:
|
||||||
self.add_error(None, _('Too many unconfirmed mail addresses!'))
|
self.add_error(None, _("Too many unconfirmed mail addresses!"))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check whether or not a confirmation email has been
|
# Check whether or not a confirmation email has been
|
||||||
# sent by this user already
|
# sent by this user already
|
||||||
if UnconfirmedEmail.objects.filter( # pylint: disable=no-member
|
if UnconfirmedEmail.objects.filter( # pylint: disable=no-member
|
||||||
user=user, email=self.cleaned_data['email']).exists():
|
user=user, email=self.cleaned_data["email"]
|
||||||
self.add_error(
|
).exists():
|
||||||
'email',
|
self.add_error("email", _("Address already added, currently unconfirmed"))
|
||||||
_('Address already added, currently unconfirmed'))
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check whether or not the email is already confirmed (by someone)
|
# Check whether or not the email is already confirmed (by someone)
|
||||||
check_mail = ConfirmedEmail.objects.filter(
|
check_mail = ConfirmedEmail.objects.filter(email=self.cleaned_data["email"])
|
||||||
email=self.cleaned_data['email'])
|
|
||||||
if check_mail.exists():
|
if check_mail.exists():
|
||||||
msg = _('Address already confirmed (by someone else)')
|
msg = _("Address already confirmed (by someone else)")
|
||||||
if check_mail.first().user == request.user:
|
if check_mail.first().user == request.user:
|
||||||
msg = _('Address already confirmed (by you)')
|
msg = _("Address already confirmed (by you)")
|
||||||
self.add_error('email', msg)
|
self.add_error("email", msg)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
unconfirmed = UnconfirmedEmail()
|
unconfirmed = UnconfirmedEmail()
|
||||||
unconfirmed.email = self.cleaned_data['email']
|
unconfirmed.email = self.cleaned_data["email"]
|
||||||
unconfirmed.user = user
|
unconfirmed.user = user
|
||||||
unconfirmed.save()
|
unconfirmed.save()
|
||||||
unconfirmed.send_confirmation_mail(url=request.build_absolute_uri('/')[:-1])
|
unconfirmed.send_confirmation_mail(url=request.build_absolute_uri("/")[:-1])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class UploadPhotoForm(forms.Form):
|
class UploadPhotoForm(forms.Form):
|
||||||
'''
|
"""
|
||||||
Form handling photo upload
|
Form handling photo upload
|
||||||
'''
|
"""
|
||||||
|
|
||||||
photo = forms.FileField(
|
photo = forms.FileField(
|
||||||
label=_('Photo'),
|
label=_("Photo"),
|
||||||
error_messages={'required': _('You must choose an image to upload.')})
|
error_messages={"required": _("You must choose an image to upload.")},
|
||||||
|
)
|
||||||
not_porn = forms.BooleanField(
|
not_porn = forms.BooleanField(
|
||||||
label=_('suitable for all ages (i.e. no offensive content)'),
|
label=_("suitable for all ages (i.e. no offensive content)"),
|
||||||
required=True,
|
required=True,
|
||||||
error_messages={
|
error_messages={
|
||||||
'required':
|
"required": _(
|
||||||
_('We only host "G-rated" images and so this field must be checked.')
|
'We only host "G-rated" images and so this field must be checked.'
|
||||||
})
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
can_distribute = forms.BooleanField(
|
can_distribute = forms.BooleanField(
|
||||||
label=_('can be freely copied'),
|
label=_("can be freely copied"),
|
||||||
required=True,
|
required=True,
|
||||||
error_messages={
|
error_messages={
|
||||||
'required':
|
"required": _(
|
||||||
_('This field must be checked since we need to be able to distribute photos to third parties.')
|
"This field must be checked since we need to be able to distribute photos to third parties."
|
||||||
})
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def save(request, data):
|
def save(request, data):
|
||||||
'''
|
"""
|
||||||
Save the model and assign it to the current user
|
Save the model and assign it to the current user
|
||||||
'''
|
"""
|
||||||
# Link this file to the user's profile
|
# Link this file to the user's profile
|
||||||
photo = Photo()
|
photo = Photo()
|
||||||
photo.user = request.user
|
photo.user = request.user
|
||||||
@@ -119,47 +124,48 @@ class UploadPhotoForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class AddOpenIDForm(forms.Form):
|
class AddOpenIDForm(forms.Form):
|
||||||
'''
|
"""
|
||||||
Form to handle adding OpenID
|
Form to handle adding OpenID
|
||||||
'''
|
"""
|
||||||
|
|
||||||
openid = forms.URLField(
|
openid = forms.URLField(
|
||||||
label=_('OpenID'),
|
label=_("OpenID"),
|
||||||
min_length=MIN_LENGTH_URL,
|
min_length=MIN_LENGTH_URL,
|
||||||
max_length=MAX_LENGTH_URL,
|
max_length=MAX_LENGTH_URL,
|
||||||
initial='http://'
|
initial="http://",
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean_openid(self):
|
def clean_openid(self):
|
||||||
'''
|
"""
|
||||||
Enforce restrictions
|
Enforce restrictions
|
||||||
'''
|
"""
|
||||||
# Lowercase hostname port of the URL
|
# Lowercase hostname port of the URL
|
||||||
url = urlsplit(self.cleaned_data['openid'])
|
url = urlsplit(self.cleaned_data["openid"])
|
||||||
data = urlunsplit(
|
data = urlunsplit(
|
||||||
(url.scheme.lower(), url.netloc.lower(), url.path,
|
(url.scheme.lower(), url.netloc.lower(), url.path, url.query, url.fragment)
|
||||||
url.query, url.fragment))
|
)
|
||||||
|
|
||||||
# TODO: Domain restriction as in libravatar?
|
# TODO: Domain restriction as in libravatar?
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def save(self, user):
|
def save(self, user):
|
||||||
'''
|
"""
|
||||||
Save the model, ensuring some safety
|
Save the model, ensuring some safety
|
||||||
'''
|
"""
|
||||||
if ConfirmedOpenId.objects.filter( # pylint: disable=no-member
|
if ConfirmedOpenId.objects.filter( # pylint: disable=no-member
|
||||||
openid=self.cleaned_data['openid']).exists():
|
openid=self.cleaned_data["openid"]
|
||||||
self.add_error('openid', _('OpenID already added and confirmed!'))
|
).exists():
|
||||||
|
self.add_error("openid", _("OpenID already added and confirmed!"))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if UnconfirmedOpenId.objects.filter( # pylint: disable=no-member
|
if UnconfirmedOpenId.objects.filter( # pylint: disable=no-member
|
||||||
openid=self.cleaned_data['openid']).exists():
|
openid=self.cleaned_data["openid"]
|
||||||
self.add_error(
|
).exists():
|
||||||
'openid',
|
self.add_error("openid", _("OpenID already added, but not confirmed yet!"))
|
||||||
_('OpenID already added, but not confirmed yet!'))
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
unconfirmed = UnconfirmedOpenId()
|
unconfirmed = UnconfirmedOpenId()
|
||||||
unconfirmed.openid = self.cleaned_data['openid']
|
unconfirmed.openid = self.cleaned_data["openid"]
|
||||||
unconfirmed.user = user
|
unconfirmed.user = user
|
||||||
unconfirmed.save()
|
unconfirmed.save()
|
||||||
|
|
||||||
@@ -167,40 +173,50 @@ class AddOpenIDForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class UpdatePreferenceForm(forms.ModelForm):
|
class UpdatePreferenceForm(forms.ModelForm):
|
||||||
'''
|
"""
|
||||||
Form for updating user preferences
|
Form for updating user preferences
|
||||||
'''
|
"""
|
||||||
|
|
||||||
class Meta: # pylint: disable=too-few-public-methods
|
class Meta: # pylint: disable=too-few-public-methods
|
||||||
'''
|
"""
|
||||||
Meta class for UpdatePreferenceForm
|
Meta class for UpdatePreferenceForm
|
||||||
'''
|
"""
|
||||||
|
|
||||||
model = UserPreference
|
model = UserPreference
|
||||||
fields = ['theme']
|
fields = ["theme"]
|
||||||
|
|
||||||
|
|
||||||
class UploadLibravatarExportForm(forms.Form):
|
class UploadLibravatarExportForm(forms.Form):
|
||||||
'''
|
"""
|
||||||
Form handling libravatar user export upload
|
Form handling libravatar user export upload
|
||||||
'''
|
"""
|
||||||
|
|
||||||
export_file = forms.FileField(
|
export_file = forms.FileField(
|
||||||
label=_('Export file'),
|
label=_("Export file"),
|
||||||
error_messages={'required': _('You must choose an export file to upload.')})
|
error_messages={"required": _("You must choose an export file to upload.")},
|
||||||
|
)
|
||||||
not_porn = forms.BooleanField(
|
not_porn = forms.BooleanField(
|
||||||
label=_('suitable for all ages (i.e. no offensive content)'),
|
label=_("suitable for all ages (i.e. no offensive content)"),
|
||||||
required=True,
|
required=True,
|
||||||
error_messages={
|
error_messages={
|
||||||
'required':
|
"required": _(
|
||||||
_('We only host "G-rated" images and so this field must be checked.')
|
'We only host "G-rated" images and so this field must be checked.'
|
||||||
})
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
can_distribute = forms.BooleanField(
|
can_distribute = forms.BooleanField(
|
||||||
label=_('can be freely copied'),
|
label=_("can be freely copied"),
|
||||||
required=True,
|
required=True,
|
||||||
error_messages={
|
error_messages={
|
||||||
'required':
|
"required": _(
|
||||||
_('This field must be checked since we need to be able to\
|
"This field must be checked since we need to be able to\
|
||||||
distribute photos to third parties.')
|
distribute photos to third parties."
|
||||||
})
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeleteAccountForm(forms.Form):
|
class DeleteAccountForm(forms.Form):
|
||||||
password = forms.CharField(label=_('Password'), required=False, widget=forms.PasswordInput())
|
password = forms.CharField(
|
||||||
|
label=_("Password"), required=False, widget=forms.PasswordInput()
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Helper method to fetch Gravatar image
|
Helper method to fetch Gravatar image
|
||||||
'''
|
"""
|
||||||
from ssl import SSLError
|
from ssl import SSLError
|
||||||
from urllib.request import urlopen, HTTPError, URLError
|
from urllib.request import urlopen, HTTPError, URLError
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -11,43 +12,47 @@ URL_TIMEOUT = 5 # in seconds
|
|||||||
|
|
||||||
|
|
||||||
def get_photo(email):
|
def get_photo(email):
|
||||||
'''
|
"""
|
||||||
Fetch photo from Gravatar, given an email address
|
Fetch photo from Gravatar, given an email address
|
||||||
'''
|
"""
|
||||||
hash_object = hashlib.new('md5')
|
hash_object = hashlib.new("md5")
|
||||||
hash_object.update(email.lower().encode('utf-8'))
|
hash_object.update(email.lower().encode("utf-8"))
|
||||||
thumbnail_url = 'https://secure.gravatar.com/avatar/' + \
|
thumbnail_url = (
|
||||||
hash_object.hexdigest() + '?s=%i&d=404' % AVATAR_MAX_SIZE
|
"https://secure.gravatar.com/avatar/"
|
||||||
image_url = 'https://secure.gravatar.com/avatar/' + hash_object.hexdigest(
|
+ hash_object.hexdigest()
|
||||||
) + '?s=512&d=404'
|
+ "?s=%i&d=404" % AVATAR_MAX_SIZE
|
||||||
|
)
|
||||||
|
image_url = (
|
||||||
|
"https://secure.gravatar.com/avatar/" + hash_object.hexdigest() + "?s=512&d=404"
|
||||||
|
)
|
||||||
|
|
||||||
# Will redirect to the public profile URL if it exists
|
# Will redirect to the public profile URL if it exists
|
||||||
service_url = 'http://www.gravatar.com/' + hash_object.hexdigest()
|
service_url = "http://www.gravatar.com/" + hash_object.hexdigest()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
urlopen(image_url, timeout=URL_TIMEOUT)
|
urlopen(image_url, timeout=URL_TIMEOUT)
|
||||||
except HTTPError as exc:
|
except HTTPError as exc:
|
||||||
if exc.code != 404 and exc.code != 503:
|
if exc.code != 404 and exc.code != 503:
|
||||||
print( # pragma: no cover
|
print( # pragma: no cover
|
||||||
'Gravatar fetch failed with an unexpected %s HTTP error' %
|
"Gravatar fetch failed with an unexpected %s HTTP error" % exc.code
|
||||||
exc.code)
|
)
|
||||||
return False
|
return False
|
||||||
except URLError as exc: # pragma: no cover
|
except URLError as exc: # pragma: no cover
|
||||||
print(
|
print(
|
||||||
'Gravatar fetch failed with URL error: %s' %
|
"Gravatar fetch failed with URL error: %s" % exc.reason
|
||||||
exc.reason) # pragma: no cover
|
) # pragma: no cover
|
||||||
return False # pragma: no cover
|
return False # pragma: no cover
|
||||||
except SSLError as exc: # pragma: no cover
|
except SSLError as exc: # pragma: no cover
|
||||||
print(
|
print(
|
||||||
'Gravatar fetch failed with SSL error: %s' %
|
"Gravatar fetch failed with SSL error: %s" % exc.reason
|
||||||
exc.reason) # pragma: no cover
|
) # pragma: no cover
|
||||||
return False # pragma: no cover
|
return False # pragma: no cover
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'thumbnail_url': thumbnail_url,
|
"thumbnail_url": thumbnail_url,
|
||||||
'image_url': image_url,
|
"image_url": image_url,
|
||||||
'width': AVATAR_MAX_SIZE,
|
"width": AVATAR_MAX_SIZE,
|
||||||
'height': AVATAR_MAX_SIZE,
|
"height": AVATAR_MAX_SIZE,
|
||||||
'service_url': service_url,
|
"service_url": service_url,
|
||||||
'service_name': 'Gravatar'
|
"service_name": "Gravatar",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from django.db import models
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.urls import reverse_lazy, reverse
|
from django.urls import reverse_lazy, reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from django.contrib.auth.views import LoginView
|
|||||||
from django.contrib.auth.views import (
|
from django.contrib.auth.views import (
|
||||||
PasswordResetView as PasswordResetViewOriginal,
|
PasswordResetView as PasswordResetViewOriginal,
|
||||||
)
|
)
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
from django.urls import reverse_lazy, reverse
|
from django.urls import reverse_lazy, reverse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Django settings for ivatar project.
|
Django settings for ivatar project.
|
||||||
"""
|
"""
|
||||||
@@ -6,7 +7,7 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
log_level = logging.DEBUG # pylint: disable=invalid-name
|
log_level = logging.DEBUG # pylint: disable=invalid-name
|
||||||
logger = logging.getLogger('ivatar') # pylint: disable=invalid-name
|
logger = logging.getLogger("ivatar") # pylint: disable=invalid-name
|
||||||
logger.setLevel(log_level)
|
logger.setLevel(log_level)
|
||||||
|
|
||||||
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
|
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||||
@@ -14,7 +15,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = '=v(+-^t#ahv^a&&e)uf36g8algj$d1@6ou^w(r0@%)#8mlc*zk'
|
SECRET_KEY = "=v(+-^t#ahv^a&&e)uf36g8algj$d1@6ou^w(r0@%)#8mlc*zk"
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
@@ -25,52 +26,52 @@ ALLOWED_HOSTS = []
|
|||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'ivatar.urls'
|
ROOT_URLCONF = "ivatar.urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [],
|
"DIRS": [],
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.request',
|
"django.template.context_processors.request",
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'ivatar.wsgi.application'
|
WSGI_APPLICATION = "ivatar.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +81,16 @@ DATABASES = {
|
|||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', # noqa
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", # noqa
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', # noqa
|
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", # noqa
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', # noqa
|
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", # noqa
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', # noqa
|
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", # noqa
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -97,9 +98,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/2.0/topics/i18n/
|
# https://docs.djangoproject.com/en/2.0/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = "en-us"
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
TIME_ZONE = "UTC"
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
@@ -109,15 +110,10 @@ USE_TZ = True
|
|||||||
|
|
||||||
|
|
||||||
# Static files configuration (esp. req. during dev.)
|
# Static files configuration (esp. req. during dev.)
|
||||||
PROJECT_ROOT = os.path.abspath(
|
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||||
os.path.join(
|
STATIC_URL = "/static/"
|
||||||
os.path.dirname(__file__),
|
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||||
os.pardir
|
|
||||||
)
|
|
||||||
)
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
from config import * # pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import
|
from config import * # pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Test various other parts of ivatar/libravatar in order
|
Test various other parts of ivatar/libravatar in order
|
||||||
to increase the overall test coverage. Test in here, didn't
|
to increase the overall test coverage. Test in here, didn't
|
||||||
fit anywhere else.
|
fit anywhere else.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@@ -12,35 +13,35 @@ from ivatar.ivataraccount.models import pil_format, UserPreference
|
|||||||
|
|
||||||
|
|
||||||
class Tester(TestCase):
|
class Tester(TestCase):
|
||||||
'''
|
"""
|
||||||
Main test class
|
Main test class
|
||||||
'''
|
"""
|
||||||
|
|
||||||
user = None
|
user = None
|
||||||
username = random_string()
|
username = random_string()
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
'''
|
"""
|
||||||
Prepare tests.
|
Prepare tests.
|
||||||
- Create user
|
- Create user
|
||||||
'''
|
"""
|
||||||
self.user = User.objects.create_user(
|
self.user = User.objects.create_user(
|
||||||
username=self.username,
|
username=self.username,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_pil_format(self):
|
def test_pil_format(self):
|
||||||
'''
|
"""
|
||||||
Test pil format function
|
Test pil format function
|
||||||
'''
|
"""
|
||||||
self.assertEqual(pil_format('jpg'), 'JPEG')
|
self.assertEqual(pil_format("jpg"), "JPEG")
|
||||||
self.assertEqual(pil_format('jpeg'), 'JPEG')
|
self.assertEqual(pil_format("jpeg"), "JPEG")
|
||||||
self.assertEqual(pil_format('png'), 'PNG')
|
self.assertEqual(pil_format("png"), "PNG")
|
||||||
self.assertEqual(pil_format('gif'), 'GIF')
|
self.assertEqual(pil_format("gif"), "GIF")
|
||||||
self.assertEqual(pil_format('abc'), None)
|
self.assertEqual(pil_format("abc"), None)
|
||||||
|
|
||||||
def test_userprefs_str(self):
|
def test_userprefs_str(self):
|
||||||
'''
|
"""
|
||||||
Test if str representation of UserPreferences is as expected
|
Test if str representation of UserPreferences is as expected
|
||||||
'''
|
"""
|
||||||
up = UserPreference(theme='default', user=self.user)
|
up = UserPreference(theme="default", user=self.user)
|
||||||
print(up)
|
print(up)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Test our views in ivatar.ivataraccount.views and ivatar.views
|
Test our views in ivatar.ivataraccount.views and ivatar.views
|
||||||
'''
|
"""
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
import os
|
import os
|
||||||
import django
|
import django
|
||||||
@@ -11,33 +12,34 @@ from django.contrib.auth.models import User
|
|||||||
|
|
||||||
from ivatar.utils import random_string
|
from ivatar.utils import random_string
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings'
|
os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
|
|
||||||
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||||
'''
|
"""
|
||||||
Main test class
|
Main test class
|
||||||
'''
|
"""
|
||||||
|
|
||||||
client = Client()
|
client = Client()
|
||||||
user = None
|
user = None
|
||||||
username = random_string()
|
username = random_string()
|
||||||
password = random_string()
|
password = random_string()
|
||||||
email = '%s@%s.%s' % (username, random_string(), random_string(2))
|
email = "%s@%s.%s" % (username, random_string(), random_string(2))
|
||||||
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
||||||
openid = 'http://%s.%s.%s/' % (username, random_string(), 'org')
|
openid = "http://%s.%s.%s/" % (username, random_string(), "org")
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
'''
|
"""
|
||||||
Login as user
|
Login as user
|
||||||
'''
|
"""
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
'''
|
"""
|
||||||
Prepare for tests.
|
Prepare for tests.
|
||||||
- Create user
|
- Create user
|
||||||
'''
|
"""
|
||||||
self.user = User.objects.create_user(
|
self.user = User.objects.create_user(
|
||||||
username=self.username,
|
username=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
@@ -47,19 +49,19 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"""
|
"""
|
||||||
Test contact page
|
Test contact page
|
||||||
"""
|
"""
|
||||||
response = self.client.get(reverse('contact'))
|
response = self.client.get(reverse("contact"))
|
||||||
self.assertEqual(response.status_code, 200, 'no 200 ok?')
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
|
|
||||||
def test_description_page(self):
|
def test_description_page(self):
|
||||||
"""
|
"""
|
||||||
Test description page
|
Test description page
|
||||||
"""
|
"""
|
||||||
response = self.client.get(reverse('description'))
|
response = self.client.get(reverse("description"))
|
||||||
self.assertEqual(response.status_code, 200, 'no 200 ok?')
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
|
|
||||||
def test_security_page(self):
|
def test_security_page(self):
|
||||||
"""
|
"""
|
||||||
Test security page
|
Test security page
|
||||||
"""
|
"""
|
||||||
response = self.client.get(reverse('security'))
|
response = self.client.get(reverse("security"))
|
||||||
self.assertEqual(response.status_code, 200, 'no 200 ok?')
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Test our utils from ivatar.utils
|
Test our utils from ivatar.utils
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
@@ -8,18 +9,18 @@ from ivatar.utils import openid_variations
|
|||||||
|
|
||||||
|
|
||||||
class Tester(TestCase):
|
class Tester(TestCase):
|
||||||
'''
|
"""
|
||||||
Main test class
|
Main test class
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def test_openid_variations(self):
|
def test_openid_variations(self):
|
||||||
'''
|
"""
|
||||||
Test if the OpenID variation "generator" does the correct thing
|
Test if the OpenID variation "generator" does the correct thing
|
||||||
'''
|
"""
|
||||||
openid0 = 'http://user.url/'
|
openid0 = "http://user.url/"
|
||||||
openid1 = 'http://user.url'
|
openid1 = "http://user.url"
|
||||||
openid2 = 'https://user.url/'
|
openid2 = "https://user.url/"
|
||||||
openid3 = 'https://user.url'
|
openid3 = "https://user.url"
|
||||||
|
|
||||||
# First variation
|
# First variation
|
||||||
self.assertEqual(openid_variations(openid0)[0], openid0)
|
self.assertEqual(openid_variations(openid0)[0], openid0)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Test our views in ivatar.ivataraccount.views and ivatar.views
|
Test our views in ivatar.ivataraccount.views and ivatar.views
|
||||||
'''
|
"""
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
import os
|
import os
|
||||||
import django
|
import django
|
||||||
@@ -10,33 +11,34 @@ from django.contrib.auth.models import User
|
|||||||
|
|
||||||
from ivatar.utils import random_string
|
from ivatar.utils import random_string
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings'
|
os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
|
|
||||||
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||||
'''
|
"""
|
||||||
Main test class
|
Main test class
|
||||||
'''
|
"""
|
||||||
|
|
||||||
client = Client()
|
client = Client()
|
||||||
user = None
|
user = None
|
||||||
username = random_string()
|
username = random_string()
|
||||||
password = random_string()
|
password = random_string()
|
||||||
email = '%s@%s.%s' % (username, random_string(), random_string(2))
|
email = "%s@%s.%s" % (username, random_string(), random_string(2))
|
||||||
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
||||||
openid = 'http://%s.%s.%s/' % (username, random_string(), 'org')
|
openid = "http://%s.%s.%s/" % (username, random_string(), "org")
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
'''
|
"""
|
||||||
Login as user
|
Login as user
|
||||||
'''
|
"""
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
'''
|
"""
|
||||||
Prepare for tests.
|
Prepare for tests.
|
||||||
- Create user
|
- Create user
|
||||||
'''
|
"""
|
||||||
self.user = User.objects.create_user(
|
self.user = User.objects.create_user(
|
||||||
username=self.username,
|
username=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
@@ -46,8 +48,9 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"""
|
"""
|
||||||
Test incorrect digest
|
Test incorrect digest
|
||||||
"""
|
"""
|
||||||
response = self.client.get('/avatar/%s' % 'x'*65, follow=True)
|
response = self.client.get("/avatar/%s" % "x" * 65, follow=True)
|
||||||
self.assertRedirects(
|
self.assertRedirects(
|
||||||
response=response,
|
response=response,
|
||||||
expected_url='/static/img/deadbeef.png',
|
expected_url="/static/img/deadbeef.png",
|
||||||
msg_prefix='Why does an invalid hash not redirect to deadbeef?')
|
msg_prefix="Why does an invalid hash not redirect to deadbeef?",
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Unit tests for WSGI
|
Unit tests for WSGI
|
||||||
'''
|
"""
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import django
|
import django
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings'
|
|
||||||
|
os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
|
|
||||||
class TestCase(unittest.TestCase):
|
class TestCase(unittest.TestCase):
|
||||||
'''
|
"""
|
||||||
Simple testcase to see if WSGI loads correctly
|
Simple testcase to see if WSGI loads correctly
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def test_run_wsgi(self):
|
def test_run_wsgi(self):
|
||||||
'''
|
"""
|
||||||
Run wsgi import
|
Run wsgi import
|
||||||
'''
|
"""
|
||||||
import ivatar.wsgi # pylint: disable=import-outside-toplevel
|
import ivatar.wsgi # pylint: disable=import-outside-toplevel
|
||||||
self.assertEqual(ivatar.wsgi.application.__class__,
|
|
||||||
django.core.handlers.wsgi.WSGIHandler)
|
self.assertEqual(
|
||||||
|
ivatar.wsgi.application.__class__, django.core.handlers.wsgi.WSGIHandler
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Classes for our ivatar.tools.forms
|
Classes for our ivatar.tools.forms
|
||||||
'''
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.forms.utils import ErrorList
|
from django.forms.utils import ErrorList
|
||||||
|
|
||||||
@@ -12,45 +13,40 @@ from ivatar.settings import MIN_LENGTH_EMAIL, MAX_LENGTH_EMAIL
|
|||||||
|
|
||||||
|
|
||||||
class CheckDomainForm(forms.Form):
|
class CheckDomainForm(forms.Form):
|
||||||
'''
|
"""
|
||||||
Form handling domain check
|
Form handling domain check
|
||||||
'''
|
"""
|
||||||
|
|
||||||
domain = forms.CharField(
|
domain = forms.CharField(
|
||||||
label=_('Domain'),
|
label=_("Domain"),
|
||||||
required=True,
|
required=True,
|
||||||
error_messages={
|
error_messages={"required": _("Cannot check without a domain name.")},
|
||||||
'required':
|
|
||||||
_('Cannot check without a domain name.')
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CheckForm(forms.Form):
|
class CheckForm(forms.Form):
|
||||||
'''
|
"""
|
||||||
Form handling check
|
Form handling check
|
||||||
'''
|
"""
|
||||||
|
|
||||||
mail = forms.EmailField(
|
mail = forms.EmailField(
|
||||||
label=_('E-Mail'),
|
label=_("E-Mail"),
|
||||||
required=False,
|
required=False,
|
||||||
min_length=MIN_LENGTH_EMAIL,
|
min_length=MIN_LENGTH_EMAIL,
|
||||||
max_length=MAX_LENGTH_EMAIL,
|
max_length=MAX_LENGTH_EMAIL,
|
||||||
error_messages={
|
error_messages={"required": _("Cannot check without a domain name.")},
|
||||||
'required':
|
)
|
||||||
_('Cannot check without a domain name.')
|
|
||||||
})
|
|
||||||
|
|
||||||
openid = forms.CharField(
|
openid = forms.CharField(
|
||||||
label=_('OpenID'),
|
label=_("OpenID"),
|
||||||
required=False,
|
required=False,
|
||||||
min_length=MIN_LENGTH_URL,
|
min_length=MIN_LENGTH_URL,
|
||||||
max_length=MAX_LENGTH_URL,
|
max_length=MAX_LENGTH_URL,
|
||||||
error_messages={
|
error_messages={"required": _("Cannot check without an openid name.")},
|
||||||
'required':
|
)
|
||||||
_('Cannot check without an openid name.')
|
|
||||||
})
|
|
||||||
|
|
||||||
size = forms.IntegerField(
|
size = forms.IntegerField(
|
||||||
label=_('Size'),
|
label=_("Size"),
|
||||||
initial=80,
|
initial=80,
|
||||||
min_value=5,
|
min_value=5,
|
||||||
max_value=AVATAR_MAX_SIZE,
|
max_value=AVATAR_MAX_SIZE,
|
||||||
@@ -58,24 +54,24 @@ class CheckForm(forms.Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
default_opt = forms.ChoiceField(
|
default_opt = forms.ChoiceField(
|
||||||
label=_('Default'),
|
label=_("Default"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.RadioSelect,
|
widget=forms.RadioSelect,
|
||||||
choices=[
|
choices=[
|
||||||
('retro', _('Retro style (similar to GitHub)')),
|
("retro", _("Retro style (similar to GitHub)")),
|
||||||
('robohash', _('Roboter style')),
|
("robohash", _("Roboter style")),
|
||||||
('pagan', _('Retro adventure character')),
|
("pagan", _("Retro adventure character")),
|
||||||
('wavatar', _('Wavatar style')),
|
("wavatar", _("Wavatar style")),
|
||||||
('monsterid', _('Monster style')),
|
("monsterid", _("Monster style")),
|
||||||
('identicon', _('Identicon style')),
|
("identicon", _("Identicon style")),
|
||||||
('mm', _('Mystery man')),
|
("mm", _("Mystery man")),
|
||||||
('mmng', _('Mystery man NextGen')),
|
("mmng", _("Mystery man NextGen")),
|
||||||
('none', _('None')),
|
("none", _("None")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
default_url = forms.URLField(
|
default_url = forms.URLField(
|
||||||
label=_('Default URL'),
|
label=_("Default URL"),
|
||||||
min_length=1,
|
min_length=1,
|
||||||
max_length=MAX_LENGTH_URL,
|
max_length=MAX_LENGTH_URL,
|
||||||
required=False,
|
required=False,
|
||||||
@@ -83,28 +79,27 @@ class CheckForm(forms.Form):
|
|||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.cleaned_data = super().clean()
|
self.cleaned_data = super().clean()
|
||||||
mail = self.cleaned_data.get('mail')
|
mail = self.cleaned_data.get("mail")
|
||||||
openid = self.cleaned_data.get('openid')
|
openid = self.cleaned_data.get("openid")
|
||||||
default_url = self.cleaned_data.get('default_url')
|
default_url = self.cleaned_data.get("default_url")
|
||||||
default_opt = self.cleaned_data.get('default_opt')
|
default_opt = self.cleaned_data.get("default_opt")
|
||||||
if default_url and default_opt and default_opt != 'none':
|
if default_url and default_opt and default_opt != "none":
|
||||||
if not 'default_url' in self._errors:
|
if "default_url" not in self._errors:
|
||||||
self._errors['default_url'] = ErrorList()
|
self._errors["default_url"] = ErrorList()
|
||||||
if not 'default_opt' in self._errors:
|
if "default_opt" not in self._errors:
|
||||||
self._errors['default_opt'] = ErrorList()
|
self._errors["default_opt"] = ErrorList()
|
||||||
|
|
||||||
errstring = _('Only default URL OR default keyword may be specified')
|
errstring = _("Only default URL OR default keyword may be specified")
|
||||||
self._errors['default_url'].append(errstring)
|
self._errors["default_url"].append(errstring)
|
||||||
self._errors['default_opt'].append(errstring)
|
self._errors["default_opt"].append(errstring)
|
||||||
if not mail and not openid:
|
if not mail and not openid:
|
||||||
raise ValidationError(_('Either OpenID or mail must be specified'))
|
raise ValidationError(_("Either OpenID or mail must be specified"))
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
def clean_openid(self):
|
def clean_openid(self):
|
||||||
data = self.cleaned_data['openid']
|
data = self.cleaned_data["openid"]
|
||||||
return data.lower()
|
return data.lower()
|
||||||
|
|
||||||
def clean_mail(self):
|
def clean_mail(self):
|
||||||
data = self.cleaned_data['mail']
|
data = self.cleaned_data["mail"]
|
||||||
print(data)
|
|
||||||
return data.lower()
|
return data.lower()
|
||||||
|
|||||||
@@ -1,57 +1,48 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Test our views in ivatar.ivataraccount.views and ivatar.views
|
Test our views in ivatar.ivataraccount.views and ivatar.views
|
||||||
'''
|
"""
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
from urllib.parse import urlsplit
|
|
||||||
from io import BytesIO
|
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import django
|
import django
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth import authenticate
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
from libravatar import libravatar_url
|
os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings'
|
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
# pylint: disable=wrong-import-position
|
||||||
from ivatar import settings
|
|
||||||
from ivatar.ivataraccount.forms import MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
|
|
||||||
from ivatar.ivataraccount.models import Photo, ConfirmedOpenId
|
|
||||||
from ivatar.utils import random_string
|
from ivatar.utils import random_string
|
||||||
|
|
||||||
# pylint: enable=wrong-import-position
|
# pylint: enable=wrong-import-position
|
||||||
|
|
||||||
|
|
||||||
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||||
'''
|
"""
|
||||||
Main test class
|
Main test class
|
||||||
'''
|
"""
|
||||||
|
|
||||||
client = Client()
|
client = Client()
|
||||||
user = None
|
user = None
|
||||||
username = random_string()
|
username = random_string()
|
||||||
password = random_string()
|
password = random_string()
|
||||||
email = '%s@%s.%s' % (username, random_string(), random_string(2))
|
email = "%s@%s.%s" % (username, random_string(), random_string(2))
|
||||||
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
||||||
openid = 'http://%s.%s.%s/' % (username, random_string(), 'org')
|
openid = "http://%s.%s.%s/" % (username, random_string(), "org")
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
'''
|
"""
|
||||||
Login as user
|
Login as user
|
||||||
'''
|
"""
|
||||||
self.client.login(username=self.username, password=self.password)
|
self.client.login(username=self.username, password=self.password)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
'''
|
"""
|
||||||
Prepare for tests.
|
Prepare for tests.
|
||||||
- Create user
|
- Create user
|
||||||
'''
|
"""
|
||||||
self.user = User.objects.create_user(
|
self.user = User.objects.create_user(
|
||||||
username=self.username,
|
username=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
@@ -61,12 +52,12 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"""
|
"""
|
||||||
Test check page
|
Test check page
|
||||||
"""
|
"""
|
||||||
response = self.client.get(reverse('tools_check'))
|
response = self.client.get(reverse("tools_check"))
|
||||||
self.assertEqual(response.status_code, 200, 'no 200 ok?')
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
|
|
||||||
def test_check_domain(self):
|
def test_check_domain(self):
|
||||||
"""
|
"""
|
||||||
Test check domain page
|
Test check domain page
|
||||||
"""
|
"""
|
||||||
response = self.client.get(reverse('tools_check_domain'))
|
response = self.client.get(reverse("tools_check_domain"))
|
||||||
self.assertEqual(response.status_code, 200, 'no 200 ok?')
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
ivatar/tools URL configuration
|
ivatar/tools URL configuration
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from .views import CheckView, CheckDomainView
|
from .views import CheckView, CheckDomainView
|
||||||
|
|
||||||
urlpatterns = [ # pylint: disable=invalid-name
|
urlpatterns = [ # pylint: disable=invalid-name
|
||||||
url('check/', CheckView.as_view(), name='tools_check'),
|
url("check/", CheckView.as_view(), name="tools_check"),
|
||||||
url('check_domain/', CheckDomainView.as_view(), name='tools_check_domain'),
|
url("check_domain/", CheckDomainView.as_view(), name="tools_check_domain"),
|
||||||
url('check_domain$', CheckDomainView.as_view(), name='tools_check_domain'),
|
url("check_domain$", CheckDomainView.as_view(), name="tools_check_domain"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
View classes for ivatar/tools/
|
View classes for ivatar/tools/
|
||||||
'''
|
"""
|
||||||
from socket import inet_ntop, AF_INET6
|
from socket import inet_ntop, AF_INET6
|
||||||
import hashlib
|
import hashlib
|
||||||
import random
|
import random
|
||||||
@@ -16,49 +17,59 @@ from libravatar import SECURE_BASE_URL as LIBRAVATAR_SECURE_BASE_URL
|
|||||||
from libravatar import BASE_URL as LIBRAVATAR_BASE_URL
|
from libravatar import BASE_URL as LIBRAVATAR_BASE_URL
|
||||||
|
|
||||||
from ivatar.settings import SECURE_BASE_URL, BASE_URL
|
from ivatar.settings import SECURE_BASE_URL, BASE_URL
|
||||||
from .forms import CheckDomainForm, CheckForm # pylint: disable=relative-beyond-top-level
|
from .forms import (
|
||||||
|
CheckDomainForm,
|
||||||
|
CheckForm,
|
||||||
|
) # pylint: disable=relative-beyond-top-level
|
||||||
|
|
||||||
|
|
||||||
class CheckDomainView(FormView):
|
class CheckDomainView(FormView):
|
||||||
'''
|
"""
|
||||||
View class for checking a domain
|
View class for checking a domain
|
||||||
'''
|
"""
|
||||||
template_name = 'check_domain.html'
|
|
||||||
|
template_name = "check_domain.html"
|
||||||
form_class = CheckDomainForm
|
form_class = CheckDomainForm
|
||||||
success_url = reverse('tools_check_domain')
|
success_url = reverse("tools_check_domain")
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
result = {}
|
result = {}
|
||||||
super().form_valid(form)
|
super().form_valid(form)
|
||||||
domain = form.cleaned_data['domain']
|
domain = form.cleaned_data["domain"]
|
||||||
result['avatar_server_http'] = lookup_avatar_server(domain, False)
|
result["avatar_server_http"] = lookup_avatar_server(domain, False)
|
||||||
if result['avatar_server_http']:
|
if result["avatar_server_http"]:
|
||||||
result['avatar_server_http_ipv4'] = lookup_ip_address(
|
result["avatar_server_http_ipv4"] = lookup_ip_address(
|
||||||
result['avatar_server_http'],
|
result["avatar_server_http"], False
|
||||||
False)
|
)
|
||||||
result['avatar_server_http_ipv6'] = lookup_ip_address(
|
result["avatar_server_http_ipv6"] = lookup_ip_address(
|
||||||
result['avatar_server_http'],
|
result["avatar_server_http"], True
|
||||||
True)
|
)
|
||||||
result['avatar_server_https'] = lookup_avatar_server(domain, True)
|
result["avatar_server_https"] = lookup_avatar_server(domain, True)
|
||||||
if result['avatar_server_https']:
|
if result["avatar_server_https"]:
|
||||||
result['avatar_server_https_ipv4'] = lookup_ip_address(
|
result["avatar_server_https_ipv4"] = lookup_ip_address(
|
||||||
result['avatar_server_https'],
|
result["avatar_server_https"], False
|
||||||
False)
|
)
|
||||||
result['avatar_server_https_ipv6'] = lookup_ip_address(
|
result["avatar_server_https_ipv6"] = lookup_ip_address(
|
||||||
result['avatar_server_https'],
|
result["avatar_server_https"], True
|
||||||
True)
|
)
|
||||||
return render(self.request, self.template_name, {
|
return render(
|
||||||
'form': form,
|
self.request,
|
||||||
'result': result,
|
self.template_name,
|
||||||
})
|
{
|
||||||
|
"form": form,
|
||||||
|
"result": result,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CheckView(FormView):
|
class CheckView(FormView):
|
||||||
'''
|
"""
|
||||||
View class for checking an e-mail or openid address
|
View class for checking an e-mail or openid address
|
||||||
'''
|
"""
|
||||||
template_name = 'check.html'
|
|
||||||
|
template_name = "check.html"
|
||||||
form_class = CheckForm
|
form_class = CheckForm
|
||||||
success_url = reverse('tools_check')
|
success_url = reverse("tools_check")
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
mailurl = None
|
mailurl = None
|
||||||
@@ -73,82 +84,88 @@ class CheckView(FormView):
|
|||||||
|
|
||||||
super().form_valid(form)
|
super().form_valid(form)
|
||||||
|
|
||||||
if form.cleaned_data['default_url']:
|
if form.cleaned_data["default_url"]:
|
||||||
default_url = form.cleaned_data['default_url']
|
default_url = form.cleaned_data["default_url"]
|
||||||
elif form.cleaned_data['default_opt'] and form.cleaned_data['default_opt'] != 'none':
|
elif (
|
||||||
default_url = form.cleaned_data['default_opt']
|
form.cleaned_data["default_opt"]
|
||||||
|
and form.cleaned_data["default_opt"] != "none"
|
||||||
|
):
|
||||||
|
default_url = form.cleaned_data["default_opt"]
|
||||||
else:
|
else:
|
||||||
default_url = None
|
default_url = None
|
||||||
|
|
||||||
if 'size' in form.cleaned_data:
|
if "size" in form.cleaned_data:
|
||||||
size = form.cleaned_data['size']
|
size = form.cleaned_data["size"]
|
||||||
if form.cleaned_data['mail']:
|
if form.cleaned_data["mail"]:
|
||||||
mailurl = libravatar_url(
|
mailurl = libravatar_url(
|
||||||
email=form.cleaned_data['mail'],
|
email=form.cleaned_data["mail"], size=size, default=default_url
|
||||||
size=size,
|
)
|
||||||
default=default_url)
|
|
||||||
mailurl = mailurl.replace(LIBRAVATAR_BASE_URL, BASE_URL)
|
mailurl = mailurl.replace(LIBRAVATAR_BASE_URL, BASE_URL)
|
||||||
mailurl_secure = libravatar_url(
|
mailurl_secure = libravatar_url(
|
||||||
email=form.cleaned_data['mail'],
|
email=form.cleaned_data["mail"],
|
||||||
size=size,
|
size=size,
|
||||||
https=True,
|
https=True,
|
||||||
default=default_url)
|
default=default_url,
|
||||||
|
)
|
||||||
mailurl_secure = mailurl_secure.replace(
|
mailurl_secure = mailurl_secure.replace(
|
||||||
LIBRAVATAR_SECURE_BASE_URL,
|
LIBRAVATAR_SECURE_BASE_URL, SECURE_BASE_URL
|
||||||
SECURE_BASE_URL)
|
)
|
||||||
mail_hash = parse_user_identity(
|
mail_hash = parse_user_identity(
|
||||||
email=form.cleaned_data['mail'],
|
email=form.cleaned_data["mail"], openid=None
|
||||||
openid=None)[0]
|
)[0]
|
||||||
hash_obj = hashlib.new('sha256')
|
hash_obj = hashlib.new("sha256")
|
||||||
hash_obj.update(form.cleaned_data['mail'].encode('utf-8'))
|
hash_obj.update(form.cleaned_data["mail"].encode("utf-8"))
|
||||||
mail_hash256 = hash_obj.hexdigest()
|
mail_hash256 = hash_obj.hexdigest()
|
||||||
mailurl_secure_256 = mailurl_secure.replace(
|
mailurl_secure_256 = mailurl_secure.replace(mail_hash, mail_hash256)
|
||||||
mail_hash,
|
if form.cleaned_data["openid"]:
|
||||||
mail_hash256)
|
if not form.cleaned_data["openid"].startswith(
|
||||||
if form.cleaned_data['openid']:
|
"http://"
|
||||||
if not form.cleaned_data['openid'].startswith('http://') and \
|
) and not form.cleaned_data["openid"].startswith("https://"):
|
||||||
not form.cleaned_data['openid'].startswith('https://'):
|
form.cleaned_data["openid"] = "http://%s" % form.cleaned_data["openid"]
|
||||||
form.cleaned_data['openid'] = 'http://%s' % form.cleaned_data['openid']
|
|
||||||
openidurl = libravatar_url(
|
openidurl = libravatar_url(
|
||||||
openid=form.cleaned_data['openid'],
|
openid=form.cleaned_data["openid"], size=size, default=default_url
|
||||||
size=size,
|
)
|
||||||
default=default_url)
|
|
||||||
openidurl = openidurl.replace(LIBRAVATAR_BASE_URL, BASE_URL)
|
openidurl = openidurl.replace(LIBRAVATAR_BASE_URL, BASE_URL)
|
||||||
openidurl_secure = libravatar_url(
|
openidurl_secure = libravatar_url(
|
||||||
openid=form.cleaned_data['openid'],
|
openid=form.cleaned_data["openid"],
|
||||||
size=size,
|
size=size,
|
||||||
https=True,
|
https=True,
|
||||||
default=default_url)
|
default=default_url,
|
||||||
|
)
|
||||||
openidurl_secure = openidurl_secure.replace(
|
openidurl_secure = openidurl_secure.replace(
|
||||||
LIBRAVATAR_SECURE_BASE_URL,
|
LIBRAVATAR_SECURE_BASE_URL, SECURE_BASE_URL
|
||||||
SECURE_BASE_URL)
|
)
|
||||||
openid_hash = parse_user_identity(
|
openid_hash = parse_user_identity(
|
||||||
openid=form.cleaned_data['openid'],
|
openid=form.cleaned_data["openid"], email=None
|
||||||
email=None)[0]
|
)[0]
|
||||||
|
|
||||||
return render(self.request, self.template_name, {
|
return render(
|
||||||
'form': form,
|
self.request,
|
||||||
'mailurl': mailurl,
|
self.template_name,
|
||||||
'openidurl': openidurl,
|
{
|
||||||
'mailurl_secure': mailurl_secure,
|
"form": form,
|
||||||
'mailurl_secure_256': mailurl_secure_256,
|
"mailurl": mailurl,
|
||||||
'openidurl_secure': openidurl_secure,
|
"openidurl": openidurl,
|
||||||
'mail_hash': mail_hash,
|
"mailurl_secure": mailurl_secure,
|
||||||
'mail_hash256': mail_hash256,
|
"mailurl_secure_256": mailurl_secure_256,
|
||||||
'openid_hash': openid_hash,
|
"openidurl_secure": openidurl_secure,
|
||||||
'size': size,
|
"mail_hash": mail_hash,
|
||||||
})
|
"mail_hash256": mail_hash256,
|
||||||
|
"openid_hash": openid_hash,
|
||||||
|
"size": size,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def lookup_avatar_server(domain, https):
|
def lookup_avatar_server(domain, https):
|
||||||
'''
|
"""
|
||||||
Extract the avatar server from an SRV record in the DNS zone
|
Extract the avatar server from an SRV record in the DNS zone
|
||||||
|
|
||||||
The SRV records should look like this:
|
The SRV records should look like this:
|
||||||
|
|
||||||
_avatars._tcp.example.com. IN SRV 0 0 80 avatars.example.com
|
_avatars._tcp.example.com. IN SRV 0 0 80 avatars.example.com
|
||||||
_avatars-sec._tcp.example.com. IN SRV 0 0 443 avatars.example.com
|
_avatars-sec._tcp.example.com. IN SRV 0 0 443 avatars.example.com
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if domain and len(domain) > 60:
|
if domain and len(domain) > 60:
|
||||||
domain = domain[:60]
|
domain = domain[:60]
|
||||||
@@ -161,27 +178,35 @@ def lookup_avatar_server(domain, https):
|
|||||||
|
|
||||||
DNS.DiscoverNameServers()
|
DNS.DiscoverNameServers()
|
||||||
try:
|
try:
|
||||||
dns_request = DNS.Request(name=service_name, qtype='SRV').req()
|
dns_request = DNS.Request(name=service_name, qtype="SRV").req()
|
||||||
except DNS.DNSError as message:
|
except DNS.DNSError as message:
|
||||||
print("DNS Error: %s (%s)" % (message, domain))
|
print("DNS Error: %s (%s)" % (message, domain))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if dns_request.header['status'] == 'NXDOMAIN':
|
if dns_request.header["status"] == "NXDOMAIN":
|
||||||
# Not an error, but no point in going any further
|
# Not an error, but no point in going any further
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if dns_request.header['status'] != 'NOERROR':
|
if dns_request.header["status"] != "NOERROR":
|
||||||
print("DNS Error: status=%s (%s)" % (dns_request.header['status'], domain))
|
print("DNS Error: status=%s (%s)" % (dns_request.header["status"], domain))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
records = []
|
records = []
|
||||||
for answer in dns_request.answers:
|
for answer in dns_request.answers:
|
||||||
if ('data' not in answer) or (not answer['data']) or \
|
if (
|
||||||
(not answer['typename']) or (answer['typename'] != 'SRV'):
|
("data" not in answer)
|
||||||
|
or (not answer["data"])
|
||||||
|
or (not answer["typename"])
|
||||||
|
or (answer["typename"] != "SRV")
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
record = {'priority': int(answer['data'][0]), 'weight': int(answer['data'][1]),
|
record = {
|
||||||
'port': int(answer['data'][2]), 'target': answer['data'][3]}
|
"priority": int(answer["data"][0]),
|
||||||
|
"weight": int(answer["data"][1]),
|
||||||
|
"port": int(answer["data"][2]),
|
||||||
|
"target": answer["data"][3],
|
||||||
|
}
|
||||||
|
|
||||||
records.append(record)
|
records.append(record)
|
||||||
|
|
||||||
@@ -194,38 +219,38 @@ def lookup_avatar_server(domain, https):
|
|||||||
|
|
||||||
|
|
||||||
def srv_hostname(records):
|
def srv_hostname(records):
|
||||||
'''
|
"""
|
||||||
Return the right (target, port) pair from a list of SRV records.
|
Return the right (target, port) pair from a list of SRV records.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if len(records) < 1:
|
if len(records) < 1:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
if len(records) == 1:
|
if len(records) == 1:
|
||||||
ret = records[0]
|
ret = records[0]
|
||||||
return (ret['target'], ret['port'])
|
return (ret["target"], ret["port"])
|
||||||
|
|
||||||
# Keep only the servers in the top priority
|
# Keep only the servers in the top priority
|
||||||
priority_records = []
|
priority_records = []
|
||||||
total_weight = 0
|
total_weight = 0
|
||||||
top_priority = records[0]['priority'] # highest priority = lowest number
|
top_priority = records[0]["priority"] # highest priority = lowest number
|
||||||
|
|
||||||
for ret in records:
|
for ret in records:
|
||||||
if ret['priority'] > top_priority:
|
if ret["priority"] > top_priority:
|
||||||
# ignore the record (ret has lower priority)
|
# ignore the record (ret has lower priority)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Take care - this if is only a if, if the above if
|
# Take care - this if is only a if, if the above if
|
||||||
# uses continue at the end. else it should be an elsif
|
# uses continue at the end. else it should be an elsif
|
||||||
if ret['priority'] < top_priority:
|
if ret["priority"] < top_priority:
|
||||||
# reset the aretay (ret has higher priority)
|
# reset the aretay (ret has higher priority)
|
||||||
top_priority = ret['priority']
|
top_priority = ret["priority"]
|
||||||
total_weight = 0
|
total_weight = 0
|
||||||
priority_records = []
|
priority_records = []
|
||||||
|
|
||||||
total_weight += ret['weight']
|
total_weight += ret["weight"]
|
||||||
|
|
||||||
if ret['weight'] > 0:
|
if ret["weight"] > 0:
|
||||||
priority_records.append((total_weight, ret))
|
priority_records.append((total_weight, ret))
|
||||||
else:
|
else:
|
||||||
# zero-weigth elements must come first
|
# zero-weigth elements must come first
|
||||||
@@ -233,7 +258,7 @@ def srv_hostname(records):
|
|||||||
|
|
||||||
if len(priority_records) == 1:
|
if len(priority_records) == 1:
|
||||||
unused, ret = priority_records[0] # pylint: disable=unused-variable
|
unused, ret = priority_records[0] # pylint: disable=unused-variable
|
||||||
return (ret['target'], ret['port'])
|
return (ret["target"], ret["port"])
|
||||||
|
|
||||||
# Select first record according to RFC2782 weight ordering algorithm (page 3)
|
# Select first record according to RFC2782 weight ordering algorithm (page 3)
|
||||||
random_number = random.randint(0, total_weight)
|
random_number = random.randint(0, total_weight)
|
||||||
@@ -242,9 +267,9 @@ def srv_hostname(records):
|
|||||||
weighted_index, ret = record
|
weighted_index, ret = record
|
||||||
|
|
||||||
if weighted_index >= random_number:
|
if weighted_index >= random_number:
|
||||||
return (ret['target'], ret['port'])
|
return (ret["target"], ret["port"])
|
||||||
|
|
||||||
print('There is something wrong with our SRV weight ordering algorithm')
|
print("There is something wrong with our SRV weight ordering algorithm")
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
|
|
||||||
@@ -263,19 +288,21 @@ def lookup_ip_address(hostname, ipv6):
|
|||||||
print("DNS Error: %s (%s)" % (message, hostname))
|
print("DNS Error: %s (%s)" % (message, hostname))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if dns_request.header['status'] != 'NOERROR':
|
if dns_request.header["status"] != "NOERROR":
|
||||||
print("DNS Error: status=%s (%s)" % (dns_request.header['status'], hostname))
|
print("DNS Error: status=%s (%s)" % (dns_request.header["status"], hostname))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for answer in dns_request.answers:
|
for answer in dns_request.answers:
|
||||||
if ('data' not in answer) or (not answer['data']):
|
if ("data" not in answer) or (not answer["data"]):
|
||||||
continue
|
continue
|
||||||
if (ipv6 and answer['typename'] != 'AAAA') or (not ipv6 and answer['typename'] != 'A'):
|
if (ipv6 and answer["typename"] != "AAAA") or (
|
||||||
|
not ipv6 and answer["typename"] != "A"
|
||||||
|
):
|
||||||
continue # skip CNAME records
|
continue # skip CNAME records
|
||||||
|
|
||||||
if ipv6:
|
if ipv6:
|
||||||
return inet_ntop(AF_INET6, answer['data'])
|
return inet_ntop(AF_INET6, answer["data"])
|
||||||
|
|
||||||
return answer['data']
|
return answer["data"]
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
ivatar URL configuration
|
ivatar URL configuration
|
||||||
'''
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
@@ -10,33 +11,48 @@ from ivatar import settings
|
|||||||
from .views import AvatarImageView, GravatarProxyView, StatsView
|
from .views import AvatarImageView, GravatarProxyView, StatsView
|
||||||
|
|
||||||
urlpatterns = [ # pylint: disable=invalid-name
|
urlpatterns = [ # pylint: disable=invalid-name
|
||||||
path('admin/', admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path('i18n/', include('django.conf.urls.i18n')),
|
path("i18n/", include("django.conf.urls.i18n")),
|
||||||
url('openid/', include('django_openid_auth.urls')),
|
url("openid/", include("django_openid_auth.urls")),
|
||||||
url('tools/', include('ivatar.tools.urls')),
|
url("tools/", include("ivatar.tools.urls")),
|
||||||
|
url(r"avatar/(?P<digest>\w{64})", AvatarImageView.as_view(), name="avatar_view"),
|
||||||
|
url(r"avatar/(?P<digest>\w{32})", AvatarImageView.as_view(), name="avatar_view"),
|
||||||
|
url(r"avatar/$", AvatarImageView.as_view(), name="avatar_view"),
|
||||||
url(
|
url(
|
||||||
r'avatar/(?P<digest>\w{64})',
|
r"avatar/(?P<digest>\w*)",
|
||||||
AvatarImageView.as_view(), name='avatar_view'),
|
RedirectView.as_view(url="/static/img/deadbeef.png"),
|
||||||
|
name="invalid_hash",
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'avatar/(?P<digest>\w{32})',
|
r"gravatarproxy/(?P<digest>\w*)",
|
||||||
AvatarImageView.as_view(), name='avatar_view'),
|
GravatarProxyView.as_view(),
|
||||||
url(r'avatar/$', AvatarImageView.as_view(), name='avatar_view'),
|
name="gravatarproxy",
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'avatar/(?P<digest>\w*)',
|
"description/",
|
||||||
RedirectView.as_view(url='/static/img/deadbeef.png'), name='invalid_hash'),
|
TemplateView.as_view(template_name="description.html"),
|
||||||
url(
|
name="description",
|
||||||
r'gravatarproxy/(?P<digest>\w*)',
|
),
|
||||||
GravatarProxyView.as_view(), name='gravatarproxy'),
|
|
||||||
url('description/', TemplateView.as_view(template_name='description.html'), name='description'),
|
|
||||||
# The following two are TODO TODO TODO TODO TODO
|
# The following two are TODO TODO TODO TODO TODO
|
||||||
url('run_your_own/',
|
url(
|
||||||
TemplateView.as_view(template_name='run_your_own.html'), name='run_your_own'),
|
"run_your_own/",
|
||||||
url('features/', TemplateView.as_view(template_name='features.html'), name='features'),
|
TemplateView.as_view(template_name="run_your_own.html"),
|
||||||
url('security/', TemplateView.as_view(template_name='security.html'), name='security'),
|
name="run_your_own",
|
||||||
url('privacy/', TemplateView.as_view(template_name='privacy.html'), name='privacy'),
|
),
|
||||||
url('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'),
|
url(
|
||||||
path('talk_to_us/', RedirectView.as_view(url='/contact'), name='talk_to_us'),
|
"features/",
|
||||||
url('stats/', StatsView.as_view(), name='stats'),
|
TemplateView.as_view(template_name="features.html"),
|
||||||
|
name="features",
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
"security/",
|
||||||
|
TemplateView.as_view(template_name="security.html"),
|
||||||
|
name="security",
|
||||||
|
),
|
||||||
|
url("privacy/", TemplateView.as_view(template_name="privacy.html"), name="privacy"),
|
||||||
|
url("contact/", TemplateView.as_view(template_name="contact.html"), name="contact"),
|
||||||
|
path("talk_to_us/", RedirectView.as_view(url="/contact"), name="talk_to_us"),
|
||||||
|
url("stats/", StatsView.as_view(), name="stats"),
|
||||||
]
|
]
|
||||||
|
|
||||||
MAINTENANCE = False
|
MAINTENANCE = False
|
||||||
@@ -47,11 +63,15 @@ except: # pylint: disable=bare-except
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if MAINTENANCE:
|
if MAINTENANCE:
|
||||||
urlpatterns.append(url('', TemplateView.as_view(template_name='maintenance.html'), name='home'))
|
urlpatterns.append(
|
||||||
urlpatterns.insert(3, url('accounts/', RedirectView.as_view(url='/')))
|
url("", TemplateView.as_view(template_name="maintenance.html"), name="home")
|
||||||
|
)
|
||||||
|
urlpatterns.insert(3, url("accounts/", RedirectView.as_view(url="/")))
|
||||||
else:
|
else:
|
||||||
urlpatterns.append(url('', TemplateView.as_view(template_name='home.html'), name='home'))
|
urlpatterns.append(
|
||||||
urlpatterns.insert(3, url('accounts/', include('ivatar.ivataraccount.urls')))
|
url("", TemplateView.as_view(template_name="home.html"), name="home")
|
||||||
|
)
|
||||||
|
urlpatterns.insert(3, url("accounts/", include("ivatar.ivataraccount.urls")))
|
||||||
|
|
||||||
|
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|||||||
@@ -1,50 +1,56 @@
|
|||||||
'''
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Simple module providing reusable random_string function
|
Simple module providing reusable random_string function
|
||||||
'''
|
"""
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
|
||||||
def random_string(length=10):
|
def random_string(length=10):
|
||||||
'''
|
"""
|
||||||
Return some random string with default length 10
|
Return some random string with default length 10
|
||||||
'''
|
"""
|
||||||
return ''.join(random.SystemRandom().choice(
|
return "".join(
|
||||||
string.ascii_lowercase + string.digits) for _ in range(length))
|
random.SystemRandom().choice(string.ascii_lowercase + string.digits)
|
||||||
|
for _ in range(length)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def openid_variations(openid):
|
def openid_variations(openid):
|
||||||
'''
|
"""
|
||||||
Return the various OpenID variations, ALWAYS in the same order:
|
Return the various OpenID variations, ALWAYS in the same order:
|
||||||
- http w/ trailing slash
|
- http w/ trailing slash
|
||||||
- http w/o trailing slash
|
- http w/o trailing slash
|
||||||
- https w/ trailing slash
|
- https w/ trailing slash
|
||||||
- https w/o trailing slash
|
- https w/o trailing slash
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# Make the 'base' version: http w/ trailing slash
|
# Make the 'base' version: http w/ trailing slash
|
||||||
if openid.startswith('https://'):
|
if openid.startswith("https://"):
|
||||||
openid = openid.replace('https://', 'http://')
|
openid = openid.replace("https://", "http://")
|
||||||
if openid[-1] != '/':
|
if openid[-1] != "/":
|
||||||
openid = openid + '/'
|
openid = openid + "/"
|
||||||
|
|
||||||
# http w/o trailing slash
|
# http w/o trailing slash
|
||||||
var1 = openid[0:-1]
|
var1 = openid[0:-1]
|
||||||
var2 = openid.replace('http://', 'https://')
|
var2 = openid.replace("http://", "https://")
|
||||||
var3 = var2[0:-1]
|
var3 = var2[0:-1]
|
||||||
return (openid, var1, var2, var3)
|
return (openid, var1, var2, var3)
|
||||||
|
|
||||||
def mm_ng(idhash, size=80, add_red=0, add_green=0, add_blue=0): #pylint: disable=too-many-locals
|
|
||||||
'''
|
def mm_ng(
|
||||||
|
idhash, size=80, add_red=0, add_green=0, add_blue=0
|
||||||
|
): # pylint: disable=too-many-locals
|
||||||
|
"""
|
||||||
Return an MM (mystery man) image, based on a given hash
|
Return an MM (mystery man) image, based on a given hash
|
||||||
add some red, green or blue, if specified
|
add some red, green or blue, if specified
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# Make sure the lightest bg color we paint is e0, else
|
# Make sure the lightest bg color we paint is e0, else
|
||||||
# we do not see the MM any more
|
# we do not see the MM any more
|
||||||
if idhash[0] == 'f':
|
if idhash[0] == "f":
|
||||||
idhash = 'e0'
|
idhash = "e0"
|
||||||
|
|
||||||
# How large is the circle?
|
# How large is the circle?
|
||||||
circlesize = size * 0.6
|
circlesize = size * 0.6
|
||||||
@@ -62,44 +68,47 @@ def mm_ng(idhash, size=80, add_red=0, add_green=0, add_blue=0): #pylint: disabl
|
|||||||
blue = idhash[0:2]
|
blue = idhash[0:2]
|
||||||
|
|
||||||
# Add some red (i/a) and make sure it's not over 255
|
# Add some red (i/a) and make sure it's not over 255
|
||||||
red = hex(int(red, 16)+add_red).replace('0x', '')
|
red = hex(int(red, 16) + add_red).replace("0x", "")
|
||||||
if int(red, 16) > 255:
|
if int(red, 16) > 255:
|
||||||
red = 'ff'
|
red = "ff"
|
||||||
if len(red) == 1:
|
if len(red) == 1:
|
||||||
red = '0%s' % red
|
red = "0%s" % red
|
||||||
|
|
||||||
# Add some green (i/a) and make sure it's not over 255
|
# Add some green (i/a) and make sure it's not over 255
|
||||||
green = hex(int(green, 16)+add_green).replace('0x', '')
|
green = hex(int(green, 16) + add_green).replace("0x", "")
|
||||||
if int(green, 16) > 255:
|
if int(green, 16) > 255:
|
||||||
green = 'ff'
|
green = "ff"
|
||||||
if len(green) == 1:
|
if len(green) == 1:
|
||||||
green = '0%s' % green
|
green = "0%s" % green
|
||||||
|
|
||||||
# Add some blue (i/a) and make sure it's not over 255
|
# Add some blue (i/a) and make sure it's not over 255
|
||||||
blue = hex(int(blue, 16)+add_blue).replace('0x', '')
|
blue = hex(int(blue, 16) + add_blue).replace("0x", "")
|
||||||
if int(blue, 16) > 255:
|
if int(blue, 16) > 255:
|
||||||
blue = 'ff'
|
blue = "ff"
|
||||||
if len(blue) == 1:
|
if len(blue) == 1:
|
||||||
blue = '0%s' % blue
|
blue = "0%s" % blue
|
||||||
|
|
||||||
# Assemable the bg color "string" in webnotation. Eg. '#d3d3d3'
|
# Assemable the bg color "string" in webnotation. Eg. '#d3d3d3'
|
||||||
bg_color = '#' + red + green + blue
|
bg_color = "#" + red + green + blue
|
||||||
|
|
||||||
# Image
|
# Image
|
||||||
image = Image.new('RGB', (size, size))
|
image = Image.new("RGB", (size, size))
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
# Draw background
|
# Draw background
|
||||||
draw.rectangle(((0, 0), (size, size)), fill=bg_color)
|
draw.rectangle(((0, 0), (size, size)), fill=bg_color)
|
||||||
|
|
||||||
# Draw MMs head
|
# Draw MMs head
|
||||||
draw.ellipse((start_x, start_y, end_x, end_y), fill='white')
|
draw.ellipse((start_x, start_y, end_x, end_y), fill="white")
|
||||||
|
|
||||||
# Draw MMs 'body'
|
# Draw MMs 'body'
|
||||||
draw.polygon((
|
draw.polygon(
|
||||||
|
(
|
||||||
(start_x + circlesize / 2, size / 2.5),
|
(start_x + circlesize / 2, size / 2.5),
|
||||||
(size * 0.15, size),
|
(size * 0.15, size),
|
||||||
(size-size*0.15, size)),
|
(size - size * 0.15, size),
|
||||||
fill='white')
|
),
|
||||||
|
fill="white",
|
||||||
|
)
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from django.http import HttpResponse, HttpResponseRedirect
|
|||||||
from django.http import HttpResponseNotFound, JsonResponse
|
from django.http import HttpResponseNotFound, JsonResponse
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.cache import cache, caches
|
from django.core.cache import cache, caches
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
sys.stderr.buffer.write(b'%s' % bytes(os.environ.get("QUERY_STRING", "No Query String in url"), 'utf-8'))
|
sys.stderr.buffer.write(
|
||||||
|
b"%s" % bytes(os.environ.get("QUERY_STRING", "No Query String in url"), "utf-8")
|
||||||
|
)
|
||||||
|
|
||||||
link = 'https://www.libravatar.org/avatar/%s' % os.environ.get("QUERY_STRING", 'x'*32)
|
link = "https://www.libravatar.org/avatar/%s" % os.environ.get("QUERY_STRING", "x" * 32)
|
||||||
sys.stderr.buffer.write(b'%s' % bytes(link, 'utf-8'))
|
sys.stderr.buffer.write(b"%s" % bytes(link, "utf-8"))
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
with urllib.request.urlopen(link) as f:
|
with urllib.request.urlopen(link) as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
|
||||||
for header in f.headers._headers:
|
for header in f.headers._headers:
|
||||||
if header[0] == 'Content-Type':
|
if header[0] == "Content-Type":
|
||||||
sys.stdout.buffer.write(b"%s: %s\n\n" % (bytes(header[0], 'utf-8'), bytes(header[1], 'utf-8')))
|
sys.stdout.buffer.write(
|
||||||
|
b"%s: %s\n\n" % (bytes(header[0], "utf-8"), bytes(header[1], "utf-8"))
|
||||||
|
)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
autopep8
|
autopep8
|
||||||
bcrypt
|
bcrypt
|
||||||
defusedxml
|
defusedxml
|
||||||
Django < 4
|
Django < 4.0
|
||||||
django-anymail[mailgun]
|
django-anymail[mailgun]
|
||||||
django-auth-ldap
|
django-auth-ldap
|
||||||
django-bootstrap4
|
django-bootstrap4
|
||||||
|
|||||||
Reference in New Issue
Block a user