od-database/od_util.py
2019-03-24 20:23:05 -04:00

249 lines
8.8 KiB
Python

import os
import re
from ftplib import FTP
from urllib.parse import urljoin, urlparse
import requests
import validators
from bs4 import BeautifulSoup
# TODO: find a better way to do this
try:
from . import config
except (ImportError, SystemError):
import config
import urllib3
urllib3.disable_warnings()
def truncate_path(path, max_len):
pattern = re.compile(r"/?.*?/")
for i in range(1, path.count("/")):
new_path = pattern.sub(".../", path, i)
if len(new_path) < max_len:
return new_path
return ".../" + path.rsplit("/", maxsplit=1)[1] if "/" in path else path
category_map = {
# Application category
'bcpio': 'application', 'bin': 'application', 'cdf': 'application',
'csh': 'application', 'dll': 'application', 'doc': 'application',
'dot': 'application', 'dvi': 'application', 'eml': 'application',
'exe': 'application', 'hdf': 'application',
'man': 'application', 'me': 'application', 'mht': 'application',
'mhtml': 'application', 'mif': 'application', 'ms': 'application',
'nc': 'application', 'nws': 'application', 'o': 'application',
'obj': 'application', 'oda': 'application', 'p12': 'application',
'p7c': 'application', 'pfx': 'application', 'tr': 'application',
'ppa': 'application', 'pps': 'application', 'ppt': 'application',
'ps': 'application', 'pwz': 'application', 'pyc': 'application',
'pyo': 'application', 'ram': 'application', 'rdf': 'application',
'roff': 'application', 'sh': 'application', 'so': 'application',
'src': 'application', 'sv4cpio': 'application', 'sv4crc': 'application',
't': 'application', 'tcl': 'application', 'tex': 'application',
'texi': 'application', 'texinfo': 'application', 'ustar': 'application',
'wiz': 'application', 'wsdl': 'application', 'xlb': 'application',
'xls': 'application', 'xpdl': 'application', 'xsl': 'application',
'torrent': 'application', 'rpm': 'application', 'deb': 'application',
'atr': 'application', 'class': 'application', 'ttf': 'application',
'img': 'application', 'msi': 'application', 'run': 'application',
'drpm': 'application', 'udeb': 'application', 'patch': 'application',
'nes': 'application', 'ebuild': 'application', 'scr': 'application',
# Text category
'java': 'text', 'cpp': 'text', 'rb': 'text',
'bat': 'text', 'latex': 'text', 'xml': 'text',
'etx': 'text', 'htm': 'text', 'c': 'text',
'css': 'text', 'csv': 'text', 'html': 'text',
'js': 'text', 'json': 'text', 'ksh': 'text',
'pl': 'text', 'pot': 'application', 'py': 'text',
'h': 'text', 'tsv': 'text', 'rtx': 'text',
'sgm': 'text', 'sgml': 'text', 'txt': 'text',
'vcf': 'text', 'pdf': 'text', 'epub': 'text',
'srt': 'text', 'inc': 'text', 'php': 'text',
'cbz': 'text', 'docx': 'text', 'mobi': 'text',
'chm': 'text', 'xlsx': "text", 'djvu': 'text',
'rtf': 'text', 'log': 'text', 'md': 'text',
'dsc': 'text', 'info': 'text',
# Video category
'3g2': 'video', '3gp': 'video', 'asf': 'video',
'asx': 'video', 'avi': 'video', 'flv': 'video',
'swf': 'video', 'vob:': 'video', 'qt': 'video',
'webm': 'video', 'mov': 'video', 'm1v': 'video',
'm3u': 'video', 'm3u8': 'video', 'movie': 'video',
'mp4': 'video', 'mpa': 'video', 'mpe': 'video',
'mpeg': 'video', 'mpg': 'video', 'mkv': 'video',
'wmv': 'video', 'm4s': 'video', 'ogv': 'video',
'm4b': 'video', 'm4v': 'video', 'ts': 'video',
# Audio category
'wav': 'audio', 'snd': 'audio', 'mp2': 'audio',
'aif': 'audio', 'iff': 'audio', 'm4a': 'audio',
'mid': 'audio', 'midi': 'audio', 'mp3': 'audio',
'wma': 'audio', 'ra': 'audio', 'aifc': 'audio',
'aiff': 'audio', 'au': 'audio', 'flac': 'audio',
'ogg': 'audio', 'oga': 'audio', 'mka': 'video',
'ac3': 'audio',
# Image category
'bmp': 'image', 'gif': 'image', 'jpg': 'image',
'xwd': 'image', 'tif': 'image', 'tiff': 'image',
'png': 'image', 'pnm': 'image', 'ras': 'image',
'ico': 'image', 'ief': 'image', 'pgm': 'image',
'jpe': 'image', 'pbm': 'image', 'jpeg': 'image',
'ppm': 'image', 'xpm': 'image', 'xbm': 'image',
'rgb': 'image', 'svg': 'image', 'psd': 'image',
'yuv': 'image', 'ai': 'image', 'eps': 'image',
'bw': 'image', 'hdr': 'image',
# Archive category
'ar': 'archive', 'cpio': 'archive', 'shar': 'archive',
'iso': 'archive', 'lbr': 'archive', 'mar': 'archive',
'sbx': 'archive', 'bz2': 'archive', 'f': 'archive',
'gz': 'archive', 'lz': 'archive', 'lzma': 'archive',
'lzo': 'archive', 'rz': 'archive', 'sfark': 'archive',
'sz': 'archive', 'z': 'archive', '7z': 'archive',
's7z': 'archive', 'ace': 'archive', 'afa': 'archive',
'alz': 'archive', 'apk': 'archive', 'arc': 'archive',
'arj': 'archive', 'b1': 'archive', 'b6z': 'archive',
'a': 'archive', 'bh': 'archive', 'cab': 'archive',
'car': 'archive', 'cfs': 'archive', 'cpt': 'archive',
'dar': 'archive', 'dd': 'archive', 'dgc': 'archive',
'dmg': 'archive', 'ear': 'archive', 'gca': 'archive',
'ha': 'archive', 'hki': 'archive', 'ice': 'archive',
'jar': 'archive', 'kgb': 'archive', 'lzh': 'archive',
'lha': 'archive', 'lzx': 'archive', 'pak': 'archive',
'partimg': 'archive', 'paq6': 'archive', 'paq7': 'archive',
'paq8': 'archive', 'pea': 'archive', 'pim': 'archive',
'pit': 'archive', 'qda': 'archive', 'rar': 'archive',
'rk': 'archive', 'sda': 'archive', 'sea': 'archive',
'sen': 'archive', 'sfx': 'archive', 'shk': 'archive',
'sit': 'archive', 'sitx': 'archive', 'sqx': 'archive',
'tbz2': 'archive', 'tlz': 'archive', 'xz': 'archive',
'txz': 'archive', 'uc': 'archive', 'uc0': 'archive',
'uc2': 'archive', 'ucn': 'archive', 'ur2': 'archive',
'ue2': 'archive', 'uca': 'archive', 'uha': 'archive',
'war': 'archive', 'wim': 'archive', 'xar': 'archive',
'xp3': 'archive', 'yz1': 'archive', 'zip': 'archive',
'zipx': 'archive', 'zoo': 'archive', 'zpaq': 'archive',
'zz': 'archive', 'xpi': 'archive', 'tgz': 'archive',
'tbz': 'archive', 'tar': 'archive', 'bz': 'archive',
'diz': 'archive',
}
colors = {
"application": "bg-application",
"text": "bg-text",
"video": "bg-video",
"image": "bg-image",
"audio": "bg-audio",
"archive": "bg-archive"
}
def get_color(category):
return colors.get(category, None)
def get_category(extension):
return category_map.get(extension, None)
def is_valid_url(url):
if not url.endswith("/"):
return False
if not url.startswith(("http://", "https://", "ftp://")):
return False
return validators.url(url)
def has_extension(link):
return len(os.path.splitext(link)[1]) > 0
def is_external_link(base_url, url: str):
url = urljoin(base_url, url).strip()
if base_url in url:
return False
return True
def is_od(url):
if not url.endswith("/"):
print("Url does not end with trailing /")
return False
try:
if url.startswith("ftp://") and config.SUBMIT_FTP:
ftp = FTP(urlparse(url).netloc)
ftp.login()
ftp.close()
return True
elif config.SUBMIT_HTTP:
r = requests.get(url, timeout=30, allow_redirects=False, verify=False)
if r.status_code != 200:
# print("No redirects allowed!")
return False
soup = BeautifulSoup(r.text, "lxml")
external_links = sum(1 if is_external_link(url, a.get("href")) else 0 for a in soup.find_all("a"))
link_tags = len(list(soup.find_all("link")))
script_tags = len(list(soup.find_all("script")))
if external_links > 11:
# print("Too many external links!")
return False
if link_tags > 5:
# print("Too many link tags!")
return False
if script_tags > 7:
# print("Too many script tags!")
return False
return True
except Exception as e:
# print(e)
return False
def has_parent_dir(url):
parsed_url = urlparse(url)
if parsed_url.path == "/":
return False
parent_url = urljoin(url, "../")
try:
r = requests.get(parent_url, timeout=30, allow_redirects=False, verify=False)
if r.status_code != 200:
return False
soup = BeautifulSoup(r.text, "lxml")
for anchor in soup.find_all("a"):
if anchor.get("href") and anchor.get("href").endswith("/") and urljoin(parent_url, anchor.get("href")) == url:
# The parent page exists, and has a link to the child directory
return is_od(parent_url)
except:
return False
# Parent page exists, but does not have a link to the child directory
return False
def get_top_directory(url):
if url.startswith("ftp://"):
return url
while has_parent_dir(url):
url = urljoin(url, "../")
return url