PageRenderTime 363ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/scrambled_egg.py

http://scrambled-egg.googlecode.com/
Python | 1594 lines | 1407 code | 53 blank | 134 comment | 119 complexity | 299b740a6d5cd97d5e602f606e907489 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. #!/usr/bin/env python
  2. # ---
  3. # An application by Cristi Constantin,
  4. # E-mail : cristi.constantin@live.com,
  5. # Blog : http://cristi-constantin.com.
  6. # ---
  7. import os, sys
  8. import platform
  9. import subprocess
  10. import re, math
  11. import string
  12. import struct
  13. import urllib
  14. import base64
  15. import json
  16. import bz2, zlib
  17. import tarfile
  18. import binascii as ba
  19. from collections import OrderedDict
  20. from cStringIO import StringIO
  21. from Padding import appendPadding, removePadding
  22. from pbkdf2 import PBKDF2
  23. from Crypto.Cipher import AES
  24. from Crypto.Cipher import ARC2
  25. from Crypto.Cipher import CAST
  26. from Crypto.Cipher import Blowfish
  27. from Crypto.Cipher import DES3
  28. from Crypto.PublicKey import RSA
  29. import sip
  30. sip.setapi('QString', 2)
  31. sip.setapi('QVariant', 2)
  32. from PyQt4 import QtCore
  33. from PyQt4 import QtGui
  34. try: import Image
  35. except: Image = None
  36. #
  37. ROT = string.maketrans('nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
  38. #
  39. SCRAMBLE = ['None', 'ROT13', 'ZLIB', 'BZ2']
  40. SCRAMBLE_D = {'None':'N', 'ROT13':'R', 'ZLIB':'ZL', 'BZ2':'BZ'}
  41. ENC = OrderedDict([('AES', 'AES'), ('Blowfish', 'B'), ('ARC2', 'ARC'), ('CAST', 'CA'), ('DES3', 'D'), ('RSA', 'RSA'), ('None', 'N')])
  42. ENCODE = ['Base64 Codec', 'Base32 Codec', 'HEX Codec', 'Quopri Codec', 'Json', 'XML']
  43. ENCODE_D = {'Base64 Codec':'64', 'Base32 Codec':'32', 'HEX Codec':'H', 'Quopri Codec':'Q', 'Json':'JS', 'XML':'XML'}
  44. #
  45. NO_TAGS = re.compile(
  46. '<#>(?P<ts>[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3})</?#>|' \
  47. '\[#\](?P<tq>[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3})\[#\]|' \
  48. '\{#\}(?P<ta>[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3})\{#\}|' \
  49. '\(#\)(?P<tp>[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3}:[0-9a-zA-Z ]{1,3})\(#\)|' \
  50. '(?P<tx><pre>[0-9a-zA-Z ]{1,3}</pre>\s*?<enc>[0-9a-zA-Z ]{1,3}</enc>\s*?<post>[0-9a-zA-Z ]{1,3}</post>)|' \
  51. '(?P<tj>"pre": "[0-9a-zA-Z ]{1,3}",\s*?"enc": "[0-9a-zA-Z ]{1,3}",\s*?"post": "[0-9a-zA-Z ]{1,3}")')
  52. #
  53. # These numbers are used when (re)creating PNG images.
  54. SCRAMBLE_NR = {'None':'1', 'ROT13':'2', 'ZLIB':'3', 'BZ2':'4'}
  55. ENCRYPT_NR = {'AES':'1', 'ARC2':'2', 'CAST':'3', 'Blowfish':'5', 'DES3':'4', 'RSA':'6', 'None':'9'}
  56. ENCODE_NR = {'Base64 Codec':'4', 'Base32 Codec':'2', 'HEX Codec':'1', 'Quopri Codec':'9', 'XML':'7'}
  57. #
  58. C = {} # Config.
  59. D = {} # Themes.
  60. #
  61. __version__ = 'ver 0.5'
  62. #
  63. def findg(g):
  64. for i in g:
  65. if i: return ''.join(i.split())
  66. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
  67. # Scrambled-Egg Encryption Engine
  68. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
  69. class ScrambledEgg():
  70. def __init__(self):
  71. self.error = '' # Error string.
  72. self.pre = '' # Current operations, in order.
  73. self.enc = ''
  74. self.post = ''
  75. self.rsa_path = ''
  76. def __error(self, step, pre, enc, post, field='R'):
  77. #
  78. if step==1:
  79. if field=='R':
  80. pre += ' (ERROR!)'
  81. else:
  82. pre += ' (IGNORED!)'
  83. elif step==2:
  84. enc += ' (ERROR!)'
  85. elif step==3:
  86. post += ' (ERROR!)'
  87. if field=='R':
  88. self.error = ' Decryption mode step 1: %s , step 2: %s , step 3: %s' % (pre, enc, post)
  89. else:
  90. self.error = ' Encryption mode step 1: %s , step 2: %s , step 3: %s' % (pre, enc, post)
  91. #
  92. def _fix_password(self, pwd, enc):
  93. '''
  94. Scramble and adapt the password for each encryption. \n\
  95. AES accepts maxim 32 characters. \n\
  96. ARC2 accepts maxim 128 characters. \n\
  97. CAST accepts maxim 8 characters. \n\
  98. Blowfish accepts maxim 56 characters. \n\
  99. DES3 accepts maxim 24 characters.
  100. '''
  101. #
  102. # Accepting ANY type of password.
  103. pwd = ba.b2a_base64(pwd)
  104. if type(enc) == type(''):
  105. enc = enc.decode()
  106. if enc == 'AES' or enc == ENC['AES']:
  107. key_size = 32
  108. elif enc == 'Blowfish' or enc == ENC['Blowfish']:
  109. key_size = 56
  110. elif enc == 'ARC2' or enc == ENC['ARC2']:
  111. key_size = 128
  112. elif enc == 'CAST' or enc == ENC['CAST']:
  113. key_size = 8
  114. elif enc == 'DES3' or enc == ENC['DES3']:
  115. key_size = 24
  116. elif enc == 'RSA' and self.rsa_path:
  117. key_size = 56
  118. # Read the public/ private key from file, encrypt password and return.
  119. rsa_key = open(self.rsa_path, 'rb').read()
  120. o = RSA.importKey(rsa_key)
  121. # RSA text is max 128 characters.
  122. rsa_pwd = pwd[:128]
  123. pwd = o.encrypt(rsa_pwd, 0)[0]
  124. del o, rsa_key, rsa_pwd
  125. elif not enc or enc == 'None':
  126. return pwd
  127. else:
  128. raise Exception('Fix password: Invalid encryption mode "%s" !' % enc)
  129. if not pwd:
  130. # Only for NULL passwords.
  131. return key_size * 'X'
  132. # Scramble the password many times.
  133. # Can't use random salt, 'cose the same pass must be recreated for decryption.
  134. hash_key = PBKDF2(passphrase=pwd, salt='scregg', iterations=1024)
  135. # The password for encryption/ decryption.
  136. # This is very strong, binary data!
  137. return hash_key.read(key_size)
  138. #
  139. def encrypt(self, txt, pre, enc, post, pwd, tags=True):
  140. #
  141. if type(txt) != type('') and type(txt) != type(u''):
  142. raise TypeError('Invalid data type for encryption: "%s" !' % str(type(txt)))
  143. #
  144. # Scramble operation.
  145. if pre in ['None', 'None']:
  146. pass
  147. elif pre in ['ZLIB', 'ZLIB']:
  148. txt = zlib.compress(txt)
  149. elif pre in ['BZ2', 'BZ2']:
  150. txt = bz2.compress(txt)
  151. elif pre in ['ROT13', 'ROT13']:
  152. txt = string.translate(txt, ROT)
  153. else:
  154. raise Exception('Invalid scramble "%s" !' % pre)
  155. #
  156. # Check RSA key path.
  157. if enc in ['RSA', 'RSA'] and not os.path.exists(self.rsa_path):
  158. print('RSA encryption must specify a valid path !')
  159. self.__error(2, pre, enc, post, field='L')
  160. return
  161. #
  162. pwd = self._fix_password(pwd, enc)
  163. #
  164. if enc in ['AES', 'AES']:
  165. o = AES.new(pwd, mode=2)
  166. elif enc in ['ARC2', 'ARC2']:
  167. o = ARC2.new(pwd, mode=2)
  168. elif enc in ['CAST', 'CAST']:
  169. o = CAST.new(pwd, mode=2)
  170. elif enc in ['Blowfish', 'Blowfish']:
  171. o = Blowfish.new(pwd, mode=2)
  172. elif enc in ['DES3', 'DES3']:
  173. o = DES3.new(pwd, mode=2)
  174. elif enc in ['RSA', 'RSA']:
  175. # Using Blowfish encryption for RSA.
  176. o = Blowfish.new(pwd, mode=3)
  177. elif not enc or enc in ['None', 'None']:
  178. o = None
  179. else:
  180. raise Exception('Invalid encryption mode "%s" !' % enc)
  181. #
  182. # Encryption operation.
  183. if o:
  184. temp = appendPadding(txt, blocksize=16)
  185. txt = o.encrypt(temp)
  186. del temp
  187. #
  188. # Codec operation.
  189. if post in ['Base64 Codec', 'Base64 Codec']:
  190. if tags:
  191. txt = '<#>%s:%s:%s<#>%s' % \
  192. (SCRAMBLE_D[pre], ENC[enc], ENCODE_D[post].replace(' Codec',''), ba.b2a_base64(txt).decode())
  193. else:
  194. txt = ba.b2a_base64(txt).decode()
  195. elif post in ['Base32 Codec', 'Base32 Codec']:
  196. if tags:
  197. txt = '<#>%s:%s:%s<#>%s' % \
  198. (SCRAMBLE_D[pre], ENC[enc], ENCODE_D[post].replace(' Codec',''), base64.b32encode(txt).decode())
  199. else:
  200. txt = base64.b32encode(txt).decode()
  201. elif post in ['HEX Codec', 'HEX Codec']:
  202. if tags:
  203. txt = '<#>%s:%s:%s<#>%s' % \
  204. (SCRAMBLE_D[pre], ENC[enc], ENCODE_D[post].replace(' Codec',''), ba.b2a_hex(txt).decode())
  205. else:
  206. txt = ba.b2a_hex(txt).decode()
  207. elif post in ['Quopri Codec', 'Quopri Codec']:
  208. if tags:
  209. txt = '<#>%s:%s:%s<#>%s' % (SCRAMBLE_D[pre], ENC[enc], ENCODE_D[post].replace(' Codec',''), \
  210. ba.b2a_qp(txt, quotetabs=True, header=True).decode())
  211. else:
  212. txt = ba.b2a_qp(txt, quotetabs=True, header=True).decode()
  213. elif post in ['Json', 'Json']:
  214. if tags:
  215. # Format : {"pre": "AAA", "enc": "BBB", "post": "CCC", "data": "Blah blah blah"}
  216. txt = '{"pre": "%s", "enc": "%s", "post": "%s", "data": "%s"}' % \
  217. (SCRAMBLE_D[pre], ENC[enc], ENCODE_D[post], ba.b2a_base64(txt).rstrip().decode())
  218. else:
  219. txt = json.dumps({'data':ba.b2a_base64(txt).rstrip().decode()})
  220. elif post in ['XML', 'XML']:
  221. if tags:
  222. # Format : <root><pre>AAA</pre> <enc>BBB</enc> <post>CCC</post> <data>Blah blah blah</data></root>
  223. txt = '<root>\n<pre>%s</pre><enc>%s</enc><post>%s</post>\n<data>%s</data>\n</root>' % \
  224. (SCRAMBLE_D[pre], ENC[enc], ENCODE_D[post], ba.b2a_base64(txt).rstrip().decode())
  225. else:
  226. txt = '<root>\n<data>%s</data>\n</root>' % ba.b2a_base64(txt).rstrip().decode()
  227. else:
  228. raise Exception('Invalid codec "%s" !' % post)
  229. #
  230. # The final text must be String, to be used in GUI
  231. return txt
  232. #
  233. def decrypt(self, txt, pre, enc, post, pwd):
  234. #
  235. if type(txt) != type('') and type(txt) != type(u''):
  236. raise TypeError('Invalid data type for decryption: "%s" !' % str(type(txt)))
  237. #
  238. if not (pre and enc and post):
  239. # Trying to identify and/or delete pre/enc/post tags.
  240. #try:
  241. re_groups = re.search(NO_TAGS, txt).groups()
  242. tags = findg(re_groups)
  243. # If Json.
  244. if '{' in txt and '}' in txt and '"data":' in txt:
  245. pre = 'Json'
  246. temp = json.loads(txt.decode())
  247. enc = temp.get('enc').encode()
  248. post = temp.get('pre').encode()
  249. txt = temp['data'].encode()
  250. # If XML.
  251. elif '<data>' in txt and '</data>' in txt:
  252. pre = 'XML'
  253. enc = re.search('<enc>([0-9a-zA-Z ]{1,3})</enc>', tags).group(1)
  254. post = re.search('<pre>([0-9a-zA-Z ]{1,3})</pre>', tags).group(1)
  255. txt = re.search('<data>(.+)</data>', txt, re.S).group(1)
  256. else:
  257. pre = tags.split(':')[2]
  258. enc = tags.split(':')[1]
  259. post = tags.split(':')[0]
  260. txt = re.sub(NO_TAGS, '', txt)
  261. pre = self.pre = pre.decode()
  262. enc = self.enc = enc.decode()
  263. post = self.post = post.decode()
  264. #except:
  265. # print('Cannot decrypt, caught error with', pre, enc, post, '!')
  266. else:
  267. # If Json.
  268. if '{' in txt and '}' in txt and '"data":' in txt:
  269. pre = 'Json'
  270. temp = json.loads(txt.decode())
  271. txt = temp['data'].encode()
  272. # If XML.
  273. elif '<data>' in txt and '</data>' in txt:
  274. pre = 'XML'
  275. txt = re.search('<data>(.+)</data>', txt, re.S).group(1)
  276. else:
  277. txt = re.sub(NO_TAGS, '', txt)
  278. #
  279. # Check RSA key path.
  280. if enc == 'RSA' and not os.path.exists(self.rsa_path):
  281. print('RSA decryption must specify a valid path !')
  282. self.__error(2, pre, enc, post)
  283. return
  284. #
  285. # Adapting password for encryption.
  286. pwd = self._fix_password(pwd, enc)
  287. #
  288. # Codec operation.
  289. if not pre:
  290. self.__error(1, 'None', enc, post) ; return
  291. elif pre == 'Base64 Codec' or pre == ENCODE_D['Base64 Codec']:
  292. try: txt = ba.a2b_base64(txt)
  293. except: self.__error(1, pre, enc, post) ; return
  294. elif pre == 'Base32 Codec' or pre == ENCODE_D['Base32 Codec']:
  295. try: txt = base64.b32decode(txt)
  296. except: self.__error(1, pre, enc, post) ; return
  297. elif pre == 'HEX Codec' or pre == ENCODE_D['HEX Codec']:
  298. try: txt = ba.a2b_hex(txt)
  299. except: self.__error(1, pre, enc, post) ; return
  300. elif pre == 'Quopri Codec' or pre == ENCODE_D['Quopri Codec']:
  301. try: txt = ba.a2b_qp(txt, header=True)
  302. except: self.__error(1, pre, enc, post) ; return
  303. elif pre == 'Json' or pre == ENCODE_D['Json']:
  304. try: txt = ba.a2b_base64(txt)
  305. except: self.__error(1, pre, enc, post) ; return
  306. elif pre == 'XML':
  307. try: txt = ba.a2b_base64(txt)
  308. except: self.__error(1, pre, enc, post) ; return
  309. else:
  310. raise Exception('Invalid codec "%s" !' % pre)
  311. #
  312. if enc == 'AES' or enc == ENC['AES']:
  313. o = AES.new(pwd, mode=2)
  314. elif enc == 'ARC2' or enc == ENC['ARC2']:
  315. o = ARC2.new(pwd, mode=2)
  316. elif enc == 'CAST' or enc == ENC['CAST']:
  317. o = CAST.new(pwd, mode=2)
  318. elif enc == 'Blowfish' or enc == ENC['Blowfish']:
  319. o = Blowfish.new(pwd, mode=2)
  320. elif enc == 'DES3' or enc == ENC['DES3']:
  321. o = DES3.new(pwd, mode=2)
  322. elif enc == 'RSA':
  323. # Using Blowfish decryption for RSA.
  324. o = Blowfish.new(pwd, mode=3)
  325. elif not enc or enc == 'None':
  326. o = None
  327. else:
  328. raise Exception('Invalid decrypt "%s" !' % enc)
  329. #
  330. # Decryption operation.
  331. if o:
  332. try:
  333. temp = o.decrypt(txt)
  334. txt = removePadding(temp, 16)
  335. del temp
  336. except: self.__error(2, pre, enc, post) ; return
  337. #
  338. # Un-scramble operation.
  339. if not post or post == 'N' or post == 'None':
  340. pass
  341. elif post == 'ZLIB' or post == SCRAMBLE_D['ZLIB']:
  342. try: txt = zlib.decompress(txt)
  343. except: self.__error(3, pre, enc, post) ; return
  344. elif post == 'BZ2' or post == SCRAMBLE_D['BZ2']:
  345. try: txt = bz2.decompress(txt)
  346. except: self.__error(3, pre, enc, post) ; return
  347. elif post == 'ROT13' or post == SCRAMBLE_D['ROT13']:
  348. txt = string.translate(txt, ROT)
  349. else:
  350. raise Exception('Invalid scramble "%s" !' % post)
  351. #
  352. return txt
  353. #
  354. def toImage(self, txt, pre, enc, post, pwd, path, encrypt=True):
  355. '''
  356. Any information, text and/or files, can be encoded inside a little PNG image. \n\
  357. Depending on how you encode the crypted data, images come in 3 flavors: HEX, Base32 and Base64. \n\
  358. Normally each letter can be encoded inside a color value from 1 to 255,
  359. and 4 colors become one RGBA pixel. \n\
  360. HEX encoding is `high density`. Two letters are transformed into a color from 1 to 255,
  361. so one pixel consists of 8 letters, instead of 4 letters.
  362. '''
  363. if not pre: pre = 'None'
  364. if not enc: enc = 'None'
  365. if post not in ('HEX Codec', 'Base32 Codec', 'Base64 Codec'):
  366. print('Encoding must be HEX, Base32, or Base64! Exiting!')
  367. return
  368. # Input can be string, or file. If's file, read it.
  369. if str(type(txt)) == "<type 'file'>":
  370. txt.seek(0)
  371. txt = txt.read()
  372. # All text must be reversed, to pop from the end of the characters list.
  373. if encrypt: # If text MUST be encrypted first, encrypt without pre/enc/post tags.
  374. val = self.encrypt(txt, pre, enc, post, pwd, False)[::-1]
  375. if not val:
  376. return
  377. else: # Else, the text is already encrypted.
  378. # Strip pre/enc/post tags.
  379. txt = re.sub(NO_TAGS, '', txt)
  380. # All information is reversed, because it's faster to just Pop from the end of a list,
  381. # Rather then using Pop(0), from the beggining of a list.
  382. # So the "first pixel" will be added at the end, but will be fetched first,
  383. # When creating the final image.
  384. val = txt[::-1]
  385. del txt
  386. # Calculate the edge of the square and blank square.
  387. if post == 'HEX Codec':
  388. # Length.
  389. edge = math.ceil(math.sqrt( float(len(val) + 1)/8.0 ))
  390. blank = math.ceil(edge * edge - float(len(val)) / 8.0)
  391. if blank:
  392. blank -= 1
  393. else:
  394. # Length + 5, just to make sure there are enough blank pixels.
  395. edge = math.ceil(math.sqrt( float(len(val) + 5)/4.0 ))
  396. blank = math.ceil((edge * edge - float(len(val))/4.0) / 2.0)
  397. # `Second pixel` : a number representing the length of valid characters.
  398. # This is only used for HEX, because when decrypting, this number of letters is trimmed from the end of the string.
  399. if post == 'HEX Codec':
  400. second_pixel = struct.pack('BB', int(blank/255), (blank%256)).encode('hex')
  401. val += second_pixel[::-1]
  402. #print '! Second pixel', second_pixel
  403. del second_pixel
  404. # `First pixel` : a string with 4 numbers representing Pre/ Enc/ Post information.
  405. # For Base64/ Base32, this variabile is encoded in one pixel (4 characters).
  406. # For HEX, First Pixel + Second Pixel are both encoded in one pixel (8 characters).
  407. if post == 'HEX Codec':
  408. first_pixel = '0'
  409. else:
  410. first_pixel = '1'
  411. # Add first pixel at the end of the reversed string.
  412. first_pixel += SCRAMBLE_NR[pre] + ENCRYPT_NR[enc] + ENCODE_NR[post]
  413. val += first_pixel[::-1]
  414. #print '! First pixel', first_pixel
  415. del first_pixel
  416. # Explode the encrypted string.
  417. list_val = list(val)
  418. # Creating new square image.
  419. print('Creating img, %ix%i, blank : %i, string to encode : %i chars.' % (edge, edge, blank, len(val)))
  420. edge = int(edge)
  421. if not Image:
  422. im = QtGui.QImage(edge, edge, QtGui.QImage.Format_ARGB32)
  423. _pix = im.setPixel
  424. _rgba = QtGui.qRgba
  425. else:
  426. im = Image.new('RGBA', (edge, edge))
  427. _pix = im.load()
  428. _int = int
  429. _ord = ord
  430. # HEX codec.
  431. if post == 'HEX Codec':
  432. for i in range(edge):
  433. for j in range(edge):
  434. #
  435. _r = _g = _b = _a = 255
  436. # Red
  437. if len(list_val) >= 2:
  438. _r = _int(list_val.pop()+list_val.pop(), 16)
  439. elif len(list_val) == 1:
  440. _r = _int(list_val.pop(), 16)
  441. # Green
  442. if len(list_val) >= 2:
  443. _g = _int(list_val.pop()+list_val.pop(), 16)
  444. elif len(list_val) == 1:
  445. _g = _int(list_val.pop(), 16)
  446. # Blue
  447. if len(list_val) >= 2:
  448. _b = _int(list_val.pop()+list_val.pop(), 16)
  449. elif len(list_val) == 1:
  450. _b = _int(list_val.pop(), 16)
  451. # Alpha
  452. if len(list_val) >= 2:
  453. _a = _int(list_val.pop()+list_val.pop(), 16)
  454. elif len(list_val) == 1:
  455. _a = _int(list_val.pop(), 16)
  456. #
  457. if not Image:
  458. _pix(j, i, _rgba(_r, _g, _b, _a))
  459. else:
  460. _pix[j, i] = (_r, _g, _b, _a)
  461. #
  462. # Base 64 and Base 32.
  463. else:
  464. for i in range(edge):
  465. for j in range(edge):
  466. #
  467. if blank:
  468. blank -= 1
  469. # Put one #FFFFFFFF, completely white pixel.
  470. if not Image:
  471. _pix(j, i, 4294967295)
  472. else:
  473. _pix[j, i] = (255,255,255,255)
  474. continue
  475. #
  476. _r = _g = _b = _a = 255
  477. if len(list_val) >= 1:
  478. _r = _ord(list_val.pop())
  479. if len(list_val) >= 1:
  480. _g = _ord(list_val.pop())
  481. if len(list_val) >= 1:
  482. _b = _ord(list_val.pop())
  483. if len(list_val) >= 1:
  484. _a = _ord(list_val.pop())
  485. if not Image:
  486. _pix(j, i, _rgba(_r, _g, _b, _a))
  487. else:
  488. _pix[j, i] = (_r, _g, _b, _a)
  489. #
  490. #
  491. try:
  492. im.save(path, 'PNG') #, -1)
  493. except:
  494. print('Cannot save PNG file "%s" !' % path)
  495. #
  496. def fromImage(self, pwd, path, decrypt=True):
  497. #
  498. if not os.path.isfile(path):
  499. print('Cannot find file "%s" !' % path)
  500. return
  501. try:
  502. if not Image:
  503. im = QtGui.QImage()
  504. im.load(path, 'PNG')
  505. W = im.width()
  506. H = im.height()
  507. _r = QtGui.qRed
  508. _g = QtGui.qGreen
  509. _b = QtGui.qBlue
  510. _a = QtGui.qAlpha
  511. _pix = im.pixel
  512. else:
  513. im = Image.open(path, 'r')
  514. W = im.size[0] # Width
  515. H = im.size[1] # Height
  516. _pix = im.load()
  517. except:
  518. print('Image "%s" is not a valid RGBA PNG !' % path)
  519. return
  520. fp_val = 0
  521. list_val = []
  522. # Calculate First Pixel.
  523. for i in range(W):
  524. for j in range(H):
  525. #
  526. if fp_val:
  527. break
  528. if not Image:
  529. #
  530. pix1 = _pix(j, i)
  531. # For QtColor
  532. if pix1 != 4294967295: # Color #FFFFFFFF, completely white pixel.
  533. fp_val = [_r(pix1), _g(pix1), _b(pix1), _a(pix1)]
  534. break
  535. #
  536. else:
  537. #
  538. pix1 = _pix[j, i]
  539. # For PIL Image
  540. if pix1 != (255,255,255,255):
  541. fp_val = pix1
  542. break
  543. #
  544. # Calculate the colors of first pixel.
  545. # For HEX: Red+Green represents pre/enc/post information and Blue+Alpha value represents nr of valid characters.
  546. # For Base64/ Base32, first pixel represents only the Pre/ Enc/ Post information.
  547. first_pixel_hex = struct.pack('BB', fp_val[0], fp_val[1]).encode('hex')
  548. blank = int(struct.pack('BB', fp_val[2], fp_val[3]).encode('hex'), 16)
  549. first_pixel_base = [unichr(fp_val[0]), unichr(fp_val[1]), unichr(fp_val[2]), unichr(fp_val[3])]
  550. # Reverse number dictionaries.
  551. reverse_s = dict(zip(SCRAMBLE_NR.values(), SCRAMBLE_NR.keys()))
  552. reverse_ey = dict(zip(ENCRYPT_NR.values(), ENCRYPT_NR.keys()))
  553. reverse_ed = dict(zip(ENCODE_NR.values(), ENCODE_NR.keys()))
  554. if first_pixel_hex[0] == '0' and first_pixel_base[0] != '0':
  555. post = reverse_s[first_pixel_hex[1]]
  556. enc = reverse_ey[first_pixel_hex[2]]
  557. pre = reverse_ed[first_pixel_hex[3]]
  558. else:
  559. post = reverse_s[first_pixel_base[1]]
  560. enc = reverse_ey[first_pixel_base[2]]
  561. pre = reverse_ed[first_pixel_base[3]]
  562. # Save Pre/ Enc/ Post information for GUI.
  563. self.pre = pre
  564. self.enc = enc
  565. self.post = post
  566. # For HEX and PyQt.
  567. if pre == 'HEX Codec' and not Image:
  568. for i in range(W):
  569. for j in range(H):
  570. #
  571. rgba = _pix(j, i)
  572. #
  573. # For each channel in current pixel.
  574. for v in [_r(rgba), _g(rgba), _b(rgba), _a(rgba)]:
  575. # This is much faster than struct.pack('B',v).encode('hex')
  576. # I need 2 characters; in HEX 16=>10; less than 16 must have 0 in front of it.
  577. if v < 16:
  578. list_val.append('0'+hex(v)[-1])
  579. else:
  580. list_val.append(hex(v)[-2:])
  581. #
  582. # For HEX and PIL Image.
  583. elif pre == 'HEX Codec':
  584. for i in range(W):
  585. for j in range(H):
  586. #
  587. rgba = _pix[j, i]
  588. #
  589. # For each channel in current pixel.
  590. for v in rgba:
  591. # I need 2 characters; in HEX 16=>10; less than 16 must have 0 in front of it.
  592. if v < 16:
  593. list_val.append('0'+hex(v)[-1])
  594. else:
  595. list_val.append(hex(v)[-2:])
  596. #
  597. # For the rest, with PyQt.
  598. elif not Image:
  599. for i in range(W):
  600. for j in range(H):
  601. #
  602. rgba = _pix(j, i)
  603. #
  604. # For each channel in current pixel.
  605. for v in [_r(rgba), _g(rgba), _b(rgba), _a(rgba)]:
  606. if v and v != 255:
  607. list_val.append(unichr(v))
  608. # If this color is 0 or 255, the rest of the pixel is blank.
  609. else:
  610. break
  611. #
  612. # For the rest, with PIL Image.
  613. else:
  614. for i in range(W):
  615. for j in range(H):
  616. #
  617. rgba = _pix[j, i]
  618. #
  619. # For each channel in current pixel.
  620. for v in rgba:
  621. if v and v != 255:
  622. list_val.append(unichr(v))
  623. # If this color is 0 or 255, the rest of the pixel is blank.
  624. else:
  625. break
  626. #
  627. # Fix `blank` value.
  628. if blank:
  629. blank = - blank * 8
  630. else:
  631. blank = len(list_val) * 8
  632. # Used for DEBUG.
  633. #ff = open('dump.txt', 'wb')
  634. #ff.write('\nColor: %s ; FP Val: %s ; FP Hex: %s ; FP B64/32: %s ; Blank: %i' % (cc.name(),str(fp_val),first_pixel_hex,''.join(first_pixel_b),blank))
  635. #ff.write('\n'+''.join(list_val)+'\n')
  636. #ff.write(''.join(list_val)[8:blank])
  637. #ff.close() ; del ff, fp_val
  638. # If the text MUST be decrypted.
  639. if decrypt:
  640. if pre == 'HEX Codec':
  641. val = self.decrypt(''.join(list_val)[8:blank], pre, enc, post, pwd)
  642. else:
  643. val = self.decrypt(''.join(list_val[4:]), pre, enc, post, pwd)
  644. if not val:
  645. print('Error from image (decrypt)! ' + self.error.strip())
  646. else:
  647. return val
  648. # Else, don't decrypt.
  649. else:
  650. if pre == 'HEX Codec':
  651. val = ''.join(list_val)[8:blank]
  652. else:
  653. val = ''.join(list_val[4:])
  654. if not val:
  655. print('Error from image (no decrypt)!')
  656. else:
  657. return val
  658. #
  659. def _import(self, pre, enc, post, pwd, fpath, decrypt=True):
  660. #
  661. if not os.path.isfile(fpath):
  662. print('Cannot find file "%s" !' % fpath)
  663. return
  664. #
  665. ext = os.path.splitext(fpath)[1].lower()
  666. # For PNG files.
  667. if ext=='.png':
  668. return self.fromImage(pwd, fpath, decrypt)
  669. # For the rest of the files.
  670. if decrypt:
  671. val = self.decrypt(open(fpath, 'rb').read(), pre, enc, post, pwd)
  672. if not val:
  673. print(self.error)
  674. else:
  675. return val
  676. # Else, don't decrypt.
  677. else:
  678. val = open(fpath, 'rb').read()
  679. return val
  680. #
  681. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
  682. # GUI Container Widget
  683. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
  684. class ContainerWidget(QtGui.QWidget):
  685. def __init__(self, parent):
  686. '''
  687. Main container. It's not transparent. \n\
  688. I must use this, to accept file drops.
  689. '''
  690. super(ContainerWidget, self).__init__(parent)
  691. self.mMoving = False
  692. self.setMouseTracking(False)
  693. self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum))
  694. def mousePressEvent(self, event):
  695. if(event.button() == QtCore.Qt.LeftButton):
  696. self.mMoving = True
  697. self.mOffset = event.pos()
  698. def mouseMoveEvent(self, event):
  699. if(self.mMoving):
  700. self.parentWidget().move(event.globalPos() - self.mOffset)
  701. def mouseReleaseEvent(self, event):
  702. if(event.button() == QtCore.Qt.LeftButton):
  703. self.mMoving = False
  704. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
  705. # MAIN Scrambled-Egg Window
  706. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
  707. class Window(QtGui.QMainWindow):
  708. def __init__(self):
  709. '''
  710. Main window class.
  711. It's frameless and transparent.
  712. '''
  713. super(Window, self).__init__(None, QtCore.Qt.FramelessWindowHint)
  714. QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('CleanLooks'))
  715. QtGui.QApplication.setPalette(QtGui.QApplication.style().standardPalette())
  716. icon_path = os.path.split(os.path.abspath(__file__))[0] + '/config/icon.ico'
  717. self.setWindowIcon(QtGui.QIcon(icon_path))
  718. self.resize(C['W_WIDTH'], C['W_HEIGHT'])
  719. self.setMaximumHeight(C['W_MAX_HEIGHT'])
  720. self.setStyleSheet(D['STYLE_MAIN'])
  721. self.setWindowTitle('Scrambled Egg %s :: Live Crypt' % __version__)
  722. self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
  723. self.setWindowOpacity(0.9)
  724. self.setAcceptDrops(True)
  725. self.SE = ScrambledEgg()
  726. self.centralWidget = ContainerWidget(self) # Central Widget.
  727. self.setCentralWidget(self.centralWidget)
  728. self.container = QtGui.QWidget(self.centralWidget) # Container Widget.
  729. self.container.setObjectName('Container')
  730. self.container.setStyleSheet(D['STYLE_CONTAINER'])
  731. self.textBar = QtGui.QLabel(self) # Top text bar.
  732. self.layout = QtGui.QGridLayout(self.centralWidget) # Main Layout.
  733. self.centralWidget.setLayout(self.layout)
  734. self.leftText = QtGui.QTextEdit('', self.centralWidget) # To write clean text.
  735. self.rightText = QtGui.QPlainTextEdit('' , self.centralWidget) # To view encrypted text.
  736. self.buttonCryptMode = QtGui.QPushButton(self.centralWidget)
  737. self.buttonDecryptMode = QtGui.QPushButton(self.centralWidget)
  738. self.buttonBrowseRSAL = QtGui.QPushButton('Browse', self.centralWidget)
  739. self.buttonBrowseRSAR = QtGui.QPushButton('Browse', self.centralWidget)
  740. self.preProcess = QtGui.QComboBox(self.centralWidget) # Left side.
  741. self.comboCrypt = QtGui.QComboBox(self.centralWidget) # Left side.
  742. self.postProcess = QtGui.QComboBox(self.centralWidget) # Left side.
  743. self.linePasswordL = QtGui.QLineEdit(self.centralWidget) # Left password line.
  744. self.lineRSAPathL = QtGui.QLineEdit(self.centralWidget) # Left RSA Path line.
  745. self.checkPwdL = QtGui.QCheckBox('<- Pwd', self.centralWidget) # Left side.
  746. self.nrLettersL = QtGui.QLabel('', self.centralWidget) # Left side.
  747. self.setFormatting = QtGui.QCheckBox('Formatted text', self.centralWidget) # Left side.
  748. self.showHTML = QtGui.QCheckBox('Show HTML', self.centralWidget) # Left side.
  749. self.setTags = QtGui.QCheckBox('No tags', self.centralWidget) # Left side.
  750. self.preDecrypt = QtGui.QComboBox(self.centralWidget) # Right side.
  751. self.comboDecrypt = QtGui.QComboBox(self.centralWidget) # Right side.
  752. self.postDecrypt = QtGui.QComboBox(self.centralWidget) # Right side.
  753. self.linePasswordR = QtGui.QLineEdit(self.centralWidget) # Right password line.
  754. self.lineRSAPathR = QtGui.QLineEdit(self.centralWidget) # Right RSA Path line.
  755. self.checkPwdR = QtGui.QCheckBox('<- Pwd', self.centralWidget) # Right side.
  756. self.nrLettersR = QtGui.QLabel('', self.centralWidget) # Right side.
  757. self.loadFile = QtGui.QPushButton('Import', self.centralWidget) # Right side.
  758. self.saveFile = QtGui.QPushButton('Export', self.centralWidget) # Right side.
  759. self.helpButton = QtGui.QPushButton('Help !', self.centralWidget) # Right side.
  760. self.minButton = QtGui.QPushButton(D['MIN_BTN_TXT'], self.centralWidget)
  761. self.closeButton = QtGui.QPushButton(D['CLOSE_BTN_TXT'], self.centralWidget)
  762. self.micLayout = QtGui.QHBoxLayout()
  763. self.micLayout.addWidget(self.minButton)
  764. self.micLayout.addWidget(self.closeButton)
  765. # Row, Col, rowSpan, colSpan
  766. self.layout.addWidget(self.container, 0, 1, 43, 12)
  767. self.layout.addWidget(self.textBar, 1, 1, 3, 8)
  768. self.layout.addLayout(self.micLayout, 1, 10+C['MIC_BTNS_POS'], 1, C['MIC_BTNS_SPAN'])
  769. self.layout.addItem(QtGui.QSpacerItem(1, 8), 3, 1, 1, 1)
  770. self.layout.addWidget(self.buttonCryptMode, 4, 2, 1, 5)
  771. self.layout.addWidget(self.buttonDecryptMode, 4, 7, 1, 5)
  772. self.layout.addWidget(self.preProcess, 5, 2, 1, 1)
  773. self.layout.addWidget(self.comboCrypt, 5, 3, 1, 1)
  774. self.layout.addWidget(self.postProcess, 5, 4, 1, 1)
  775. self.layout.addWidget(self.preDecrypt, 5, 7, 1, 1)
  776. self.layout.addWidget(self.comboDecrypt, 5, 8, 1, 1)
  777. self.layout.addWidget(self.postDecrypt, 5, 9, 1, 1)
  778. self.layout.addWidget(self.linePasswordL, 6, 2, 1, 4)
  779. self.layout.addWidget(self.linePasswordR, 6, 7, 1, 4)
  780. self.layout.addWidget(self.checkPwdL, 6, 6, 1, 1)
  781. self.layout.addWidget(self.checkPwdR, 6, 11, 1, 1)
  782. self.layout.addWidget(self.lineRSAPathL, 7, 2, 1, 4)
  783. self.layout.addWidget(self.lineRSAPathR, 7, 7, 1, 4)
  784. self.layout.addWidget(self.buttonBrowseRSAL, 7, 6, 1, 1)
  785. self.layout.addWidget(self.buttonBrowseRSAR, 7, 11, 1, 1)
  786. self.layout.addWidget(self.leftText, 8, 2, 32, 5)
  787. self.layout.addWidget(self.rightText, 8, 7, 32, 5)
  788. self.layout.addWidget(self.setFormatting, 40, 2, 1, 1)
  789. self.layout.addWidget(self.setTags, 40, 3, 1, 1)
  790. self.layout.addWidget(self.showHTML, 40, 4, 1, 1)
  791. self.layout.addWidget(self.loadFile, 40, 7, 1, 1)
  792. self.layout.addWidget(self.saveFile, 40, 8, 1, 1)
  793. self.layout.addWidget(self.helpButton, 40, 9, 1, 1)
  794. self.layout.addWidget(self.nrLettersL, 40, 6, 1, 1)
  795. self.layout.addWidget(self.nrLettersR, 40, 11, 1, 1)
  796. self.__setup() # Prepair all components!
  797. self.__connect() # Connect all components!
  798. def __setup(self):
  799. '''
  800. Setup all components.
  801. '''
  802. #
  803. # Toogle buttons.
  804. self.buttonCryptMode.setCheckable(True)
  805. self.buttonCryptMode.setChecked(True)
  806. self.buttonCryptMode.setToolTip('Switch to Encryption mode')
  807. self.buttonCryptMode.setStyleSheet(D['STYLE_BUTTON'])
  808. self.buttonDecryptMode.setCheckable(True)
  809. self.buttonDecryptMode.setToolTip('Switch to Decryption mode')
  810. self.buttonDecryptMode.setStyleSheet(D['STYLE_BUTTON'])
  811. self.helpButton.setStyleSheet(D['STYLE_HELP_BUTTON'])
  812. self.minButton.setMaximumWidth(25)
  813. self.minButton.setMaximumHeight(25)
  814. self.minButton.setStyleSheet(D['STYLE_MIN_BUTTON'])
  815. self.closeButton.setMaximumWidth(25)
  816. self.closeButton.setMaximumHeight(25)
  817. self.closeButton.setStyleSheet(D['STYLE_CLOSE_BUTTON'])
  818. # Some styles.
  819. self.loadFile.setStyleSheet(D['STYLE_BUTTON'])
  820. self.saveFile.setStyleSheet(D['STYLE_BUTTON'])
  821. self.leftText.setStyleSheet(D['STYLE_L_TEXTEDIT'])
  822. self.rightText.setStyleSheet(D['STYLE_R_TEXTEDIT'])
  823. # Password fields.
  824. self.linePasswordL.setEchoMode(QtGui.QLineEdit.Password)
  825. self.linePasswordL.setToolTip('Password used for encrypting the text')
  826. self.linePasswordL.setMaxLength(99)
  827. self.linePasswordL.setStyleSheet(D['STYLE_LINEEDIT'])
  828. self.checkPwdL.setTristate(False)
  829. self.checkPwdL.setStyleSheet(D['STYLE_CHECKBOX'])
  830. self.linePasswordR.setEchoMode(QtGui.QLineEdit.Password)
  831. self.linePasswordR.setToolTip('Password used for decrypting the text')
  832. self.linePasswordR.setMaxLength(99)
  833. self.linePasswordR.setDisabled(True)
  834. self.linePasswordR.setStyleSheet(D['STYLE_LINEEDIT'])
  835. self.checkPwdR.setTristate(False)
  836. self.checkPwdR.setStyleSheet(D['STYLE_CHECKBOX'])
  837. # RSA Path.
  838. self.lineRSAPathL.setStyleSheet(D['STYLE_LINEEDIT'])
  839. self.lineRSAPathL.setToolTip('RSA Encryption requires both a password and the path to a public/ private RSA key')
  840. self.lineRSAPathL.hide()
  841. self.lineRSAPathR.setStyleSheet(D['STYLE_LINEEDIT'])
  842. self.lineRSAPathR.setToolTip('RSA Decryption requires both a password and the path to a public/ private RSA key')
  843. self.lineRSAPathR.hide()
  844. self.lineRSAPathR.setDisabled(True)
  845. self.buttonBrowseRSAL.setStyleSheet(D['STYLE_BUTTON'])
  846. self.buttonBrowseRSAL.hide()
  847. self.buttonBrowseRSAL.setMaximumWidth(60)
  848. self.buttonBrowseRSAR.setStyleSheet(D['STYLE_BUTTON'])
  849. self.buttonBrowseRSAR.hide()
  850. self.buttonBrowseRSAR.setMaximumWidth(60)
  851. self.buttonBrowseRSAR.setDisabled(True)
  852. # Formatted text.
  853. self.setFormatting.setTristate(False)
  854. self.setFormatting.setToolTip('Encrypt this text as HTML')
  855. self.setFormatting.setStyleSheet(D['STYLE_CHECKBOX'])
  856. self.setTags.setTristate(False)
  857. self.setTags.setToolTip('Strip pre/enc/post tags')
  858. self.setTags.setStyleSheet(D['STYLE_CHECKBOX'])
  859. self.showHTML.setTristate(False)
  860. self.showHTML.setToolTip('Toogle view HTML source behind the formatted text')
  861. self.showHTML.setStyleSheet(D['STYLE_CHECKBOX'])
  862. # All combo boxes.
  863. MIN = 120
  864. self.preProcess.setMinimumWidth(MIN)
  865. self.preProcess.setStyleSheet(D['STYLE_COMBOBOX'])
  866. self.comboCrypt.setMinimumWidth(MIN)
  867. self.comboCrypt.setStyleSheet(D['STYLE_COMBOBOX'])
  868. self.postProcess.setMinimumWidth(MIN)
  869. self.postProcess.setStyleSheet(D['STYLE_COMBOBOX'])
  870. self.preDecrypt.setMinimumWidth(MIN)
  871. self.preDecrypt.setStyleSheet(D['STYLE_COMBOBOX'])
  872. self.comboDecrypt.setMinimumWidth(MIN)
  873. self.comboDecrypt.setStyleSheet(D['STYLE_COMBOBOX'])
  874. self.postDecrypt.setMinimumWidth(MIN)
  875. self.postDecrypt.setStyleSheet(D['STYLE_COMBOBOX'])
  876. # Pre combo-boxes.
  877. self.preProcess.setToolTip('Select pre-process')
  878. self.postDecrypt.setToolTip('Select post-decrypt')
  879. for scramble in SCRAMBLE:
  880. self.preProcess.addItem(scramble, scramble)
  881. self.postDecrypt.addItem(scramble, scramble)
  882. # Encryption/ decryption combo-boxes.
  883. self.comboCrypt.setToolTip('Select encryption algorithm; it will use the provided password')
  884. self.comboDecrypt.setToolTip('Select encryption algorithm; it will use the provided password')
  885. for enc in ENC.keys():
  886. self.comboCrypt.addItem(enc, enc)
  887. self.comboDecrypt.addItem(enc, enc)
  888. # Post combo-boxes.
  889. self.postProcess.setToolTip('Select post-process')
  890. self.preDecrypt.setToolTip('Select pre-decrypt')
  891. for encode in ENCODE:
  892. self.postProcess.addItem(encode, encode)
  893. self.preDecrypt.addItem(encode, encode)
  894. #
  895. def __connect(self):
  896. '''
  897. Connect all components.
  898. '''
  899. #
  900. self.linePasswordL.textChanged.connect(self.onLeftTextChanged)
  901. self.leftText.textChanged.connect(self.onLeftTextChanged)
  902. self.checkPwdL.stateChanged.connect(lambda x: \
  903. self.linePasswordL.setEchoMode(QtGui.QLineEdit.Normal) if self.checkPwdL.isChecked() \
  904. else self.linePasswordL.setEchoMode(QtGui.QLineEdit.Password))
  905. self.buttonCryptMode.clicked.connect(self.onCryptMode)
  906. #
  907. self.linePasswordR.textChanged.connect(self.onRightTextChanged)
  908. self.rightText.textChanged.connect(self.onRightTextChanged)
  909. self.checkPwdR.stateChanged.connect(lambda x: \
  910. self.linePasswordR.setEchoMode(QtGui.QLineEdit.Normal) if self.checkPwdR.isChecked() \
  911. else self.linePasswordR.setEchoMode(QtGui.QLineEdit.Password))
  912. self.buttonDecryptMode.clicked.connect(self.onDecryptMode)
  913. #
  914. self.buttonBrowseRSAL.clicked.connect(self.browseRSAkey)
  915. self.buttonBrowseRSAR.clicked.connect(self.browseRSAkey)
  916. #
  917. self.preProcess.currentIndexChanged.connect(self.onLeftTextChanged)
  918. self.comboCrypt.currentIndexChanged.connect(self.onLeftTextChanged)
  919. self.postProcess.currentIndexChanged.connect(self.onLeftTextChanged)
  920. #
  921. self.preDecrypt.currentIndexChanged.connect(self.onRightTextChanged)
  922. self.comboDecrypt.currentIndexChanged.connect(self.onRightTextChanged)
  923. self.postDecrypt.currentIndexChanged.connect(self.onRightTextChanged)
  924. #
  925. self.saveFile.clicked.connect(self.onSave)
  926. self.loadFile.clicked.connect(self.onLoad)
  927. self.helpButton.clicked.connect(self.onHelp)
  928. self.setFormatting.toggled.connect(self.onLeftTextChanged)
  929. self.setTags.toggled.connect(self.onLeftTextChanged)
  930. self.showHTML.toggled.connect(self.toggleHtml)
  931. #
  932. self.minButton.clicked.connect(lambda: self.setWindowState(QtCore.Qt.WindowMinimized))
  933. self.closeButton.clicked.connect(lambda: self.close())
  934. #
  935. # ACTION !
  936. self.onCryptMode()
  937. #
  938. def dragEnterEvent(self, e):
  939. #
  940. mime_data = e.mimeData()
  941. # Accept plain text, HTML text and file paths.
  942. if mime_data.hasHtml() or mime_data.hasText() or mime_data.hasFormat('text/uri-list'):
  943. e.accept()
  944. else:
  945. e.ignore()
  946. #
  947. def dropEvent(self, e):
  948. #
  949. mime_data = e.mimeData()
  950. if mime_data.hasFormat('text/html'):
  951. dataf = mime_data.html()
  952. self.leftText.setHtml(dataf)
  953. elif mime_data.hasFormat('text/plain'):
  954. dataf = mime_data.text()
  955. self.leftText.setPlainText(dataf)
  956. elif mime_data.hasFormat('text/uri-list'):
  957. uri = mime_data.data('text/uri-list')
  958. uris = str(uri).split('\r\n')[:-1]
  959. # List of dragged files.
  960. for url in uris:
  961. #
  962. f_name = urllib.unquote(url)
  963. # Ignore windows shortcuts. They are ugly :)
  964. if os.path.splitext(f_name)[1].lower() == '.lnk':
  965. continue
  966. o = urllib.urlopen(url)
  967. sz = os.fstat(o.fileno())[6]
  968. t = tarfile.TarInfo(os.path.split(f_name)[1])
  969. t.size = sz
  970. #self.attach.addFile(t, o) #?
  971. #
  972. print('I should encrypt on DROP!')
  973. #
  974. def browseRSAkey(self):
  975. #
  976. f = QtGui.QFileDialog()
  977. path = f.getOpenFileName(self, 'Path to RSA public or private key', os.getcwd(), 'All files (*.*)')
  978. if not path:
  979. return
  980. #
  981. self.SE.rsa_path = path
  982. self.lineRSAPathL.setText(path)
  983. self.lineRSAPathR.setText(path)
  984. #
  985. def onCryptMode(self):
  986. #
  987. self.buttonCryptMode.setChecked(True)
  988. self.buttonCryptMode.setText('Encrypt Mode is Enabled')
  989. self.buttonDecryptMode.setChecked(False)
  990. self.buttonDecryptMode.setText('Decrypt Mode')
  991. #
  992. self.linePasswordL.setDisabled(False)
  993. self.lineRSAPathL.setDisabled(False)
  994. self.buttonBrowseRSAL.setDisabled(False)
  995. self.leftText.setDisabled(False)
  996. #
  997. self.linePasswordR.setDisabled(True)
  998. self.lineRSAPathR.setDisabled(True)
  999. self.buttonBrowseRSAR.setDisabled(True)
  1000. self.rightText.setDisabled(True)
  1001. #
  1002. self.checkPwdL.setDisabled(False)
  1003. self.checkPwdR.setDisabled(True)
  1004. #
  1005. self.preProcess.setCurrentIndex(self.postDecrypt.currentIndex())
  1006. self.comboCrypt.setCurrentIndex(self.comboDecrypt.currentIndex())
  1007. self.postProcess.setCurrentIndex(self.preDecrypt.currentIndex())
  1008. #
  1009. def onDecryptMode(self):
  1010. #
  1011. self.buttonCryptMode.setChecked(False)
  1012. self.buttonCryptMode.setText('Encrypt Mode')
  1013. self.buttonDecryptMode.setChecked(True)
  1014. self.buttonDecryptMode.setText('Decrypt Mode is Enabled')
  1015. #
  1016. self.linePasswordL.setDisabled(True)
  1017. self.lineRSAPathL.setDisabled(True)
  1018. self.buttonBrowseRSAL.setDisabled(True)
  1019. self.leftText.setDisabled(True)
  1020. #
  1021. self.linePasswordR.setDisabled(False)
  1022. self.lineRSAPathR.setDisabled(False)
  1023. self.buttonBrowseRSAR.setDisabled(False)
  1024. self.rightText.setDisabled(False)
  1025. #
  1026. self.checkPwdL.setDisabled(True)
  1027. self.checkPwdR.setDisabled(False)
  1028. #
  1029. self.postDecrypt.setCurrentIndex(self.preProcess.currentIndex())
  1030. self.comboDecrypt.setCurrentIndex(self.comboCrypt.currentIndex())
  1031. self.preDecrypt.setCurrentIndex(self.postProcess.currentIndex())
  1032. #
  1033. def cleanupHtml(self, txt):
  1034. #
  1035. def spacerepl(matchobj):
  1036. return matchobj.group(0).replace(b' ', b'&nbsp;')
  1037. # Replacing all spaces with &nbsp;
  1038. txt = re.sub(b'>([^<>]+)<(?!/style>)', spacerepl, txt)
  1039. # Write the new file
  1040. open('doc.htm', 'w').write(txt)
  1041. # Process the file with Tidy
  1042. if platform.uname()[0].lower() == 'windows':
  1043. p = subprocess.Popen(['tidy.exe', '-config', 'tidy.txt', 'doc.htm']).wait()
  1044. elif platform.uname()[0].lower() == 'linux':
  1045. p = subprocess.Popen(['./tidy', '-config', 'tidy.txt', 'doc.htm']).wait()
  1046. else:
  1047. print('Platform `%s` is not supported yet!\n' % platform.uname()[0])
  1048. return ''
  1049. txt = open('doc.htm', 'r').read()
  1050. # Delete the wrong/ obsolete tags
  1051. txt = txt.replace(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n"http://www.w3.org/TR/html4/loose.dtd">\n', '')
  1052. txt = txt.replace(b'<meta name="generator" content="HTML Tidy for Windows (vers 25 March 2009), see www.w3.org">\n', '')
  1053. txt = txt.replace(b'<meta name="generator" content="HTML Tidy for Linux (vers 25 March 2009), see www.w3.org">\n', '')
  1054. txt = txt.replace(b'<meta name="qrichtext" content="1">\n', '')
  1055. txt = txt.replace(b'<title></title>\n', '')
  1056. txt = txt.replace(b'</style>\n\n<style type="text/css">\n', '')
  1057. txt = txt.replace(b'<br>\n', '\n')
  1058. # The clean file, for debug...
  1059. open('doc.htm', 'w').write(txt)
  1060. return txt
  1061. #
  1062. def toggleHtml(self):
  1063. #
  1064. if self.showHTML.isChecked():
  1065. txt = self.leftText.toHtml()
  1066. self.leftText.clear()
  1067. self.leftText.setFontFamily("Verdana")
  1068. self.leftText.setFontItalic(False)
  1069. self.leftText.setFontUnderline(False)
  1070. self.leftText.setFontWeight(10)
  1071. self.leftText.setFontPointSize(10)
  1072. self.leftText.setTextColor(QtGui.QColor())
  1073. self.leftText.setPlainText(self.cleanupHtml(txt))
  1074. else:
  1075. txt = self.leftText.toPlainText()
  1076. self.leftText.clear()
  1077. self.leftText.setHtml(self.cleanupHtml(txt))
  1078. #
  1079. def onLeftTextChanged(self):
  1080. #
  1081. if not self.buttonCryptMode.isChecked():
  1082. return
  1083. #
  1084. # Save all pre/enc/post operations.
  1085. pre = self.preProcess.currentText()
  1086. enc = self.comboCrypt.currentText()
  1087. post = self.postProcess.currentText()
  1088. #
  1089. # If encryption mode is RSA, reveal key path.
  1090. if enc=='RSA':
  1091. self.lineRSAPathL.show()
  1092. self.lineRSAPathR.show()
  1093. self.buttonBrowseRSAL.show()
  1094. self.buttonBrowseRSAR.show()
  1095. else:
  1096. self.lineRSAPathL.hide()
  1097. self.lineRSAPathR.hide()
  1098. self.buttonBrowseRSAL.hide()
  1099. self.buttonBrowseRSAR.hide()
  1100. #
  1101. if not self.leftText.toPlainText():
  1102. self.rightText.clear()
  1103. return
  1104. #
  1105. pwd = self.linePasswordL.text().encode()
  1106. tags = not self.setTags.isChecked()
  1107. #
  1108. if self.setFormatting.isChecked() and not self.showHTML.isChecked():
  1109. # HTML string.
  1110. txt = self.leftText.toHtml().encode()
  1111. # Cleanup HTML string.
  1112. txt = self.cleanupHtml(tx

Large files files are truncated, but you can click here to view the full file