still WIP

This commit is contained in:
shimun 2018-11-23 16:53:09 +01:00
parent 68a15dbfc8
commit 1b9d432af9
9 changed files with 322 additions and 209 deletions

View File

@ -7,6 +7,7 @@ pipeline:
modules: modules:
image: python:3.5 image: python:3.5
commands: commands:
- rm -rf modules
- mkdir -p .pip3 - mkdir -p .pip3
- ./make-modules.sh --cache-dir .pip3 - ./make-modules.sh --cache-dir .pip3
package: package:
@ -24,7 +25,3 @@ pipeline:
- sha256 - sha256
when: when:
event: tag event: tag
clean:
image: kramos/alpine-zip
commands:
- rm -rf modules

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
modules modules
.pip3 .pip3
seafile.zip seafile.zip
lib

View File

@ -1,15 +1,23 @@
import bson#, scrypt from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
import bson # , scrypt
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Util import Counter from Crypto.Util import Counter
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
from Crypto.PublicKey import DSA from Crypto.PublicKey import DSA
from salsa20 import Salsa20_keystream from salsa20 import Salsa20_keystream
import os, struct, time, hashlib, hashlib, random, binascii import os
import struct
import time
import hashlib
import hashlib
import random
import binascii
class EncryptedScreenshot: class EncryptedScreenshot:
def __init__(self, metadata,password=None,id=None,signer=None,password_encryptor=None): def __init__(self, metadata, password=None, id=None, signer=None, password_encryptor=None):
def rand(len): def rand(len):
return ''.join( return ''.join(
random.choice("1234567890ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz") for _ in range(len)) random.choice("1234567890ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz") for _ in range(len))
@ -24,14 +32,16 @@ class EncryptedScreenshot:
self.password_encryptor = password_encryptor self.password_encryptor = password_encryptor
def caesar_encrypted_password(self): def caesar_encrypted_password(self):
caesar_key = reduce(lambda sum,charcode: sum+charcode, map(ord,list(self.id))) caesar_key = reduce(lambda sum, charcode: sum +
charcode, map(ord, list(self.id)))
caesar_plaintext = map(ord, self.password) caesar_plaintext = map(ord, self.password)
caesar_ciphertext = Caesar().encrypt(caesar_plaintext,caesar_key) caesar_ciphertext = Caesar().encrypt(caesar_plaintext, caesar_key)
return ''.join(map(chr,caesar_ciphertext)) return ''.join(map(chr, caesar_ciphertext))
def passphrase(self): def passphrase(self):
# new ScryptParameters(64, 8, 1,32, new Uint8List.fromList(new List<int>())) # new ScryptParameters(64, 8, 1,32, new Uint8List.fromList(new List<int>()))
print("Password units: %s" % (map(ord, self.password.encode("utf-8")),)) print("Password units: %s" %
(map(ord, self.password.encode("utf-8")),))
sha = hashlib.sha256() sha = hashlib.sha256()
sha.update(self.password.encode("utf-8")) sha.update(self.password.encode("utf-8"))
return sha.digest() # scrypt.hash(self.password.encode("utf-8"), '', 64, 8, 1, 32) return sha.digest() # scrypt.hash(self.password.encode("utf-8"), '', 64, 8, 1, 32)
@ -41,7 +51,8 @@ class EncryptedScreenshot:
self.metadata["hash"] = image_digest self.metadata["hash"] = image_digest
unencrypted_metadata = bson.dumps(self.metadata) unencrypted_metadata = bson.dumps(self.metadata)
if len(unencrypted_metadata) % 16 != 0: if len(unencrypted_metadata) % 16 != 0:
unencrypted_metadata += b' ' * (16 - len(unencrypted_metadata) % 16) unencrypted_metadata += b' ' * \
(16 - len(unencrypted_metadata) % 16)
(encryptor, iv) = self.encryptor(len(unencrypted_metadata)) (encryptor, iv) = self.encryptor(len(unencrypted_metadata))
encrypted_metadata = b'' encrypted_metadata = b''
@ -54,7 +65,7 @@ class EncryptedScreenshot:
#print("Unencrypted: %s" % (unencrypted_metadata.encode("hex"))) #print("Unencrypted: %s" % (unencrypted_metadata.encode("hex")))
#print("Password %s" % self.password) #print("Password %s" % self.password)
#print(bson.loads(unencrypted_metadata)) # print(bson.loads(unencrypted_metadata))
fields = { fields = {
"image": encrypted_image, "image": encrypted_image,
@ -64,25 +75,27 @@ class EncryptedScreenshot:
} }
if self.signer is not None: if self.signer is not None:
fields["signature"] = self.signer.signature(encrypted_metadata if self.metadata_encryption else bson.dumps(self.metadata)) fields["signature"] = self.signer.signature(
encrypted_metadata if self.metadata_encryption else bson.dumps(self.metadata))
if self.password_encryptor is not None: if self.password_encryptor is not None:
fields["password"] = self.password_encryptor.encrypt_password(self.password) fields["password"] = self.password_encryptor.encrypt_password(
self.password)
return bson.dumps(fields) return bson.dumps(fields)
def encryptor(self,length=0): def encryptor(self, length=0):
iv = os.urandom(16) iv = os.urandom(16)
nonce = int(binascii.hexlify(iv), 16) nonce = int(binascii.hexlify(iv), 16)
ctr = Counter.new(128,initial_value = nonce)# initial_value = nonce) ctr = Counter.new(128, initial_value=nonce) # initial_value = nonce)
print("IV: %s" % binascii.hexlify(iv)) print("IV: %s" % binascii.hexlify(iv))
cipher = AES.new(self.passphrase(), AES.MODE_CTR, counter=ctr) cipher = AES.new(self.passphrase(), AES.MODE_CTR, counter=ctr)
#salsa # salsa
# keysstream = bytearray(Salsa20_keystream(length,iv[:8],self.passphrase())) # keysstream = bytearray(Salsa20_keystream(length,iv[:8],self.passphrase()))
# offset={"offset": 0} # offset={"offset": 0}
def encrypt(data): def encrypt(data):
#Salsa20 # Salsa20
# array = bytearray(data) # array = bytearray(data)
# for i in range(len(array)): # for i in range(len(array)):
# array[i] ^= keysstream[i+offset["offset"]] # array[i] ^= keysstream[i+offset["offset"]]
@ -109,16 +122,14 @@ class EncryptedScreenshot:
binary += encryptor(chunk) binary += encryptor(chunk)
return (digest.digest(), iv + binary) return (digest.digest(), iv + binary)
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
class Signer: class Signer:
@staticmethod @staticmethod
def default(): def default():
return Signer(RSA.importKey("-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n"),"PUBLIC") return Signer(RSA.importKey("-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n"), "PUBLIC")
def __init__(self,privateKey,privateKeyId, mode=RSA): def __init__(self, privateKey, privateKeyId, mode=RSA):
self.mode = mode self.mode = mode
self.privateKeyId = privateKeyId self.privateKeyId = privateKeyId
self._signer = PKCS1_v1_5.new(privateKey) self._signer = PKCS1_v1_5.new(privateKey)
@ -128,7 +139,7 @@ class Signer:
digest.update(data) digest.update(data)
return self._signer.sign(digest) return self._signer.sign(digest)
def signature(self,data): def signature(self, data):
signed = self.sign(data) signed = self.sign(data)
return {"signed-hash": signed, return {"signed-hash": signed,
"signature-algorithm": "SHA-256/%s" % ("RSA" if self.mode == RSA else "DSA"), "signature-algorithm": "SHA-256/%s" % ("RSA" if self.mode == RSA else "DSA"),
@ -138,40 +149,41 @@ class Signer:
class EncryptedPassword: class EncryptedPassword:
def __init__(self,publicKey,publicKeyId,mode=RSA): def __init__(self, publicKey, publicKeyId, mode=RSA):
assert mode == RSA assert mode == RSA
self.mode = mode self.mode = mode
self.publicKey = publicKey self.publicKey = publicKey
self.publicKeyId = publicKeyId self.publicKeyId = publicKeyId
def encrypt(self,password): def encrypt(self, password):
return self.publicKey.encrypt(password.encode("utf-8"), os.urandom(32))[0] return self.publicKey.encrypt(password.encode("utf-8"), os.urandom(32))[0]
def encrypt_password(self,password): def encrypt_password(self, password):
return {"algorithm": unicode( "RSA" if self.mode == RSA else "DSA"), return {"algorithm": unicode("RSA" if self.mode == RSA else "DSA"),
"key-id": unicode(self.publicKeyId), "key-id": unicode(self.publicKeyId),
"password": self.encrypt(password) "password": self.encrypt(password)
} }
class Caesar: class Caesar:
def __init__(self,charset = map(ord,list("1234567890ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz"))): def __init__(self, charset=map(ord, list("1234567890ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz"))):
self.charset = charset self.charset = charset
def encrypt(self,plaintext,key): def encrypt(self, plaintext, key):
if key >= len(self.charset) or key < 0: if key >= len(self.charset) or key < 0:
key = key % len(self.charset) key = key % len(self.charset)
def addKey(charcode): def addKey(charcode):
pos = self.charset.index(charcode) pos = self.charset.index(charcode)
if pos + key >= len(self.charset): if pos + key >= len(self.charset):
return self.charset[pos+key - len(self.charset)] return self.charset[pos+key - len(self.charset)]
else: else:
return self.charset[pos+key] return self.charset[pos+key]
return map(addKey,plaintext) return map(addKey, plaintext)
def decrypt(self,ciphertext,key): def decrypt(self, ciphertext, key):
return self.encrypt(ciphertext,key*-1) return self.encrypt(ciphertext, key*-1)
if __name__ == "__main__": if __name__ == "__main__":
@ -179,22 +191,26 @@ if __name__ == "__main__":
publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUF1VEVIVldqdHdGeWRHL1ZtSEtyMQo1cXltSWt3WklhTm5RZUttQnp6bituODdaamRna0VEdENFWVNxM1cxVnhsZ1V3MURoOEtUVHIzSzFMUXdjRGZ4Cjh2ZEFyazk5T1g0bXhxcFlXUmJkZkVRazF3VUQ3Y3RyMkU1ZUtRY0NjYkFsL09LWUZhL1BrWTNLc0J1bnMwNlIKbGY4NXNIU1o3Q2ZpcnFINkZvZWZyWERaVkFKT21yUWhUaC8wcUdsQnkxZjEzYzJhMTJzRk5PbW5jTHM4VnA0Zgp0SnhMcjlHb0U0dnNRdlgvNm9Rdmk3UHFXWW12bHdtVVV2aENQUy9KU0Y4a0N1dm12ZmdjRjhPWE9qMVE2aXRPCkxaRHQ4L1p1WmVvazduUzhKeGhSNzNIekhSNGFIV25xQzhVa0l4MUNSL1Z2bFpocC83Z2JTL3kxRU9SUmFWbE8KM1lUa0YyVU1UdEYyQm9Dc1k1Z2xwZWFsRFY2Rmg5WVp3cTY4QVBiNS9jbGV1WHU4dGViWXBvNXl1d0g5RzhtMApicDdtSXJWVW5hQlBDRS9Xc2o2SEZVTG5pVWk4ME44MTQ5Y00ya0xtbTdjK0pOQzRRVXVwYzNpeTNxeWxxaDFLCkZsVTBTV1BBaW5EYk9EU0hxblBpV1YwSUhqQzltVmVBbU1EamcvNlI3dWFBdWJMejBySnNvL29kazBrZldZTzkKOEQ0M0h2N0MrVEcvSWY5b0U3SmRzZ1NDQ2ZlVHVhS0U1WEgzQzNHZWdjaURPeXI2U2QvMDhLcHJRd3Z5N25acApKSnh6cHlyMk1vSi9uOE82TzRJcUR0NzZFQVp6NzZuVXZKelBYU29VR3k3OTQyUU93eXdwUllCNk5iclZwUFVlCm1VQ3NVdHRVSnpoZ0hVNzBFbHFFMmhzQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=" publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUF1VEVIVldqdHdGeWRHL1ZtSEtyMQo1cXltSWt3WklhTm5RZUttQnp6bituODdaamRna0VEdENFWVNxM1cxVnhsZ1V3MURoOEtUVHIzSzFMUXdjRGZ4Cjh2ZEFyazk5T1g0bXhxcFlXUmJkZkVRazF3VUQ3Y3RyMkU1ZUtRY0NjYkFsL09LWUZhL1BrWTNLc0J1bnMwNlIKbGY4NXNIU1o3Q2ZpcnFINkZvZWZyWERaVkFKT21yUWhUaC8wcUdsQnkxZjEzYzJhMTJzRk5PbW5jTHM4VnA0Zgp0SnhMcjlHb0U0dnNRdlgvNm9Rdmk3UHFXWW12bHdtVVV2aENQUy9KU0Y4a0N1dm12ZmdjRjhPWE9qMVE2aXRPCkxaRHQ4L1p1WmVvazduUzhKeGhSNzNIekhSNGFIV25xQzhVa0l4MUNSL1Z2bFpocC83Z2JTL3kxRU9SUmFWbE8KM1lUa0YyVU1UdEYyQm9Dc1k1Z2xwZWFsRFY2Rmg5WVp3cTY4QVBiNS9jbGV1WHU4dGViWXBvNXl1d0g5RzhtMApicDdtSXJWVW5hQlBDRS9Xc2o2SEZVTG5pVWk4ME44MTQ5Y00ya0xtbTdjK0pOQzRRVXVwYzNpeTNxeWxxaDFLCkZsVTBTV1BBaW5EYk9EU0hxblBpV1YwSUhqQzltVmVBbU1EamcvNlI3dWFBdWJMejBySnNvL29kazBrZldZTzkKOEQ0M0h2N0MrVEcvSWY5b0U3SmRzZ1NDQ2ZlVHVhS0U1WEgzQzNHZWdjaURPeXI2U2QvMDhLcHJRd3Z5N25acApKSnh6cHlyMk1vSi9uOE82TzRJcUR0NzZFQVp6NzZuVXZKelBYU29VR3k3OTQyUU93eXdwUllCNk5iclZwUFVlCm1VQ3NVdHRVSnpoZ0hVNzBFbHFFMmhzQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="
encrypted = EncryptedScreenshot({"owner": u"test", "format": u"jpg", "title": u"5x5 Red", "timestamp": int(time.time()*1000)}, encrypted = EncryptedScreenshot({"owner": u"test", "format": u"jpg", "title": u"5x5 Red", "timestamp": int(time.time()*1000)},
signer=Signer.default(), signer=Signer.default(),
password_encryptor=EncryptedPassword(RSA.importKey(publicKey.decode("base64")),"PUBLIC")) password_encryptor=EncryptedPassword(RSA.importKey(publicKey.decode("base64")), "PUBLIC"))
fixed_id = encrypted.id #"W9u9Zm0u" fixed_id = encrypted.id # "W9u9Zm0u"
print(len(encrypted.passphrase())) print(len(encrypted.passphrase()))
out = open("/home/marvin/Dokumente/IdeaProjects/EncryptedScreencloud/Frontend/WebApp/web/data/" + fixed_id, 'wb') out = open(
"/home/marvin/Dokumente/IdeaProjects/EncryptedScreencloud/Frontend/WebApp/web/data/" + fixed_id, 'wb')
assembled = encrypted.assemble("5x5red.png") assembled = encrypted.assemble("5x5red.png")
out.write(assembled) out.write(assembled)
b = bson.loads(assembled) b = bson.loads(assembled)
del b["image"] del b["image"]
print(b) print(b)
#print(assembled.encode("base64").replace("\n","") # print(assembled.encode("base64").replace("\n","")
print("http://localhost:8080/#%s%s" % (fixed_id, encrypted.password)) print("http://localhost:8080/#%s%s" % (fixed_id, encrypted.password))
caesar_key = reduce(lambda sum,charcode: sum+charcode, map(ord,list(fixed_id))) caesar_key = reduce(lambda sum, charcode: sum +
caesar_plaintext = map(ord,encrypted.password) charcode, map(ord, list(fixed_id)))
caesar_ciphertext = Caesar().encrypt(caesar_plaintext,caesar_key) caesar_plaintext = map(ord, encrypted.password)
assert caesar_plaintext == Caesar().decrypt(caesar_ciphertext,caesar_key) caesar_ciphertext = Caesar().encrypt(caesar_plaintext, caesar_key)
print("caesar_key=%s %s -> %s" % (caesar_key,encrypted.password,''.join(map(chr,caesar_ciphertext)))) assert caesar_plaintext == Caesar().decrypt(caesar_ciphertext, caesar_key)
print("http://localhost:8080/#%s%sC" % (fixed_id, encrypted.caesar_encrypted_password())) print("caesar_key=%s %s -> %s" %
(caesar_key, encrypted.password, ''.join(map(chr, caesar_ciphertext))))
print("http://localhost:8080/#%s%sC" %
(fixed_id, encrypted.caesar_encrypted_password()))
print("http://localhost:8080/#%s" % (fixed_id, )) print("http://localhost:8080/#%s" % (fixed_id, ))
out.close() out.close()

View File

@ -2,6 +2,7 @@ import hashlib
import binascii import binascii
class HashDerivedKey: class HashDerivedKey:
@staticmethod @staticmethod
@ -13,18 +14,20 @@ class HashDerivedKey:
assert isinstance(b, bytes) assert isinstance(b, bytes)
return b return b
ensure_bytes(master) ensure_bytes(master)
def derive(seed): def derive(seed):
ensure_bytes(seed) ensure_bytes(seed)
sha = hashlib.sha256() sha = hashlib.sha256()
sha.update(master) sha.update(master)
sha.update(seed) sha.update(seed)
return sha.digest() return sha.digest()
hex = lambda b: binascii.hexlify(b).decode("utf-8")
def hex(b): return binascii.hexlify(b).decode("utf-8")
self.master = master self.master = master
self.master_hex = lambda: hex(master) self.master_hex = lambda: hex(master)
self.derive = derive self.derive = derive
self.derive_hex = lambda seed: hex(derive(seed)) self.derive_hex = lambda seed: hex(derive(seed))
def derive_metadata(self,seed): def derive_metadata(self, seed):
key = self.derive(seed) key = self.derive(seed)
return (key,{"key_seed": binascii.hexlify(seed).decode("utf-8")}) return (key, {"key_seed": binascii.hexlify(seed).decode("utf-8")})

View File

@ -2,101 +2,122 @@ import time
from encryptedscreenshot import EncryptedScreenshot, Signer from encryptedscreenshot import EncryptedScreenshot, Signer
from hashderive import HashDerivedKey from hashderive import HashDerivedKey
import getpass, os, sys import getpass
import os
import sys
from PythonQt.QtGui import QInputDialog from PythonQt.QtGui import QInputDialog
from PythonQt.QtCore import QSettings from PythonQt.QtCore import QSettings
from seafapi import * from seafapi import *
class Processor: class Processor:
def configure(self,parent): def name(self):
pass
def configure(self, parent):
pass pass
def is_configured(self): def is_configured(self):
return True return True
def upload(self,file): def load_settings(self, settings):
pass pass
def save_settings(self, settings):
pass
def upload(self, file, name):
pass
class DummyProcessor(Processor): class DummyProcessor(Processor):
def name(self):
return None
def is_configured(self): def is_configured(self):
return False return False
class DefaultProcessor(Processor): class DefaultProcessor(Processor):
def __init__(self,seaf_lib,lib_path): def name(self):
return "default"
def __init__(self, seaf_lib, lib_path):
self.seaf_lib = seaf_lib self.seaf_lib = seaf_lib
self.lib_path = lib_path self.lib_path = lib_path
def upload(self,file,name): def upload(self, file, name):
return self.seaf_lib.upload(file,name,self.lib_path).share() return self.seaf_lib.upload(file, name, self.lib_path).share()
class EncryptedProcessor(Processor): class EncryptedProcessor(Processor):
def __init__(self,seaf_lib,lib_path): def name(self):
return "enc"
def __init__(self, seaf_lib, lib_path):
self.seaf_lib = seaf_lib self.seaf_lib = seaf_lib
self.lib_path = lib_path self.lib_path = lib_path
self.host = None self.host = None
self.derived_key = HashDerivedKey(os.urandom(32)) self.derived_key = HashDerivedKey(os.urandom(32))
self.load_settings() self.load_settings()
def load_settings(self): def load_settings(self, settings=QSettings()):
settings = QSettings()
settings.beginGroup("uploaders")
settings.beginGroup("seafile")
self.host = settings.value("encscreen-url", None) self.host = settings.value("encscreen-url", None)
if settings.value("encscreen-derived-key", None): if settings.value("encscreen-derived-key", None):
self.derived_key = HashDerivedKey.from_hex(settings.value("encscreen-derived-key", None)) self.derived_key = HashDerivedKey.from_hex(
settings.value("encscreen-derived-key", None))
settings.endGroup() settings.endGroup()
settings.endGroup() settings.endGroup()
def save_settings(self): def save_settings(self, settings=QSettings()):
settings = QSettings()
settings.beginGroup("uploaders")
settings.beginGroup("seafile")
settings.setValue("encscreen-url", self.host) settings.setValue("encscreen-url", self.host)
if self.derived_key: settings.setValue(self.derived_key.master_hex()) if self.derived_key:
settings.setValue(self.derived_key.master_hex())
settings.endGroup() settings.endGroup()
settings.endGroup() settings.endGroup()
def is_configured(self): def is_configured(self):
return self.host is not None and self.host != "" return self.host is not None and self.host != ""
def configure(self,parent): def configure(self, parent):
self.host = QInputDialog.getText(parent, 'Encscreen Server Setup', 'Enter server url (ex. https://servertld/s#%id%key):', text=(self.host or "https://screens.shimun.net/s#%id%key")) self.host = QInputDialog.getText(parent, 'Encscreen Server Setup', 'Enter server url (ex. https://servertld/s#%id%key):',
master = QInputDialog.getText(parent, 'Encscreen Master Key Setup', 'Enter master key (hex encoded):', text=(self.derived_key.master_hex() if self.derived_key else "<random>")) text=(self.host or "https://screens.shimun.net/s#%id%key"))
master = QInputDialog.getText(parent, 'Encscreen Master Key Setup', 'Enter master key (hex encoded):', text=(
self.derived_key.master_hex() if self.derived_key else "<random>"))
try: try:
self.derived_key = HashDerivedKey.from_hex(master) self.derived_key = HashDerivedKey.from_hex(master)
except: except:
self.derived_key = HashDerivedKey(os.urandom(32)) self.derived_key = HashDerivedKey(os.urandom(32))
self.save_settings() self.save_settings()
def upload(self,file,name): def upload(self, file, name):
def derive(): def derive():
seed = os.urandom(16) seed = os.urandom(16)
(_, key_meta) = self.derived_key.derive_metadata(seed) (_, key_meta) = self.derived_key.derive_metadata(seed)
return (self.derived_key.derive_hex(seed)[0:16], key_meta) return (self.derived_key.derive_hex(seed)[0:16], key_meta)
(key, key_meta) = derive() (key, key_meta) = derive()
enrypted = EncryptedScreenshot(metadata = { enrypted = EncryptedScreenshot(metadata={
"owner": getpass.getuser(), "owner": getpass.getuser(),
"format": str(file).split('.')[-1], "format": str(file).split('.')[-1],
"title": name, "title": name,
"timestamp": int(time.time() * 1000), "timestamp": int(time.time() * 1000),
"size": os.stat(file).st_size, "size": os.stat(file).st_size,
"public": key_meta "public": key_meta
},password=key,signer=Signer.default()) }, password=key, signer=Signer.default())
tmpHandle = open(file + "c", 'wb') tmpHandle = open(file + "c", 'wb')
tmpHandle.write(enrypted.assemble(file)) tmpHandle.write(enrypted.assemble(file))
tmpHandle.close() tmpHandle.close()
seaf_file = self.seaf_lib.upload(file + "c",name + ".enc",self.lib_path) seaf_file = self.seaf_lib.upload(
file + "c", name + ".enc", self.lib_path)
if not seaf_file: if not seaf_file:
return return
seaf_link = seaf_file.share() seaf_link = seaf_file.share()
if seaf_link: if seaf_link:
id = str(seaf_link).split('/')[-2] id = str(seaf_link).split('/')[-2]
return self.host.replace('%id', id).replace('%key',"%s" % enrypted.password) return self.host.replace('%id', id).replace('%key', "%s" % enrypted.password)
else: else:
return False return False

View File

@ -1,57 +1,65 @@
import time, os import time
import os
import requests import requests
class SeafileClient: class SeafileClient:
def __init__(self,server,username,password=None,token=None): def __init__(self, server, username, password=None, token=None):
self.server = server self.server = server
self.token = token self.token = token
self.session = requests.Session() self.session = requests.Session()
if token: if token:
self.session.headers.update({'Authorization': "Token %s" % self.token.token}) self.session.headers.update(
self.login = (username,password) {'Authorization': "Token %s" % self.token.token})
self.login = (username, password)
def api_endpoint(self): def api_endpoint(self):
return "%s/api2" % self.server return "%s/api2" % self.server
def ping(self,auth=False): def ping(self, auth=False):
try: try:
return self.session.get("%s%s/ping" % (self.api_endpoint(),"/auth" if auth else "")).text == "\"pong\"" return self.session.get("%s%s/ping" % (self.api_endpoint(), "/auth" if auth else "")).text == "\"pong\""
except: except:
return False return False
def obtain_token(self): def obtain_token(self):
user,passw = self.login user, passw = self.login
try: try:
req=requests.post("%s/auth-token/" % self.api_endpoint(), data = {'username': user, 'password': passw }) req = requests.post("%s/auth-token/" % self.api_endpoint(),
data={'username': user, 'password': passw})
json = req.json() json = req.json()
if "non_field_errors" in json: if "non_field_errors" in json:
print(json["non_field_errors"]) print(json["non_field_errors"])
return False return False
return SeafileToken(user,json["token"]) return SeafileToken(user, json["token"])
except: except:
return False return False
def authorize(self): def authorize(self):
self.token = self.obtain_token() self.token = self.obtain_token()
if self.token: if self.token:
self.session.headers.update({'Authorization': "Token %s" % self.token.token}) self.session.headers.update(
{'Authorization': "Token %s" % self.token.token})
return self.token != False return self.token != False
#curl -H 'Authorization: Token 24fd3c026886e3121b2ca630805ed425c272cb96' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/ # curl -H 'Authorization: Token 24fd3c026886e3121b2ca630805ed425c272cb96' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/
def libraries(self): def libraries(self):
resp=self.session.get("%s/repos/" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' }) resp = self.session.get("%s/repos/" % self.api_endpoint(), headers={
if not resp.status_code == 200: return 'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4'})
libraries=[] if not resp.status_code == 200:
return
libraries = []
for lib in resp.json(): for lib in resp.json():
if not lib['encrypted']: if not lib['encrypted']:
libraries.append(SeafileLibrary(self,lib['id'],lib['name'],lib['owner'])) libraries.append(SeafileLibrary(
self, lib['id'], lib['name'], lib['owner']))
return libraries return libraries
class SeafileLibrary: class SeafileLibrary:
def __init__(self,client,id,name,owner): def __init__(self, client, id, name, owner):
self.client = client self.client = client
self.session = client.session self.session = client.session
self.id = id self.id = id
@ -59,67 +67,73 @@ class SeafileLibrary:
self.owner = owner self.owner = owner
def api_endpoint(self): def api_endpoint(self):
return "%s/repos/%s" % (self.client.api_endpoint(),self.id) return "%s/repos/%s" % (self.client.api_endpoint(), self.id)
def upload(self,file,file_name,directory,link=None): def upload(self, file, file_name, directory, link=None):
def obtain_link(): def obtain_link():
#curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/ # curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/
print("%s/upload-link" % self.api_endpoint()) print("%s/upload-link" % self.api_endpoint())
quoted = self.session.get("%s/upload-link" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.client.token.token,}).text quoted = self.session.get("%s/upload-link" % self.api_endpoint(), headers={
'Authorization': "Token %s" % self.client.token.token, }).text
return quoted[1:-1] return quoted[1:-1]
#curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" -F file=@test.txt -F filename=test.txt -F parent_dir=/ http://cloud.seafile.com:8082/upload-api/73c5d117-3bcf-48a0-aa2a-3f48d5274ae3 # curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" -F file=@test.txt -F filename=test.txt -F parent_dir=/ http://cloud.seafile.com:8082/upload-api/73c5d117-3bcf-48a0-aa2a-3f48d5274ae3
resp = self.session.post(link or obtain_link() , resp = self.session.post(link or obtain_link(),
files={'file': (file_name,open(file, 'rb')), 'parent_dir': directory, 'target_file': "%s/%s" % (directory,file_name)}, files={'file': (file_name, open(
headers = {'Authorization': "Token %s" % self.client.token.token,} file, 'rb')), 'parent_dir': directory, 'target_file': "%s/%s" % (directory, file_name)},
) headers={
'Authorization': "Token %s" % self.client.token.token, }
)
if resp.status_code == 200: if resp.status_code == 200:
return SeafileFile(self,("%s/%s" % (directory,file_name)).replace('//','/').replace('//','/'),resp.text) return SeafileFile(self, ("%s/%s" % (directory, file_name)).replace('//', '/').replace('//', '/'), resp.text)
return False return False
#curl -H 'Authorization: Token f2210dacd3606d94ff8e61d99b477fd' -H 'Accept: application/json; charset=utf-8; indent=4' https://cloud.seafile.com/api2/repos/dae8cecc-2359-4d33-aa42-01b7846c4b32/file/detail/?p=/foo.c # curl -H 'Authorization: Token f2210dacd3606d94ff8e61d99b477fd' -H 'Accept: application/json; charset=utf-8; indent=4' https://cloud.seafile.com/api2/repos/dae8cecc-2359-4d33-aa42-01b7846c4b32/file/detail/?p=/foo.c
def file_info(self,path): def file_info(self, path):
resp=self.session.get("%s/file/detail/?p=%s" % (self.api_endpoint(),path), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' }) resp = self.session.get("%s/file/detail/?p=%s" % (self.api_endpoint(), path), headers={
'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4'})
if resp.status_code == 200: if resp.status_code == 200:
json = resp.json() json = resp.json()
return SeafileFile(self,path,json['id'],json['size']) return SeafileFile(self, path, json['id'], json['size'])
return False return False
def __str__(self): def __str__(self):
return "%s on %s by %s" % (self.name,self.client.server,self.owner) return "%s on %s by %s" % (self.name, self.client.server, self.owner)
class SeafileFile: class SeafileFile:
def __init__(self,library,path,id,size=0): def __init__(self, library, path, id, size=0):
self.id = id self.id = id
self.path = path self.path = path
self.library = library self.library = library
self.session = library.session self.session = library.session
self.size = size self.size = size
#curl -v -X PUT -d "p=/foo.md" -H 'Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/afc3b694-7d4c-4b8a-86a4-89c9f3261b12/file/shared-link/ # curl -v -X PUT -d "p=/foo.md" -H 'Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/afc3b694-7d4c-4b8a-86a4-89c9f3261b12/file/shared-link/
def share(self): def share(self):
resp = self.session.put("%s/repos/%s/file/shared-link/" % (self.library.client.api_endpoint(),self.library.id), resp = self.session.put("%s/repos/%s/file/shared-link/" % (self.library.client.api_endpoint(), self.library.id),
headers = {'Authorization': "Token %s" % self.library.client.token.token, 'Accept': 'application/json; indent=4'}, headers={'Authorization': "Token %s" % self.library.client.token.token,
data = {'p': self.path} 'Accept': 'application/json; indent=4'},
) data={'p': self.path}
)
return resp.headers.get("location") return resp.headers.get("location")
def update(self,file): def update(self, file):
def obtain_link(): def obtain_link():
#curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/ # curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/
quoted = self.session.get("%s/update-link" % self.library.api_endpoint(), headers = {'Authorization': "Token %s" % self.library.client.token.token,}).text quoted = self.session.get("%s/update-link" % self.library.api_endpoint(), headers={
'Authorization': "Token %s" % self.library.client.token.token, }).text
return quoted[1:-1] return quoted[1:-1]
directory, name = os.path.split(self.path) directory, name = os.path.split(self.path)
return self.library.upload(file,name,directory,obtain_link()) return self.library.upload(file, name, directory, obtain_link())
class SeafileToken: class SeafileToken:
def __init__(self,username,token): def __init__(self, username, token):
self.username = username self.username = username
self.token = token self.token = token
def __str__(self): def __str__(self):
return "%s@%s" % (self.token,self.username) return "%s@%s" % (self.token, self.username)

72
imports/settings.py Normal file
View File

@ -0,0 +1,72 @@
from processors import DummyProcessor, DefaultProcessor, EncryptedProcessor
from seafapi import *
class SeafScreencloudSettings:
def __init__(self):
self.processor = DummyProcessor()
self.library = None # (Id, Name)
self.library_path = "/"
self.token = None # SefileToken
self.upload_scheme = "regular"
self.url = None
def _client(self):
if self.token and self.url:
client = SeafileClient(
self.url, self.token.username, token=self.token)
def c():
return client
self.client = c
return c()
return None
def load(self, settings):
self.client = self._client
self.url = settings.value("seafile-url", None)
lib = tuple(settings.value("library", "/").split("/"))
(id, name) = lib
if len(id) == 0 or self.client() == None:
self.library = None
else:
self.library = SeafileLibrary(self.client(), id, name, name)
self.library_path = settings.value("library-path", "/")
(user, tkn) = (settings.value("auth-username", None),
settings.value("auth-token", None))
if user == None or tkn == None:
self.token = None
else:
self.token = SeafileToken(user, tkn)
processors = {
"default": DefaultProcessor,
"enc": EncryptedProcessor
}
if settings.value("processor", None) in processors:
self.processor = processors[settings.value("processor", None)](
self.library, self.library_path)
self.processor.load_settings(settings)
elif self.library:
self.processor = DefaultProcessor(self.library, self.library_path)
else:
self.processor = DummyProcessor()
# TODO:
self.upload_scheme = "regular"
def save(self, settings):
settings.setValue("seafile-url", self.url)
if self.library is not None:
settings.setValue("library", "%s/%s" %
(self.library.id, self.library.name))
settings.setValue("library-path", self.library_path)
if self.token != None:
settings.setValue("auth-username", self.token.username)
settings.setValue("auth-token", self.token.token)
settings.setValue("processor", self.processor.name())
self.processor.save_settings(settings)

150
main.py
View File

@ -3,9 +3,10 @@ import time
from PythonQt.QtCore import QFile, QSettings from PythonQt.QtCore import QFile, QSettings
from PythonQt.QtGui import QDesktopServices, QMessageBox from PythonQt.QtGui import QDesktopServices, QMessageBox
from PythonQt.QtUiTools import QUiLoader from PythonQt.QtUiTools import QUiLoader
from multiprocessing import Pool,Process from multiprocessing import Pool, Process
from seafapi import * from seafapi import *
from settings import SeafScreencloudSettings
from processors import DummyProcessor, DefaultProcessor, EncryptedProcessor from processors import DummyProcessor, DefaultProcessor, EncryptedProcessor
############################### ###############################
## This is a temporary fix, should be removed when a newer python version is used ## ## This is a temporary fix, should be removed when a newer python version is used ##
@ -13,25 +14,27 @@ import logging
logging.captureWarnings(True) logging.captureWarnings(True)
############################### ###############################
class SeafileUploader(): class SeafileUploader():
def __init__(self): def __init__(self):
self.processor = DummyProcessor() self.set = SeafScreencloudSettings()
self.seaf_lib = None
self.seaf_path = "/"
self.libs = None self.libs = None
self.uil = QUiLoader() self.uil = QUiLoader()
self.loadSettings() self.loadSettings()
self.pool = Pool(2) self.pool = Pool(2)
self.seaf_client = None
def showSettingsUI(self, parentWidget): def showSettingsUI(self, parentWidget):
self.parentWidget = parentWidget self.parentWidget = parentWidget
#self.processor.configure(parentWidget) # self.set.processor.configure(parentWidget)
self.settingsDialog = self.uil.load(QFile(workingDir + "/settings.ui"), parentWidget) self.settingsDialog = self.uil.load(
self.settingsDialog.group_account.widget_loggedIn.loginButton.connect("clicked()", self.startAuthenticationProcess) QFile(workingDir + "/settings.ui"), parentWidget)
self.settingsDialog.group_account.widget_loggedIn.loginButton.connect(
"clicked()", self.startAuthenticationProcess)
#self.settingsDialog.group_name.input_name.connect("textChanged(QString)", self.nameFormatEdited) #self.settingsDialog.group_name.input_name.connect("textChanged(QString)", self.nameFormatEdited)
self.settingsDialog.group_location.widget_location.pathEdit.connect("textChanged(QString)", self.locationUpdate) self.settingsDialog.group_location.widget_location.pathEdit.connect(
self.settingsDialog.group_location.widget_location.libraryEditDrop.connect("currentIndexChanged(QString)", self.locationUpdate) "textChanged(QString)", self.locationUpdate)
self.settingsDialog.group_location.widget_location.libraryEditDrop.connect(
"currentIndexChanged(QString)", self.locationUpdate)
self.settingsDialog.connect("accepted()", self.saveSettings) self.settingsDialog.connect("accepted()", self.saveSettings)
self.loadSettings() self.loadSettings()
@ -43,29 +46,35 @@ class SeafileUploader():
drop.clear() drop.clear()
drop.setEnabled(False) drop.setEnabled(False)
select = 0 select = 0
if self.seaf_client and self.seaf_client.ping(): if self.set.client() and self.set.client().ping():
self.libs = (self.seaf_client or self.seaf_lib.client).libraries() self.libs = self.set.client().libraries()
i=0 i = 0
for lib in self.libs: for lib in self.libs:
if self.seaf_lib is not None and lib.id == self.seaf_lib.id: if self.set.library is not None and (lib.id == self.set.library.id or lib.name == self.set.library.name):
select = i select = i
print("set %s" % lib.name) print("set %s" % lib.name)
drop.addItem(lib.name) drop.addItem(lib.name)
i=i+1 i = i+1
drop.setCurrentIndex(select) drop.setCurrentIndex(select)
drop.setEnabled(True) drop.setEnabled(True)
def updateUi(self): def updateUi(self):
self.settingsDialog.group_account.widget_loggedIn.usernameEdit.setText(self.access_token.username if not (self.access_token is None) else "") self.settingsDialog.group_account.widget_loggedIn.usernameEdit.setText(
self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.setText(self.seaf_url or "https://seaf.shimun.net") self.set.token.username if not (self.set.token is None) else "")
self.settingsDialog.group_account.widget_loggedIn.loginButton.setText("Logout" if self.access_token is not None else "Login") self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.setText(
self.set.url or "https://seaf.shimun.net")
self.settingsDialog.group_account.widget_loggedIn.loginButton.setText(
"Logout" if self.set.token is not None else "Login")
# #
if self.access_token is None and len(self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text) > 0: if self.set.token is None and len(self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text) > 0:
self.settingsDialog.group_account.widget_loggedIn.passwordEdit.setText("X" * 8) self.settingsDialog.group_account.widget_loggedIn.passwordEdit.setText(
"X" * 8)
for elm in [self.settingsDialog.group_account.widget_loggedIn.usernameEdit, self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit, self.settingsDialog.group_account.widget_loggedIn.passwordEdit]: for elm in [self.settingsDialog.group_account.widget_loggedIn.usernameEdit, self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit, self.settingsDialog.group_account.widget_loggedIn.passwordEdit]:
elm.setEnabled(self.access_token is None) elm.setEnabled(self.set.token is None)
self.settingsDialog.group_location.setEnabled(self.access_token is not None) self.settingsDialog.group_location.setEnabled(
self.settingsDialog.group_location.widget_location.pathEdit.setText(self.lib_path or "/") self.set.token is not None)
self.settingsDialog.group_location.widget_location.pathEdit.setText(
self.set.library_path or "/")
self.settingsDialog.linkCopyCheck.setChecked(self.copyLink) self.settingsDialog.linkCopyCheck.setChecked(self.copyLink)
self.populateLibrarySelector() self.populateLibrarySelector()
self.settingsDialog.adjustSize() self.settingsDialog.adjustSize()
@ -74,53 +83,30 @@ class SeafileUploader():
settings = QSettings() settings = QSettings()
settings.beginGroup("uploaders") settings.beginGroup("uploaders")
settings.beginGroup("seafile") settings.beginGroup("seafile")
self.seaf_url = settings.value("seafile-url", "") self.set.load(settings)
(self.lib_id,self.lib_name) = str(settings.value("library", "/")).split("/")
self.lib_path = settings.value("library-path", "")
self.copyLink = settings.value("copy-link", "true") in ['true', True] self.copyLink = settings.value("copy-link", "true") in ['true', True]
if settings.value("auth-token", False) and settings.value("auth-username", False): self.nameFormat = settings.value(
self.access_token = SeafileToken(settings.value("auth-username", False),settings.value("auth-token", False)) "name-format", "Screenshot %Y-%m-%d %H_%M_%S")
else:
self.access_token = None
self.nameFormat = settings.value("name-format", "Screenshot %Y-%m-%d %H_%M_%S")
self.upload_scheme = settings.value("upload-scheme", "regular") self.upload_scheme = settings.value("upload-scheme", "regular")
settings.endGroup() settings.endGroup()
settings.endGroup() settings.endGroup()
if self.seaf_url and self.access_token:
self.seaf_client = SeafileClient(self.seaf_url,self.access_token.username,token=self.access_token)
if self.seaf_client.ping():
for lib in self.seaf_client.libraries():
if lib.id == self.lib_id:
self.seaf_lib = lib
if self.seaf_lib and self.seaf_path:
self.processor = EncryptedProcessor(self.seaf_lib,self.lib_path)#DefaultProcessor(self.seaf_lib,self.lib_path)
def saveSettings(self): def saveSettings(self):
settings = QSettings() settings = QSettings()
settings.beginGroup("uploaders") settings.beginGroup("uploaders")
settings.beginGroup("seafile") settings.beginGroup("seafile")
self.set.save(settings)
try: try:
self.copyLink = self.settingsDialog.linkCopyCheck.checked self.copyLink = self.settingsDialog.linkCopyCheck.checked
except: except:
pass pass
settings.setValue("copy-link", self.copyLink) settings.setValue("copy-link", self.copyLink)
if self.access_token is not None:
settings.setValue("auth-username", self.access_token.username )
settings.setValue("auth-token", self.access_token.token)
settings.setValue("seafile-url", self.seaf_url)
if self.seaf_lib is not None:
settings.setValue("library", "%s/%s" % (self.seaf_lib.id,self.seaf_lib.name))
settings.setValue("library-path", self.lib_path)
settings.setValue("name-format", self.nameFormat) settings.setValue("name-format", self.nameFormat)
settings.setValue("upload-scheme", self.upload_scheme)
#settings.setValue("name-format", self.settingsDialog.group_name.input_name.text)
print(self.seaf_lib, self.lib_path)
settings.endGroup() settings.endGroup()
settings.endGroup() settings.endGroup()
def isConfigured(self): def isConfigured(self):
return self.access_token and self.seaf_url and self.processor.is_configured() return self.set.library and self.set.processor.is_configured()
def getFilename(self): def getFilename(self):
self.loadSettings() self.loadSettings()
@ -128,52 +114,55 @@ class SeafileUploader():
def upload(self, screenshot, name): def upload(self, screenshot, name):
self.loadSettings() self.loadSettings()
#Make sure we have a up to date token # Make sure we have a up to date token
if not self.seaf_lib: if not self.set.library:
ScreenCloud.setError("Not configured properly") ScreenCloud.setError("Not configured properly")
return False return False
#Save to a temporary file # Save to a temporary file
timestamp = time.time() timestamp = time.time()
def tempfile(name): def tempfile(name):
try: try:
return QDesktopServices.storageLocation(QDesktopServices.TempLocation) + "/" + name return QDesktopServices.storageLocation(QDesktopServices.TempLocation) + "/" + name
except AttributeError: except AttributeError:
from PythonQt.QtCore import QStandardPaths #fix for Qt5 from PythonQt.QtCore import QStandardPaths # fix for Qt5
return QStandardPaths.writableLocation(QStandardPaths.TempLocation) + "/" + name return QStandardPaths.writableLocation(QStandardPaths.TempLocation) + "/" + name
tmpFilename = tempfile(ScreenCloud.formatFilename(str(timestamp))) tmpFilename = tempfile(ScreenCloud.formatFilename(str(timestamp)))
screenshot.save(QFile(tmpFilename), ScreenCloud.getScreenshotFormat()) screenshot.save(QFile(tmpFilename), ScreenCloud.getScreenshotFormat())
#Upload! # Upload!
link=None link = None
if True: # if True:
#try: try:
link = self.processor.upload(tmpFilename,self.getFilename()) link = self.set.processor.upload(tmpFilename, self.getFilename())
if self.copyLink: if self.copyLink:
ScreenCloud.setUrl(link) ScreenCloud.setUrl(link)
#except Exception as e: except Exception as e:
# ScreenCloud.setError("Failed to upload to seafile. " + e.message) ScreenCloud.setError("Failed to upload to seafile. Using: %s\n\n%s" % (
# return False self.set.processor.name(), e.message))
return False
return True return True
def startAuthenticationProcess(self): def startAuthenticationProcess(self):
self.saveSettings() self.saveSettings()
self.loadSettings() self.loadSettings()
if self.access_token is not None: if self.set.token is not None:
self.logOut() self.logOut()
return return
if self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.text \ if self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.text \
and self.settingsDialog.group_account.widget_loggedIn.usernameEdit.text \ and self.settingsDialog.group_account.widget_loggedIn.usernameEdit.text \
and self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text: and self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text:
self.seaf_url = self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.text self.set.url = self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.text
if True: if True:
#try: # try:
self.login(self.settingsDialog.group_account.widget_loggedIn.usernameEdit.text,self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text) self.login(self.settingsDialog.group_account.widget_loggedIn.usernameEdit.text,
self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text)
self.saveSettings() self.saveSettings()
self.loadSettings() self.loadSettings()
self.populateLibrarySelector() self.populateLibrarySelector()
#except Exception as e: # except Exception as e:
#QMessageBox.critical(self.settingsDialog, "Failed to login", "Verify your server url and credentials" + e.message) #QMessageBox.critical(self.settingsDialog, "Failed to login", "Verify your server url and credentials" + e.message)
self.saveSettings() self.saveSettings()
self.updateUi() self.updateUi()
@ -181,31 +170,32 @@ class SeafileUploader():
def locationUpdate(self): def locationUpdate(self):
drop = self.settingsDialog.group_location.widget_location.libraryEditDrop drop = self.settingsDialog.group_location.widget_location.libraryEditDrop
selected_lib = drop.currentText selected_lib = drop.currentText
if not drop.isEnabled(): return #not populated if not drop.isEnabled():
self.lib_path = self.settingsDialog.group_location.widget_location.pathEdit.text return # not populated
print(self.lib_path, selected_lib) self.set.library_path = self.settingsDialog.group_location.widget_location.pathEdit.text
if self.libs is None: return print(self.set.library_path, selected_lib)
if self.libs is None:
return
for lib in self.libs: for lib in self.libs:
if lib.name == selected_lib: if lib.name == selected_lib:
self.seaf_lib = lib self.set.library = lib
print("%s user selected" % selected_lib) print("%s user selected" % selected_lib)
def login(self, user, password):
def login(self,user,password): cli = SeafileClient(self.set.url, user, password)
cli = SeafileClient(self.seaf_url,user,password) self.set.token = cli.obtain_token()
self.access_token = cli.obtain_token()
def logOut(self): def logOut(self):
settings = QSettings() settings = QSettings()
settings.beginGroup("uploaders") settings.beginGroup("uploaders")
settings.beginGroup("seafile") settings.beginGroup("seafile")
settings.remove("auth-token") settings.remove("auth-token")
self.access_token = None self.set.token = None
settings.endGroup() settings.endGroup()
settings.endGroup() settings.endGroup()
self.loadSettings() self.loadSettings()
self.updateUi() self.updateUi()
def nameFormatEdited(self, nameFormat): def nameFormatEdited(self, nameFormat):
self.settingsDialog.group_name.label_example.setText(ScreenCloud.formatFilename(nameFormat, False)) self.settingsDialog.group_name.label_example.setText(
ScreenCloud.formatFilename(nameFormat, False))

View File

@ -1,5 +1,4 @@
rm -rf modules mkdir -p modules
mkdir modules pip3 install --install-option="--prefix=$PWD" -r requirements.txt "$@"
pip3 install --install-option="--prefix=$PWD" -r requirements.txt mv lib/python3.*/site-packages/* modules/
mv lib/python3.5/site-packages/* modules/
cp imports/* -r modules/ cp imports/* -r modules/