From 15c0f4fd793fc41d9da72cb52b9c2e14fb5c8348 Mon Sep 17 00:00:00 2001
From: John Doe
Date: Mon, 12 May 2025 16:37:30 +0200
Subject: [PATCH] Add lost password recovery
---
.gitignore | 3 +-
pywallter.py | 10 +-
static/blog.css | 206 ++++++++++++++++++++++++++
static/cover.css | 16 ++
static/theme.css | 4 +
static/up.css | 8 +-
templates/#_footer.html# | 5 +
templates/#new_article_blog.html# | 39 +++++
templates/_flash_msgs.html | 15 ++
templates/_footer.html | 7 +
templates/_head.html | 2 +-
templates/_js-core.html | 5 +
templates/_js-gallery.html | 1 +
templates/_js.html | 1 +
templates/_js_editor.html | 10 ++
templates/_nav_userlogin.html | 115 +++++++++------
templates/accueil.html | 5 +-
templates/blog.html | 114 +++------------
templates/edit_article.html | 40 +++++
templates/gallery.html | 4 -
templates/index_blog.html | 27 ++++
templates/list_articles.html | 44 ++++++
templates/lost_password.html | 53 +++++++
templates/mailbox.html | 4 +-
templates/new_article_blog.html | 40 +++++
templates/postedit.html | 11 +-
templates/profil.html | 2 +-
templates/up_list.html | 59 ++++++--
templates/up_squelette.html | 9 +-
templates/up_up.html | 8 +-
tools/databaseinit.py | 69 ++++++++-
tools/filesutils.py | 12 ++
tools/mailer.py | 65 +++++++++
tools/utils.py | 54 +++++--
views/blog.py | 215 ++++++++++++++++-----------
views/filesupload.py | 235 +++++++++++++++++++-----------
views/loginlogout.py | 69 +++++++--
views/profil.py | 110 +++++++++++++-
38 files changed, 1299 insertions(+), 397 deletions(-)
create mode 100644 static/blog.css
create mode 100644 templates/#_footer.html#
create mode 100644 templates/#new_article_blog.html#
create mode 100644 templates/_flash_msgs.html
create mode 100644 templates/_footer.html
create mode 100644 templates/_js-core.html
create mode 100644 templates/_js-gallery.html
create mode 100644 templates/_js_editor.html
create mode 100644 templates/edit_article.html
create mode 100644 templates/index_blog.html
create mode 100644 templates/list_articles.html
create mode 100644 templates/lost_password.html
create mode 100644 templates/new_article_blog.html
create mode 100644 tools/filesutils.py
create mode 100644 tools/mailer.py
diff --git a/.gitignore b/.gitignore
index 717ebd9..e86de7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ base.db
log.txt
config.py
users/
-
+sys
+*~
\ No newline at end of file
diff --git a/pywallter.py b/pywallter.py
index 7e93812..3e057d6 100755
--- a/pywallter.py
+++ b/pywallter.py
@@ -9,7 +9,7 @@ from flask_bcrypt import Bcrypt
from os import system
-from views.blog import postit
+from views.blog import blog
from views.filesupload import filesupload
from views.inscription import inscription
from views.profil import profil
@@ -31,7 +31,7 @@ if init_dir():
print ("Le repertoire des utilisateurs a été créer")
-#### Variables ####################################################################################
+#### Variables Globales #########################################################################
DOSSIER_PERSO= app.config['DOSSIER_APP']
@@ -39,7 +39,7 @@ DOSSIER_PERSO= app.config['DOSSIER_APP']
extensionimg = app.config['EXT_IMG']
MAIL_SERVER = app.config['MAIL_SERVER']
XMPP_SERVER = app.config['XMPP_SERVER']
-##################################################################################################
+#################################################################################################
xmpp_server_not_installed = system('whereis prosodyctl')
mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + \
@@ -59,7 +59,7 @@ if MAIL_SERVER and mail_server_not_installed :
app.register_blueprint(inscription)
-app.register_blueprint(postit)
+app.register_blueprint(blog)
app.register_blueprint(filesupload)
app.register_blueprint(profil)
app.register_blueprint(logs)
@@ -113,4 +113,4 @@ def create_app():
return app
if __name__ == '__main__' :
- app.run(host='127.0.0.1', port=8000, debug=False)
+ app.run(host='127.0.0.1', port=8000, debug=True)
diff --git a/static/blog.css b/static/blog.css
new file mode 100644
index 0000000..19bbf3f
--- /dev/null
+++ b/static/blog.css
@@ -0,0 +1,206 @@
+ /* kitoy */
+
+ :root
+ {
+ --color-text: #fdfdfddd;
+ --color-background: #202020;
+ }
+
+ html{
+ height: 100%;
+ width: 80%;
+ margin-left: 10%;
+
+ }
+
+
+ body {
+ color: var(--color-text);
+ background-color: var(--color-background);
+ }
+
+
+ hr {
+ color: var(--color-text);
+ }
+
+ * {-moz-box-sizing: border-box; box-sizing: border-box;}
+
+
+ a {
+ color: var(--color-text);
+ text-decoration: none;
+ }
+
+ .date {
+ margin-bottom: 0;
+ }
+
+ .slug {
+ //margin-left: 1rem;
+ text-align: left;
+ margin-bottom: 2rem;
+ }
+
+ .readmore {
+ text-align: right;
+ }
+
+ .readmore a {
+ color: var(--color-text);
+ text-decoration: underline;
+ }
+
+
+ .copyleft {
+ display:inline-block;
+ transform: rotate(180deg);
+ padding-bottom: -15px;
+ }
+
+ pre {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+
+
+ ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ }
+
+
+ .articles {
+ max-width: 80%;
+ margin-left: auto;
+ margin-right: auto;
+ margin-top: 5rem;
+ margin-bottom: 3rem;
+ padding:0.5em;
+ border: 7px double;
+ border-color: var(--text-color);
+ border-radius: 10px 10px 10px 10px;
+ line-height: 1.5;
+ letter-spacing: 0.1vw;
+ text-align: justify;
+ }
+
+ .articles ul {
+ list-style-type: disc;
+ margin: 5vw;
+ padding-top: 1vw;
+ padding-bottom: 1vw;
+ padding-left: 1.5vw;
+ }
+
+
+.titre {
+ text-align: center;
+ }
+
+
+.index {
+ text-align: left;
+}
+
+ .articles img {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ width: 50%;
+ margin-bottom: 2vw;
+
+ }
+
+ .articles .description {
+ font-weight: 300;
+ font-style: italic;
+ font-size: 1.5vw;
+ padding-bottom: 30px;
+ color: ;
+ }
+
+
+ .pagination a {
+ border: 1px solid;
+ border-color: $color_title;
+ padding: 3px;
+ font-size: 13px;
+ }
+
+ .center {
+ margin: auto;
+ width: 50%;
+ padding: 10px;
+ }
+
+ .contact{
+ text-align:center;
+ }
+
+ footer {
+ position: relative;
+ bottom:0px;
+ width: 100%;
+ text-align:center;
+ }
+
+ @media only screen and (max-width: 980px)
+ {
+
+ .articles .description {
+ font-size: 4vw;
+ }
+
+ .articles {
+ margin-left: 0;
+ position: relative;
+ font-size: 3.5vw;
+
+ }
+
+ footer {
+ font-size: 3vw;
+ }
+
+ .articles img {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ width: 80%;
+ }
+
+
+
+ }
+
+
+
+ @media only screen and (max-width: 980px){
+
+ html{
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ }
+ }
+
+ @media only screen and (max-width: 768px)
+ {
+ .h1 {
+ font-size: 5vw;
+ }
+
+ .icons img {
+ width: 7vw;
+ height: 7vw;
+ margin: 20px;
+ display:inline-block;
+ align-items:center;
+ }
+
+ }
diff --git a/static/cover.css b/static/cover.css
index 01a7c20..80887ab 100644
--- a/static/cover.css
+++ b/static/cover.css
@@ -40,6 +40,10 @@ body {
text-shadow: 0 1px 3px rgba(0,0,0,.5);
}
+.container {
+ margin-bottom: 5vw;
+}
+
a {
color: #428bca;
@@ -158,6 +162,17 @@ a:focus, a:hover {
border-bottom-color: #fff;
}
+
+/* Footer */
+
+footer {
+ position: bottom;
+ width: 100%;
+ bottom: 0;
+ text-align: center;
+}
+
+
@media (min-width: 768px) {
.masthead-brand {
float: left;
@@ -332,3 +347,4 @@ a:focus, a:hover {
.post-it h3 {
font-size: 0.9vw;
}
+
diff --git a/static/theme.css b/static/theme.css
index f544f0d..381e2c6 100644
--- a/static/theme.css
+++ b/static/theme.css
@@ -27,3 +27,7 @@ a:focus, a:hover {
text-decoration: underline;
}
+
+footer {
+ margin-top: 3em;
+}
diff --git a/static/up.css b/static/up.css
index 968a776..7495365 100644
--- a/static/up.css
+++ b/static/up.css
@@ -48,13 +48,13 @@
}
.succes p {
- background-color: #CDCBD0;
- color: #00A310;
+ background-color: #444;
+ color: #00C613;
}
.error p {
- background-color: #CDCBD0;
- color: #B80000;
+ background-color: #444;
+ color: #FF4A4A;
}
#majuscule {
diff --git a/templates/#_footer.html# b/templates/#_footer.html#
new file mode 100644
index 0000000..7b2da95
--- /dev/null
+++ b/templates/#_footer.html#
@@ -0,0 +1,5 @@
+
+
diff --git a/templates/#new_article_blog.html# b/templates/#new_article_blog.html#
new file mode 100644
index 0000000..edae829
--- /dev/null
+++ b/templates/#new_article_blog.html#
@@ -0,0 +1,39 @@
+{% extends 'up_squelette.html' %}
+
+
+{% block main %}
+
+
+
+
+
Hello {{ session['username'] }} !
+ Bienvenue sur la création d'un nouvel article de blog. Vous pouvez créer ou importer un article de blog ici,
+ vous avez le choix de le rendre publique dès sa création en cochant publique ou le laisser en privé
+ si vous souhaitez le modifier plus-tard avant sa publication.
+ Par défaut il est laissé en privé pour éviter les publications
+ accidentelles.
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/templates/_flash_msgs.html b/templates/_flash_msgs.html
new file mode 100644
index 0000000..e5b03d2
--- /dev/null
+++ b/templates/_flash_msgs.html
@@ -0,0 +1,15 @@
+
+ {# on affiche les messages d'erreur puis les messages de succes #}
+ {% for categorie in ['error', 'succes'] %}
+ {% with msgs = get_flashed_messages(category_filter=[categorie]) %}
+ {% if msgs %}
+
+ {% for m in msgs %}
+
{{ m|safe }}
+ {% endfor %}
+
+ {% endif %}
+ {% endwith %}
+ {% endfor %}
+
+
diff --git a/templates/_footer.html b/templates/_footer.html
new file mode 100644
index 0000000..89a19b5
--- /dev/null
+++ b/templates/_footer.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/templates/_head.html b/templates/_head.html
index fbb681f..5113315 100644
--- a/templates/_head.html
+++ b/templates/_head.html
@@ -15,5 +15,5 @@
-
+
diff --git a/templates/_js-core.html b/templates/_js-core.html
new file mode 100644
index 0000000..5d72254
--- /dev/null
+++ b/templates/_js-core.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/templates/_js-gallery.html b/templates/_js-gallery.html
new file mode 100644
index 0000000..64270b8
--- /dev/null
+++ b/templates/_js-gallery.html
@@ -0,0 +1 @@
+
diff --git a/templates/_js.html b/templates/_js.html
index 350d6de..8368800 100644
--- a/templates/_js.html
+++ b/templates/_js.html
@@ -3,6 +3,7 @@
+
diff --git a/templates/_js_editor.html b/templates/_js_editor.html
new file mode 100644
index 0000000..38a52ac
--- /dev/null
+++ b/templates/_js_editor.html
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/templates/_nav_userlogin.html b/templates/_nav_userlogin.html
index b6bb41d..24167dc 100644
--- a/templates/_nav_userlogin.html
+++ b/templates/_nav_userlogin.html
@@ -1,6 +1,5 @@
+
diff --git a/templates/accueil.html b/templates/accueil.html
index 2464caf..caf0de5 100644
--- a/templates/accueil.html
+++ b/templates/accueil.html
@@ -1,5 +1,5 @@
-
+
{% include '_head.html' %}
@@ -35,6 +35,7 @@
+ Mouarf j'ai perdu mon mot de passe
Login
@@ -49,6 +50,8 @@
{% endblock %}
+{% include '_footer.html' %}
+
{% include '_js.html' %}
diff --git a/templates/blog.html b/templates/blog.html
index 16fd4c1..a3fa05a 100644
--- a/templates/blog.html
+++ b/templates/blog.html
@@ -1,100 +1,24 @@
-{% extends 'up_squelette.html' %}
-
-
-{% block main %}
-
-
-
-
{% if fichiers %}
diff --git a/templates/index_blog.html b/templates/index_blog.html
new file mode 100644
index 0000000..7132a49
--- /dev/null
+++ b/templates/index_blog.html
@@ -0,0 +1,27 @@
+
+
+
+
Blog de {{ user }}
+
+
+
+
+
+
+
+ {% for post in posts %}
+
+
+
+
+ Publié le {{ post.time }}
+
+
+
+ {% endfor %}
+
+
+
diff --git a/templates/list_articles.html b/templates/list_articles.html
new file mode 100644
index 0000000..a59721d
--- /dev/null
+++ b/templates/list_articles.html
@@ -0,0 +1,44 @@
+{% extends 'up_squelette.html' %}
+
+
+{% block main %}
+
+
+
+
+
+
+
+
Vos articles de blog
+
+
+
+ Titre {{ nb_articles }}
+ Créé le :
+ Dernière modification
+ status
+
+
+
+
+
+
+
+ {% for article in list_posts %}
+
+ {{ article.title }}
+ {{ article.time }}
+ {{ article.last_updated }}
+ {{ article.status }}
+ Editer
+ Supprimer
+ Publier
+
+ {% endfor %}
+
+
+
+
+
+
+{% endblock %}
diff --git a/templates/lost_password.html b/templates/lost_password.html
new file mode 100644
index 0000000..506aae2
--- /dev/null
+++ b/templates/lost_password.html
@@ -0,0 +1,53 @@
+
+
+
+{% include '_head.html' %}
+
+
+{% block main %}
+
+
+
+
+
+
+
+
+
+
+
J'ai perdu mon mot de passe
+
+ Hé oui ca arrive à tout le monde... Il existe des gestionnaire des mots de passe pour éviter que t'arrives trop souvent. Tiens en voilà un par exemple et ca existe pour iphone et pour android
+
+
+
+
+ {% include '_flash_msgs.html' %}
+
+
+
+ Nom d'utilisateur
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% include '_footer.html' %}
+
+{% include '_js.html' %}
+
+
+
+
diff --git a/templates/mailbox.html b/templates/mailbox.html
index f06b510..c586ecf 100644
--- a/templates/mailbox.html
+++ b/templates/mailbox.html
@@ -19,8 +19,8 @@
+
+{% endblock %}
diff --git a/templates/postedit.html b/templates/postedit.html
index 9212b88..cfcb258 100644
--- a/templates/postedit.html
+++ b/templates/postedit.html
@@ -5,30 +5,31 @@
-
Vous pouvez modifier votre article. Actuellement seule la date de première édition sera publiée. Prochainement : intégration des edit'time dans la base de donnée.
+
Vous pouvez modifier votre article. Actuellement seule la date de première édition sera publiée. Prochainement : intégration de la date de mise à jour dans la base de donnée.
{{ oldpost[1] }}
+ Visibilité
{% if oldpost[2] == 'public' %}
Privé
-
Public
+
Publique
{% else %}
-
Privé
+
Privé
-
Public
+
Public
{% endif %}
- Publier
+ Mettre à jour
diff --git a/templates/profil.html b/templates/profil.html
index d1d48de..4808259 100644
--- a/templates/profil.html
+++ b/templates/profil.html
@@ -49,7 +49,7 @@
Age
Mail de secours
-
+
Envoyer
{# on affiche les messages d'erreur puis les messages de succes #}
diff --git a/templates/up_list.html b/templates/up_list.html
index a4c94a6..11d490d 100644
--- a/templates/up_list.html
+++ b/templates/up_list.html
@@ -1,34 +1,69 @@
{% extends 'up_squelette.html' %}
-{% include '_nav_userlogin.html' %}
{% block main %}
+
Fichiers privés (Seul les personnes connectées et l'administrateur de l'ordinateur peuvent les voirs)
+ {% if listFilesPrivate %}
- Fichier(s) {{ i }}
- Taille (en octets)
+ Fichier(s) {{ nb_pv }}
+ Taille (en Megaoctect)
- {% if listeFichiers %}
- {% for fichier in listeFichiers %}
+
+ {% for file in listFilesPrivate %}
- {{ fichier[0] }}
- {{ fichier[1] }}
- {{ fichier[2] }}
- Supprimer
-
+ {{ file[0] }}
+ {{ file[1] }}
+ {{ file[2] }}
+ Supprimer
+ Rendre Publique
+
{% endfor %}
- {% endif %}
-
+
+ {% else %}
+
Vous n'avez aucun fichiers privés
+ {% endif %}
+
+
+
+
Fichiers publics (Tout le monde peut les voirs)
+ {% if listFilesPublic %}
+
+
+
+
+ Fichier(s) {{ nb_pu }}
+ Taille (en Megaoctets)
+
+
+
+
+
+ {% for file in listFilesPublic %}
+
+ {{ file[0] }}
+ {{ file[1] }}
+ {{ file[2] }}
+ Supprimer
+ Rendre Privée
+
+ {% endfor %}
+
+
+ {% else %}
+
Vous n'avez aucun fichiers publics
+ {% endif %}
+
{% endblock %}
diff --git a/templates/up_squelette.html b/templates/up_squelette.html
index f61c8e6..7e38475 100644
--- a/templates/up_squelette.html
+++ b/templates/up_squelette.html
@@ -1,8 +1,12 @@
-
+
{% include '_head.html' %}
+
+
+
+
{% include '_nav_userlogin.html'%}
@@ -11,7 +15,10 @@
{% block main %}{% endblock %}
+{% include '_flash_msgs.html' %}
+{% include '_footer.html' %}
{% include '_js.html' %}
+
diff --git a/templates/up_up.html b/templates/up_up.html
index daed71e..6d9db84 100644
--- a/templates/up_up.html
+++ b/templates/up_up.html
@@ -1,10 +1,10 @@
-{% extends 'up_squelette.html' %}
+
{% include '_nav_userlogin.html' %}
-
+{% extends 'up_squelette.html' %}
{% block main %}
-
+
diff --git a/tools/databaseinit.py b/tools/databaseinit.py
index cd6c2b6..f622bba 100755
--- a/tools/databaseinit.py
+++ b/tools/databaseinit.py
@@ -29,7 +29,9 @@ def init_db():
prenom TEXT,
age TEXT,
website TEXT,
- Token CHAR(30),
+ blog_theme TEXT,
+ Token CHAR(64),
+ Lost_password_token CHAR(128),
invitations INTEGER DEFAULT (20),
Mail_rescue TEXT )
""")
@@ -47,10 +49,27 @@ def init_db():
status TEXT
)
""")
+
+ cursor.execute("""
+ CREATE TABLE IF NOT EXISTS Blog_posts(
+ title TEXT NOT NULL,
+ subtitle TEXT,
+ comments TEXT,
+ filename TEXT,
+ time TEXT,
+ last_updated TEXT,
+ category TEXT,
+ author TEXT,
+ status TEXT,
+ PRIMARY KEY(title, author)
+ )
+ """)
conn.commit()
+
+
cursor.execute("""select * from users""")
accounts = cursor.fetchall()
- # Si aucun account n'est crée on créé l'utilisateur
+ # Si aucun compte utilisateur existe on créé l'utilisateur
# pywallter qui permet la première inscription
if not(accounts) :
user = "pywallter"
@@ -73,12 +92,50 @@ def db_migrate():
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('users');""")
db_columns = cursor.fetchall()
- present = False
+ invitations_col = False
+ blog_theme_col = False
+ updated_col = False
+ lost_password_token_col = False
+
for col in db_columns:
if "invitations" == col[0]:
- present = True
+ invitations_col = True
+ if "Lost_password_token" == col[0]:
+ lost_password_token_col = True
+
- if not(present):
+ cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('Blog_posts');""")
+ db_columns = cursor.fetchall()
+ for col in db_columns:
+ if "blog_theme" == col[0]:
+ blog_theme_col = True
+ if "last_updated" == col[0]:
+ updated_col = True
+
+
+
+ if not(invitations_col):
cursor.execute("""ALTER TABLE users ADD COLUMN invitations INTEGER DEFAULT (20);""")
conn.commit()
- print ("Ajout du champ invitations")
+ print ("Ajout du champ invitations dans la table users")
+
+
+ if not(lost_password_token_col):
+ cursor.execute("""ALTER TABLE Users ADD COLUMN Lost_password_token CHAR(64);""")
+ conn.commit()
+ print ("Ajout du champ Lost_password_token dans la table Users")
+
+
+ if not(blog_theme_col):
+ cursor.execute("""ALTER TABLE Blog_posts ADD COLUMN blog_theme TEXT;""")
+ conn.commit()
+ print ("Ajout du champ blog_theme dans la table Blog")
+
+ if not(updated_col):
+ cursor.execute("""ALTER TABLE Blog_posts ADD COLUMN last_updated TEXT;""")
+ conn.commit()
+ print ("Ajout du champ updated dans la table BLog")
+
+
+
+ conn.close()
diff --git a/tools/filesutils.py b/tools/filesutils.py
new file mode 100644
index 0000000..53f3744
--- /dev/null
+++ b/tools/filesutils.py
@@ -0,0 +1,12 @@
+import os
+from math import floor
+
+def getFileSizeMo(filename): # Prend un nom de fichier en arguments renvoie la taille en Mo
+ tmp = os.path.getsize(filename)
+ size = floor (tmp / 1024) / 1000
+ return size
+
+def getFileSizeKo(filename): # Prend un nom de fichier en arguments renvoie la taille en Mo
+ tmp = os.path.getsize(filename)
+ size = floor(tmp / 1024)
+ return size
diff --git a/tools/mailer.py b/tools/mailer.py
new file mode 100644
index 0000000..e3f947f
--- /dev/null
+++ b/tools/mailer.py
@@ -0,0 +1,65 @@
+from flask import Flask
+import os, smtplib, ssl
+from email.message import EmailMessage
+
+
+app = Flask( 'pywallter' )
+app.config.from_pyfile('config.py')
+
+
+
+class Mailer:
+
+ def __init__(self):
+ self._smtp_server = app.config['SMTP_SERVER']
+ self._smtp_port = app.config['SMTP_PORT']
+ self._smtp_user = app.config['SMTP_USER']
+ self._smtp_passwd = app.config['SMTP_PASSWD']
+ self._sender_address = app.config['SENDER_ADDRESS']
+
+
+ def get_smtp_conf(self):
+ print ("Serveur SMTP: _smtp_server")
+
+ return self._smtp_server
+
+ def send_email(self, receiver_email, subject, message):
+
+ mail = EmailMessage()
+ mail['Subject'] = subject
+ mail['From'] = self._sender_address
+ mail['To'] = receiver_email
+ mail.set_content(message)
+
+ match self._smtp_port:
+ case "465":
+ self._send_ssl_mail(receiver_email, mail)
+ case "587":
+ self._send_starttls_mail(receiver_email, mail)
+ case "25":
+ with smtplib.SMTP(self._smtp_server, self._smtp_port) as server:
+ server.login(self._smtp_user, self._smtp_password)
+ server.sendmail(self._sender_address, receiver_email, mail.as_string())
+ case _:
+ print ("There are problem with mail port configuration ")
+
+
+ def _send_starttls_mail(self, receiver_email, mail):
+
+ context = ssl.create_default_context()
+ with smtplib.SMTP(self._smtp_server, self._smtp_port) as server:
+ server.ehlo() # Can be omitted
+ server.starttls(context=context)
+ server.ehlo() # Can be omitted
+ server.login(self._smtp_user, self._smtp_passwd)
+ server.sendmail(self._sender_address, receiver_email, mail.as_string())
+
+
+
+ def _send_ssl_mail(receiver_email, mail):
+
+ context = ssl.create_default_context()
+ with smtplib.SMTP(self._smtp_server, self._smtp_port) as server:
+
+ server.login(sender_email, password)
+ server.sendmail(self._sender_address, receiver_email, mail.as_string())
diff --git a/tools/utils.py b/tools/utils.py
index 87fa5ee..50ed7e5 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -55,9 +55,6 @@ def email_disp(email):
if alias:
if email in alias:
disp=False
-
-
-
else:
disp = False
@@ -72,16 +69,20 @@ def valid_passwd(password):
-def valid_token_register(token):
+def valid_token_register(token, token_type):
valid = True
print(token)
- if len(token) != 30:
+ if len(token) != 30 and len(token) != 64 :
valid = False
if valid:
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
- cursor.execute("""SELECT name, invitations FROM users where Token=?""", (token,))
+ match token_type:
+ case "Lost password":
+ cursor.execute("""SELECT name FROM users where Lost_password_token=?""", (token,))
+ case "Invitation":
+ cursor.execute("""SELECT name FROM users where token=?""", (token,))
tmp = cursor.fetchone()
conn.close()
print (tmp)
@@ -89,13 +90,42 @@ def valid_token_register(token):
valid = True
else:
valid = False
- print(valid)
+
return valid
-#Génère un token de 30 caratères aléatoires
-def gen_token():
- letters = random.choices(string.ascii_letters, k=20)
- digits = random.choices(string.digits, k=10)
- sample = ''.join(random.sample(digits + letters, 30))
+
+def get_user_by_token(token, token_type):
+
+
+ if len(token) != 30 and len(token) != 64:
+ user = "Invalid Token"
+
+
+ conn = sqlite3.connect(DATABASE)
+ cursor = conn.cursor()
+ match token_type:
+ case "Lost password":
+ cursor.execute("""SELECT name FROM users where Lost_password_token=?""", (token,))
+ case "Invitation":
+ cursor.execute("""SELECT name FROM users where token=?""", (token,))
+ user = cursor.fetchone()[0]
+ conn.close()
+ print ("User: " + user)
+
+ if not(user):
+ user = "Invalid Token"
+ return user
+
+
+
+#Génère un token de 30 ou 64 caratères aléatoires
+def gen_token(token_type):
+ letters = random.choices(string.ascii_letters, k=128)
+ digits = random.choices(string.digits, k=30)
+ match token_type:
+ case "Invitation":
+ sample = ''.join(random.sample(digits + letters, 30))
+ case "Lost password":
+ sample = ''.join(random.sample(digits + letters, 64))
return sample
diff --git a/views/blog.py b/views/blog.py
index 3f9035b..ffd77e9 100644
--- a/views/blog.py
+++ b/views/blog.py
@@ -1,140 +1,177 @@
# -*- coding: utf-8 -*-
-
from flask import Blueprint, render_template, session, redirect, url_for, request, flash, abort, Flask
import time
from markupsafe import escape
import sqlite3
from markdown import markdown
-postit = Blueprint('post-it', __name__, template_folder='templates')
+from tools.filesutils import getFileSizeKo
+import string
+
+blog = Blueprint('blog', __name__, template_folder='templates')
app = Flask( 'pywallter' )
app.config.from_pyfile('config.py')
-#### Variables ####################################################################################
-
-DOSSIER_PERSO= app.config['DOSSIER_APP']
-
+########################### Variables Globales #################################
extensionimg = app.config['EXT_IMG']
-
DATABASE = app.config['DATABASE']
-
BASE_URL = app.config['BASE_URL']
-##################################################################################################
+DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
+DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
+################################################################################
-
-
-
-@postit.route('/post-it/', methods=['GET', 'POST'])
-def racine_blog():
+@blog.route('/myblog/new-article/', methods=['GET', 'POST'])
+def new_article():
if 'username' in session:
- UTILISATEUR='%s'% escape(session['username'])
+ user = '%s'% escape(session['username'])
+ folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
if request.method == 'POST':
- title= request.form['title']
+ title = request.form['title']
+ subtitle = request.form['subtitle']
content = request.form['content']
- #category = request.form['category']
status = request.form['status']
- post_date = time.strftime("%A %d %B %Y %H:%M:%S")
- conn = sqlite3.connect(DATABASE) # Connexion la base de donne
- cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""INSERT INTO posts(title, content, time, author, status) VALUES(?, ?, ?, ?, ?)""", (title, content, post_date, UTILISATEUR, status)) # Insérer des valeurs
- conn.commit()
- cursor.execute("""SELECT avatar FROM users WHERE name=? """, (UTILISATEUR,))
- user_info = cursor.fetchone()
- cursor.execute("""SELECT title, content, time, author, status FROM posts where author=?""" , (UTILISATEUR,))
- list_posts = cursor.fetchall()
- conn.close()
- posts=list()
- id=0
- for post in list_posts:
- posts.append(dict(title=post[0], id_postit=id ,content=markdown(post[1]), time=post[2], author=post[3],status=post[4], avatar=user_info[0]))
- id=id+1
- return render_template('blog.html', posts=posts)
- else:
+ post_date = time.strftime("%d/%m/%Y %H:%M:%S")
+ filename = title.replace(" ", "_") + ".md"
+
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""SELECT avatar FROM users WHERE name=?""", (UTILISATEUR,))
- user_info = cursor.fetchone()
- cursor.execute("""SELECT title, content, time, author, status FROM posts WHERE author=?""" , (UTILISATEUR,))
- list_posts = cursor.fetchall()
- conn.close()
- posts=list()
- id=0
- for post in list_posts:
- posts.append(dict(title=post[0], id_postit=id, content=markdown(post[1]), time=post[2], author=post[3],status=post[4], avatar=user_info[0]))
- id=id+1
- return render_template('blog.html', section='Post-it', posts=posts)
+ cursor.execute("""INSERT INTO Blog_posts(title, subtitle, filename, time, author, status) VALUES(?, ?, ?, ?, ?, ?)""", (title, subtitle, filename, post_date, user, status)) # Insérer des valeurs
+ conn.commit()
+ ## On génère le fichiers markdown
+ with open(folder_blog + filename, 'w') as f:
+ f.write(content)
+
+ return redirect(url_for('blog.list_articles_blog'))
+ else:
+ return render_template('new_article_blog.html')
+ else:
+ return redirect(BASE_URL, code=401)
+
+@blog.route('/myblog/list-articles/', methods=['GET'])
+def list_articles_blog():
+ if 'username' in session:
+ user = '%s'% escape(session['username'])
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+ cursor.execute("""SELECT title, subtitle, time, last_updated, status FROM Blog_posts WHERE author=? """, (user,) )
+ list_posts=cursor.fetchall()
+ posts=list()
+ nb_articles=0
+ for post in list_posts:
+ posts.append(dict(title=post[0],
+ subtitle=post[1],
+ time=post[2],
+ last_updated=post[3],
+ status=post[4]))
+ nb_articles =+ 1
+
+ return render_template('list_articles.html',
+ section="Articles",
+ list_posts=posts,
+ nb_articles=nb_articles
+ )
else:
return redirect(BASE_URL, code=401)
-
-@postit.route('/delete/
/')
-def delete(title, time):
+@blog.route('/myblog/delete/')
+def delete(title):
if 'username' in session :
+ user='%s'% escape(session['username'])
+ folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
+ folder_blog_public = DOSSIER_PUBLIC + user + "/blog/articles/"
+ filename = title.replace(" ", "_")
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""DELETE FROM posts WHERE title=? AND time=?""", (title, time))
+ cursor.execute("""DELETE FROM Blog_posts WHERE title=? AND author=?""", (title, user))
conn.commit()
conn.close()
- return redirect(url_for('post-it.racine_blog'))
+ os.remove(folder_blog+filename+".md")
+ os.remove(folder_blog_public+filename+".html")
+ return redirect(url_for('blog.list_articles_blog'))
else:
return redirect(BASE_URL, code=401) # sinon on redirige vers login
-@postit.route('/edit//', methods=['GET', 'POST'])
-def edit(title, time):
+@blog.route('/myblog/edit/', methods=['GET', 'POST'])
+def edit(title):
if 'username' in session :
+ user='%s'% escape(session['username'])
+ filename = title.replace(" ", "_") + ".md"
+ folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
+
if request.method == 'POST' :
- newtitle = request.form['title']
+ subtitle = request.form['subtitle']
newcontent = request.form['content']
newstatus = request.form['status']
+ updated = time.strftime("%d/%m/%Y %H:%M:%S")
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
- cursor.execute("""UPDATE posts SET title=?, content=?, status=? WHERE title=? AND time=?""",
- (newtitle, newcontent, newstatus, title, time))
+ cursor.execute("""UPDATE Blog_posts SET subtitle=?, last_updated=?, status=? WHERE title=? AND author=?""", (subtitle, updated, newstatus, title, user))
conn.commit()
conn.close()
- return redirect(url_for('post-it.racine_blog'))
+
+ with open(folder_blog + filename, 'w') as f:
+ f.write(newcontent)
+
+
+ return redirect(url_for('blog.list_articles_blog'))
else:
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""SELECT title, content, status FROM posts WHERE title=? AND time =?""", (title, time))
+ cursor.execute("""SELECT title, subtitle, status FROM Blog_posts WHERE title=? AND author=?""", (title, user))
oldpost = cursor.fetchone()
conn.close()
- return render_template('postedit.html',
- section='Post-it',
- oldpost=oldpost)
- else:
-
- return redirect(BASE_URL, code=401)
-
-
-
-@postit.route('/postit/board', methods=['GET'])
-def viewsheet():
- if 'username' in session:
- conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
- cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""SELECT title, content, time, author, status FROM posts WHERE status='public' """)
- list_posts=cursor.fetchall()
- posts=list()
- id=0
- for post in list_posts:
- author = post[3]
- cursor.execute("""SELECT avatar FROM users WHERE name=?""", (author,))
-
- tmp = cursor.fetchone()
- if tmp != None :
- author_avatar = tmp[0]
- else:
- author_avatar = tmp
-
- posts.append(dict(title=post[0], id_postit=id, content=markdown(post[1]), time=post[2], author=post[3],status=post[4], avatar=author_avatar))
- id=id+1
- conn.close()
- return render_template('board.html', section='Post-it', posts=posts)
+ with open(folder_blog + filename, 'r') as f:
+ content = f.read()
+
+ return render_template('edit_article.html',
+ section='Post-it',
+ oldpost=oldpost,
+ content=content)
else:
return redirect(BASE_URL, code=401)
+
+@blog.route('/blog//', methods=['GET'])
+def view(username):
+ user = username
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+ cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE status='public' AND author=? """, (user,) )
+ list_posts=cursor.fetchall()
+ posts=list()
+ id=0
+
+ conn.close()
+ print (list_posts)
+ if list_posts != None:
+ for post in list_posts:
+ posts.append(dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
+ else:
+ return redirect(BASE_URL, code=404)
+
+
+ return render_template('index_blog.html', section='Blog', posts=posts, user=user)
+
+@blog.route('/blog//', methods=['GET'])
+def viewArticle(username, title):
+ folder_blog = DOSSIER_PERSO + username + "/blog/articles/"
+ filename = title.replace(" ", "_") + ".md"
+ user = username
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+ cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE author=? AND title=? """, (user, title) )
+ post = cursor.fetchone()
+ conn.close()
+ if post != None:
+ post_info = (dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
+ with open(folder_blog + filename, 'r') as f:
+ content_md = f.read()
+ content = markdown(content_md)
+ return render_template('blog.html', post_info=post_info, content=content)
+ else:
+ flash(u"Cet article n'existe pas", 'error');
+
diff --git a/views/filesupload.py b/views/filesupload.py
index bc3a067..80aeb3f 100644
--- a/views/filesupload.py
+++ b/views/filesupload.py
@@ -7,7 +7,8 @@ from PIL import Image
import time
import sqlite3
import os
-
+from shutil import move
+from tools.filesutils import getFileSizeMo
filesupload = Blueprint('filesupload', __name__, template_folder='templates')
@@ -17,69 +18,54 @@ app.config.from_pyfile('config.py')
#### Variables ####################################################################################
-DOSSIER_PERSO= app.config['DOSSIER_APP']
+DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
+DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
extensionimg = app.config['EXT_IMG']
-
DATABASE = app.config['DATABASE']
-
BASE_URL= app.config['BASE_URL']
##################################################################################################
+
@filesupload.route( '/filesupload/', methods=['GET', 'POST'])
def uploadfiles():
if 'username' in session :
- UTILISATEUR='%s'% escape(session['username'])
+ user = '%s'% escape(session['username'])
if request.method == 'POST' :
files = request.files.getlist('fic')
for f in files :
- if f: # On vérifie qu'un fichier a bien été envoyé
- nom = secure_filename(f.filename)
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom):
- flash(u'Fichier déjà existant, merci de spécifier un autre nom de fichier', 'error')
- else:
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom):
- flash(u'Image déjà existante, merci de spécifier un autre nom de fichier', 'error')
- else:
- file, ext = os.path.splitext(nom)
- if ext in extensionimg :
- f.save(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom)
- image=DOSSIER_PERSO + UTILISATEUR + '/images/' + nom
- with Image.open(image) as img :
- img.thumbnail((300,300))
- img.save( DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom )
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) :
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom):
- TIME=time.strftime("%A %d %B %Y %H:%M:%S")
- IP=request.environ['REMOTE_ADDR']
- CLIENT_PLATFORM=request.headers.get('User-Agent')
- log_file=os.path.join(DOSSIER_PERSO, UTILISATEUR, "log.txt")
- LOG=open(log_file, "a")
- LOG.write (TIME + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n')
- LOG.close()
- flash(u'Image envoyée et traitée avec succés', 'succes')
- else:
- flash(u'Échec lors du traitement de l\'image', 'error')
- return redirect(url_for('filesupload.uploadfiles'))
- else:
- flash(u'Éches lors de l\'envoi de l\'image', 'error')
- return redirect(url_for('filesupload.uploadfiles'))
- else:
- f.save(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom)
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom) :
- TIME=time.strftime("%A %d %B %Y %H:%M:%S")
- IP=request.environ['REMOTE_ADDR']
- CLIENT_PLATFORM=request.headers.get('User-Agent')
- LOG=open("log.txt", "a") # Ouvre fichier log.txt
- LOG.write (TIME + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n') # Écrit dans log
- LOG.close() # Ferme log.txt
- flash(u'Fichier envoyé avec succés', 'succes')
- #return redirect(url_for('filesupload.upload'))
- else:
- return redirect(url_for('filesupload.uploadfiles'))
- else:
- flash(u'Error : Vous avez oublié le fichier !', 'error')
- return redirect(url_for('filesupload.uploadfiles'))
+ nom = secure_filename(f.filename)
+ if os.path.isfile(DOSSIER_PERSO + user + '/files/' + nom) or os.path.isfile(DOSSIER_PERSO + user + '/images/' + nom):
+ flash(u'Un fichier avec le même nom existe déjà, merci de spécifier un autre nom de fichier', 'error')
+ else:
+ file, ext = os.path.splitext(nom)
+ if ext in extensionimg :
+ f.save(DOSSIER_PERSO + user + '/images/' + nom)
+ image = DOSSIER_PERSO + user + '/images/' + nom
+ with Image.open(image) as img :
+ img.thumbnail((300,300))
+ img.save( DOSSIER_PERSO + user + '/images/thumbnails/' + nom )
+ TIME=time.strftime("%A %d %B %Y %H:%M:%S")
+ IP=request.environ['REMOTE_ADDR']
+ CLIENT_PLATFORM=request.headers.get('User-Agent')
+ log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
+ LOG=open(log_file, "a")
+ LOG.write (TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n')
+ LOG.close()
+ flash(u'Image envoyée et traitée avec succés', 'succes')
+ else:
+ f.save(DOSSIER_PERSO + user + '/files/' + nom)
+ TIME=time.strftime("%A %d %B %Y %H:%M:%S")
+ IP=request.environ['REMOTE_ADDR']
+ CLIENT_PLATFORM=request.headers.get('User-Agent')
+ LOG=open("log.txt", "a") # Ouvre fichier log.txt
+ LOG.write (TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n') # Écrit dans log
+ LOG.close() # Ferme log.txt
+ flash(u'Fichier envoyé avec succés', 'succes')
+
+ else:
+ flash(u'Error : Vous avez oublié le fichier !', 'error')
+ return redirect(url_for('filesupload.uploadfiles'))
resp = make_response(render_template('up_up.html', section="Upload"))
resp.set_cookie('username', session['username'])
return resp
@@ -90,51 +76,122 @@ def uploadfiles():
@filesupload.route('/view/')
def list():
if 'username' in session :
- UTILISATEUR='%s'% escape(session['username'])
- i = 0
- fichiers = os.listdir(DOSSIER_PERSO + UTILISATEUR + '/files/')
- listeFichiers = []
- if fichiers:
- for fich in fichiers:
- i += 1
- size = os.path.getsize(DOSSIER_PERSO + UTILISATEUR + '/files/' + fich) # size = taille des fichiers
- listeFichiers.append([i, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
- return render_template('up_list.html',
- section="Files",
- size=size,
- i=i,
- listeFichiers=listeFichiers)
- else :
- flash(u'Aucun fichier uploadé ! Redirection vers Upload', 'error')
- return redirect(url_for('filesupload.uploadfiles'))
+ user = '%s'% escape(session['username'])
+ files_public = os.listdir(DOSSIER_PUBLIC + user + '/files')
+ files_private = os.listdir(DOSSIER_PERSO + user + '/files/')
+ listFilesPublic = []
+ listFilesPrivate = []
+ nb_pv = 0
+ if files_private:
+ for fich in files_private:
+ nb_pv += 1
+ size = getFileSizeMo(DOSSIER_PERSO + user + '/files/' + fich) # size = taille des fichiers
+ listFilesPrivate.append([nb_pv, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
+
+ nb_pu = 0
+ if files_public:
+ for fich in files_public:
+ nb_pu += 1
+ size = getFileSizeMo(DOSSIER_PUBLIC + user + '/files/' + fich) # size = taille des fichiers
+ listFilesPublic.append([nb_pu, fich, size])
+
+ return render_template('up_list.html',
+ section="Files",
+ size=size,
+ username=user,
+ nb_pv=nb_pv,
+ nb_pu=nb_pu,
+ listFilesPrivate=listFilesPrivate,
+ listFilesPublic=listFilesPublic)
+
else :
return redirect(BASE_URL, code=401)
-@filesupload.route('/myfiles/')
-def myfiles(filename):
+@filesupload.route('/myfiles//')
+def myfiles(username, filename):
if 'username' in session :
- UTILISATEUR='%s' % escape(session['username'])
+ user = '%s' % escape(session['username'])
return send_from_directory(
- os.path.join(DOSSIER_PERSO, UTILISATEUR, 'files'), filename )
+ os.path.join(DOSSIER_PERSO, username, 'files'), filename )
else :
return redirect(BASE_URL, code=401)
-@filesupload.route('/remove/')
-def remove(nom):
- if 'username' in session :
- UTILISATEUR='%s' % escape(session['username'])
- nom = secure_filename(nom)
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom): # si le fichier existe
- os.remove(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom) # on le supprime
- return redirect(url_for('filesupload.list', _external=True))
- else:
- if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom): # si le fichier existe
- os.remove(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom) # on le supprime
- os.remove(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) # on le supprime
- return redirect(url_for('gallery'))
- else:
- flash(u'Fichier {nom} inexistant.'.format(nom=nom), 'error')
- return redirect(url_for('filesupload.list', _external=True)) # sinon on redirige vers la liste, avec un message d'erreur
+@filesupload.route('/make_public/')
+def move_public(filename):
+ if 'username' in session:
+ user = '%s' % escape(session['username'])
+ src = os.path.join(DOSSIER_PERSO, user, 'files', filename)
+ dst = os.path.join(DOSSIER_PUBLIC, user, 'files/')
+ move (src, dst)
+ return redirect(url_for('filesupload.list', _external=True))
+ else:
+ return redirect(BASE_URL, code=401)
+@filesupload.route('/make_private/')
+def move_private(filename):
+ if 'username' in session:
+ user = '%s' % escape(session['username'])
+ src = os.path.join(DOSSIER_PUBLIC, user, 'files', filename)
+ dst = os.path.join(DOSSIER_PERSO, user, 'files/')
+ move (src, dst)
+ return redirect(url_for('filesupload.list', _external=True))
+ else:
+ return redirect(BASE_URL, code=401)
+
+
+@filesupload.route('/public//')
+def publicfiles(username, filename):
+ return send_from_directory(
+ os.path.join(DOSSIER_PUBLIC, username, 'files'), filename )
+
+
+@filesupload.route('/remove_privateFile/')
+def remove_privateFile(filename):
+ if 'username' in session :
+ user = '%s' % escape(session['username'])
+ filename = secure_filename(filename)
+ try:
+ os.remove(DOSSIER_PERSO + user + '/files/' + filename) # on le supprime
+ except FileNotFoundError:
+ flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
+ return redirect(url_for('filesupload.list', _external=True))
else :
return redirect(BASE_URL, code=401)
+
+@filesupload.route('/remove_privateImage/')
+def remove_privateImage(filename):
+ if 'username' in session :
+ user = '%s' % escape(session['username'])
+ filename = secure_filename(filename)
+ try:
+ os.remove(DOSSIER_PERSO + user + '/images/thumbnails/' + filename) # on le supprime
+ os.remove(DOSSIER_PERSO + user + '/images/' + filename) # on le supprime
+ except FileNotFoundError:
+ flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
+ return redirect(url_for('gallery'))
+
+
+@filesupload.route('/remove_publicFile/')
+def remove_publicFile(filename):
+ if 'username' in session :
+ user = '%s' % escape(session['username'])
+ filename = secure_filename(filename)
+ try:
+ os.remove(DOSSIER_PUBLIC + user + '/files/' + filename) # on le supprime
+ except FileNotFoundError:
+ flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
+ return redirect(url_for('filesupload.list', _external=True))
+ else :
+ return redirect(BASE_URL, code=401)
+
+@filesupload.route('/remove_publicImage/')
+def remove_publicImage(filename):
+ if 'username' in session :
+ user = '%s' % escape(session['username'])
+ filename = secure_filename(filename)
+ try:
+ os.remove(DOSSIER_PUBLIC + user + '/images/thumbnails/' + filename) # on le supprime
+ os.remove(DOSSIER_PUBLIC + user + '/images/' + filename) # on le supprime
+ except FileNotFoundError:
+ flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
+ return redirect(url_for('gallery'))
diff --git a/views/loginlogout.py b/views/loginlogout.py
index 50f3e38..9de4530 100644
--- a/views/loginlogout.py
+++ b/views/loginlogout.py
@@ -4,6 +4,8 @@ from markupsafe import escape
from flask_bcrypt import Bcrypt
from socket import gethostname
from os import remove, system
+from tools.utils import email_disp, valid_token_register, valid_passwd, valid_username, gen_token
+from tools.mailer import Mailer
app = Flask( 'pywallter' )
app.config.from_pyfile('config.py')
@@ -35,19 +37,25 @@ def login() :
else :
resp = redirect(url_for('loginlogout.login', _external=True))
if request.method == 'POST' :
+ user = request.form['user']
+ password = request.form['passwd']
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""SELECT name, passwd FROM users""")
- users = cursor.fetchall()
+ cursor.execute("""SELECT name, passwd FROM users WHERE name=?""", (user,))
+ user_exist = cursor.fetchone()
conn.close()
- password = request.form['passwd']
- for user in users:
- passwd = str(user[1] )
- if user[0] == request.form['user'] and bcrypt.check_password_hash(user[1], password) is True:
+
+ if user_exist:
+ user = user_exist[0]
+ passwd_bcrypt = user_exist[1].decode()
+
+ if user == request.form['user'] and bcrypt.check_password_hash(passwd_bcrypt, password) is True:
session['username'] = request.form['user']
resp = redirect(url_for('profil.profile', _external=True))
else:
- flash(u'Mauvais nom d\'utilisateur ou mot de passe', 'error')
+ flash(u'Mauvais mot de passe', 'error')
+ else:
+ flash(u"L'utilisateur n'existe pas", 'error')
else:
resp = render_template('accueil.html', signin_enable=app.config['SIGNIN_ENABLE'])
return resp
@@ -58,22 +66,23 @@ def logout():
session.pop('username', None) # Supprimer username de la session s'il s'y trouve
return redirect(url_for('loginlogout.index'))
+
@loginlogout.route( '/delete_me/', methods=['GET','POST'])
def delete_account():
if 'username' in session :
- UTILISATEUR='%s'% escape(session['username'])
+ user='%s'% escape(session['username'])
resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
if request.method == 'POST' :
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""SELECT passwd FROM users WHERE name=?""", (UTILISATEUR,))
- passwd = cursor.fetchone()[0]
+ cursor.execute("""SELECT passwd FROM users WHERE name=?""", (user,))
+ passwd = cursor.fetchone()[0].decode()
conn.close()
password = request.form['passwd']
if bcrypt.check_password_hash(passwd, password) is True:
not_error = True
try:
- cmd = 'rm -r ' + DATAS_USER + '/' + UTILISATEUR
+ cmd = 'rm -r ' + DATAS_USER + '/' + user
if system(cmd) != 0:
raise TypeError("Remove directory error")
except:
@@ -101,7 +110,7 @@ def delete_account():
try:
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
- cursor.execute("""DELETE FROM users WHERE name=?""", (UTILISATEUR,))
+ cursor.execute("""DELETE FROM users WHERE name=?""", (user,))
conn.commit()
conn.close()
except:
@@ -114,6 +123,42 @@ def delete_account():
return resp
+@loginlogout.route( '/lost_password/', methods=['GET', 'POST'])
+def lost_password():
+ if request.method == 'POST' :
+ user = request.form['user']
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+ cursor.execute("""SELECT name, Mail_rescue FROM users WHERE name=?""", (user,))
+ find_user = cursor.fetchone()
+
+ if find_user:
+ token = gen_token("Lost password")
+ cursor.execute("UPDATE users SET Lost_password_token=? WHERE name=?",
+ (token, user))
+ conn.commit()
+ mail_lost_password=Mailer()
+ message = """
+ "Vous avez fait une demande pour changer votre mot de passe, cliquez sur le liens en
+ dessous pour changer votre mot de passe :
+ """+ BASE_URL + url_for('profil.change_passwd_lost', token=token) + """
+
+ Si ce n'est pas vous qui avez fait cette demande vous pouvez détruire le lien de changement
+ de mot de passe en cliquant sur le lien en dessous \n
+ """+ BASE_URL + url_for('profil.deltoken_passwd_lost', token=token) + """
+
+
+ Au plaisir de vous revoir sur pywallter """
+
+ if find_user[1]:
+ flash(u"Un lien pour changer votre mot de passe a été envoyer à votre adresse email de secour ", 'succes')
+ mail_lost_password.send_email(find_user[1], "Récupération de votre mot de passe", message )
+ else:
+ flash(u"L'utilisateur "+ user + " n'existe pas.", 'error')
+
+
+ return render_template('lost_password.html')
+
@loginlogout.route( '/' )
def index():
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
diff --git a/views/profil.py b/views/profil.py
index d773116..1a35013 100644
--- a/views/profil.py
+++ b/views/profil.py
@@ -7,7 +7,7 @@ import sqlite3
import os
from socket import gethostname
from flask_bcrypt import Bcrypt
-from tools.utils import email_disp, append_to_log, gen_token, valid_passwd
+from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, valid_token_register, get_user_by_token
profil = Blueprint('profil', __name__, template_folder='templates')
@@ -49,7 +49,7 @@ def profile() :
UTILISATEUR='%s' % escape(session['username'])
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- cursor.execute("""SELECT avatar, nom, prenom, age, mail_rescue FROM users WHERE name=?""", (UTILISATEUR,))
+ cursor.execute("""SELECT avatar, nom, prenom, age, Mail_rescue FROM users WHERE name=?""", (UTILISATEUR,))
tmp = (cursor.fetchone())
profil_user = dict()
profil_user['avatar'] = tmp[0]
@@ -133,7 +133,7 @@ def change_passwd() :
if password == password_confirm and valid_passwd(password):
mail_passwd_change = 0
- xmmp_passwd_change = 0
+ xmpp_passwd_change = 0
passwd = request.form['password']
if MAIL_SERVER:
@@ -144,8 +144,8 @@ def change_passwd() :
if XMPP_SERVER:
tmp = mailbox['Mail'].split('@')
cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'"
- res = os.system(cmd)
- if res != 0:
+ xmpp_passwd_change = os.system(cmd)
+ if xmpp_passwd_change != 0:
flash(u'Il y a eu un problème pour le changement du mot de passe du compte XMPP !', 'error')
@@ -177,6 +177,100 @@ def change_passwd() :
return redirect(BASE_URL, code=401)
+@profil.route('/change-password-lost/', methods=['GET','POST'] )
+def change_passwd_lost(token) :
+
+ if valid_token_register(token, "Lost password"):
+
+ user = get_user_by_token(token, "Lost password")
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+ cursor.execute("""SELECT Mail, alias, xmpp FROM users WHERE name=?""", (user,))
+ tmp = cursor.fetchone()
+ mailbox = dict()
+ mailbox['Mail'] = tmp[0]
+ mailbox['alias'] = tmp[1]
+ mailbox['xmpp'] = tmp[2]
+
+
+ if request.method == 'GET' :
+ return render_template('mailbox.html',
+ section="Profil",
+ address=mailbox['Mail'],
+ username=user)
+ else:
+
+ password = request.form['password']
+ password_confirm = request.form['passwd_confirm']
+
+ if password == password_confirm and valid_passwd(password):
+ mail_passwd_change = 0
+ xmpp_passwd_change = 0
+
+
+ if MAIL_SERVER:
+ cmd = SETUID+ ' set_mail_passwd ' + '"'+mailbox['Mail']+'" '+ '"'+password+'"'
+ mail_passwd_change = os.system(cmd)
+
+ if XMPP_SERVER:
+ tmp = mailbox['Mail'].split('@')
+ cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+password+"'"
+ xmpp_change_passwd = os.system(cmd)
+ if xmpp_passwd_change != 0:
+ flash(u'Il y a eu un problème pour le changement du mot de passe du compte XMPP !', 'error')
+
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+
+ if mail_passwd_change == 0:
+ passwd_bcrypt = bcrypt.generate_password_hash(password)
+ cursor.execute("UPDATE users SET passwd=? WHERE name=?",
+ (passwd_bcrypt, user))
+ conn.commit()
+ TIME=time.strftime("%A %d %B %Y %H:%M:%S")
+ IP=request.environ['REMOTE_ADDR']
+ CLIENT_PLATFORM=request.headers.get('User-Agent')
+ log=TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Changement du mot de passe" + '\n'
+ append_to_log(log, user)
+ flash(u'Votre mot de passe a été changé', 'succes')
+ cursor.execute("""UPDATE users set Lost_password_token='' where name=?""", (user,))
+ conn.close()
+ resp = redirect(url_for('loginlogout.login'))
+
+ else:
+ if not( valid_passwd(password) ):
+ flash(u'Le mot de passe ne peut pas contenir les caractères " et &', 'error')
+ else:
+ flash(u'Les mot de passes ne sont pas identique :/ ', 'error')
+
+ resp = render_template('mailbox.html',
+ section="Profil",
+ address=mailbox['Mail'],
+ username=user)
+
+ return resp
+ else:
+
+ return redirect(BASE_URL, code=401)
+
+
+@profil.route('/deltoken-password-lost/', methods=['GET','POST'] )
+def deltoken_passwd_lost(token) :
+
+ if valid_token_register(token, "Lost password"):
+ user = get_user_by_token(token, "Lost password")
+ conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
+ cursor = conn.cursor() # Création de l'objet "curseur"
+
+ cursor.execute("""UPDATE users set Lost_password_token='' where name=?""", (user,))
+ conn.commit()
+ conn.close()
+ flash(u'Votre jeton pour changer votre mot de passe a été supprimé', 'succes')
+ else:
+ flash(u'Votre jeton est invalide', 'succes')
+ return redirect(url_for('loginlogout.login', _external=True))
+
+
@profil.route('/mymailbox/alias', methods=['GET', 'POST'] )
def myalias():
hostname=gethostname()
@@ -271,12 +365,12 @@ def remove_alias(aliasrm):
else:
flash(u'Il y a eu une erreur', 'error')
-
-
return redirect(url_for('profil.myalias', _external=True))
else:
return redirect(BASE_URL, code=401)
+
+
@profil.route('/invitation/', methods=['GET'])
def invitation():
if 'username' in session:
@@ -307,7 +401,7 @@ def generate_token():
UTILISATEUR='%s' % escape(session['username'])
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
- token = gen_token()
+ token = gen_token("Invitation")
cursor.execute("UPDATE users SET Token=? WHERE name=?",
(token, UTILISATEUR))
conn.commit()