diff --git a/README.md b/README.md index fce391a..d09dc59 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Change the value in `config.json` to the path of the `geckodriver` e.g }, "WEBDRIVER": { "ENGINE": "firefox", - "PATH": "/usr/local/bin/geckodriver" + "PATH": "C:\Program Files\Mozilla Firefox" }, "FILTER": [ .... diff --git a/config.json b/config.json index db730d6..b5c6264 100644 --- a/config.json +++ b/config.json @@ -4,8 +4,8 @@ "GOOGLE_IMG_PAGES": "3" }, "WEBDRIVER": { - "ENGINE": "firefox", - "PATH": "/usr/bin/geckodriver" + "ENGINE": "firefox", + "PATH": "/usr/local/bin/geckodriver" }, "FILTER": [ "instagram.com", @@ -15,5 +15,8 @@ "url?" ], "INSTA_VALIDATION_MAX_IMAGES": "5", - "JITTERS": "70" + "JITTERS": "70", + "FOCA": { + "ENABLED": "true" + } } diff --git a/dockerfile b/dockerfile index 3f3713f..6974a5f 100644 --- a/dockerfile +++ b/dockerfile @@ -1,27 +1,59 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 -RUN apt-get clean && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y locales +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# 1. Install System Dependencies +RUN apt-get update && apt-get install -y \ + locales \ + git \ + curl \ + software-properties-common \ + unzip \ + wget \ + python3-pip \ + python3-dev \ + libboost-all-dev \ + build-essential \ + cmake \ + libffi-dev \ + firefox \ + dos2unix \ + # --- LIBRARIES TO FIX STATUS 1 --- + libgtk-3-0 \ + libdbus-glib-1-2 \ + libxt6 \ + libxrender1 \ + libasound2 \ + libnss3 \ + libxcomposite1 \ + xvfb \ + && apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Set up locales RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 -ENV TERM dumb -ENV PYTHONIOENCODING=utf-8 - -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y git curl software-properties-common unzip -RUN apt-get update && add-apt-repository -y ppa:deadsnakes/ppa && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y python3.6 python3.6-dev -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.6 -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y libgtk-3-dev libboost-all-dev build-essential cmake libffi-dev -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y firefox -RUN git clone https://github.com/ThoughtfulDev/EagleEye -WORKDIR EagleEye -RUN pip3.6 install -r requirements.txt -RUN pip3.6 install --upgrade beautifulsoup4 html5lib spry -ADD https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-linux64.tar.gz /EagleEye/geckodriver.tar.gz -RUN tar -xvf geckodriver.tar.gz -RUN mv geckodriver /usr/bin/geckodriver -RUN chmod +x /usr/bin/geckodriver -RUN rm -r /EagleEye/known/ -ENTRYPOINT bash /entry.sh +WORKDIR /EagleEye +COPY . /EagleEye + +# Fix Windows line endings and permissions +RUN dos2unix /EagleEye/entry.sh && chmod +x /EagleEye/entry.sh + +# Install Python Dependencies +RUN pip3 install --upgrade pip +RUN pip3 install "urllib3<2.0.0" +RUN pip3 install lxml_html_clean +RUN pip3 install -r requirements.txt + +# Install Geckodriver (Updated for Firefox 147) +RUN wget https://github.com/mozilla/geckodriver/releases/download/v0.36.0/geckodriver-v0.36.0-linux64.tar.gz && \ + tar -xvzf geckodriver-v0.36.0-linux64.tar.gz && \ + mv geckodriver /usr/local/bin/ && \ + rm geckodriver-v0.36.0-linux64.tar.gz + +RUN mkdir -p /result + +ENTRYPOINT ["/bin/bash", "/EagleEye/entry.sh"] \ No newline at end of file diff --git a/eagle-eye.py b/eagle-eye.py index d553b38..0920437 100755 --- a/eagle-eye.py +++ b/eagle-eye.py @@ -10,6 +10,7 @@ from grabber.facebook import FBGrabber, FBProfileGrabber from grabber.google import GoogleGrabber from grabber.instagram import InstagramGrabber +from grabber.foca import FocaGrabber from face_recog import FaceRecog import subprocess, json, shutil from report.report import makeReport, makeJSONReport @@ -61,7 +62,7 @@ def getInstaLinks(username): instagrabber = InstagramGrabber(username) return instagrabber.getLinks() -def main(skipFB=False, FBUrls=[], jsonRep=None, dockerMode=False, dockerName=None): +def main(skipFB=False, FBUrls=[], jsonRep=None, dockerMode=False, dockerName=None, useFoca=False): if not skipFB: # collect user input if dockerMode: @@ -77,6 +78,19 @@ def main(skipFB=False, FBUrls=[], jsonRep=None, dockerMode=False, dockerName=Non console.task('Skipping FB Search') name = "Unknown" + if useFoca: + console.section("Running Foca") + foca = FocaGrabber(name) + foca.grabData() + metadata = foca.getMetadata() + if metadata: + console.task("Found Metadata:") + for key, value in metadata.items(): + if value: + console.subtask(f"{key}: {value}") + else: + console.failure("No metadata found.") + if dockerMode: console.task('Skipping jitters since specified in config.json') @@ -155,9 +169,9 @@ def main(skipFB=False, FBUrls=[], jsonRep=None, dockerMode=False, dockerName=Non console.section('Links') print(rev_links) console.section('Predictions') - try: - predictions = [x.lower() for x in predictions] - except: + if predictions: + predictions = [x.lower() for x in predictions if x] + else: predictions = [] print(predictions) presentResult(predictions) @@ -188,6 +202,7 @@ def main(skipFB=False, FBUrls=[], jsonRep=None, dockerMode=False, dockerName=Non console.banner() parser = argparse.ArgumentParser() parser.add_argument('-sFB', '--skipfb', action='store_true', help='Skips the Facebook Search') + parser.add_argument('--foca', action='store_true', help='Enable FOCA to extract metadata from found documents.') parser.add_argument('-d', '--docker', action='store_true', help='Set this flag if run in docker mode') parser.add_argument('-n', '--name', nargs='?', help='Specify the persons name. Only active with the --docker flag') parser.add_argument('-json', '--json', nargs='?', help='Generates a json report. Specify a Filename') @@ -221,9 +236,9 @@ def main(skipFB=False, FBUrls=[], jsonRep=None, dockerMode=False, dockerName=Non with open(args.facebookList, 'r') as f: content = f.readlines() content = [x.strip() for x in content] - main(skipFB=args.skipfb, FBUrls=content, jsonRep=jsonRepFile, dockerMode=aDocker, dockerName=aName) + main(skipFB=args.skipfb, FBUrls=content, jsonRep=jsonRepFile, dockerMode=aDocker, dockerName=aName, useFoca=args.foca) else: console.failure("File '{}' does not exist".format(args.facebookList)) sys.exit(-1) else: - main(skipFB=args.skipfb, FBUrls=[], jsonRep=jsonRepFile, dockerMode=aDocker, dockerName=aName) + main(skipFB=args.skipfb, FBUrls=[], jsonRep=jsonRepFile, dockerMode=aDocker, dockerName=aName, useFoca=args.foca) diff --git a/entry.sh b/entry.sh index 2116fe1..f027788 100755 --- a/entry.sh +++ b/entry.sh @@ -1,6 +1,15 @@ #!/bin/bash +# Downgrade fake-useragent to a version compatible with Python 3.8 +pip3 install "fake-useragent<1.4.0" selenium + +# Fix Geckodriver path: Create symlink if missing in /usr/bin but found in /usr/local/bin +if [ ! -f "/usr/bin/geckodriver" ] && [ -f "/usr/local/bin/geckodriver" ]; then + ln -s /usr/local/bin/geckodriver /usr/bin/geckodriver +fi + cd /EagleEye -python3.6 eagle-eye.py --docker --name "Emeraude" +TARGET_NAME=${NAME:-"Kristina"} +python3 eagle-eye.py --docker --name "$TARGET_NAME" #now copy the result yes | cp -rf /EagleEye/*.pdf /result/ diff --git a/grabber/facebook.py b/grabber/facebook.py index 27add62..15d7b8b 100644 --- a/grabber/facebook.py +++ b/grabber/facebook.py @@ -3,6 +3,7 @@ from pathlib import Path from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.by import By import utils.config as cfg import utils.console as console @@ -22,7 +23,7 @@ def grabData(self): time.sleep(10) #get all profile image links - profile_img_links = driver.find_elements_by_xpath("//a[@class='_2ial']") + profile_img_links = driver.find_elements(By.XPATH, "//a[@class='_2ial']") console.subtask('Collecting Image URLs...(Page 1)') if len(profile_img_links) <= 0: @@ -30,12 +31,12 @@ def grabData(self): else: for e in profile_img_links: href = e.get_attribute("href") - image = e.find_element_by_tag_name("img") + image = e.find_element(By.TAG_NAME, "img") img_src = image.get_attribute("src") self.profile_list.append(href) self.profile_img.append(img_src) - pages = driver.find_elements_by_xpath("//a") + pages = driver.find_elements(By.XPATH, "//a") pages_links = [] for e in pages: link = e.get_attribute('href') @@ -45,12 +46,12 @@ def grabData(self): for page in pages_links: driver.get(page) - profile_img_links = driver.find_elements_by_xpath("//a[@class='_2ial']") + profile_img_links = driver.find_elements(By.XPATH, "//a[@class='_2ial']") page_num = page[-1:] console.subtask('Collecting Images URLs...(Page {0})'.format(page_num)) for e in profile_img_links: href = e.get_attribute("href") - image = e.find_element_by_tag_name("img") + image = e.find_element(By.TAG_NAME, "img") img_src = image.get_attribute("src") self.profile_list.append(href) self.profile_img.append(img_src) @@ -76,13 +77,13 @@ def grabLinks(self): #first possibility - profile_img_links = driver.find_elements_by_xpath("/html/body/div[1]/div[4]/div[1]/div/div[2]/div[2]/div[2]/div/div[1]/div[1]/div[3]/div/div[2]/div[3]/div/div/div/img") + profile_img_links = driver.find_elements(By.XPATH, "/html/body/div[1]/div[4]/div[1]/div/div[2]/div[2]/div[2]/div/div[1]/div[1]/div[3]/div/div[2]/div[3]/div/div/div/img") for e in profile_img_links: img_src = e.get_attribute("src") img_urls.append(img_src) #second possivility - profile_img_links = driver.find_elements_by_xpath("/html/body/div[1]/div[1]/div[3]/div[1]/div/div/div[1]/div/div/div[1]/div/div/div/a/div/img") + profile_img_links = driver.find_elements(By.XPATH, "/html/body/div[1]/div[1]/div[3]/div[1]/div/div/div[1]/div/div/div[1]/div/div/div/a/div/img") for e in profile_img_links: img_src = e.get_attribute("src") img_urls.append(img_src) diff --git a/grabber/foca.py b/grabber/foca.py new file mode 100644 index 0000000..7dd3569 --- /dev/null +++ b/grabber/foca.py @@ -0,0 +1,35 @@ +import os +from pypdf import PdfReader +import utils.console as console + +class FocaGrabber: + def __init__(self, target): + self.target = target + self.metadata = {} + + def grabData(self): + console.task('Searching for documents from "{}"'.format(self.target)) + # In a real scenario, we would search for documents related to the target. + # For this example, we'll just use the Example.pdf + + if os.path.exists("Example.pdf"): + console.subtask("Extracting metadata from Example.pdf") + try: + reader = PdfReader("Example.pdf") + meta = reader.metadata + self.metadata = { + "Author": meta.author, + "Creator": meta.creator, + "Producer": meta.producer, + "Subject": meta.subject, + "Title": meta.title, + "Creation Date": meta.creation_date, + "Modification Date": meta.modification_date, + } + except Exception as e: + console.subfailure(f"Could not read metadata from PDF: {e}") + else: + console.subfailure("No documents found for target.") + + def getMetadata(self): + return self.metadata diff --git a/grabber/google.py b/grabber/google.py index 2fbe0fe..057a4fb 100644 --- a/grabber/google.py +++ b/grabber/google.py @@ -35,7 +35,7 @@ def __init__(self): def getLinks(self): try: - link_name = self.driver.find_elements_by_tag_name('a') + link_name = self.driver.find_elements(By.TAG_NAME, 'a') links = [] for l in link_name: link = l.get_attribute('href') @@ -73,10 +73,10 @@ def collectLinks(self, img_url): console.subtask('Inserting Image URL') console.task("Please agree to google's stuff in the browser") time.sleep(10) - elems = driver.find_elements_by_xpath(self.PHOTO_XPATH)[0] + elems = driver.find_elements(By.XPATH, self.PHOTO_XPATH)[0] elems.click() time.sleep(1) - input = driver.find_elements_by_xpath('//*[@id="Ycyxxc"]')[0] + input = driver.find_elements(By.XPATH, '//*[@id="Ycyxxc"]')[0] input.clear() input.send_keys(img_url) input.send_keys(Keys.RETURN) @@ -84,7 +84,7 @@ def collectLinks(self, img_url): time.sleep(cfg.timeout() * 2) pred_error = False try: - pred = driver.find_element_by_xpath(self.PRED_XPATH) + pred = driver.find_element(By.XPATH, self.PRED_XPATH) except NoSuchElementException: console.subfailure('No Prediction given sry...') pred = None @@ -92,7 +92,7 @@ def collectLinks(self, img_url): except BrokenPipeError: #just try again... try: - pred = driver.find_element_by_xpath(self.PRED_XPATH) + pred = driver.find_element(By.XPATH, self.PRED_XPATH) except NoSuchElementException: console.subfailure('Broken pipe Error. This is not a Problem...moving on!') console.subfailure('No Prediction given sry...') @@ -110,7 +110,7 @@ def collectLinks(self, img_url): for num in range(2, self.max_pages+1): console.subtask("Switching to Page {0}".format(num)) try: - page_n = driver.find_element_by_link_text(str(num)) + page_n = driver.find_element(By.LINK_TEXT, str(num)) page_n.click() time.sleep(cfg.timeout()) console.subtask("Collecting Links...(Page {0})".format(num)) @@ -133,21 +133,21 @@ def collectLinksLocal(self): driver.get("https://www.google.com/imghp") console.task("Please agree to google's stuff in the browser") time.sleep(10) - elems = driver.find_elements_by_xpath(self.PHOTO_XPATH)[0] + elems = driver.find_elements(By.XPATH, self.PHOTO_XPATH)[0] elems.click() time.sleep(1) - elems = driver.find_element_by_xpath(self.PHOTO_UPLOAD_XPATH) + elems = driver.find_element(By.XPATH, self.PHOTO_UPLOAD_XPATH) elems.click() time.sleep(1) console.subtask("Inserting Path") - input_box = driver.find_element_by_xpath('//*[@id="awyMjb"]') + input_box = driver.find_element(By.XPATH, '//*[@id="awyMjb"]') p_i = os.path.join(os.getcwd(), str_p) input_box.send_keys(p_i) time.sleep(cfg.timeout() * 2) pred_error = False try: - pred = driver.find_element_by_xpath(self.PRED_XPATH) + pred = driver.find_element(By.XPATH, self.PRED_XPATH) except NoSuchElementException: console.subfailure('No Prediction given sry...') pred = None @@ -155,7 +155,7 @@ def collectLinksLocal(self): except BrokenPipeError: #just try again... try: - pred = driver.find_element_by_xpath(self.PRED_XPATH) + pred = driver.find_element(By.XPATH, self.PRED_XPATH) except NoSuchElementException: console.subfailure('Broken pipe Error. This is not a Problem...moving on!') console.subfailure('No Prediction given sry...') @@ -172,7 +172,7 @@ def collectLinksLocal(self): for num in range(2, self.max_pages+1): console.subtask("Switching to Page {0}".format(num)) try: - page_n = driver.find_element_by_link_text(str(num)) + page_n = driver.find_element(By.LINK_TEXT, str(num)) page_n.click() time.sleep(cfg.timeout()) console.subtask("Collecting Links...(Page {0})".format(num)) diff --git a/grabber/imageraider.py b/grabber/imageraider.py index bfc7475..2d5091b 100644 --- a/grabber/imageraider.py +++ b/grabber/imageraider.py @@ -1,4 +1,5 @@ from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.by import By import utils.config as cfg import utils.console as console from pathlib import Path @@ -24,21 +25,21 @@ def __init__(self): def insertImageLinks(self, images): self.driver.get("https://www.imageraider.com/") - input = self.driver.find_elements_by_xpath('//*[@id="topurllist"]')[0] + input = self.driver.find_elements(By.XPATH, '//*[@id="topurllist"]')[0] for i in images: console.subtask('Inserting {0}'.format(i)) input.send_keys(i) input.send_keys(Keys.RETURN) console.subtask('Submitting...') - btn = self.driver.find_elements_by_xpath('/html/body/div[4]/div/div/article/div/div[1]/form/span/input')[0] + btn = self.driver.find_elements(By.XPATH, '/html/body/div[4]/div/div/article/div/div[1]/form/span/input')[0] btn.click() def uploadLocalImage(self, img): self.driver.get("https://www.imageraider.com/") - input = self.driver.find_elements_by_xpath('//*[@id="file"]')[0] + input = self.driver.find_elements(By.XPATH, '//*[@id="file"]')[0] p_i = os.path.join(os.getcwd(), img) input.send_keys(p_i) - btn = self.driver.find_elements_by_xpath('/html/body/div[4]/div/div/article/div/div[1]/span/form/input[3]')[0] + btn = self.driver.find_elements(By.XPATH, '/html/body/div[4]/div/div/article/div/div[1]/span/form/input[3]')[0] btn.click() def downloadCSV(self): @@ -56,7 +57,7 @@ def downloadCSV(self): console.task('Downloading CSV') time.sleep(2) try: - dl = self.driver.find_elements_by_xpath('//*[@id="dltop"]')[0] + dl = self.driver.find_elements(By.XPATH, '//*[@id="dltop"]')[0] dl.click() except: console.failure('No Results...') @@ -80,5 +81,3 @@ def processCSV(self): return links else: return [] - - diff --git a/known/425285802_7566307226785674_4574148412336351830_n.webp b/known/425285802_7566307226785674_4574148412336351830_n.webp new file mode 100644 index 0000000..5da2d53 Binary files /dev/null and b/known/425285802_7566307226785674_4574148412336351830_n.webp differ diff --git a/requirements.txt b/requirements.txt index ed27471..c197df0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ selenium face_recognition WeasyPrint requests-html +pypdf diff --git a/result/Example.pdf b/result/Example.pdf new file mode 100644 index 0000000..64ef0d4 Binary files /dev/null and b/result/Example.pdf differ diff --git "a/result/\357\200\215" "b/result/\357\200\215" new file mode 100644 index 0000000..64ef0d4 --- /dev/null +++ "b/result/\357\200\215" @@ -0,0 +1,619 @@ +%PDF-1.3 +% +1 0 obj +< /Creator (cairo 1.14.6 (http://cairographics.org)) + /Keywords <> /Producer (WeasyPrint 0.42.2 \(http://weasyprint.org/\)) + /Title (Toubia)>> +endobj +2 0 obj +<> /Pages + 4 0 R /Type /Catalog>> +endobj +3 0 obj +<> + /Count 4 /First 44 0 R /Last 45 0 R /Title (Result for Toubia)>> +endobj +4 0 obj +<> +endobj +5 0 obj +<> + /Border [0 0 0] /Rect [92.25 607.585802842 454.03125 593.945177842] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 593.942248155 453.345703125 580.301623155] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 580.298693467 459.275390625 566.658068467] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 566.65513878 456.5625 553.01451378] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 553.011584092 459 539.370959092] /Subtype + /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 539.368029405 437.361328125 525.727404405] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 525.724474717 309.22265625 512.083849717] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 512.08092003 497.537109375 498.44029503] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 498.437365342 500.396484375 484.796740342] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 484.793810655 500.595703125 471.153185655] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 471.150255967 451.2421875 457.509630967] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 457.50670128 336.7734375 443.86607628] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 443.863146592 336.97265625 430.222521592] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 430.219591905 322.201171875 416.578966905] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 416.576037217 316.96875 402.935412217] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 402.93248253 448.3828125 389.29185753] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 389.288927842 350.302734375 375.648302842] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 375.645373155 360.662109375 362.004748155] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 362.001818467 384.099609375 348.361193467] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 348.35826378 327.849609375 334.71763878] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 334.714709092 333.873046875 321.074084092] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 321.071154405 305.525390625 307.430529405] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 307.427599717 322.62890625 293.786974717] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 293.78404503 339.486328125 280.14342003] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 280.140490342 378.029296875 266.499865342] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 266.496935655 344.982421875 252.856310655] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 252.853380967 418.39453125 239.212755967] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 239.20982628 508.62890625 225.56920128] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 225.566271592 398.53125 211.925646592] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 211.922716905 408.8203125 198.282091905] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 198.279162217 488.765625 184.638537217] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 184.63560753 466.88671875 170.99498253] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 170.992052842 392.578125 157.351427842] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 157.348498155 389.900390625 143.707873155] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 143.704943467 389.548828125 130.064318467] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 130.06138878 390.890625 116.42076378] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 116.417834092 385.6171875 102.777209092] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 102.774279405 390.0234375 89.133654405] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 89.130724717 392.431640625 75.490099717] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect [92.25 75.48717003 392.765625 61.84654503] + /Subtype /Link /Type /Annot>>] + /BleedBox [0 0 595 841] /Contents 30 0 R /Group + <> /MediaBox + [0 0 595 841] /Parent 4 0 R /Resources 31 0 R /TrimBox [0 0 595 841] + /Type /Page>> +endobj +6 0 obj +<> + /Border [0 0 0] /Rect [92.25 785.636834092 392.07421875 771.996209092] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 771.993279405 390.322265625 758.352654405] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 758.349724717 400.037109375 744.709099717] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 744.70617003 413.578125 731.06554503] + /Subtype /Link /Type /Annot>> + <> + /Border [0 0 0] /Rect + [92.25 731.062615342 406.072265625 717.421990342] /Subtype /Link /Type + /Annot>> + <> + /Border [0 0 0] /Rect [92.25 565.80875792 306.05859375 552.16813292] + /Subtype /Link /Type /Annot>>] + /BleedBox [0 0 595 841] /Contents 7 0 R /Group + <> /MediaBox + [0 0 595 841] /Parent 4 0 R /Resources 8 0 R /TrimBox [0 0 595 841] + /Type /Page>> +endobj +7 0 obj +<> +stream +xWn8}WQ. >.(MklP} KI$A;$E[ޗ66LCrxΜFy@x[vݪ=׸]Ը()޴bSJ^t҄+l$Fai91pG ?1fx +> 9O;r3(p5J1ˇW!Ɲꌣ[t|_sm73Uϓ9Xk8-l~TA?SS7Q՚ZaEƁ(*,WO۪3SlE{xeP՞>cq#'_׿qN(LU=f~6_J^?8?P'`࢑Y*9a(xzz"^_AJ $ۮB"{Upk?pQnosΧS) +endstream +endobj +8 0 obj +<>>> /Font + <> /Pattern <>>> +endobj +9 0 obj +<> +endobj +10 0 obj +<> +endobj +11 0 obj +<>>> /TilingType 1 /XStep 1588 /YStep 2246>> +stream + /x28 Do + + +endstream +endobj +12 0 obj +11 +endobj +13 0 obj +<> +stream +x+ +T(2P0221S043T544235R(JUW*2T0B 2VHO4PH/VЯ06Rp +Ba  +endstream +endobj +14 0 obj +77 +endobj +15 0 obj +<>>> /XObject <>>> +endobj +16 0 obj +<> /Length + 17 0 R /Resources 18 0 R /Subtype /Form /Type /XObject>> +stream +x+ + +endstream +endobj +17 0 obj +12 +endobj +18 0 obj +<<>> +endobj +19 0 obj +<> +endobj +20 0 obj +<> +stream +x]SAn0<&@2W"k@0P$u\bIN=Q;Ѳz>iϼovrd;y4I_MUȧuquU^^|2V?r{=r.7KO< BV$YW'zᶨog~߀Ts ^z, Tx˹^"ai{.Z +endstream +endobj +21 0 obj +452 +endobj +22 0 obj +<> +stream +x{y|ս93}Ѿk$K&.cFC"⺜ݔmZ*WnY;jivڢ]hՄԿNscX/W_6[bxJr9V_DOF89ͮSGƙR&4V="+l"MIRIV^HgQIvNJJUԠ\'`6ذ&Ά,Ƞrf4&a2B$⚸mpGU&GtNJǟz +ri(ҲDv\k=cK;2 bq52s~JpQI&|D:PJSF]fПԨo8D}]4Y~Q~^ Nh`yR &aFq(\A +_5Z0w;~EWfY9aZZqh5+< 3eA ="Ջh>wrF2 lhyCX)RYY{|C骩us*Fr[/ٜoχzp:$!=A#> +()eW*bcd(Tz2c{j(?PN*FdYM:M胪 3lFkJfP![-P'E婙&**MɸlφboK_;y7o7I+|=n,M=q_~TɰwE4\PλqZk ̟b2r7o;  ΩѐB|G'@L (܅cyIXf8O*#QbJ`Y6;NfF@0X4'St;w|^/_ x-u+EA1\a8(c4`F;T#UIo8%*ІAM;! 6Jݢ?A+fތ+$_(ĹՊVB +B$+$Ȫ0z0Ǖ Cm7( mb\6Zm: +၃$r9FIܷPJmxt0X,W;h,1e)H f Pv_#E6J;wTs`jϬ+ö;NYsnz%T5]geUl@,<^!(0Ŝ^Q:)x$˲EF ٬sehD0hΐh+4PA'o6W񼹪U%8水9Q +'d*h{)UQFajROsށt2ґJX&;E-*yXMւI:#Gɠnv/]{-6br$F2uFO}KӈT ۂ".ꯣME/—~/igmvn`:m?uL#iA *0?󰗓^:yeSԘNfDj,e=z{Hdm^M:=^2Q_@Zճ0( C /3~%U W}ҹtYmު;b3ҢVmuGV낹u>lM +5|8SIȨS*=֒{tw1X{+0:: e,?F\ Z5!ih ıI2~Dyo1?OuqjÍSȍ.fWo *q`*1gf3ٙx-UaggP_+D\d<^4bݚp2yMPtJo󢟤4 +& 1U].2BPF2[Wұ#OmvΓ;6o вΧ5OqۆUk.SMwF?Tu׿wv(/X^O;E|͌Xb`7\WNpɩ?ehbEhC4 K+x +"Sb_'bTnyPUR'Q>$  ;,|y}@$QT]չ"@c=6 = N-[-p%1֨aIl &جvd`F@ķ]'QH`#ND ȎN˸` +.j)R)EF {PGxbUڐt:X xLʹez2xbڼ9=h$*\ѿ<_G]_O#HFk mRZq#UM릜i0j'~c}R1fEvq66uԆv燚: Qk"SQ:ZckR[ݱ:؜C:2r"b$v2b8;W¾uI%+A0ا{C8OQ>aCl8^E5J!l8#8$T0Uqkg^TReg]cK훍n&nq:děܻID_N3li2Uy؎Hd$?ȇϥ}lڕ`W )E=q怕H"qc32&+@褜,gqIԝVyT0x'N~ޅ?JWIbV(4]8Wl.>re"yv"DS;?(L>0B߯Dj݆*bb ]lJlv `B@Yf!Y2C8ɛ=*x'+/ + 9q +RDc؃/ )wO8F|NhTkxj}".: ,HfNpQN]CJ6p8'TN;yKx{_լ }t(қLb0I$?,b(!wɯ3D)(B BhGe#Qg#8EuS9Z^[3l|"5G +_2Ξս;Zo/IާisZg*_@5~Fk8?J+[yVhkV G˨;$bD"noqR"C".Qq8$$r/d.6HC(%Sbx?WLNkxN/ F14 HԼ226p+cLJįKdE%:jaNI&K)>:TC %#D0.HX3Fգq-v>Utksѭ=-ߵpPSWr6 qy>Q-uUʔZ!E쥐. FeFѹ4bpazx^|;p xQ uyTD6G>?r'4<' ݛ6;~>T/leςzng>vjZ]{ GVT]x1jn?[Rқ,odN%-)-qy@ ;-JGʆ|3bXjZE$"4S@e# BDsc80&fPeR  Pr.R(m=cVVlGDF[|UqV'Z28h -ri?㛋iuG\G{Y馱ǘJEVl@Մ9 +pEPjSt yloywҧ$LV0޴zl,k16+E>ˆX{#CbЛMxaM.ϷԗcL[WmqH XeM*&g(ogFl?ıtsxW7UU/Mcm=dl TYK6 6܇e^Xl$)qBvR$$D +:6|%6yp#/L0Z2GL}]{~8vǏ~rMeY_˳g-&s_|( +Djp[1_J/ "Y劒@I0pJW*Bސ<J<5z 5JRA+X?em +BP2߱na>fbgq(e.叨mwO1Y|[n7hz(jFi_}N*نtz;°zk)Rdssr[ ּŎ9UŜ \3+ +ϹLa",azĞÔ%]w@fʭ++.J=f,aJP% +tq`0r%B:aF[@&,'Hmcm[MDJat@JmּUXwKk1G*)=YmuöbB6oFO3@1>5ofjܝ'jƼjB݊5b؍j3 jvcʇv1 $qL-?P# raaI;yC0o@0B? ax8O`->o!\˵S~팕o, *&w5/۰˱2*s|4H8SAn5+/-nJRF#dlI$dUDJC cdTَ}8ίU! Q*7סP[ItTUH l>e`k~T_ wS2q)EcD'և/bO]eZ#- +#Kj$օA|ݹhf[Z2;\,c֑ۥQ岸N޾}X`FD\2QjG"!t7Y3Q]G[ ~O.82(C+bWqvni_Ri|(0ᔖd$"J as7?G9ΨsN"A$XY[,1yǓ~vM?ʚ(RYVʖ왾ŕDQyQD3Ջ) ̾)Kfz]e _ʋ8A hYb,lrKw[K1`5 vʄpW$ tQIH&d8gA{S9ɍ3nŽwʠRDgq"°|+1 }#fR6̞=NduYHwכb!cf~WY;^dfgv"a?yB6o#+=--;؊];Wne6!Jt۟Ъ{OHC^_@"% Coo;Ʀa,YݛC0IK<)\PӨcb1)!#&! ކFM/NyG;e%;F~{wv\#|NAi?ڕ=>>WJ{Plqˠ\IYw8 .n +^FH_N,6( + +)IZP7bTtW:D訖r3h u= oWl gZZ:A}UUZg1_@oE4<_F/XF-PIu`ހt@B&UQ3Uۥ]BX'gʃ2e2(c6/|SzMo+iG8'&KJ,l}q~SrMKl!}/kO*_Dpbc5f,]%S njϜ_Kת an+[[ SW izpeIm:T7ӢQ%7Kr>&fZ̮츭:0\3 &-/!bѾ^;aDx!D[ݷ2=vi٭#_JqH+ :!10\[b ((eK!i)m+%426+DU5&BWrƾ0Vv٨f->ک ֻ>#׺MR5?"-5ѲU]>nVk% %ˆ9D6 D1U,g1Ų1ͤB!k$kɛVݍul1$յMv<ڭketCYII+e妖bR^K˴ipo Sɟ&S^RoB0AG<q|njl562g햎HG(F4=^a)T$h?9zbUB%drLEWTBӹszcW|0~UL6@Hhnfӽpk-"A$wʌPۧgjQ平-:P%1 ? n_7p|~iyulҳ_1Y',N0aGƾu׏\ɡ"%䖐nYP2@} P,甭v//[W"V*]&T3 F0hhk`\QETT0%7LA<.'RNQ{RtDį}@@ê"hY.Ѡ\,0w1YkXAű<B$K7g}rc6YAj1coYOJuK7y]ms^cK6A솙74ƾfCfi: }ьd/V!=]oDhw?JK,4h ȨjӔK zN[N\,K,WK +GYDB<.gq3L"-Yޣlbߋ/{#V1|iEX SʧmN? yt Bg'o ùp w{]~WU3>os`QסD7o< @K2|O\2dMn;phvL + ϓ}'JM-x#zh D ATqc9 ˂ixU]hEtWhMe7W8V8@Q T:}M[~~s7o3Kh?~ ~G{3x +LF޽@7nAf0F??E>l 8 F` #}<]WW9zZp#TipC9(̎-B8 ~_}ȡzqx/ViX w`c5\:LTcQ,RK~]aY-fѠi52D,"p R8ln:cmn;R[Z}gaEn_Uw\UwNg p3? h`膡~u3/njZiZ=liXӃhd(plUgQH)8#Wе}g`s- +XsS HUamx5ano*0IGw'wt[˗సaX"k0du),˙KA[ }M<5L jq5M=4e;j5tv؆4 +ZndÇ̫]|uhhkɽnmF/<Ġ5GC1 +X޳5KlZڻBxۅw.mEjhi9Ӵ|:t Dk.4.@=uNWb̒F[~'Zz +-`zkkbХ)"qwCtWE>q f,)}_lf{WPВK imkAޅ:;^pTO/B9yNWt*@,K! Q}NʠBmCt?7@6F+Ri(n;RTݕRch>zba3P외AT Rėުah7+ú.܆uK K0aSKCh.0TxX5bvQٛ˴^j|Ka< VgD 1Go/&kly{:'w$('2LGҙˠe'MbRD|`6G@/.S0+wD;h(AZT: )h'8HOݠՄt*b k]v#X%Y O'AϞ0?k|9ZL?G 9Pb900ÏEX_ag#q>*ޖw/)T|Eu2E_kz5OiCڋEFva [͟?C@xC7l10?w1KKzsWy%HMym~}=z_^\h@ +EvJ mya+-t>|O~H=z`끣`S}^EXJ*#ݿw]XWr?+{{vY̅g09YI= ܧD¢Y{}O)H>E?''ߐ?PW TJW4 }DۇqB?<>ѣ`){~=[i}H>O?JڟC?t?idcGA+}렇eWevjv PAA8N_=yvNwz;Kz<;aξK#d(. #=&fw`4:J"s6BN > +endobj +25 0 obj +<> +stream +x]Rn0+|LbHJ/Pi?ː]oJ=3ޑ٩{=ΦU|.E)h*4ߖλY4>psY&wOv>ÃRfoBEN=S52m+-8> +stream +xz{|93w6;{^Iv7,lB $ *(5 jrb"о}QimmՂ>=3>wf̹Μ;}g@KVw'p_r:vo|wY@/_qY6=]X<+PU.;җQڻb NVYu]z:!΢4=> (}|ʀ%)F_z%1(w: + iS l xƹx7 ^''F:Ve@ܛ,([]׻)pw^M= IzC!3U'WW2]-I~$KvdDs. r*A&XܕaDXe0Pf Q*CEP&ro87pT[~9ΣqTAӓO?-hi$smm{W<:Wɩ +?\-=X6jdJS8TUR[\\C!i`POQls̞~>BFHk3Bl['hd$ƏZfjQTg|Z(B# 2@Ea8hF#,8TF=رt+}դn|J.v#s&0]so _ Oé 2fvұ$]^7/痕/7;OOƲc󎧣> +:"T M)I! 3wE9mQC}>љIDvRzXJzWLR#<: B)!E H1Qd* J"U (+P&#)Z4MdC)1 d⥉ MT#7'kKK'6N-1 ޼<>䊃dsdH#xГjV\w*B /(钙%h߯4cg\`*`EJ+vV ˚*Ʉn+/HZ'+$Pɍ5 + aHf9RT[0QmLUUkqF8xt \&v+3j={(Pa 5X-_xj{:\ȸvЩ a<"UT uIhl~<ء=T964> \<?w{$Va`-8#mNcՄIeR1?IQWQhq?WqD,5UNdJEidb܏INK/\C+'h.# gxA(Q;Lx$w_Q\e A.3gaZOm49Jw`VA>o/KD@W\JTT8}KZl4eXky@ (ǁ̕u%y>#J ",VZ41?xu~$ +Nk ˈ'N}y=H5&v{[﷮ʵJ97;hm\TWvZh*T[++Z+:+V7;6>}7=3fh3qDi햹k+zaZ[CCݏ7m80}zNR=LvJYwN8Q… X+~  WWLa^QIz8;z!eS!MBE3QSM8p@)Ed#E'1(LThhA9YL1QkJlYl3/11=kXUn&IY: 谚h7\FEB4K2̇5x:5emhoV!`n{=8c`5$c]!Gʡ}V܅3⚾C8\ .fA/82N疬<8]m~K{[ YĎ{.낋rBǼU@LxG, ?Da`IާH(:7)6JMKq~'T40v+x>xk|gPTѡ~ZayŏcbU%XXh@AGgqK& ӭ3Z_iUQ;KdIi/%bOJ:kbٝzzrw6Nο3 +w$JsIz"gqi}.;:)olcH)L ,@` kS[ӹlQ=kc&|t9=f_?cƺ1|h*)YмkrjUm57Z%um4Œz0|+MjjV%痯Xնɴ6U`*ZJӕ"0RPU;XI-5f^xj,MOǟsRG'G(/Ha$\TM&WD&wn®ٜɈ +MAl{IZ]kmMEdwus;RMؤ;tM~ӿ^ >ai B␯dCjԊjeI\qB KB[JB C%I$U*%JHH8-; +&:g"fyx%U*T*TdiPb%b=l-hƠ~^n(ԋkˬb٣ Dm Ә(m*,b-z}lzќ?ћmZoeKfp $` tnr2^Bxv8{6L~5wcC3z<uLT{zyldR5U!!K,K4 (Yr FťYOy0K gW'Ic@*wq殰f2< ii-h.gq:&!c!V +#|4lMқ IފUZi36WVºI!}LhKY)%q؄w+ ׼[k2#m<fʌzMGS6 APZr"Ġ> m˖>SwWrEգeGxƃ`!ؑZ,KJ{ǥ3($1aIGxc#D֝P&AnGSz3X`~+njr!& ` yko5w +PYMӴ7х˃~/$/}qza'vq͝'2(TՍ*b}}$jfy}h`VZ_y~\Sg ѿ@lKeSAHyWDwi_QjhWH W:R˃X0Q*pb* b@_ ^'p?aGF}Lca/9sr +,DR RR1%Nq4?ӝcx [Y(D)pJAY"'} ?Ҧ&0Kdsë_=fuIvR5 |&pf=9L 6 U!WNAa:Pnƍryݬ[ݎ(CAџ_sym~g<5 ~S +?'N)M7Q +VlYabp4`FuҺKiqFz;Bhc1dJp|a?P Q6vbVCXDCi5ߑnƺxn(Y̕ O] kQD$&v k.pɪt*hؐИ4cyz1 KpNgpB}FS[)؋!Q]/fvBqf6Y%3MfW'( ~8ܚr_gs%>ouO˭V%w҈䩛Xz@VWrtٍ.gOJCVWa[ˊV$,b;E!M6O97(]9яEy2UOx(w{0Za6S&$娤F$fdC=Wstut#4dQK j ;O4UV!ωeVkZ(/UK,:\ + ha3Dp g0ka&d3$EdCfy gJj($(kɼܵ7g:; ZXp ߴĬ' KTGk\_Xb:oyCv/j@`=w +hA/`{`el9;3:*|Cp&D,PyzWa(<Ρt;wS0cFN$Pp|i-hN¾1:$TB+i_:FħH_Q)!oyqH)g~2&pwǗ=p{kZ[;v0S6Vp;؂T0.*QtfĞ^}`>\נn] Zydӌ=^=M(Ήp: 7p;H&0t-?o>4Іq/Z#=Ly7=ex Vgg*Ƿ@|^2 @*Y]oJ#P$y9e\NF[-fѠQVV)rTBb0kk?MvQZL>>/,sK;?v]Od1 > C@x 4LGo*6JwҷfNԢlG|+Ou\ReiVPTbni0!=4Mi$g1_ZVfS;QGOB%%Ȑc +9]+ \C@6 tt[̱7pzڴt'zk[;juvv{ᡎ,չC4af{ݭK%R)X*q8H +T!)n jN{ɩ]F]P$hbdxQ[gS`1Jdf,Xl0NXiP7Vry'rFd/GRfCzŸ,4MByP׎۱B BLF+&k xIxME.K !fo7mv6=p] +9ő +zTZ1P\JH #AW٘+l QYD$F ċUR{倓F )% "]|8GAE{.G/H2`q& MHߗ͕KW,T|ܢjSZИ5NվPb!3Ͽ?=zmL T/HE9^BxO袖JCa۹>3ѕzNWh??LsJ*#s4S>΢GgQ˟I= N غ]ةs]ڐ9:!ڞw%ѿ@Zܶ|]H~Wu / 7ttg3X뙧`?Ϡzu jU%f3om*澭lŶs/P~K3Q]_ODz޽,Mtub7{"d(- #=u.Fkw85Vڦ1-IC m 1fS1C  1$S5ZBRKer5NHbjŻ0F +yiU$O֒$QX+>c<` 9NI9L[ fٓ&gcig`f6U-h? :Pn{,Dvx,n BZ( };vOXGGؙ6=F.g;݁W$ؕ}dw\iбapxf(u֍ oC: +PU 1ֲUw +endstream +endobj +28 0 obj +7946 +endobj +29 0 obj +921 +endobj +30 0 obj +<> +stream +xX]o}Ki  (.Pm>,B[$tV6qrQ#<w/F?TM^FhnbT)1izJ(jmC9/!Z$/bӂד}FsWpC:yFx F ;#G$ x$$MDn8SzB~(Id=;yEGW:<6BlnnM>V>ӁyD!+o)zcJ~_z;Գ=-o߰]/|{3ZSk׃G8"HEot;TCi}8~=~3Ra0t▯+~拸WW >pf׫{N)jG0Kf-`sop5`U|+x!榩?ΟᕟqXY S%ug]mQ&N#p`DSO*9tk3mgj1nQq}zUfSmQr{kS1 է?7{~xģWJ{ZcGCDZf56@W[sP>bDciq&P8e$ ƲGʥ(V"%ޑr^W3J$]l1!P+JNI%eKe%Oaw@Jn\m ʓl%[Ei[:LW1E:e]-qR"S))Ł"Fgˢ r! !몙o泿n^-'i(g3"0"崕4F;w\⒵2or/2͖00qsT5m`[5`d\rrȆN=z^VjѼT25C!ei'y*f_#4INV֕1Ii&>h{\JIeh\WUӸ\CE%葙SH(9.o) (N3J&^NV~=Wr gV㽌1HLaF֦-^)MRY PT{u;r?iQ Q-RΟq\hbIF{J-R[QD>/csnIH9?8ux?,&$8Ё>mBgف3;V +@e;¬b!ʩS#1ad`A9l7.Xjb#թ*M!O>pЎ;-PS9 +endstream +endobj +31 0 obj +<>>> /Font + <> /Pattern <>>> +endobj +32 0 obj +<>>> /TilingType 1 /XStep 1588 /YStep 2246>> +stream + /x10 Do + + +endstream +endobj +33 0 obj +<>>> /TilingType 1 /XStep 1126 + /YStep 200>> +stream + /x12 Do + + +endstream +endobj +34 0 obj +<> +stream +x+*253V0BC˥h^_ahB _ +endstream +endobj +35 0 obj +42 +endobj +36 0 obj +<>>> /XObject <>>> +endobj +37 0 obj +<> +stream +x흡TWƫYII"$4YIEIMQMeMf% f%?\Y C`wvy;~lw_g#@r>}<?>HQWN5Q[fjeGm~Zޗoy&~'{/TWN5Q[fjeGm~ZޗzE0' ^!"uL} qi89j}WH]S~o`>N0mZ_"Rԧ`'L۟֗t~tڟg%jթ?+߬P_ͷO+=n]M;|L:Goji'{_M X{L:Goji'{_M/ {byB9Zu7+W-?jJO`VV(B06$&)j >Y?8$SM?ԧ?K_`R'u'g L +{ӓŦGRӋOr߿~˓go蘺Zu7+W-?jJO|3D\<wWκ.NNOx7UbE&O/v2օ7}Fc't>1Y?`Zguc}L_vL+|ߐ o3:q-%8ae6XF]Vd_͟)?1)d/`"<Ⱦ.?4B0Ld_ie +!& o42Lׅ7s`FBڎI~"j|Z!|3#j›]oм/XqjuPj]d_ԟaX]o1Dt~bEMzmպZɾ.?V|R/ !& of)Z j]d_i;SxպZɾ.?vhU7u}]x3S6b o=8tÞo1eօ73guV[ Q/c6 +U7u}]x3SF_aue2nz<{G޽68w1|bJVW+ׅ73guV[ Q/@bDyZW+{?+|PMB&͉VW+{?+|PMB&͉VW+{?+|PMB&͉VW+{?+|PMB&͉VW+{?+|PM2G鷺Azu}]x?+|PM1QCdzG]`GER vml_nLg>iE`ε?/6@`?NqsWj>; {XJ48~5JM7bg_5aS̃iqS>m^| VKBԎ~Z_Wj>%{!4LOjzdi &[5=}^4xv-烚پd/`f<~AMl_ZI:=zx|5JM7ڵ5}qm(C:[[^d?ռ"J'=_MO;u`|&!UQ:jzة3{!4d?뽭(=c'ɮ){[Q 5=QL{>JO{]Sjz |&!\mE-Do0]va.~{փWhԿ9ƺW/{+UOjz򹐘+pLz~c+ٗWQLygEc'm>lvA-a^Xo V߫җ番~o5=O +endstream +endobj +38 0 obj +2651 +endobj +39 0 obj +<> +stream +x+ +T(2P0221S043T544235R(JUW*2T0B 2VHO4PH/VЯ04Sp +Ba +endstream +endobj +40 0 obj +<>>> /XObject <>>> +endobj +41 0 obj +<> /Length + 17 0 R /Resources 42 0 R /Subtype /Form /Type /XObject>> +stream +x+ + +endstream +endobj +42 0 obj +<<>> +endobj +43 0 obj +1910 +endobj +44 0 obj +<> + /Count 1 /Next 46 0 R /Parent 3 0 R /Title (Social Media URLs found)>> +endobj +45 0 obj +<> + /Count 1 /Parent 3 0 R /Prev 46 0 R /Title + (Validated Instagram Profiles)>> +endobj +46 0 obj +<> + /Count 1 /Next 45 0 R /Parent 3 0 R /Prev 44 0 R /Title + (Google Reverse Image Search Suggestions)>> +endobj +xref +0 47 +0000000000 65535 f +0000000015 00000 n +0000000186 00000 n +0000000299 00000 n +0000000450 00000 n +0000000511 00000 n +0000008779 00000 n +0000010208 00000 n +0000011202 00000 n +0000011320 00000 n +0000011753 00000 n +0000012230 00000 n +0000012461 00000 n +0000012480 00000 n +0000012705 00000 n +0000012724 00000 n +0000012804 00000 n +0000013028 00000 n +0000013047 00000 n +0000013068 00000 n +0000013299 00000 n +0000013825 00000 n +0000013845 00000 n +0000025910 00000 n +0000025932 00000 n +0000026168 00000 n +0000026600 00000 n +0000026620 00000 n +0000034655 00000 n +0000034676 00000 n +0000034696 00000 n +0000036680 00000 n +0000036809 00000 n +0000037040 00000 n +0000037277 00000 n +0000037464 00000 n +0000037483 00000 n +0000037563 00000 n +0000040407 00000 n +0000040428 00000 n +0000040653 00000 n +0000040733 00000 n +0000040957 00000 n +0000040978 00000 n +0000040999 00000 n +0000041158 00000 n +0000041324 00000 n +trailer + +<> +startxref +41514 +%%EOF diff --git a/utils/config.py b/utils/config.py index b8f79f9..138e4d0 100644 --- a/utils/config.py +++ b/utils/config.py @@ -1,21 +1,20 @@ import json import os import sys -import tempfile from selenium import webdriver +from selenium.webdriver.firefox.options import Options +from selenium.webdriver.firefox.service import Service +# Load config.json with open('./config.json') as json_data: cfg = json.load(json_data) - def timeout(): return int(cfg['DEFAULTS']['SLEEP_DELAY']) - def google_img_pages(): return int(cfg['DEFAULTS']['GOOGLE_IMG_PAGES']) - def google_filter(): return cfg['FILTER'] @@ -27,22 +26,26 @@ def jitters(): def getWebDriver(): if not os.path.isfile(cfg['WEBDRIVER']['PATH']): - print("{0} does not exist - install a webdriver".format(cfg['WEBDRIVER']['PATH'])) + print(f"{cfg['WEBDRIVER']['PATH']} does not exist") sys.exit(-2) - d = cfg['WEBDRIVER']['ENGINE'] - if d.lower() == 'firefox': - os.environ["webdriver.firefox.driver"] = cfg['WEBDRIVER']['PATH'] - p = os.path.join(tempfile.gettempdir(), 'imageraider') - if not os.path.isdir(p): - os.makedirs(p) - profile = webdriver.FirefoxProfile() - profile.set_preference('browser.download.folderList', 2) # custom location - profile.set_preference('browser.download.manager.showWhenStarting', False) - profile.set_preference('browser.download.dir', p) - profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'text/csv') - profile.set_preference("browser.link.open_newwindow", 3) - profile.set_preference("browser.link.open_newwindow.restriction", 2) - return webdriver.Firefox(profile) - else: - os.environ["webdriver.chrome.driver"] = cfg['WEBDRIVER']['PATH'] - return webdriver.Chrome() + + driver_type = cfg['WEBDRIVER']['ENGINE'].lower() + + if driver_type == 'firefox': + options = Options() + + # Essential Docker Stability Flags + options.add_argument("--headless") + options.add_argument("--no-sandbox") + options.add_argument("--disable-dev-shm-usage") + options.add_argument("--window-size=1920,1080") + + # Extra 'Status 1' Killers + options.add_argument("--disable-gpu") + options.add_argument("--disable-software-rasterizer") + options.set_preference("browser.tabs.remote.autostart", False) + + service = Service(executable_path=cfg['WEBDRIVER']['PATH']) + return webdriver.Firefox(service=service, options=options) + + return webdriver.Chrome() \ No newline at end of file