461 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			461 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
| __author__ = "Lukas Mahler"
 | |
| __copyright__ = "Copyright 2018-2021"
 | |
| __version__ = "0.6.1"
 | |
| __date__ = "23.05.2021"
 | |
| __email__ = "lm@ankerlab.de"
 | |
| __status__ = "Development"
 | |
| 
 | |
| 
 | |
| try:
 | |
|     # Defaults
 | |
|     import os
 | |
|     import sys
 | |
|     import webbrowser
 | |
|     from io import BytesIO
 | |
|     from requests import head
 | |
|     from shutil import rmtree
 | |
|     from getpass import getpass
 | |
|     from tempfile import gettempdir
 | |
|     from socket import create_connection
 | |
|     from configparser import ConfigParser
 | |
|     from time import gmtime, strftime, sleep
 | |
| 
 | |
|     # GUI
 | |
|     from PyQt5 import QtGui, QtWidgets, QtCore, Qt
 | |
|     from PyQt5.QtCore import QProcess, QPoint
 | |
|     from fillscreen import Ui_View
 | |
| 
 | |
|     # Customs
 | |
|     import owncloud
 | |
|     import win32gui
 | |
|     import win32clipboard
 | |
|     import paramiko
 | |
|     from desktopmagic.screengrab_win32 import getRectAsImage
 | |
| 
 | |
| except ImportError as e:
 | |
|     print("ERROR: Missing Module |||", e)
 | |
|     sleep(10)
 | |
|     os.system("PAUSE>NUL")
 | |
|     sys.exit()
 | |
| 
 | |
| 
 | |
| ###################################################################################################
 | |
| ###################################################################################################
 | |
| 
 | |
| 
 | |
| class Fillscreen(QtWidgets.QWidget, Ui_View):
 | |
| 
 | |
|     def __init__(self, parent=None):
 | |
|         QtWidgets.QWidget.__init__(self, parent)
 | |
| 
 | |
|         ###############################################################################################
 | |
|         ###############################################################################################
 | |
| 
 | |
|         # Monkeycode for trying to block input for content under the transparent window.
 | |
| 
 | |
|         # self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
 | |
|         # self.setBackgroundRole(QtGui.QPalette.Base)
 | |
|         # self.setAttribute(Qt.Qt.WA_NoSystemBackground, True)
 | |
|         self.setWindowFlags(
 | |
|             QtCore.Qt.WindowStaysOnTopHint |
 | |
|             QtCore.Qt.FramelessWindowHint |
 | |
|             QtCore.Qt.X11BypassWindowManagerHint
 | |
|         )
 | |
|         # self.istransparent = True
 | |
| 
 | |
|         self.setupUi(self)
 | |
| 
 | |
|         # self.setWindowOpacity(0.3)
 | |
|         # Qcolor = "rgba(255, 255, 255, 0)"
 | |
|         # self.setStyleSheet("background-color:{0}".format(Qcolor))
 | |
| 
 | |
|         ###############################################################################################
 | |
|         ###############################################################################################
 | |
| 
 | |
|         # Change Cursor to select tool on Startup
 | |
|         self.change_cursor()
 | |
|         self.begin = QtCore.QPoint()
 | |
|         self.end = QtCore.QPoint()
 | |
|         self.startingpoint = QtCore.QPoint()
 | |
|         self.endpoint = QtCore.QPoint()
 | |
| 
 | |
|     def showFullscreen(self):
 | |
|         self.showMaximized()
 | |
| 
 | |
|     # https://stackoverflow.com/a/44468898/5593051
 | |
|     def paintEvent(self, event):
 | |
|         qp = QtGui.QPainter(self)
 | |
|         # br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40))
 | |
|         qp.setPen(QtGui.QPen(QtCore.Qt.red, 3))
 | |
|         qp.drawRect(QtCore.QRect(self.begin, self.end))
 | |
| 
 | |
|     def mousePressEvent(self, event):
 | |
|         if event.button() == QtCore.Qt.RightButton:
 | |
|             print("DEBUG: Exiting on Right Click Request")
 | |
|             app.quit()
 | |
|         else:
 | |
|             self.begin = event.pos()
 | |
|             self.end = event.pos()
 | |
|             # self.startingpoint = event.pos()
 | |
|             self.startingpoint = get_real_pos()
 | |
|             # print("Startpoint:",self.startingpoint)
 | |
|             self.update()
 | |
| 
 | |
|     def mouseMoveEvent(self, event):
 | |
|         self.end = event.pos()
 | |
|         self.update()
 | |
| 
 | |
|     def mouseReleaseEvent(self, event):
 | |
|         self.begin = event.pos()
 | |
|         self.end = event.pos()
 | |
|         self.endpoint = event.pos()
 | |
|         self.endpoint = get_real_pos()
 | |
|         # print("Endpoint:",self.endpoint)
 | |
|         self.update()
 | |
|         do_screen(self.startingpoint, self.endpoint)
 | |
|         app.quit()
 | |
| 
 | |
|     @staticmethod
 | |
|     def change_cursor():
 | |
|         # http://doc.qt.io/archives/qt-4.8/qcursor.html
 | |
|         QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.CrossCursor)
 | |
|     # QtGui.QApplication.restoreOverrideCursor()
 | |
| 
 | |
| 
 | |
| ###################################################################################################
 | |
| ###################################################################################################
 | |
| 
 | |
| def sslurl(url, ssl):
 | |
|     if ssl:
 | |
|         url = "https://" + url
 | |
|     else:
 | |
|         url = "http://" + url
 | |
| 
 | |
|     return url
 | |
| 
 | |
| 
 | |
| def read_ini(rootdir):
 | |
|     myini = {}
 | |
|     config = ConfigParser(inline_comment_prefixes=";")
 | |
|     try:
 | |
|         config.read_file(open(rootdir + 'settings.ini'))
 | |
|     except FileNotFoundError:
 | |
|         print("ERROR: Couldn't locate a 'settings.ini'")
 | |
|         print("--> Trying to create new blank one.")
 | |
|         # Creating new blank ini
 | |
|         with open(rootdir + 'settings.ini', 'w') as f:
 | |
|             txt = (
 | |
|                 "# MAIN SETTINGS\n"
 | |
|                 "[MAIN]\n"
 | |
|                 "MODE = LOCAL                ; MODE EITHER SFTP, NEXTCLOUD OR LOCAL\n"
 | |
|                 "TIMEOUT = 5                 ; TIMEOUT WHEN TESTING SFTP CONNECTION\n" 
 | |
|                 "SSL = TRUE                  ; USING HTTP (FALSE) OR HTTPS (TRUE) URLS\n"
 | |
|                 "\n"
 | |
|                 "# PROVIDE SFTP INFO BELOW\n"
 | |
|                 "[SFTP]\n"
 | |
|                 "URL =                       ; URL EITHER IS AN IP OR A FQDN\n"
 | |
|                 "PORT =                      ; PORT IS THE SSH PORT\n"
 | |
|                 "USR =                       ; YOUR SSH USER, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
 | |
|                 "PASS =                      ; YOUR SSH USERS PW, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
 | |
|                 "PTH	=                    ; THE LOCAL SERVER PATH WHERE TO PUT THE SCREENSHOT\n"
 | |
|                 "WHERE =                     ; THIS SHOULD BE THE FULL ONLINE URL\n"
 | |
|                 "\n"
 | |
|                 "# PROVIDE NEXTCLOUD INFO BELOW\n"
 | |
|                 "[NEXTCLOUD]\n"
 | |
|                 "URL =                       ; URL EITHER IS AN IP OR A FQDN OF A NEXTCLOUD SERVER\n"
 | |
|                 "USR =                       ; YOUR NEXTCLOUD USER, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
 | |
|                 "PASS =                      ; YOUR NEXTCLOUD USERS PW, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\n"
 | |
|                 "PTH =                       ; THE NEXTCLOUD FOLDER PATH WHERE TO PUT THE SCREENSHOT\n"
 | |
|                 "\n"
 | |
|                 "# IF MODE IS LOCAL YOU MAY CHANGE THE SETTINGS BELOW\n"
 | |
|                 "[LOCAL]\n"
 | |
|                 "WHERE =                     ; THIS SHOULD BE A FULL LOCAL PATH\n"
 | |
|             )
 | |
|             f.write(txt)
 | |
|         config.read_file(open(rootdir + 'settings.ini'))
 | |
| 
 | |
|     # General Settings
 | |
|     timeout = int(config.get('MAIN', 'TIMEOUT'))
 | |
|     ssl = config.get('MAIN', 'SSL')
 | |
| 
 | |
|     mode = config.get('MAIN', 'MODE')
 | |
|     if mode == "SFTP":
 | |
|         url = config.get(mode, 'URL')
 | |
|         port = int(config.get(mode, 'PORT'))
 | |
|         usr = config.get(mode, 'USR')
 | |
|         passw = config.get(mode, 'PASS')
 | |
|         pth = config.get(mode, 'PTH')
 | |
|         where = config.get(mode, 'WHERE')
 | |
|         myini.update(
 | |
|             MODE    = mode,
 | |
|             URL     = url,
 | |
|             PORT    = port,
 | |
|             TIMEOUT = timeout,
 | |
|             USR     = usr,
 | |
|             PASS    = passw,
 | |
|             PTH     = pth,
 | |
|             WHERE   = sslurl(where, ssl)
 | |
|         )
 | |
|     elif mode == "NEXTCLOUD":
 | |
|         url = sslurl(config.get(mode, 'URL'), ssl)
 | |
|         usr = config.get(mode, 'USR')
 | |
|         passw = config.get(mode, 'PASS')
 | |
|         pth = config.get(mode, 'PTH')
 | |
|         myini.update(
 | |
|             MODE    = mode,
 | |
|             URL     = url,
 | |
|             USR     = usr,
 | |
|             PASS    = passw,
 | |
|             PTH     = pth,
 | |
|         )
 | |
|     elif mode == "LOCAL":
 | |
|         where = config.get('LOCAL', 'WHERE')
 | |
|         myini.update(
 | |
|             MODE	= mode,
 | |
|             WHERE	= where
 | |
|         )
 | |
|     else:
 | |
|         print("ERROR: you seem to be having a corrupt or broken 'settings.ini' please recheck!")
 | |
| 
 | |
|     return myini
 | |
| 
 | |
| 
 | |
| def get_real_pos():
 | |
|     flags, hcursor, (x, y) = win32gui.GetCursorInfo()
 | |
|     return {"x": x, "y": y}
 | |
| 
 | |
| 
 | |
| def calc_frame(startinspot, endspot):
 | |
|     # If dragging began from top
 | |
|     if startinspot['x'] < endspot['x']:
 | |
|         left = startinspot['x']
 | |
|         right = endspot['x']
 | |
|         top = startinspot['y']
 | |
|         bottom = endspot['y']
 | |
|     # If dragging began from bottom
 | |
|     else:
 | |
|         left = endspot['x']
 | |
|         right = startinspot['x']
 | |
|         top = endspot['y']
 | |
|         bottom = startinspot['y']
 | |
| 
 | |
|     # print("DEBUG:  Start:",left,"x",top)
 | |
|     # print("DEBUG:  End  :",right,"x",bottom)
 | |
| 
 | |
|     if abs(left-right) <= 10 and abs(top-bottom) <= 10:
 | |
|         coordinates = None
 | |
|     else:
 | |
|         coordinates = (left, top, right+1, bottom+1)
 | |
| 
 | |
|     return coordinates
 | |
| 
 | |
| 
 | |
| def do_screen(start, end):
 | |
|     """ This Function somehow is the Brain now """
 | |
|     # QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId()).save('screenshot.jpg', 'jpg')
 | |
|     # Hide all windows to take a Screenshot of the underlying interface
 | |
|     for window in windows:
 | |
|         window.hide()
 | |
|     frame = calc_frame(start, end)
 | |
|     print("Took Snapshot on Coordinates:", frame)
 | |
|     # https://github.com/python-pillow/Pillow/issues/1547
 | |
|     # https://github.com/ludios/Desktopmagic/blob/master/desktopmagic/screengrab_win32.py
 | |
|     # im = ImageGrab.grab(bbox=frame)
 | |
| 
 | |
|     if not frame:
 | |
|         print("ERROR: Screenshots below 5x5 px not allowed, exiting...")
 | |
|         sleep(5)
 | |
|         return
 | |
| 
 | |
|     im = getRectAsImage(frame)
 | |
|     send_to_clipboard(im)
 | |
|     timestamp = strftime("%Y-%m-%d_%H-%M-%S")
 | |
| 
 | |
|     tempdir = gettempdir()+"\\Screens"
 | |
|     # print("DEBUG:",tempdir)
 | |
|     if not os.path.exists(tempdir):
 | |
|         os.makedirs(tempdir)
 | |
|     tempsave = '{0}\\{1}.png'.format(tempdir, timestamp)
 | |
|     im.save(tempsave)
 | |
| 
 | |
|     if settings['MODE'] == "SFTP":
 | |
|         upload_to_url_sftp(tempsave)
 | |
|     elif settings['MODE'] == "NEXTCLOUD":
 | |
|         upload_to_url_nextcloud(tempsave)
 | |
|     else:
 | |
|         save_to_local(tempsave)
 | |
| 
 | |
|     clean_temp(tempdir)
 | |
| 
 | |
| 
 | |
| # https://stackoverflow.com/questions/34322132/copy-image-to-clipboard-in-python3
 | |
| def send_to_clipboard(im=None):
 | |
|     if im:
 | |
|         output = BytesIO()
 | |
|         im.convert("RGB").save(output, "BMP")
 | |
|         data = output.getvalue()[14:]
 | |
|         output.close()
 | |
|         win32clipboard.OpenClipboard()
 | |
|         win32clipboard.EmptyClipboard()
 | |
|         win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
 | |
|         win32clipboard.CloseClipboard()
 | |
|         print("Copied to Clipboard")
 | |
|     else:
 | |
|         win32clipboard.EmptyClipboard()
 | |
|         print("Cleared Clipboard")
 | |
| 
 | |
| 
 | |
| # https://stackoverflow.com/questions/432385/sftp-in-python-platform-independent
 | |
| def upload_to_url_sftp(tempsave):
 | |
| 
 | |
|     # SFTP INFO
 | |
|     url 	= settings['URL']
 | |
|     port 	= settings['PORT']
 | |
|     timeout = settings['TIMEOUT']
 | |
|     usr 	= settings['USR']
 | |
|     passw 	= settings['PASS']
 | |
|     pth		= settings['PTH'] + os.path.basename(tempsave)
 | |
|     where	= settings['WHERE'] + os.path.basename(tempsave)
 | |
| 
 | |
|     # Test Connection first or go into fallback
 | |
|     try:
 | |
|         s = create_connection((url, port), timeout)
 | |
|         s.close()
 | |
|     except Exception as e:
 | |
|         print("ERROR: Connection failed. |||", e)
 | |
|         print("\n --> Falling back to local mode")
 | |
|         save_to_local(tempsave, fallback=1)
 | |
|         return
 | |
| 
 | |
|     # If not provided ask for Username & Password
 | |
|     if not usr:
 | |
|         usr = input("Please provide a username: ")
 | |
| 
 | |
|     if not passw:
 | |
|         passw = getpass("Please provide the password for {0}: ".format(usr))
 | |
| 
 | |
|     try:
 | |
|         transport = paramiko.Transport((url, port))
 | |
|         transport.connect(username=usr, password=passw)
 | |
|     except Exception as e:
 | |
|         print("ERROR: SSH2 negotiation or authentication failed. |||", e)
 | |
|         print("\n--> Falling back to local mode")
 | |
|         save_to_local(tempsave, fallback=1)
 | |
|         return
 | |
| 
 | |
|     print("Starting to Upload...")
 | |
|     sftp = paramiko.SFTPClient.from_transport(transport)
 | |
|     sftp.put(tempsave, pth)
 | |
|     sftp.close()
 | |
|     transport.close()
 | |
| 
 | |
|     print("Screen was Uploaded: URL: " + where)
 | |
|     open_url(where)
 | |
| 
 | |
| 
 | |
| def upload_to_url_nextcloud(tempsave):
 | |
| 
 | |
|     # NEXTCLOUD INFO
 | |
|     url 	= settings['URL']
 | |
|     usr 	= settings['USR']
 | |
|     passw 	= settings['PASS']
 | |
|     pth		= settings['PTH'] + os.path.basename(tempsave)
 | |
| 
 | |
|     # Test Connection first or go into fallback
 | |
|     r = head(url)
 | |
|     httpc = str(r.status_code)[0]
 | |
|     if httpc == "4" or httpc == "5":
 | |
|         print("ERROR: HTTP Statuscode for URL [{0}] is [{1}]".format(url, r.status_code))
 | |
|         print("\n--> Falling back to local mode")
 | |
|         save_to_local(tempsave, fallback=1)
 | |
|         return
 | |
| 
 | |
|     # If not provided ask for Username & Password
 | |
|     if not usr:
 | |
|         usr = input("Please provide a username: ")
 | |
| 
 | |
|     if not passw:
 | |
|         passw = getpass("Please provide the password for {0}: ".format(usr))
 | |
| 
 | |
|     try:
 | |
|         nxt = owncloud.Client(url)
 | |
|         nxt.login(usr, passw)
 | |
|     except Exception as e:
 | |
|         print("ERROR: ", e)
 | |
|         print("\n--> Falling back to local mode")
 | |
|         save_to_local(tempsave, fallback=1)
 | |
|         return
 | |
| 
 | |
|     # Had to monkeypatch the pyocclient libary here to stop AttributeErrors on 'share_file_with_link' calls
 | |
|     # (https://github.com/owncloud/pyocclient/issues/263)
 | |
| 
 | |
|     print("Starting to Upload...")
 | |
|     nxt.put_file(pth, tempsave)
 | |
|     link_info = nxt.share_file_with_link(pth)
 | |
|     where = link_info.get_link()
 | |
| 
 | |
|     print("Screen was Uploaded: URL: " + where)
 | |
|     open_url(where)
 | |
| 
 | |
| 
 | |
| def save_to_local(tempsave, fallback=None):
 | |
| 
 | |
|     if not fallback:
 | |
|         where = settings['WHERE']
 | |
|     else:
 | |
|         print("    -> Did fallback!")
 | |
|         config = ConfigParser()
 | |
|         config.read_file(open(root+'settings.ini'))
 | |
|         where = config.get('LOCAL', 'WHERE')
 | |
| 
 | |
|     if not where:
 | |
|         print("No local Screens folder provided, creating one...")
 | |
|         where = os.path.dirname(os.path.abspath(__file__))+"\\Screens"
 | |
| 
 | |
|     print("LOCAL MODE: Screenshot Folder:", where)
 | |
| 
 | |
|     if not os.path.exists(where):
 | |
|         os.makedirs(where)
 | |
| 
 | |
|     src = tempsave
 | |
|     dst = where + "\\" + os.path.basename(tempsave)
 | |
|     # print("Source:",src,"\nDestination:",dst)
 | |
|     os.rename(src, dst)
 | |
|     os.startfile(dst)
 | |
| 
 | |
| 
 | |
| def open_url(where):
 | |
|     webbrowser.open(where, new=0, autoraise=True)
 | |
| 
 | |
| 
 | |
| def clean_temp(tempdir):
 | |
|     if os.path.exists(tempdir):
 | |
|         print("Cleaning up the tempdir [", tempdir, "]...")
 | |
|         rmtree(tempdir, ignore_errors=True)
 | |
| 
 | |
| 
 | |
| ###################################################################################################
 | |
| ###################################################################################################
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
| 
 | |
|     root = os.path.dirname(os.path.abspath(__file__))+"\\"
 | |
|     settings = read_ini(root)
 | |
|     if os.path.exists(root+"qt.conf"):
 | |
|         try:
 | |
|             os.remove(root+"qt.conf")
 | |
|         except Exception as e:
 | |
|             print("DEBUG:", e)
 | |
|     app = QtWidgets.QApplication(sys.argv)
 | |
|     windows = []
 | |
| 
 | |
|     # https://stackoverflow.com/questions/51058236/create-qt-windows-based-on-number-of-list-items
 | |
|     for i in range(QtWidgets.QApplication.desktop().screenCount()):
 | |
|         topLeft = QtWidgets.QApplication.desktop().screenGeometry(i).topLeft()
 | |
|         window = Fillscreen()
 | |
|         window.move(topLeft)
 | |
|         window.showFullscreen()
 | |
|         windows.append(window)
 | |
|     app.exec_()
 |