Auto-deleting mode-exclusive content files, new functions files.write_accessible() and files.delete_this()

This commit is contained in:
faildev_mode 2023-07-14 20:13:41 +02:00
parent 35a7c14c1a
commit d308acb01a
No known key found for this signature in database
GPG Key ID: 70845C70C0F5E205
2 changed files with 50 additions and 5 deletions

View File

@ -12,7 +12,7 @@ from functools import partial
import apis # from itself
import traceback
from item import Item # from itself
from files import save_this, link_this, scan_dir # from itself
from files import save_this, link_this, scan_dir, delete_this # from itself
from config import config # from itself
def template_for(path: str) -> str:
@ -198,6 +198,10 @@ if __name__ == '__main__':
for path in content_files:
print('F', path)
if path.endswith('.gmi'):
html_path = os.path.splitext(path)[0]+'.html'
else:
html_path = path
item = Item(path)
# parsing in gemini mode
@ -244,7 +248,8 @@ if __name__ == '__main__':
save_this('../'+path, content)
else:
print(' ! skipped for gemini')
# TODO: delete from gemini output
# delete from gemini output
delete_this('../'+path)
# parsing in www mode
if not 'mode' in item or item.mode == 'www':
@ -273,10 +278,8 @@ if __name__ == '__main__':
content = evaluate_this(tpl_item.content, namespace)
# convert to html
html_path = path
if path.endswith('.gmi'):
content = gemtext2html(content)
html_path = os.path.splitext(path)[0]+'.html'
if html_path.endswith('.html'):
# TODO: html template
@ -288,4 +291,5 @@ if __name__ == '__main__':
save_this('../html/'+html_path, content)
else:
print(' ! skipped for www')
# TODO: delete from www output
# delete from www output
delete_this('../html/'+html_path)

View File

@ -1,4 +1,5 @@
import os
import sys
cache = {} # globally shared object to store file content
@ -21,6 +22,10 @@ def save_this(path: str, content: str):
"""
global cache
# access control
if not write_accessible(path, follow_symlinks=False):
raise PermissionError(f"Attempted to write to {os.path.abspath(path)}")
mkdir_this(os.path.dirname(path))
with open(path, 'w') as fp:
fp.write(content)
@ -81,3 +86,39 @@ def scan_dir(path: str, orig_path=None) -> list:
results += scan_dir(entry.path, orig_path)
return results
def write_accessible(path: str, follow_symlinks: bool = True) -> bool:
"""Tells whether the file is in safe area (outside _src, .git, parent dirs)
Technically, nothing stops build.py from writing there, but to avoid data
loss as a result of a bug, you should always check what file you're trying
to delete or write to.
"""
PROTECTED_DIRS = (sys.path[0], '../_src', '../.git')
path = os.path.realpath(path) if follow_symlinks else os.path.abspath(path)
project_root = os.path.dirname(sys.path[0])
# protect upper dirs
if os.path.commonpath((path, project_root)) != project_root: return False
# protect lower dirs
for protected in PROTECTED_DIRS:
protected = os.path.realpath(protected)
if os.path.commonpath((path, protected)) == protected:
return False
return True
def delete_this(path: str):
"""Deletes a file if it exists"""
if not write_accessible(path, follow_symlinks=False):
raise PermissionError(f"Attempted to delete {os.path.abspath(path)}")
if not os.path.isfile(path): return
os.unlink(path)
print('file deleted!')
# TODO: cleanup empty directories