├── .gitignore
├── 10_A_Modular_Arithmetic_Module_For_The_Affine_Cipher
└── cryptomath.py
├── 11_Programming_The_Affine_Cipher
├── affineCipher.py
└── paperclip.py
├── 1_The_Reverse_Cipher
└── reverse_cipher.py
├── 2_The_Caesar_Cipher
├── caesarCipher.py
├── paperclip.py
└── paperclip.pyc
├── 3_Hacking_the_Caesar_Cipher_With_Brute_Force
└── Hacking_the_caesar_cipher_with_brute_force.py
├── 4_Encrypting_with_the_transposition_Cipher
└── encrypting_with_the_transposition_cipher.py
├── 5_Decrypting_with_the_transposition_cipher
└── transpositionDecrypt.py
├── 6_Programming_a_program_to_test_your_program
├── encrypting_with_the_transposition_cipher.py
├── transpositionDecrypt.py
└── transpositionTets.py
├── 7_Encrypting_and_Decrypting_Files
├── encrypting_with_the_transposition_cipher.py
├── frankenstein.encrypted.txt
├── frankenstein.txt
├── transpositionDecrypt.py
└── transpositionFileCipher.py
├── 8_Detecting_English_Programmatically
├── detectEnglish.py
└── dictionary.txt
├── 9_Hacking_The_Transposition_Cipher
├── detectEnglish.py
├── dictionary.txt
├── transpositionDecrypt.py
└── transpositionHacker.py
├── README.md
└── paperclip.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/10_A_Modular_Arithmetic_Module_For_The_Affine_Cipher/cryptomath.py:
--------------------------------------------------------------------------------
1 | # Cryptomath Module
2 |
3 | def gcd(a, b):
4 | #Return the GCD of a and b using Euclid's algorithm:
5 | while a != 0:
6 | a, b = b % a, a
7 | return b
8 |
9 | def findModInverse(a, m):
10 | # Return the modular inverse of a % m, which is
11 | # the number x such that a*x % m = 1
12 |
13 | if gcd(a, m) != 1 :
14 | return None # No mod inverse if a & m aren't relatively prime.
15 |
16 | # Calculate using the extended Euclidean algorithm
17 | u1, u2, u3 = 1, 0, a
18 | v1, v2, v3 = 0, 1, m
19 | while v3 != 0:
20 | q = u3 // v3 # Note that // is the integer division operator.
21 | v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2),
22 | (u3 - q * v3), v1, v2, v3
23 | return u1 % m
24 |
25 |
--------------------------------------------------------------------------------
/11_Programming_The_Affine_Cipher/affineCipher.py:
--------------------------------------------------------------------------------
1 | # Affine Cipher
2 |
3 | import sys, pyperclip, cryptomath, random
4 | SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.'
5 |
6 | def main():
7 | myMessage = """"A computer would deserve to be called intelligent if it
8 | could deceive a human into believing that it was human."-Alan Turing"""
9 | myKey = 2894
10 | myMode = 'encrypt' # Set to either 'encrypt or decrypt'
11 |
12 |
13 | if myMode == 'encrypt':
14 | translated = encryptMessage(myKey, myMessage)
15 | elif myMode == 'decrypt':
16 | translated = decryptMessage(myKey, myMessage)
17 | print('Key: %s' % (myKey))
18 | print('%sed text:' % (myMode.title()))
19 | print(translated)
20 | pyperclip.copy(translated)
21 | print('Full %sed text copied to clipboard.' % (myMode))
22 |
23 | def getKeyParts(key):
24 | keyA = key // len(SYMBOLS)
25 | keyB = key % len(SYMBOLS)
26 | return (keyA, keyB)
27 |
28 | def checkKeys(keyA, keyB, mode):
29 | if keyA == 1 and mode == 'encrypt':
30 | sys.exit('Cipher is weak if key A is 1. Choose a different key.')
31 | if keyB == 0 and mode == 'encrypt':
32 | sys.exit('Cipher is weak if key B is 0. Choose a different key.')
33 | if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1 :
34 | sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' %(len(SYMBOLS) - 1))
35 | if cryptomath.gcd(keyA, len(SYMBOLS)) != 1:
36 | sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
37 |
38 | def encryptMessage(key, message):
39 | keyA, keyB = getKeyParts(key)
40 | checkKeys(keyA, keyB, 'encrypt')
41 | ciphertext = ''
42 | for symbol in message:
43 | if symbol in SYMBOLS:
44 | # encrypt the symbol:
45 | symbolIndex = SYMBOLS.find(symbol)
46 | ciphertext += SYMBOLS[(symbolIndex * keyA + keyB) % len(SYMBOLS)]
47 | else:
48 | ciphertext += symbol #Append the symbol without encrypting.
49 | return ciphertext
50 |
51 | def decryptMessage(key, message):
52 | keyA, keyB = getKeyParts(key)
53 | checkKeys(keyA, keyB, 'decrypt')
54 | plaintext = ''
55 | modInverseOfKeyA = cryptomath.findModInverse(key, len(SYMBOLS))
56 |
57 | for symbol in message:
58 | if symbol in SYMBOLS:
59 | # Decrypt the symbol:
60 | symbolIndex = SYMBOLS.find(symbol)
61 | plaintext += SYMBOLS[(symbolIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)]
62 | else:
63 | plaintext += symbol # Append the symbol without decrypting.
64 | return plaintext
65 |
66 | def getRandomKey():
67 | while True:
68 | keyA = random.randint(2, len(SYMBOLS))
69 | keyB = random.randint(2, len(SYMBOLS))
70 | if cryptomath.gcd(keyA, len(SYMBOLS)) == 1:
71 | return keyA * len(SYMBOLS) + keyB
72 |
73 | # If afficeCipher.py is run (instead of imported as a module), call the main() function
74 | if __name__ == '__main__':
75 | main()
76 |
--------------------------------------------------------------------------------
/11_Programming_The_Affine_Cipher/paperclip.py:
--------------------------------------------------------------------------------
1 | """
2 | Pyperclip
3 |
4 | A cross-platform clipboard module for Python, with copy & paste functions for plain text.
5 | By Al Sweigart al@inventwithpython.com
6 | BSD License
7 |
8 | Usage:
9 | import pyperclip
10 | pyperclip.copy('The text to be copied to the clipboard.')
11 | spam = pyperclip.paste()
12 |
13 | if not pyperclip.is_available():
14 | print("Copy functionality unavailable!")
15 |
16 | On Windows, no additional modules are needed.
17 | On Mac, the pyobjc module is used, falling back to the pbcopy and pbpaste cli
18 | commands. (These commands should come with OS X.).
19 | On Linux, install xclip or xsel via package manager. For example, in Debian:
20 | sudo apt-get install xclip
21 | sudo apt-get install xsel
22 |
23 | Otherwise on Linux, you will need the gtk or PyQt5/PyQt4 modules installed.
24 |
25 | gtk and PyQt4 modules are not available for Python 3,
26 | and this module does not work with PyGObject yet.
27 |
28 | Note: There seems to be a way to get gtk on Python 3, according to:
29 | https://askubuntu.com/questions/697397/python3-is-not-supporting-gtk-module
30 |
31 | Cygwin is currently not supported.
32 |
33 | Security Note: This module runs programs with these names:
34 | - which
35 | - where
36 | - pbcopy
37 | - pbpaste
38 | - xclip
39 | - xsel
40 | - klipper
41 | - qdbus
42 | A malicious user could rename or add programs with these names, tricking
43 | Pyperclip into running them with whatever permissions the Python process has.
44 |
45 | """
46 | __version__ = '1.7.0'
47 |
48 | import contextlib
49 | import ctypes
50 | import os
51 | import platform
52 | import subprocess
53 | import sys
54 | import time
55 | import warnings
56 |
57 | from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar
58 |
59 |
60 | # `import PyQt4` sys.exit()s if DISPLAY is not in the environment.
61 | # Thus, we need to detect the presence of $DISPLAY manually
62 | # and not load PyQt4 if it is absent.
63 | HAS_DISPLAY = os.getenv("DISPLAY", False)
64 |
65 | EXCEPT_MSG = """
66 | Pyperclip could not find a copy/paste mechanism for your system.
67 | For more information, please visit https://pyperclip.readthedocs.io/en/latest/introduction.html#not-implemented-error """
68 |
69 | PY2 = sys.version_info[0] == 2
70 |
71 | STR_OR_UNICODE = unicode if PY2 else str # For paste(): Python 3 uses str, Python 2 uses unicode.
72 |
73 | ENCODING = 'utf-8'
74 |
75 | # The "which" unix command finds where a command is.
76 | if platform.system() == 'Windows':
77 | WHICH_CMD = 'where'
78 | else:
79 | WHICH_CMD = 'which'
80 |
81 | def _executable_exists(name):
82 | return subprocess.call([WHICH_CMD, name],
83 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
84 |
85 |
86 |
87 | # Exceptions
88 | class PyperclipException(RuntimeError):
89 | pass
90 |
91 | class PyperclipWindowsException(PyperclipException):
92 | def __init__(self, message):
93 | message += " (%s)" % ctypes.WinError()
94 | super(PyperclipWindowsException, self).__init__(message)
95 |
96 |
97 | def _stringifyText(text):
98 | if PY2:
99 | acceptedTypes = (unicode, str, int, float, bool)
100 | else:
101 | acceptedTypes = (str, int, float, bool)
102 | if not isinstance(text, acceptedTypes):
103 | raise PyperclipException('only str, int, float, and bool values can be copied to the clipboard, not %s' % (text.__class__.__name__))
104 | return STR_OR_UNICODE(text)
105 |
106 |
107 | def init_osx_pbcopy_clipboard():
108 |
109 | def copy_osx_pbcopy(text):
110 | text = _stringifyText(text) # Converts non-str values to str.
111 | p = subprocess.Popen(['pbcopy', 'w'],
112 | stdin=subprocess.PIPE, close_fds=True)
113 | p.communicate(input=text.encode(ENCODING))
114 |
115 | def paste_osx_pbcopy():
116 | p = subprocess.Popen(['pbpaste', 'r'],
117 | stdout=subprocess.PIPE, close_fds=True)
118 | stdout, stderr = p.communicate()
119 | return stdout.decode(ENCODING)
120 |
121 | return copy_osx_pbcopy, paste_osx_pbcopy
122 |
123 |
124 | def init_osx_pyobjc_clipboard():
125 | def copy_osx_pyobjc(text):
126 | '''Copy string argument to clipboard'''
127 | text = _stringifyText(text) # Converts non-str values to str.
128 | newStr = Foundation.NSString.stringWithString_(text).nsstring()
129 | newData = newStr.dataUsingEncoding_(Foundation.NSUTF8StringEncoding)
130 | board = AppKit.NSPasteboard.generalPasteboard()
131 | board.declareTypes_owner_([AppKit.NSStringPboardType], None)
132 | board.setData_forType_(newData, AppKit.NSStringPboardType)
133 |
134 | def paste_osx_pyobjc():
135 | "Returns contents of clipboard"
136 | board = AppKit.NSPasteboard.generalPasteboard()
137 | content = board.stringForType_(AppKit.NSStringPboardType)
138 | return content
139 |
140 | return copy_osx_pyobjc, paste_osx_pyobjc
141 |
142 |
143 | def init_gtk_clipboard():
144 | global gtk
145 | import gtk
146 |
147 | def copy_gtk(text):
148 | global cb
149 | text = _stringifyText(text) # Converts non-str values to str.
150 | cb = gtk.Clipboard()
151 | cb.set_text(text)
152 | cb.store()
153 |
154 | def paste_gtk():
155 | clipboardContents = gtk.Clipboard().wait_for_text()
156 | # for python 2, returns None if the clipboard is blank.
157 | if clipboardContents is None:
158 | return ''
159 | else:
160 | return clipboardContents
161 |
162 | return copy_gtk, paste_gtk
163 |
164 |
165 | def init_qt_clipboard():
166 | global QApplication
167 | # $DISPLAY should exist
168 |
169 | # Try to import from qtpy, but if that fails try PyQt5 then PyQt4
170 | try:
171 | from qtpy.QtWidgets import QApplication
172 | except:
173 | try:
174 | from PyQt5.QtWidgets import QApplication
175 | except:
176 | from PyQt4.QtGui import QApplication
177 |
178 | app = QApplication.instance()
179 | if app is None:
180 | app = QApplication([])
181 |
182 | def copy_qt(text):
183 | text = _stringifyText(text) # Converts non-str values to str.
184 | cb = app.clipboard()
185 | cb.setText(text)
186 |
187 | def paste_qt():
188 | cb = app.clipboard()
189 | return STR_OR_UNICODE(cb.text())
190 |
191 | return copy_qt, paste_qt
192 |
193 |
194 | def init_xclip_clipboard():
195 | DEFAULT_SELECTION='c'
196 | PRIMARY_SELECTION='p'
197 |
198 | def copy_xclip(text, primary=False):
199 | text = _stringifyText(text) # Converts non-str values to str.
200 | selection=DEFAULT_SELECTION
201 | if primary:
202 | selection=PRIMARY_SELECTION
203 | p = subprocess.Popen(['xclip', '-selection', selection],
204 | stdin=subprocess.PIPE, close_fds=True)
205 | p.communicate(input=text.encode(ENCODING))
206 |
207 | def paste_xclip(primary=False):
208 | selection=DEFAULT_SELECTION
209 | if primary:
210 | selection=PRIMARY_SELECTION
211 | p = subprocess.Popen(['xclip', '-selection', selection, '-o'],
212 | stdout=subprocess.PIPE,
213 | stderr=subprocess.PIPE,
214 | close_fds=True)
215 | stdout, stderr = p.communicate()
216 | # Intentionally ignore extraneous output on stderr when clipboard is empty
217 | return stdout.decode(ENCODING)
218 |
219 | return copy_xclip, paste_xclip
220 |
221 |
222 | def init_xsel_clipboard():
223 | DEFAULT_SELECTION='-b'
224 | PRIMARY_SELECTION='-p'
225 |
226 | def copy_xsel(text, primary=False):
227 | text = _stringifyText(text) # Converts non-str values to str.
228 | selection_flag = DEFAULT_SELECTION
229 | if primary:
230 | selection_flag = PRIMARY_SELECTION
231 | p = subprocess.Popen(['xsel', selection_flag, '-i'],
232 | stdin=subprocess.PIPE, close_fds=True)
233 | p.communicate(input=text.encode(ENCODING))
234 |
235 | def paste_xsel(primary=False):
236 | selection_flag = DEFAULT_SELECTION
237 | if primary:
238 | selection_flag = PRIMARY_SELECTION
239 | p = subprocess.Popen(['xsel', selection_flag, '-o'],
240 | stdout=subprocess.PIPE, close_fds=True)
241 | stdout, stderr = p.communicate()
242 | return stdout.decode(ENCODING)
243 |
244 | return copy_xsel, paste_xsel
245 |
246 |
247 | def init_klipper_clipboard():
248 | def copy_klipper(text):
249 | text = _stringifyText(text) # Converts non-str values to str.
250 | p = subprocess.Popen(
251 | ['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents',
252 | text.encode(ENCODING)],
253 | stdin=subprocess.PIPE, close_fds=True)
254 | p.communicate(input=None)
255 |
256 | def paste_klipper():
257 | p = subprocess.Popen(
258 | ['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'],
259 | stdout=subprocess.PIPE, close_fds=True)
260 | stdout, stderr = p.communicate()
261 |
262 | # Workaround for https://bugs.kde.org/show_bug.cgi?id=342874
263 | # TODO: https://github.com/asweigart/pyperclip/issues/43
264 | clipboardContents = stdout.decode(ENCODING)
265 | # even if blank, Klipper will append a newline at the end
266 | assert len(clipboardContents) > 0
267 | # make sure that newline is there
268 | assert clipboardContents.endswith('\n')
269 | if clipboardContents.endswith('\n'):
270 | clipboardContents = clipboardContents[:-1]
271 | return clipboardContents
272 |
273 | return copy_klipper, paste_klipper
274 |
275 |
276 | def init_dev_clipboard_clipboard():
277 | def copy_dev_clipboard(text):
278 | text = _stringifyText(text) # Converts non-str values to str.
279 | if text == '':
280 | warnings.warn('Pyperclip cannot copy a blank string to the clipboard on Cygwin. This is effectively a no-op.')
281 | if '\r' in text:
282 | warnings.warn('Pyperclip cannot handle \\r characters on Cygwin.')
283 |
284 | fo = open('/dev/clipboard', 'wt')
285 | fo.write(text)
286 | fo.close()
287 |
288 | def paste_dev_clipboard():
289 | fo = open('/dev/clipboard', 'rt')
290 | content = fo.read()
291 | fo.close()
292 | return content
293 |
294 | return copy_dev_clipboard, paste_dev_clipboard
295 |
296 |
297 | def init_no_clipboard():
298 | class ClipboardUnavailable(object):
299 |
300 | def __call__(self, *args, **kwargs):
301 | raise PyperclipException(EXCEPT_MSG)
302 |
303 | if PY2:
304 | def __nonzero__(self):
305 | return False
306 | else:
307 | def __bool__(self):
308 | return False
309 |
310 | return ClipboardUnavailable(), ClipboardUnavailable()
311 |
312 |
313 |
314 |
315 | # Windows-related clipboard functions:
316 | class CheckedCall(object):
317 | def __init__(self, f):
318 | super(CheckedCall, self).__setattr__("f", f)
319 |
320 | def __call__(self, *args):
321 | ret = self.f(*args)
322 | if not ret and get_errno():
323 | raise PyperclipWindowsException("Error calling " + self.f.__name__)
324 | return ret
325 |
326 | def __setattr__(self, key, value):
327 | setattr(self.f, key, value)
328 |
329 |
330 | def init_windows_clipboard():
331 | global HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, HINSTANCE, HMENU, BOOL, UINT, HANDLE
332 | from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND,
333 | HINSTANCE, HMENU, BOOL, UINT, HANDLE)
334 |
335 | windll = ctypes.windll
336 | msvcrt = ctypes.CDLL('msvcrt')
337 |
338 | safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA)
339 | safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT,
340 | INT, INT, HWND, HMENU, HINSTANCE, LPVOID]
341 | safeCreateWindowExA.restype = HWND
342 |
343 | safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow)
344 | safeDestroyWindow.argtypes = [HWND]
345 | safeDestroyWindow.restype = BOOL
346 |
347 | OpenClipboard = windll.user32.OpenClipboard
348 | OpenClipboard.argtypes = [HWND]
349 | OpenClipboard.restype = BOOL
350 |
351 | safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard)
352 | safeCloseClipboard.argtypes = []
353 | safeCloseClipboard.restype = BOOL
354 |
355 | safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard)
356 | safeEmptyClipboard.argtypes = []
357 | safeEmptyClipboard.restype = BOOL
358 |
359 | safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData)
360 | safeGetClipboardData.argtypes = [UINT]
361 | safeGetClipboardData.restype = HANDLE
362 |
363 | safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData)
364 | safeSetClipboardData.argtypes = [UINT, HANDLE]
365 | safeSetClipboardData.restype = HANDLE
366 |
367 | safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc)
368 | safeGlobalAlloc.argtypes = [UINT, c_size_t]
369 | safeGlobalAlloc.restype = HGLOBAL
370 |
371 | safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock)
372 | safeGlobalLock.argtypes = [HGLOBAL]
373 | safeGlobalLock.restype = LPVOID
374 |
375 | safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock)
376 | safeGlobalUnlock.argtypes = [HGLOBAL]
377 | safeGlobalUnlock.restype = BOOL
378 |
379 | wcslen = CheckedCall(msvcrt.wcslen)
380 | wcslen.argtypes = [c_wchar_p]
381 | wcslen.restype = UINT
382 |
383 | GMEM_MOVEABLE = 0x0002
384 | CF_UNICODETEXT = 13
385 |
386 | @contextlib.contextmanager
387 | def window():
388 | """
389 | Context that provides a valid Windows hwnd.
390 | """
391 | # we really just need the hwnd, so setting "STATIC"
392 | # as predefined lpClass is just fine.
393 | hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0,
394 | None, None, None, None)
395 | try:
396 | yield hwnd
397 | finally:
398 | safeDestroyWindow(hwnd)
399 |
400 | @contextlib.contextmanager
401 | def clipboard(hwnd):
402 | """
403 | Context manager that opens the clipboard and prevents
404 | other applications from modifying the clipboard content.
405 | """
406 | # We may not get the clipboard handle immediately because
407 | # some other application is accessing it (?)
408 | # We try for at least 500ms to get the clipboard.
409 | t = time.time() + 0.5
410 | success = False
411 | while time.time() < t:
412 | success = OpenClipboard(hwnd)
413 | if success:
414 | break
415 | time.sleep(0.01)
416 | if not success:
417 | raise PyperclipWindowsException("Error calling OpenClipboard")
418 |
419 | try:
420 | yield
421 | finally:
422 | safeCloseClipboard()
423 |
424 | def copy_windows(text):
425 | # This function is heavily based on
426 | # http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard
427 |
428 | text = _stringifyText(text) # Converts non-str values to str.
429 |
430 | with window() as hwnd:
431 | # http://msdn.com/ms649048
432 | # If an application calls OpenClipboard with hwnd set to NULL,
433 | # EmptyClipboard sets the clipboard owner to NULL;
434 | # this causes SetClipboardData to fail.
435 | # => We need a valid hwnd to copy something.
436 | with clipboard(hwnd):
437 | safeEmptyClipboard()
438 |
439 | if text:
440 | # http://msdn.com/ms649051
441 | # If the hMem parameter identifies a memory object,
442 | # the object must have been allocated using the
443 | # function with the GMEM_MOVEABLE flag.
444 | count = wcslen(text) + 1
445 | handle = safeGlobalAlloc(GMEM_MOVEABLE,
446 | count * sizeof(c_wchar))
447 | locked_handle = safeGlobalLock(handle)
448 |
449 | ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar))
450 |
451 | safeGlobalUnlock(handle)
452 | safeSetClipboardData(CF_UNICODETEXT, handle)
453 |
454 | def paste_windows():
455 | with clipboard(None):
456 | handle = safeGetClipboardData(CF_UNICODETEXT)
457 | if not handle:
458 | # GetClipboardData may return NULL with errno == NO_ERROR
459 | # if the clipboard is empty.
460 | # (Also, it may return a handle to an empty buffer,
461 | # but technically that's not empty)
462 | return ""
463 | return c_wchar_p(handle).value
464 |
465 | return copy_windows, paste_windows
466 |
467 |
468 | def init_wsl_clipboard():
469 | def copy_wsl(text):
470 | text = _stringifyText(text) # Converts non-str values to str.
471 | p = subprocess.Popen(['clip.exe'],
472 | stdin=subprocess.PIPE, close_fds=True)
473 | p.communicate(input=text.encode(ENCODING))
474 |
475 | def paste_wsl():
476 | p = subprocess.Popen(['powershell.exe', '-command', 'Get-Clipboard'],
477 | stdout=subprocess.PIPE,
478 | stderr=subprocess.PIPE,
479 | close_fds=True)
480 | stdout, stderr = p.communicate()
481 | # WSL appends "\r\n" to the contents.
482 | return stdout[:-2].decode(ENCODING)
483 |
484 | return copy_wsl, paste_wsl
485 |
486 |
487 | # Automatic detection of clipboard mechanisms and importing is done in deteremine_clipboard():
488 | def determine_clipboard():
489 | '''
490 | Determine the OS/platform and set the copy() and paste() functions
491 | accordingly.
492 | '''
493 |
494 | global Foundation, AppKit, gtk, qtpy, PyQt4, PyQt5
495 |
496 | # Setup for the CYGWIN platform:
497 | if 'cygwin' in platform.system().lower(): # Cygwin has a variety of values returned by platform.system(), such as 'CYGWIN_NT-6.1'
498 | # FIXME: pyperclip currently does not support Cygwin,
499 | # see https://github.com/asweigart/pyperclip/issues/55
500 | if os.path.exists('/dev/clipboard'):
501 | warnings.warn('Pyperclip\'s support for Cygwin is not perfect, see https://github.com/asweigart/pyperclip/issues/55')
502 | return init_dev_clipboard_clipboard()
503 |
504 | # Setup for the WINDOWS platform:
505 | elif os.name == 'nt' or platform.system() == 'Windows':
506 | return init_windows_clipboard()
507 |
508 | if platform.system() == 'Linux':
509 | with open('/proc/version', 'r') as f:
510 | if "Microsoft" in f.read():
511 | return init_wsl_clipboard()
512 |
513 | # Setup for the MAC OS X platform:
514 | if os.name == 'mac' or platform.system() == 'Darwin':
515 | try:
516 | import Foundation # check if pyobjc is installed
517 | import AppKit
518 | except ImportError:
519 | return init_osx_pbcopy_clipboard()
520 | else:
521 | return init_osx_pyobjc_clipboard()
522 |
523 | # Setup for the LINUX platform:
524 | if HAS_DISPLAY:
525 | try:
526 | import gtk # check if gtk is installed
527 | except ImportError:
528 | pass # We want to fail fast for all non-ImportError exceptions.
529 | else:
530 | return init_gtk_clipboard()
531 |
532 | if _executable_exists("xsel"):
533 | return init_xsel_clipboard()
534 | if _executable_exists("xclip"):
535 | return init_xclip_clipboard()
536 | if _executable_exists("klipper") and _executable_exists("qdbus"):
537 | return init_klipper_clipboard()
538 |
539 | try:
540 | # qtpy is a small abstraction layer that lets you write applications using a single api call to either PyQt or PySide.
541 | # https://pypi.python.org/pypi/QtPy
542 | import qtpy # check if qtpy is installed
543 | except ImportError:
544 | # If qtpy isn't installed, fall back on importing PyQt4.
545 | try:
546 | import PyQt5 # check if PyQt5 is installed
547 | except ImportError:
548 | try:
549 | import PyQt4 # check if PyQt4 is installed
550 | except ImportError:
551 | pass # We want to fail fast for all non-ImportError exceptions.
552 | else:
553 | return init_qt_clipboard()
554 | else:
555 | return init_qt_clipboard()
556 | else:
557 | return init_qt_clipboard()
558 |
559 |
560 | return init_no_clipboard()
561 |
562 |
563 | def set_clipboard(clipboard):
564 | '''
565 | Explicitly sets the clipboard mechanism. The "clipboard mechanism" is how
566 | the copy() and paste() functions interact with the operating system to
567 | implement the copy/paste feature. The clipboard parameter must be one of:
568 | - pbcopy
569 | - pbobjc (default on Mac OS X)
570 | - gtk
571 | - qt
572 | - xclip
573 | - xsel
574 | - klipper
575 | - windows (default on Windows)
576 | - no (this is what is set when no clipboard mechanism can be found)
577 | '''
578 | global copy, paste
579 |
580 | clipboard_types = {'pbcopy': init_osx_pbcopy_clipboard,
581 | 'pyobjc': init_osx_pyobjc_clipboard,
582 | 'gtk': init_gtk_clipboard,
583 | 'qt': init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5'
584 | 'xclip': init_xclip_clipboard,
585 | 'xsel': init_xsel_clipboard,
586 | 'klipper': init_klipper_clipboard,
587 | 'windows': init_windows_clipboard,
588 | 'no': init_no_clipboard}
589 |
590 | if clipboard not in clipboard_types:
591 | raise ValueError('Argument must be one of %s' % (', '.join([repr(_) for _ in clipboard_types.keys()])))
592 |
593 | # Sets pyperclip's copy() and paste() functions:
594 | copy, paste = clipboard_types[clipboard]()
595 |
596 |
597 | def lazy_load_stub_copy(text):
598 | '''
599 | A stub function for copy(), which will load the real copy() function when
600 | called so that the real copy() function is used for later calls.
601 |
602 | This allows users to import pyperclip without having determine_clipboard()
603 | automatically run, which will automatically select a clipboard mechanism.
604 | This could be a problem if it selects, say, the memory-heavy PyQt4 module
605 | but the user was just going to immediately call set_clipboard() to use a
606 | different clipboard mechanism.
607 |
608 | The lazy loading this stub function implements gives the user a chance to
609 | call set_clipboard() to pick another clipboard mechanism. Or, if the user
610 | simply calls copy() or paste() without calling set_clipboard() first,
611 | will fall back on whatever clipboard mechanism that determine_clipboard()
612 | automatically chooses.
613 | '''
614 | global copy, paste
615 | copy, paste = determine_clipboard()
616 | return copy(text)
617 |
618 |
619 | def lazy_load_stub_paste():
620 | '''
621 | A stub function for paste(), which will load the real paste() function when
622 | called so that the real paste() function is used for later calls.
623 |
624 | This allows users to import pyperclip without having determine_clipboard()
625 | automatically run, which will automatically select a clipboard mechanism.
626 | This could be a problem if it selects, say, the memory-heavy PyQt4 module
627 | but the user was just going to immediately call set_clipboard() to use a
628 | different clipboard mechanism.
629 |
630 | The lazy loading this stub function implements gives the user a chance to
631 | call set_clipboard() to pick another clipboard mechanism. Or, if the user
632 | simply calls copy() or paste() without calling set_clipboard() first,
633 | will fall back on whatever clipboard mechanism that determine_clipboard()
634 | automatically chooses.
635 | '''
636 | global copy, paste
637 | copy, paste = determine_clipboard()
638 | return paste()
639 |
640 |
641 | def is_available():
642 | return copy != lazy_load_stub_copy and paste != lazy_load_stub_paste
643 |
644 |
645 | # Initially, copy() and paste() are set to lazy loading wrappers which will
646 | # set `copy` and `paste` to real functions the first time they're used, unless
647 | # set_clipboard() or determine_clipboard() is called first.
648 | copy, paste = lazy_load_stub_copy, lazy_load_stub_paste
649 |
650 |
651 | __all__ = ['copy', 'paste', 'set_clipboard', 'determine_clipboard']
652 |
--------------------------------------------------------------------------------
/1_The_Reverse_Cipher/reverse_cipher.py:
--------------------------------------------------------------------------------
1 | #Reverse Cipher
2 |
3 | message = raw_input('Enter message to be encrypted:\n')
4 | translated = ''
5 |
6 | i = len(message) - 1
7 | while i >= 0 :
8 | translated = translated + message[i]
9 | i -= 1
10 |
11 | print(translated)
12 |
--------------------------------------------------------------------------------
/2_The_Caesar_Cipher/caesarCipher.py:
--------------------------------------------------------------------------------
1 | # Caesar Cipher
2 | # Basic example from https://www.nostarch.com/crackingcodes/ (BSD Licensed)
3 | import paperclip
4 |
5 | print('-------------------------\n')
6 | print('| Caesar Cipher |\n')
7 | print('-------------------------\n')
8 | print('Hello, What would you like to do? :')
9 | print('1. Encrypt with Caesar Cipher')
10 | print('2. Decrypt with Caesar Cipher')
11 | status = raw_input()
12 |
13 | # Whether the program encrypts or decrypts :
14 | if status == '1' or status == 'encrypt':
15 | mode = 'encrypt'
16 | elif status == '2' or status == 'decrypt':
17 | mode = 'decrypt'
18 |
19 | #The encryption/decryption key:
20 | keyMessage = 'Please enter the key to ' + mode +' :\n'
21 | key = int(raw_input(keyMessage))
22 |
23 | #The encryption/decryption message:
24 | messageInput = 'Enter message to be ' + mode +'ed:\n'
25 | message = raw_input(messageInput)
26 |
27 | # Every possible that can me encrypted :
28 | SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.'
29 |
30 | # Store the encrypted /decrypted form of the message :
31 | translated = ''
32 |
33 | for symbol in message :
34 | # Note: Only symbols in the SYMBOLS string can be encrypted/decrypted.
35 | if symbol in SYMBOLS:
36 | symbolIndex = SYMBOLS.find(symbol)
37 |
38 | # Perform encryption/decryption :
39 | if mode == 'encrypt':
40 | translatedIndex = symbolIndex + key
41 | elif mode == 'decrypt':
42 | translatedIndex = symbolIndex - key
43 |
44 | # Handle wraparound, if needed:
45 | if translatedIndex >= len(SYMBOLS):
46 | translatedIndex = translatedIndex - len(SYMBOLS)
47 | elif translatedIndex < 0:
48 | translatedIndex = translatedIndex + len(SYMBOLS)
49 |
50 | translated = translated + SYMBOLS[translatedIndex]
51 | else:
52 | # Append the symbol without encrypting/decrypting:
53 | translated = translated + symbol
54 |
55 | # Output the translated string:
56 | print(translated)
57 | paperclip.copy(translated)
58 |
--------------------------------------------------------------------------------
/2_The_Caesar_Cipher/paperclip.py:
--------------------------------------------------------------------------------
1 | """
2 | Pyperclip
3 |
4 | A cross-platform clipboard module for Python, with copy & paste functions for plain text.
5 | By Al Sweigart al@inventwithpython.com
6 | BSD License
7 |
8 | Usage:
9 | import pyperclip
10 | pyperclip.copy('The text to be copied to the clipboard.')
11 | spam = pyperclip.paste()
12 |
13 | if not pyperclip.is_available():
14 | print("Copy functionality unavailable!")
15 |
16 | On Windows, no additional modules are needed.
17 | On Mac, the pyobjc module is used, falling back to the pbcopy and pbpaste cli
18 | commands. (These commands should come with OS X.).
19 | On Linux, install xclip or xsel via package manager. For example, in Debian:
20 | sudo apt-get install xclip
21 | sudo apt-get install xsel
22 |
23 | Otherwise on Linux, you will need the gtk or PyQt5/PyQt4 modules installed.
24 |
25 | gtk and PyQt4 modules are not available for Python 3,
26 | and this module does not work with PyGObject yet.
27 |
28 | Note: There seems to be a way to get gtk on Python 3, according to:
29 | https://askubuntu.com/questions/697397/python3-is-not-supporting-gtk-module
30 |
31 | Cygwin is currently not supported.
32 |
33 | Security Note: This module runs programs with these names:
34 | - which
35 | - where
36 | - pbcopy
37 | - pbpaste
38 | - xclip
39 | - xsel
40 | - klipper
41 | - qdbus
42 | A malicious user could rename or add programs with these names, tricking
43 | Pyperclip into running them with whatever permissions the Python process has.
44 |
45 | """
46 | __version__ = '1.7.0'
47 |
48 | import contextlib
49 | import ctypes
50 | import os
51 | import platform
52 | import subprocess
53 | import sys
54 | import time
55 | import warnings
56 |
57 | from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar
58 |
59 |
60 | # `import PyQt4` sys.exit()s if DISPLAY is not in the environment.
61 | # Thus, we need to detect the presence of $DISPLAY manually
62 | # and not load PyQt4 if it is absent.
63 | HAS_DISPLAY = os.getenv("DISPLAY", False)
64 |
65 | EXCEPT_MSG = """
66 | Pyperclip could not find a copy/paste mechanism for your system.
67 | For more information, please visit https://pyperclip.readthedocs.io/en/latest/introduction.html#not-implemented-error """
68 |
69 | PY2 = sys.version_info[0] == 2
70 |
71 | STR_OR_UNICODE = unicode if PY2 else str # For paste(): Python 3 uses str, Python 2 uses unicode.
72 |
73 | ENCODING = 'utf-8'
74 |
75 | # The "which" unix command finds where a command is.
76 | if platform.system() == 'Windows':
77 | WHICH_CMD = 'where'
78 | else:
79 | WHICH_CMD = 'which'
80 |
81 | def _executable_exists(name):
82 | return subprocess.call([WHICH_CMD, name],
83 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
84 |
85 |
86 |
87 | # Exceptions
88 | class PyperclipException(RuntimeError):
89 | pass
90 |
91 | class PyperclipWindowsException(PyperclipException):
92 | def __init__(self, message):
93 | message += " (%s)" % ctypes.WinError()
94 | super(PyperclipWindowsException, self).__init__(message)
95 |
96 |
97 | def _stringifyText(text):
98 | if PY2:
99 | acceptedTypes = (unicode, str, int, float, bool)
100 | else:
101 | acceptedTypes = (str, int, float, bool)
102 | if not isinstance(text, acceptedTypes):
103 | raise PyperclipException('only str, int, float, and bool values can be copied to the clipboard, not %s' % (text.__class__.__name__))
104 | return STR_OR_UNICODE(text)
105 |
106 |
107 | def init_osx_pbcopy_clipboard():
108 |
109 | def copy_osx_pbcopy(text):
110 | text = _stringifyText(text) # Converts non-str values to str.
111 | p = subprocess.Popen(['pbcopy', 'w'],
112 | stdin=subprocess.PIPE, close_fds=True)
113 | p.communicate(input=text.encode(ENCODING))
114 |
115 | def paste_osx_pbcopy():
116 | p = subprocess.Popen(['pbpaste', 'r'],
117 | stdout=subprocess.PIPE, close_fds=True)
118 | stdout, stderr = p.communicate()
119 | return stdout.decode(ENCODING)
120 |
121 | return copy_osx_pbcopy, paste_osx_pbcopy
122 |
123 |
124 | def init_osx_pyobjc_clipboard():
125 | def copy_osx_pyobjc(text):
126 | '''Copy string argument to clipboard'''
127 | text = _stringifyText(text) # Converts non-str values to str.
128 | newStr = Foundation.NSString.stringWithString_(text).nsstring()
129 | newData = newStr.dataUsingEncoding_(Foundation.NSUTF8StringEncoding)
130 | board = AppKit.NSPasteboard.generalPasteboard()
131 | board.declareTypes_owner_([AppKit.NSStringPboardType], None)
132 | board.setData_forType_(newData, AppKit.NSStringPboardType)
133 |
134 | def paste_osx_pyobjc():
135 | "Returns contents of clipboard"
136 | board = AppKit.NSPasteboard.generalPasteboard()
137 | content = board.stringForType_(AppKit.NSStringPboardType)
138 | return content
139 |
140 | return copy_osx_pyobjc, paste_osx_pyobjc
141 |
142 |
143 | def init_gtk_clipboard():
144 | global gtk
145 | import gtk
146 |
147 | def copy_gtk(text):
148 | global cb
149 | text = _stringifyText(text) # Converts non-str values to str.
150 | cb = gtk.Clipboard()
151 | cb.set_text(text)
152 | cb.store()
153 |
154 | def paste_gtk():
155 | clipboardContents = gtk.Clipboard().wait_for_text()
156 | # for python 2, returns None if the clipboard is blank.
157 | if clipboardContents is None:
158 | return ''
159 | else:
160 | return clipboardContents
161 |
162 | return copy_gtk, paste_gtk
163 |
164 |
165 | def init_qt_clipboard():
166 | global QApplication
167 | # $DISPLAY should exist
168 |
169 | # Try to import from qtpy, but if that fails try PyQt5 then PyQt4
170 | try:
171 | from qtpy.QtWidgets import QApplication
172 | except:
173 | try:
174 | from PyQt5.QtWidgets import QApplication
175 | except:
176 | from PyQt4.QtGui import QApplication
177 |
178 | app = QApplication.instance()
179 | if app is None:
180 | app = QApplication([])
181 |
182 | def copy_qt(text):
183 | text = _stringifyText(text) # Converts non-str values to str.
184 | cb = app.clipboard()
185 | cb.setText(text)
186 |
187 | def paste_qt():
188 | cb = app.clipboard()
189 | return STR_OR_UNICODE(cb.text())
190 |
191 | return copy_qt, paste_qt
192 |
193 |
194 | def init_xclip_clipboard():
195 | DEFAULT_SELECTION='c'
196 | PRIMARY_SELECTION='p'
197 |
198 | def copy_xclip(text, primary=False):
199 | text = _stringifyText(text) # Converts non-str values to str.
200 | selection=DEFAULT_SELECTION
201 | if primary:
202 | selection=PRIMARY_SELECTION
203 | p = subprocess.Popen(['xclip', '-selection', selection],
204 | stdin=subprocess.PIPE, close_fds=True)
205 | p.communicate(input=text.encode(ENCODING))
206 |
207 | def paste_xclip(primary=False):
208 | selection=DEFAULT_SELECTION
209 | if primary:
210 | selection=PRIMARY_SELECTION
211 | p = subprocess.Popen(['xclip', '-selection', selection, '-o'],
212 | stdout=subprocess.PIPE,
213 | stderr=subprocess.PIPE,
214 | close_fds=True)
215 | stdout, stderr = p.communicate()
216 | # Intentionally ignore extraneous output on stderr when clipboard is empty
217 | return stdout.decode(ENCODING)
218 |
219 | return copy_xclip, paste_xclip
220 |
221 |
222 | def init_xsel_clipboard():
223 | DEFAULT_SELECTION='-b'
224 | PRIMARY_SELECTION='-p'
225 |
226 | def copy_xsel(text, primary=False):
227 | text = _stringifyText(text) # Converts non-str values to str.
228 | selection_flag = DEFAULT_SELECTION
229 | if primary:
230 | selection_flag = PRIMARY_SELECTION
231 | p = subprocess.Popen(['xsel', selection_flag, '-i'],
232 | stdin=subprocess.PIPE, close_fds=True)
233 | p.communicate(input=text.encode(ENCODING))
234 |
235 | def paste_xsel(primary=False):
236 | selection_flag = DEFAULT_SELECTION
237 | if primary:
238 | selection_flag = PRIMARY_SELECTION
239 | p = subprocess.Popen(['xsel', selection_flag, '-o'],
240 | stdout=subprocess.PIPE, close_fds=True)
241 | stdout, stderr = p.communicate()
242 | return stdout.decode(ENCODING)
243 |
244 | return copy_xsel, paste_xsel
245 |
246 |
247 | def init_klipper_clipboard():
248 | def copy_klipper(text):
249 | text = _stringifyText(text) # Converts non-str values to str.
250 | p = subprocess.Popen(
251 | ['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents',
252 | text.encode(ENCODING)],
253 | stdin=subprocess.PIPE, close_fds=True)
254 | p.communicate(input=None)
255 |
256 | def paste_klipper():
257 | p = subprocess.Popen(
258 | ['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'],
259 | stdout=subprocess.PIPE, close_fds=True)
260 | stdout, stderr = p.communicate()
261 |
262 | # Workaround for https://bugs.kde.org/show_bug.cgi?id=342874
263 | # TODO: https://github.com/asweigart/pyperclip/issues/43
264 | clipboardContents = stdout.decode(ENCODING)
265 | # even if blank, Klipper will append a newline at the end
266 | assert len(clipboardContents) > 0
267 | # make sure that newline is there
268 | assert clipboardContents.endswith('\n')
269 | if clipboardContents.endswith('\n'):
270 | clipboardContents = clipboardContents[:-1]
271 | return clipboardContents
272 |
273 | return copy_klipper, paste_klipper
274 |
275 |
276 | def init_dev_clipboard_clipboard():
277 | def copy_dev_clipboard(text):
278 | text = _stringifyText(text) # Converts non-str values to str.
279 | if text == '':
280 | warnings.warn('Pyperclip cannot copy a blank string to the clipboard on Cygwin. This is effectively a no-op.')
281 | if '\r' in text:
282 | warnings.warn('Pyperclip cannot handle \\r characters on Cygwin.')
283 |
284 | fo = open('/dev/clipboard', 'wt')
285 | fo.write(text)
286 | fo.close()
287 |
288 | def paste_dev_clipboard():
289 | fo = open('/dev/clipboard', 'rt')
290 | content = fo.read()
291 | fo.close()
292 | return content
293 |
294 | return copy_dev_clipboard, paste_dev_clipboard
295 |
296 |
297 | def init_no_clipboard():
298 | class ClipboardUnavailable(object):
299 |
300 | def __call__(self, *args, **kwargs):
301 | raise PyperclipException(EXCEPT_MSG)
302 |
303 | if PY2:
304 | def __nonzero__(self):
305 | return False
306 | else:
307 | def __bool__(self):
308 | return False
309 |
310 | return ClipboardUnavailable(), ClipboardUnavailable()
311 |
312 |
313 |
314 |
315 | # Windows-related clipboard functions:
316 | class CheckedCall(object):
317 | def __init__(self, f):
318 | super(CheckedCall, self).__setattr__("f", f)
319 |
320 | def __call__(self, *args):
321 | ret = self.f(*args)
322 | if not ret and get_errno():
323 | raise PyperclipWindowsException("Error calling " + self.f.__name__)
324 | return ret
325 |
326 | def __setattr__(self, key, value):
327 | setattr(self.f, key, value)
328 |
329 |
330 | def init_windows_clipboard():
331 | global HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, HINSTANCE, HMENU, BOOL, UINT, HANDLE
332 | from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND,
333 | HINSTANCE, HMENU, BOOL, UINT, HANDLE)
334 |
335 | windll = ctypes.windll
336 | msvcrt = ctypes.CDLL('msvcrt')
337 |
338 | safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA)
339 | safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT,
340 | INT, INT, HWND, HMENU, HINSTANCE, LPVOID]
341 | safeCreateWindowExA.restype = HWND
342 |
343 | safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow)
344 | safeDestroyWindow.argtypes = [HWND]
345 | safeDestroyWindow.restype = BOOL
346 |
347 | OpenClipboard = windll.user32.OpenClipboard
348 | OpenClipboard.argtypes = [HWND]
349 | OpenClipboard.restype = BOOL
350 |
351 | safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard)
352 | safeCloseClipboard.argtypes = []
353 | safeCloseClipboard.restype = BOOL
354 |
355 | safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard)
356 | safeEmptyClipboard.argtypes = []
357 | safeEmptyClipboard.restype = BOOL
358 |
359 | safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData)
360 | safeGetClipboardData.argtypes = [UINT]
361 | safeGetClipboardData.restype = HANDLE
362 |
363 | safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData)
364 | safeSetClipboardData.argtypes = [UINT, HANDLE]
365 | safeSetClipboardData.restype = HANDLE
366 |
367 | safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc)
368 | safeGlobalAlloc.argtypes = [UINT, c_size_t]
369 | safeGlobalAlloc.restype = HGLOBAL
370 |
371 | safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock)
372 | safeGlobalLock.argtypes = [HGLOBAL]
373 | safeGlobalLock.restype = LPVOID
374 |
375 | safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock)
376 | safeGlobalUnlock.argtypes = [HGLOBAL]
377 | safeGlobalUnlock.restype = BOOL
378 |
379 | wcslen = CheckedCall(msvcrt.wcslen)
380 | wcslen.argtypes = [c_wchar_p]
381 | wcslen.restype = UINT
382 |
383 | GMEM_MOVEABLE = 0x0002
384 | CF_UNICODETEXT = 13
385 |
386 | @contextlib.contextmanager
387 | def window():
388 | """
389 | Context that provides a valid Windows hwnd.
390 | """
391 | # we really just need the hwnd, so setting "STATIC"
392 | # as predefined lpClass is just fine.
393 | hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0,
394 | None, None, None, None)
395 | try:
396 | yield hwnd
397 | finally:
398 | safeDestroyWindow(hwnd)
399 |
400 | @contextlib.contextmanager
401 | def clipboard(hwnd):
402 | """
403 | Context manager that opens the clipboard and prevents
404 | other applications from modifying the clipboard content.
405 | """
406 | # We may not get the clipboard handle immediately because
407 | # some other application is accessing it (?)
408 | # We try for at least 500ms to get the clipboard.
409 | t = time.time() + 0.5
410 | success = False
411 | while time.time() < t:
412 | success = OpenClipboard(hwnd)
413 | if success:
414 | break
415 | time.sleep(0.01)
416 | if not success:
417 | raise PyperclipWindowsException("Error calling OpenClipboard")
418 |
419 | try:
420 | yield
421 | finally:
422 | safeCloseClipboard()
423 |
424 | def copy_windows(text):
425 | # This function is heavily based on
426 | # http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard
427 |
428 | text = _stringifyText(text) # Converts non-str values to str.
429 |
430 | with window() as hwnd:
431 | # http://msdn.com/ms649048
432 | # If an application calls OpenClipboard with hwnd set to NULL,
433 | # EmptyClipboard sets the clipboard owner to NULL;
434 | # this causes SetClipboardData to fail.
435 | # => We need a valid hwnd to copy something.
436 | with clipboard(hwnd):
437 | safeEmptyClipboard()
438 |
439 | if text:
440 | # http://msdn.com/ms649051
441 | # If the hMem parameter identifies a memory object,
442 | # the object must have been allocated using the
443 | # function with the GMEM_MOVEABLE flag.
444 | count = wcslen(text) + 1
445 | handle = safeGlobalAlloc(GMEM_MOVEABLE,
446 | count * sizeof(c_wchar))
447 | locked_handle = safeGlobalLock(handle)
448 |
449 | ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar))
450 |
451 | safeGlobalUnlock(handle)
452 | safeSetClipboardData(CF_UNICODETEXT, handle)
453 |
454 | def paste_windows():
455 | with clipboard(None):
456 | handle = safeGetClipboardData(CF_UNICODETEXT)
457 | if not handle:
458 | # GetClipboardData may return NULL with errno == NO_ERROR
459 | # if the clipboard is empty.
460 | # (Also, it may return a handle to an empty buffer,
461 | # but technically that's not empty)
462 | return ""
463 | return c_wchar_p(handle).value
464 |
465 | return copy_windows, paste_windows
466 |
467 |
468 | def init_wsl_clipboard():
469 | def copy_wsl(text):
470 | text = _stringifyText(text) # Converts non-str values to str.
471 | p = subprocess.Popen(['clip.exe'],
472 | stdin=subprocess.PIPE, close_fds=True)
473 | p.communicate(input=text.encode(ENCODING))
474 |
475 | def paste_wsl():
476 | p = subprocess.Popen(['powershell.exe', '-command', 'Get-Clipboard'],
477 | stdout=subprocess.PIPE,
478 | stderr=subprocess.PIPE,
479 | close_fds=True)
480 | stdout, stderr = p.communicate()
481 | # WSL appends "\r\n" to the contents.
482 | return stdout[:-2].decode(ENCODING)
483 |
484 | return copy_wsl, paste_wsl
485 |
486 |
487 | # Automatic detection of clipboard mechanisms and importing is done in deteremine_clipboard():
488 | def determine_clipboard():
489 | '''
490 | Determine the OS/platform and set the copy() and paste() functions
491 | accordingly.
492 | '''
493 |
494 | global Foundation, AppKit, gtk, qtpy, PyQt4, PyQt5
495 |
496 | # Setup for the CYGWIN platform:
497 | if 'cygwin' in platform.system().lower(): # Cygwin has a variety of values returned by platform.system(), such as 'CYGWIN_NT-6.1'
498 | # FIXME: pyperclip currently does not support Cygwin,
499 | # see https://github.com/asweigart/pyperclip/issues/55
500 | if os.path.exists('/dev/clipboard'):
501 | warnings.warn('Pyperclip\'s support for Cygwin is not perfect, see https://github.com/asweigart/pyperclip/issues/55')
502 | return init_dev_clipboard_clipboard()
503 |
504 | # Setup for the WINDOWS platform:
505 | elif os.name == 'nt' or platform.system() == 'Windows':
506 | return init_windows_clipboard()
507 |
508 | if platform.system() == 'Linux':
509 | with open('/proc/version', 'r') as f:
510 | if "Microsoft" in f.read():
511 | return init_wsl_clipboard()
512 |
513 | # Setup for the MAC OS X platform:
514 | if os.name == 'mac' or platform.system() == 'Darwin':
515 | try:
516 | import Foundation # check if pyobjc is installed
517 | import AppKit
518 | except ImportError:
519 | return init_osx_pbcopy_clipboard()
520 | else:
521 | return init_osx_pyobjc_clipboard()
522 |
523 | # Setup for the LINUX platform:
524 | if HAS_DISPLAY:
525 | try:
526 | import gtk # check if gtk is installed
527 | except ImportError:
528 | pass # We want to fail fast for all non-ImportError exceptions.
529 | else:
530 | return init_gtk_clipboard()
531 |
532 | if _executable_exists("xsel"):
533 | return init_xsel_clipboard()
534 | if _executable_exists("xclip"):
535 | return init_xclip_clipboard()
536 | if _executable_exists("klipper") and _executable_exists("qdbus"):
537 | return init_klipper_clipboard()
538 |
539 | try:
540 | # qtpy is a small abstraction layer that lets you write applications using a single api call to either PyQt or PySide.
541 | # https://pypi.python.org/pypi/QtPy
542 | import qtpy # check if qtpy is installed
543 | except ImportError:
544 | # If qtpy isn't installed, fall back on importing PyQt4.
545 | try:
546 | import PyQt5 # check if PyQt5 is installed
547 | except ImportError:
548 | try:
549 | import PyQt4 # check if PyQt4 is installed
550 | except ImportError:
551 | pass # We want to fail fast for all non-ImportError exceptions.
552 | else:
553 | return init_qt_clipboard()
554 | else:
555 | return init_qt_clipboard()
556 | else:
557 | return init_qt_clipboard()
558 |
559 |
560 | return init_no_clipboard()
561 |
562 |
563 | def set_clipboard(clipboard):
564 | '''
565 | Explicitly sets the clipboard mechanism. The "clipboard mechanism" is how
566 | the copy() and paste() functions interact with the operating system to
567 | implement the copy/paste feature. The clipboard parameter must be one of:
568 | - pbcopy
569 | - pbobjc (default on Mac OS X)
570 | - gtk
571 | - qt
572 | - xclip
573 | - xsel
574 | - klipper
575 | - windows (default on Windows)
576 | - no (this is what is set when no clipboard mechanism can be found)
577 | '''
578 | global copy, paste
579 |
580 | clipboard_types = {'pbcopy': init_osx_pbcopy_clipboard,
581 | 'pyobjc': init_osx_pyobjc_clipboard,
582 | 'gtk': init_gtk_clipboard,
583 | 'qt': init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5'
584 | 'xclip': init_xclip_clipboard,
585 | 'xsel': init_xsel_clipboard,
586 | 'klipper': init_klipper_clipboard,
587 | 'windows': init_windows_clipboard,
588 | 'no': init_no_clipboard}
589 |
590 | if clipboard not in clipboard_types:
591 | raise ValueError('Argument must be one of %s' % (', '.join([repr(_) for _ in clipboard_types.keys()])))
592 |
593 | # Sets pyperclip's copy() and paste() functions:
594 | copy, paste = clipboard_types[clipboard]()
595 |
596 |
597 | def lazy_load_stub_copy(text):
598 | '''
599 | A stub function for copy(), which will load the real copy() function when
600 | called so that the real copy() function is used for later calls.
601 |
602 | This allows users to import pyperclip without having determine_clipboard()
603 | automatically run, which will automatically select a clipboard mechanism.
604 | This could be a problem if it selects, say, the memory-heavy PyQt4 module
605 | but the user was just going to immediately call set_clipboard() to use a
606 | different clipboard mechanism.
607 |
608 | The lazy loading this stub function implements gives the user a chance to
609 | call set_clipboard() to pick another clipboard mechanism. Or, if the user
610 | simply calls copy() or paste() without calling set_clipboard() first,
611 | will fall back on whatever clipboard mechanism that determine_clipboard()
612 | automatically chooses.
613 | '''
614 | global copy, paste
615 | copy, paste = determine_clipboard()
616 | return copy(text)
617 |
618 |
619 | def lazy_load_stub_paste():
620 | '''
621 | A stub function for paste(), which will load the real paste() function when
622 | called so that the real paste() function is used for later calls.
623 |
624 | This allows users to import pyperclip without having determine_clipboard()
625 | automatically run, which will automatically select a clipboard mechanism.
626 | This could be a problem if it selects, say, the memory-heavy PyQt4 module
627 | but the user was just going to immediately call set_clipboard() to use a
628 | different clipboard mechanism.
629 |
630 | The lazy loading this stub function implements gives the user a chance to
631 | call set_clipboard() to pick another clipboard mechanism. Or, if the user
632 | simply calls copy() or paste() without calling set_clipboard() first,
633 | will fall back on whatever clipboard mechanism that determine_clipboard()
634 | automatically chooses.
635 | '''
636 | global copy, paste
637 | copy, paste = determine_clipboard()
638 | return paste()
639 |
640 |
641 | def is_available():
642 | return copy != lazy_load_stub_copy and paste != lazy_load_stub_paste
643 |
644 |
645 | # Initially, copy() and paste() are set to lazy loading wrappers which will
646 | # set `copy` and `paste` to real functions the first time they're used, unless
647 | # set_clipboard() or determine_clipboard() is called first.
648 | copy, paste = lazy_load_stub_copy, lazy_load_stub_paste
649 |
650 |
651 | __all__ = ['copy', 'paste', 'set_clipboard', 'determine_clipboard']
652 |
--------------------------------------------------------------------------------
/2_The_Caesar_Cipher/paperclip.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ioankatsi/Cracking_Codes_With_Python/075775ffd471adb7b5b2640bac19f04679827a87/2_The_Caesar_Cipher/paperclip.pyc
--------------------------------------------------------------------------------
/3_Hacking_the_Caesar_Cipher_With_Brute_Force/Hacking_the_caesar_cipher_with_brute_force.py:
--------------------------------------------------------------------------------
1 | # Caesar Cipher with Brute Force
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | print('----------------------------------------------\n')
5 | print('| Caesar Cipher With Brute-Force |\n')
6 | print('----------------------------------------------\n')
7 |
8 | #The encryption/decryption key:
9 | messageInput = 'Please enter the message to be decrypted :\n'
10 | message = raw_input(messageInput)
11 |
12 | # Every possible that can me encrypted :
13 | SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.'
14 |
15 | # Loop through every possible key:
16 | for key in range(len(SYMBOLS)):
17 | # It is important to set translated to the blank string so thta the
18 | # previous iteration's value for translated is cleared
19 | translated = ''
20 | # Loop through each symbol in message:
21 | for symbol in message :
22 | if symbol in SYMBOLS:
23 | symbolIndex = SYMBOLS.find(symbol)
24 | translatedIndex = symbolIndex - key
25 |
26 | # Handle the wraparound:
27 | if translatedIndex < 0 :
28 | translatedIndex = translatedIndex + len(SYMBOLS)
29 |
30 | # Append the decrypted symbol:
31 | translated = translated + SYMBOLS[translatedIndex]
32 |
33 | else:
34 | # Append the symbol without encrypting/decrypting
35 | translated = translated + symbol
36 |
37 | # Display every possible decryption:
38 | print('Key #%s: %s' % (key, translated))
39 |
--------------------------------------------------------------------------------
/4_Encrypting_with_the_transposition_Cipher/encrypting_with_the_transposition_cipher.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | def main():
5 | messageInput = 'Please enter the message to be encrypted :\n'
6 | myMessage = input(messageInput)
7 | myKey = 3
8 |
9 | ciphertext = encryptMessage(myKey, myMessage)
10 |
11 | # Print the encrypted string in ciphertext to the screen, with
12 | # a | ("pipe character") after it in case there are spaces at
13 | # the end of the encrypted message:
14 | print(ciphertext + '|')
15 |
16 |
17 | def encryptMessage(key, message):
18 | # Each string in ciphretext represents a column in the grid:
19 | ciphertext = ['']*key
20 |
21 | # Loop through each column in ciphertext :
22 | for column in range(key):
23 | currentIndex = column
24 |
25 | # Keep looping until currentIndex goes past the message length:
26 | while currentIndex < len(message):
27 | # Place the character at the currentIndex in message at the
28 | # end of the current column in the ciphertext list :
29 | ciphertext[column] += message[currentIndex]
30 |
31 | # Move currentIndex over :
32 | currentIndex += key
33 |
34 | # Convert the ciphertext list into a single string value and return it:
35 | return ''.join(ciphertext)
36 |
37 | if __name__ == '__main__':
38 | main()
39 |
--------------------------------------------------------------------------------
/5_Decrypting_with_the_transposition_cipher/transpositionDecrypt.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Decryption
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | import math
5 |
6 | def main():
7 | myMessage = 'Cenoonommstmme oo snnio. s s c'
8 | myKey = 8
9 |
10 | plaintext = decryptMessage(myKey, myMessage)
11 |
12 | # Print with | (called "pipe" character ) after it in case
13 | # there are spaces at the end of the decrypted message :
14 | print(plaintext + '|')
15 |
16 | def decryptMessage(key, message):
17 | # The Transposition decrypt function will simulate the columns
18 | # and rows of the grid that the plaintext is written on by using a list
19 | # of string. FIrst, we need to calculate a few values.
20 |
21 | # The number of columns in our Transposition grid:
22 | numOfColumns = int(math.ceil(len(message) / float(key)))
23 | # The number of rows in our grid :
24 | numOfRows = key
25 | # The number of 'shaded boxes' in the last 'column' of the grid :
26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message)
27 |
28 | # Each string in plaintext represents a column in the grid :
29 | plaintext = [''] * numOfColumns
30 |
31 | # The column and row variable points to where in the grid the next
32 | # character in the encrypted message will go :
33 | column = 0
34 | row = 0
35 |
36 | for symbol in message :
37 | plaintext[column] += symbol
38 | column += 1 # Point to the next column
39 |
40 | # if there are no more columns OR we are at the shaded box, go back
41 | # to the first column and the next row :
42 | if (column == numOfColumns) or (column == numOfColumns - 1 and
43 | row >= numOfRows - numOfShadedBoxes):
44 | column = 0
45 | row += 1
46 |
47 | return ''.join(plaintext)
48 |
49 | # if transpositionDecrypt.py is run (insted of imported as a module),
50 | # call the main() function:
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/6_Programming_a_program_to_test_your_program/encrypting_with_the_transposition_cipher.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | def main():
5 | messageInput = 'Please enter the message to be encrypted :\n'
6 | myMessage = input(messageInput)
7 | myKey = 3
8 |
9 | ciphertext = encryptMessage(myKey, myMessage)
10 |
11 | # Print the encrypted string in ciphertext to the screen, with
12 | # a | ("pipe character") after it in case there are spaces at
13 | # the end of the encrypted message:
14 | print(ciphertext + '|')
15 |
16 |
17 | def encryptMessage(key, message):
18 | # Each string in ciphretext represents a column in the grid:
19 | ciphertext = ['']*key
20 |
21 | # Loop through each column in ciphertext :
22 | for column in range(key):
23 | currentIndex = column
24 |
25 | # Keep looping until currentIndex goes past the message length:
26 | while currentIndex < len(message):
27 | # Place the character at the currentIndex in message at the
28 | # end of the current column in the ciphertext list :
29 | ciphertext[column] += message[currentIndex]
30 |
31 | # Move currentIndex over :
32 | currentIndex += key
33 |
34 | # Convert the ciphertext list into a single string value and return it:
35 | return ''.join(ciphertext)
36 |
37 | if __name__ == '__main__':
38 | main()
39 |
--------------------------------------------------------------------------------
/6_Programming_a_program_to_test_your_program/transpositionDecrypt.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Decryption
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | import math
5 |
6 | def main():
7 | myMessage = 'Cenoonommstmme oo snnio. s s c'
8 | myKey = 8
9 |
10 | plaintext = decryptMessage(myKey, myMessage)
11 |
12 | # Print with | (called "pipe" character ) after it in case
13 | # there are spaces at the end of the decrypted message :
14 | print(plaintext + '|')
15 |
16 | def decryptMessage(key, message):
17 | # The Transposition decrypt function will simulate the columns
18 | # and rows of the grid that the plaintext is written on by using a list
19 | # of string. FIrst, we need to calculate a few values.
20 |
21 | # The number of columns in our Transposition grid:
22 | numOfColumns = int(math.ceil(len(message) / float(key)))
23 | # The number of rows in our grid :
24 | numOfRows = key
25 | # The number of 'shaded boxes' in the last 'column' of the grid :
26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message)
27 |
28 | # Each string in plaintext represents a column in the grid :
29 | plaintext = [''] * numOfColumns
30 |
31 | # The column and row variable points to where in the grid the next
32 | # character in the encrypted message will go :
33 | column = 0
34 | row = 0
35 |
36 | for symbol in message :
37 | plaintext[column] += symbol
38 | column += 1 # Point to the next column
39 |
40 | # if there are no more columns OR we are at the shaded box, go back
41 | # to the first column and the next row :
42 | if (column == numOfColumns) or (column == numOfColumns - 1 and
43 | row >= numOfRows - numOfShadedBoxes):
44 | column = 0
45 | row += 1
46 |
47 | return ''.join(plaintext)
48 |
49 | # if transpositionDecrypt.py is run (insted of imported as a module),
50 | # call the main() function:
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/6_Programming_a_program_to_test_your_program/transpositionTets.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Test
2 | # https://www.nostarch.com/crackingcodes/
3 |
4 | import random, sys, encrypting_with_the_transposition_cipher, transpositionDecrypt
5 |
6 | def main():
7 | random.seed(42) # set the random "seed " to static value.
8 |
9 | for i in range(20): # run 20 tests
10 | # Generate random messages to test.
11 |
12 | # The message will have a random length
13 | message = 'ABCDEFGHIJKLMOPQRSTUVWXYZ' * random.randint(4,40)
14 |
15 | # Convert the message string to a list to shuffle it :
16 | message = list(message)
17 | random.shuffle(message)
18 | message = ''.join(message) # Convert the list back to a string.
19 |
20 | print('Test #%s: "%s..."' % (i + 1, message[:50]))
21 |
22 | # Check all posible keys for each message :
23 | for key in range(1, int(len(message)/2)):
24 | encrypted = encrypting_with_the_transposition_cipher.encryptMessage(key, message)
25 | decrypted = transpositionDecrypt.decryptMessage(key, encrypted)
26 |
27 | # If the decryption does not match the original message, display
28 | # an error messag and quit:
29 | if message != decrypted :
30 | print('Mismatch with key %s and message %s.' % (key, message))
31 | print('Decrypted as: ' + decrypted)
32 | sys.exit()
33 |
34 | print('Transposition cipher test passed.')
35 |
36 | # If transpositionTest.py is run (instead of imported as a module) call
37 | # the main() function:
38 | if __name__ == '__main__':
39 | main()
40 |
--------------------------------------------------------------------------------
/7_Encrypting_and_Decrypting_Files/encrypting_with_the_transposition_cipher.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | def main():
5 | messageInput = 'Please enter the message to be encrypted :\n'
6 | myMessage = input(messageInput)
7 | myKey = 3
8 |
9 | ciphertext = encryptMessage(myKey, myMessage)
10 |
11 | # Print the encrypted string in ciphertext to the screen, with
12 | # a | ("pipe character") after it in case there are spaces at
13 | # the end of the encrypted message:
14 | print(ciphertext + '|')
15 |
16 |
17 | def encryptMessage(key, message):
18 | # Each string in ciphretext represents a column in the grid:
19 | ciphertext = ['']*key
20 |
21 | # Loop through each column in ciphertext :
22 | for column in range(key):
23 | currentIndex = column
24 |
25 | # Keep looping until currentIndex goes past the message length:
26 | while currentIndex < len(message):
27 | # Place the character at the currentIndex in message at the
28 | # end of the current column in the ciphertext list :
29 | ciphertext[column] += message[currentIndex]
30 |
31 | # Move currentIndex over :
32 | currentIndex += key
33 |
34 | # Convert the ciphertext list into a single string value and return it:
35 | return ''.join(ciphertext)
36 |
37 | if __name__ == '__main__':
38 | main()
39 |
--------------------------------------------------------------------------------
/7_Encrypting_and_Decrypting_Files/frankenstein.encrypted.txt:
--------------------------------------------------------------------------------
1 | DryIt,yaome os enm aMnaiIadiGraank r nr dmnaasIgeSaeivm eim se oafcr,i l2lmre
2 | sKa5d o.oM as c
--------------------------------------------------------------------------------
/7_Encrypting_and_Decrypting_Files/frankenstein.txt:
--------------------------------------------------------------------------------
1 | Dear Sir or Madam,
My name is Ioannis Katsikavelas, I am 25 years old and I am coming from Greece.
--------------------------------------------------------------------------------
/7_Encrypting_and_Decrypting_Files/transpositionDecrypt.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Decryption
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | import math
5 |
6 | def main():
7 | myMessage = 'Cenoonommstmme oo snnio. s s c'
8 | myKey = 8
9 |
10 | plaintext = decryptMessage(myKey, myMessage)
11 |
12 | # Print with | (called "pipe" character ) after it in case
13 | # there are spaces at the end of the decrypted message :
14 | print(plaintext + '|')
15 |
16 | def decryptMessage(key, message):
17 | # The Transposition decrypt function will simulate the columns
18 | # and rows of the grid that the plaintext is written on by using a list
19 | # of string. FIrst, we need to calculate a few values.
20 |
21 | # The number of columns in our Transposition grid:
22 | numOfColumns = int(math.ceil(len(message) / float(key)))
23 | # The number of rows in our grid :
24 | numOfRows = key
25 | # The number of 'shaded boxes' in the last 'column' of the grid :
26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message)
27 |
28 | # Each string in plaintext represents a column in the grid :
29 | plaintext = [''] * numOfColumns
30 |
31 | # The column and row variable points to where in the grid the next
32 | # character in the encrypted message will go :
33 | column = 0
34 | row = 0
35 |
36 | for symbol in message :
37 | plaintext[column] += symbol
38 | column += 1 # Point to the next column
39 |
40 | # if there are no more columns OR we are at the shaded box, go back
41 | # to the first column and the next row :
42 | if (column == numOfColumns) or (column == numOfColumns - 1 and
43 | row >= numOfRows - numOfShadedBoxes):
44 | column = 0
45 | row += 1
46 |
47 | return ''.join(plaintext)
48 |
49 | # if transpositionDecrypt.py is run (insted of imported as a module),
50 | # call the main() function:
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/7_Encrypting_and_Decrypting_Files/transpositionFileCipher.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Decryption
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | import time, os, sys, encrypting_with_the_transposition_cipher, transpositionDecrypt
5 |
6 | def main():
7 | inputFilename = 'frankenstein.txt'
8 | # Be careful! if a file with the outputFilename name already exists,
9 | # this program will overwrite that file :
10 |
11 | outputFilename = 'frankenstein.encrypted.txt'
12 | myKey = 10
13 | myMode = 'decrypt' # Set to 'encrypt' or 'decrypt'.
14 |
15 | # If the input file does not exist, the program terminates early:
16 | if not os.path.exists(inputFilename):
17 | print('The file %s does not exist. Quitting...' % (inputFilename))
18 | sys.exit()
19 |
20 | # If the output file already exists, give the user a chance to quit:
21 | if os.path.exists(outputFilename):
22 | print('This will overwrite the file %s. (C)ontinue or (Q)uit' %(outputFilename))
23 | response = input('> ')
24 | if not response.lower().startswith('c'):
25 | sys.exit()
26 |
27 | # Read in the message from the input file:
28 | fileObj = open(inputFilename)
29 | content = fileObj.read()
30 | fileObj.close()
31 |
32 | print("%sing..." %(myMode.title()))
33 |
34 | # Measure how long the encryption / decryption takes:
35 | startTime = time.time()
36 | if myMode == 'encrypt':
37 | translated = encrypting_with_the_transposition_cipher.encryptMessage(myKey, content)
38 | elif myMode == 'decrypt':
39 | translated = transpositionDecrypt.decryptMessage(myKey, content)
40 | totalTime = round(time.time() - startTime, 2)
41 | print('%sion time: %s seconds' % (myMode.title(), totalTime))
42 |
43 | # Write out the translated message to the output file :
44 | outputFileObj = open(outputFilename, 'w')
45 | outputFileObj.write(translated)
46 | outputFileObj.close()
47 |
48 | print('Done %sing %s (%s characters).' % (myMode, inputFilename, len(content)))
49 | print('%sed file is %s.' % (myMode.title(), outputFilename))
50 |
51 | # If TranspositionCipherFile.py is run (insted of imported as a module),
52 | # call the main() function:
53 | if __name__ == '__main__':
54 | main()
55 |
--------------------------------------------------------------------------------
/8_Detecting_English_Programmatically/detectEnglish.py:
--------------------------------------------------------------------------------
1 | # Detect English module
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | # To use, type this code:
5 | # import detectEnglish
6 | # detectEnglish.isEnglish(someString) # returns True or False
7 | # (There must be a "dictionary.txt" file in this directory with all)
8 | # English words in it, one word per line. Ypu can download thos from
9 | # https://www.nostarch.com/crackingcodes/.)
10 |
11 | UPPERLETTERS = 'ABCDEFGHIJKLMONPQRSTUVWXYZ'
12 | LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n'
13 |
14 | def loadDictionary():
15 | dictionaryFile = open('dictionary.txt')
16 | englishWords = {}
17 | for word in dictionaryFile.read().split('\n'):
18 | englishWords[word] = None
19 | dictionaryFile.close()
20 | return englishWords
21 |
22 | ENGLISH_WORDS = loadDictionary()
23 |
24 | def getEnglishCount(message):
25 | message = message.upper()
26 | message = removeNonLetters(message)
27 | possibleWords = message.split()
28 |
29 | if possibleWords == [] :
30 | return 0.0 # No words at all, so return 0.0
31 |
32 | matches = 0
33 | for word in possibleWords:
34 | if words in ENGLISH_WORDS:
35 | matches += 1
36 | return float(matches) / len(possibleWords)
37 |
38 | def removeNonLetters(message):
39 | lettersOnly = []
40 | for symbol in message:
41 | if symbol in LETTERS_AND_SPACE:
42 | lettersOnly.append(symbol)
43 | return ''.join(lettersOnly)
44 |
45 | def isEnglish(message, wordPercentage=20, letterPerchentage=85 ):
46 | # By default, 20% of the words must exist in the dictionary file, and
47 | # 85% of all the characters in the message must be letters or spaces
48 | # (not punctuation or numbers).
49 |
50 | wordsMatch = getEnglishCount(message) * 100 >= wordPercentage
51 | numLetters = len(removeNonLetters(message))
52 | messageLettersPercentage = float(numLetters) / len(message) * 100
53 | lettersMatch = messageLettersPercentage >= letterPerchentage
54 | return wordsMatch and lettersMatch
55 |
--------------------------------------------------------------------------------
/9_Hacking_The_Transposition_Cipher/detectEnglish.py:
--------------------------------------------------------------------------------
1 | # Detect English module
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | # To use, type this code:
5 | # import detectEnglish
6 | # detectEnglish.isEnglish(someString) # returns True or False
7 | # (There must be a "dictionary.txt" file in this directory with all)
8 | # English words in it, one word per line. Ypu can download thos from
9 | # https://www.nostarch.com/crackingcodes/.)
10 |
11 | UPPERLETTERS = 'ABCDEFGHIJKLMONPQRSTUVWXYZ'
12 | LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n'
13 |
14 | def loadDictionary():
15 | dictionaryFile = open('dictionary.txt')
16 | englishWords = {}
17 | for word in dictionaryFile.read().split('\n'):
18 | englishWords[word] = None
19 | dictionaryFile.close()
20 | return englishWords
21 |
22 | ENGLISH_WORDS = loadDictionary()
23 |
24 | def getEnglishCount(message):
25 | message = message.upper()
26 | message = removeNonLetters(message)
27 | possibleWords = message.split()
28 |
29 | if possibleWords == [] :
30 | return 0.0 # No words at all, so return 0.0
31 |
32 | matches = 0
33 | for word in possibleWords:
34 | if word in ENGLISH_WORDS:
35 | matches += 1
36 | return float(matches) / len(possibleWords)
37 |
38 | def removeNonLetters(message):
39 | lettersOnly = []
40 | for symbol in message:
41 | if symbol in LETTERS_AND_SPACE:
42 | lettersOnly.append(symbol)
43 | return ''.join(lettersOnly)
44 |
45 | def isEnglish(message, wordPercentage=20, letterPerchentage=85 ):
46 | # By default, 20% of the words must exist in the dictionary file, and
47 | # 85% of all the characters in the message must be letters or spaces
48 | # (not punctuation or numbers).
49 |
50 | wordsMatch = getEnglishCount(message) * 100 >= wordPercentage
51 | numLetters = len(removeNonLetters(message))
52 | messageLettersPercentage = float(numLetters) / len(message) * 100
53 | lettersMatch = messageLettersPercentage >= letterPerchentage
54 | return wordsMatch and lettersMatch
55 |
--------------------------------------------------------------------------------
/9_Hacking_The_Transposition_Cipher/transpositionDecrypt.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Decryption
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | import math
5 |
6 | def main():
7 | myMessage = 'Cenoonommstmme oo snnio. s s c'
8 | myKey = 8
9 |
10 | plaintext = decryptMessage(myKey, myMessage)
11 |
12 | # Print with | (called "pipe" character ) after it in case
13 | # there are spaces at the end of the decrypted message :
14 | print(plaintext + '|')
15 |
16 | def decryptMessage(key, message):
17 | # The Transposition decrypt function will simulate the columns
18 | # and rows of the grid that the plaintext is written on by using a list
19 | # of string. FIrst, we need to calculate a few values.
20 |
21 | # The number of columns in our Transposition grid:
22 | numOfColumns = int(math.ceil(len(message) / float(key)))
23 | # The number of rows in our grid :
24 | numOfRows = key
25 | # The number of 'shaded boxes' in the last 'column' of the grid :
26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message)
27 |
28 | # Each string in plaintext represents a column in the grid :
29 | plaintext = [''] * numOfColumns
30 |
31 | # The column and row variable points to where in the grid the next
32 | # character in the encrypted message will go :
33 | column = 0
34 | row = 0
35 |
36 | for symbol in message :
37 | plaintext[column] += symbol
38 | column += 1 # Point to the next column
39 |
40 | # if there are no more columns OR we are at the shaded box, go back
41 | # to the first column and the next row :
42 | if (column == numOfColumns) or (column == numOfColumns - 1 and
43 | row >= numOfRows - numOfShadedBoxes):
44 | column = 0
45 | row += 1
46 |
47 | return ''.join(plaintext)
48 |
49 | # if transpositionDecrypt.py is run (insted of imported as a module),
50 | # call the main() function:
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/9_Hacking_The_Transposition_Cipher/transpositionHacker.py:
--------------------------------------------------------------------------------
1 | # Transposition Cipher Hacker
2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed)
3 |
4 | import detectEnglish, transpositionDecrypt
5 |
6 | def main():
7 |
8 | myMessage = """AaKoosoeDe5 b5sn ma reno ora'lhlrrceey e enlh na indeit n uhoretrm au ieu v er Ne2 gmanw,forwnlbsya apor tE.no euarisfatt e mealefedhsppmgAnlnoe(c -or)alat r lw o eb nglom,Ain one dtes ilhetcdba. t tg eturmudg,tfl1e1 v nitiaicynhrCsaemie-sp ncgHt nie cetrgmnoa yc r,ieaa toesa- e a0m82e1w shcnth ekh gaecnpeutaaieetgn iodhso d ro hAe snrsfcegrt NCsLc b17m8aEheideikfr aBercaeu thllnrshicwsg etriebruaisss d iorr."""
9 |
10 | hackedMessage = hackTransposition(myMessage)
11 |
12 | if hackedMessage == None :
13 | print('Failed to hack encryption.')
14 | else:
15 | print('Message is decrypted :')
16 | print(hackedMessage)
17 |
18 | def hackTransposition(message):
19 | print('Hacking...')
20 |
21 | # Python programs canbe stopped at any time by pressing
22 | # Ctrl-C (on Windows) or Ctrl-D (on macOS and Linux):
23 | print('(Press Ctrl-C (on Windows) or Ctrl-D (on macOS and Linux) to quit at any time.)')
24 |
25 | # Brute-force by looping though every possible key:
26 | for key in range(1, len(message)):
27 | print('Trying key #%s...' % (key))
28 |
29 | decryptedText = transpositionDecrypt.decryptMessage(key, message)
30 |
31 | if detectEnglish.isEnglish(decryptedText):
32 | # Ask user if this is the correct decryption:
33 | print()
34 | print('Possible encryption hack:')
35 | print('Key %s: %s' % (key, decryptedText[:100]))
36 | print()
37 | print('Enter D if done, anything else to continue hacking:')
38 | response = input('> ')
39 |
40 | if response.strip().upper().startswith('D'):
41 | return decryptedText
42 |
43 | return None
44 |
45 | if __name__ == '__main__':
46 | main()
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cracking Codes With Python
2 | ### These chapters are from Cracking Codes With Python Book written by Al Sweigart.
Every Chapter, generally alternate between a Cipher and how You can hack that cipher.
The Book can be found [Here](https://nostarch.com/crackingcodes).
3 |
4 | # Brief Contents
5 | - The Reverse Cipher
6 | - The Caesar Cipher
7 | - Hacking the Caesar Cipher with Brute-Force
8 | - Encrypting with Transposition Cipher
9 | - Decrypting with the Transposition Cipher
10 | - Programming a Program to Test Your Program
11 | - Encrypting and Decrypting Files
12 | - Detecting English Programmatically
13 | - Hacking the transposition Cipher
14 | - A Modular Arithmetic Module for the Affine Cipher
15 | - Programming the Affine Cipher
16 | - Hacking The Affine Cipher
17 | - Programming the Simple Substitution Cipher
18 | - Hacking the Simple Substitution Cipher
19 | - Programming the Vigenére Cipher
20 | - Frequency Analysis
21 | - Hacking the Vigenére Cipher
22 | - The One-Time Pad Cipher
23 | - Finding and Generating Prime Numbers
24 | - Generating Keys for the Public Key Cipher
25 | - Programming the Public Key Cipher
26 |
27 | 1) ### The Reverse Cipher
28 | The reiverse cipher encrypts a message by printing it in reverse order. So "Hello, world!" encrypts to "!dlrow , olleH". To Decrypt, or get the original message, you simpy reverse the encrypted message. The encryption and the decryption steps are the same .
29 |
30 | 2) ### The Caesar Cipher
31 | The reverse cipher encrypts the same way. But the Caesar cipher uses keys, which encrypt the message differently depending on which key is used. The keys for the Caesar cipher are the integers from 0 to 25. Even if a cryptanalyst knows the Caesar cipher was used, that alone doesn't give them enough information to break the cipher. The must also know the key.
32 |
33 | 3) ### Hacking The Caesar Cipher With Brute-Force
34 | We can hack the Caesar cipher by using a cryptanalytic technique called brute-force. A brute-force attack tries every possible decryption key for a cipher.
35 |
36 | 4) ### Encrypting with Transposition Cipher
37 | The transposition cipher is more difficult to brute force because the number of possible keys depends on the message's length. There are many different types of transposition ciphers, including the rail fence cipher, route cipher, Myszkowski transposition cipher. This example covers a simple transposition cipher called the ***columnar transposition cipher***
38 |
39 | 5) ### Decrypting with the Transposition Cipher
40 | Steps for decrypting the Transposition Cipher :
41 | * Calculate the number of columns you need by dividing the length of the message by the key and then rounding up.
42 | * Draw boxes in columns and rows. Use the number of columns and you calculated before. The number of rows is the same as the key
43 | * Calculate the number of boxes to shade in by taking the total number of boxes and subtracking the length of the ciphertext message.
44 | * Shade in the number of boxes you calculated in step 3 at the bottom of the rightmost column.
45 | * Fill in the characters of the ciphertext starting at the top row and going from left to right. Skip any of the shaded boxes.
46 | * Get the plaintext by reading the leftmost column from top to bottom, and continuing to do the same in each column.
47 |
48 | 6) ### Programming A Program to Test Your Program
49 | You can not be absolutely sure the programs always work unless you test the ***encryptMessage()*** and ***decryptMessage()*** functions with all sort of message and key parameter values. But this would take a lot of time because you would have to type a message in the encryption program, set the key, run the encryption program, paste the cipher txt into the decryption program, set the key, and then run the decryption program.
50 |
51 | 7) ### Encrypting And Decrypting Files
52 | In previous examples, our programs have only worked on small messages that we type directly into the source code as string values. The cipher program we will make in this chapter will allow us to encrypt and decrypt entire files, which can be millions of characters in size.
53 |
54 | 8) ### Detecting English Programmatically
55 | Previously, we used the transposition file cipher to encrypt and decrypt entire files,
56 | but we haven't tried writing a brute-force program to hack the cipher yet. Messages encrypted with the transposition file cipher can have thousand of possible keys, which your computer can still easily brute-force, but you would then have to look through thousands of decryptions to find the one correct plaintext. As you can imagine, this can be a big problem, but there is a work-around.
57 |
58 | When the computer decrypts a message using the wrong key, the resulting string is garbage text instead of English text. We can program the computer to recognize when a decrypted message is English. That way, if the computer decrypts using the wrong key, it knows to go on and try the next possible key. Eventually, when the computer tries a key that decrypts to english text, it can stop and bring that key to your attention, sparing you from having to look through thousands of incorrect decryptions.
59 |
60 | 9) ### Hacking The Transposition Cipher
61 | We will use a brute-force approach to hack the transposition cipher. Of the thousands of keys that could possibly be associated with the transposition cipher, the correct key should be the only one that results in legible English. Using the ***detectEnglish().py*** module we wrote in previous section, our transposition cipher hacker program will help us find the correct key.
62 |
63 | 10) ### A Modular Arithmetic Module For the Affine Cipher
64 | We will learn about the multiplicative cipher and the affine cipher. The multiplicative cipher is similar
65 | to the Caesar cipher but encrypts using multiplication rather than addition. The affine cipher combines the multiplicative cipher and the Caesar cipher, resulting in a stronger and more reliable encryption.
66 |
67 | 11) ### Programming The Affine Cipher
68 | We will build and run programs to implement the affine cipher. Because the affine cipher cipher uses two different ciphers as part of its encryption process, it needs two keys: one for the multiplicative cipher and another for the Caesar cipher. For the affine program, we will split a single integer into two keys.
69 |
--------------------------------------------------------------------------------
/paperclip.py:
--------------------------------------------------------------------------------
1 | """
2 | Pyperclip
3 |
4 | A cross-platform clipboard module for Python, with copy & paste functions for plain text.
5 | By Al Sweigart al@inventwithpython.com
6 | BSD License
7 |
8 | Usage:
9 | import pyperclip
10 | pyperclip.copy('The text to be copied to the clipboard.')
11 | spam = pyperclip.paste()
12 |
13 | if not pyperclip.is_available():
14 | print("Copy functionality unavailable!")
15 |
16 | On Windows, no additional modules are needed.
17 | On Mac, the pyobjc module is used, falling back to the pbcopy and pbpaste cli
18 | commands. (These commands should come with OS X.).
19 | On Linux, install xclip or xsel via package manager. For example, in Debian:
20 | sudo apt-get install xclip
21 | sudo apt-get install xsel
22 |
23 | Otherwise on Linux, you will need the gtk or PyQt5/PyQt4 modules installed.
24 |
25 | gtk and PyQt4 modules are not available for Python 3,
26 | and this module does not work with PyGObject yet.
27 |
28 | Note: There seems to be a way to get gtk on Python 3, according to:
29 | https://askubuntu.com/questions/697397/python3-is-not-supporting-gtk-module
30 |
31 | Cygwin is currently not supported.
32 |
33 | Security Note: This module runs programs with these names:
34 | - which
35 | - where
36 | - pbcopy
37 | - pbpaste
38 | - xclip
39 | - xsel
40 | - klipper
41 | - qdbus
42 | A malicious user could rename or add programs with these names, tricking
43 | Pyperclip into running them with whatever permissions the Python process has.
44 |
45 | """
46 | __version__ = '1.7.0'
47 |
48 | import contextlib
49 | import ctypes
50 | import os
51 | import platform
52 | import subprocess
53 | import sys
54 | import time
55 | import warnings
56 |
57 | from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar
58 |
59 |
60 | # `import PyQt4` sys.exit()s if DISPLAY is not in the environment.
61 | # Thus, we need to detect the presence of $DISPLAY manually
62 | # and not load PyQt4 if it is absent.
63 | HAS_DISPLAY = os.getenv("DISPLAY", False)
64 |
65 | EXCEPT_MSG = """
66 | Pyperclip could not find a copy/paste mechanism for your system.
67 | For more information, please visit https://pyperclip.readthedocs.io/en/latest/introduction.html#not-implemented-error """
68 |
69 | PY2 = sys.version_info[0] == 2
70 |
71 | STR_OR_UNICODE = unicode if PY2 else str # For paste(): Python 3 uses str, Python 2 uses unicode.
72 |
73 | ENCODING = 'utf-8'
74 |
75 | # The "which" unix command finds where a command is.
76 | if platform.system() == 'Windows':
77 | WHICH_CMD = 'where'
78 | else:
79 | WHICH_CMD = 'which'
80 |
81 | def _executable_exists(name):
82 | return subprocess.call([WHICH_CMD, name],
83 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
84 |
85 |
86 |
87 | # Exceptions
88 | class PyperclipException(RuntimeError):
89 | pass
90 |
91 | class PyperclipWindowsException(PyperclipException):
92 | def __init__(self, message):
93 | message += " (%s)" % ctypes.WinError()
94 | super(PyperclipWindowsException, self).__init__(message)
95 |
96 |
97 | def _stringifyText(text):
98 | if PY2:
99 | acceptedTypes = (unicode, str, int, float, bool)
100 | else:
101 | acceptedTypes = (str, int, float, bool)
102 | if not isinstance(text, acceptedTypes):
103 | raise PyperclipException('only str, int, float, and bool values can be copied to the clipboard, not %s' % (text.__class__.__name__))
104 | return STR_OR_UNICODE(text)
105 |
106 |
107 | def init_osx_pbcopy_clipboard():
108 |
109 | def copy_osx_pbcopy(text):
110 | text = _stringifyText(text) # Converts non-str values to str.
111 | p = subprocess.Popen(['pbcopy', 'w'],
112 | stdin=subprocess.PIPE, close_fds=True)
113 | p.communicate(input=text.encode(ENCODING))
114 |
115 | def paste_osx_pbcopy():
116 | p = subprocess.Popen(['pbpaste', 'r'],
117 | stdout=subprocess.PIPE, close_fds=True)
118 | stdout, stderr = p.communicate()
119 | return stdout.decode(ENCODING)
120 |
121 | return copy_osx_pbcopy, paste_osx_pbcopy
122 |
123 |
124 | def init_osx_pyobjc_clipboard():
125 | def copy_osx_pyobjc(text):
126 | '''Copy string argument to clipboard'''
127 | text = _stringifyText(text) # Converts non-str values to str.
128 | newStr = Foundation.NSString.stringWithString_(text).nsstring()
129 | newData = newStr.dataUsingEncoding_(Foundation.NSUTF8StringEncoding)
130 | board = AppKit.NSPasteboard.generalPasteboard()
131 | board.declareTypes_owner_([AppKit.NSStringPboardType], None)
132 | board.setData_forType_(newData, AppKit.NSStringPboardType)
133 |
134 | def paste_osx_pyobjc():
135 | "Returns contents of clipboard"
136 | board = AppKit.NSPasteboard.generalPasteboard()
137 | content = board.stringForType_(AppKit.NSStringPboardType)
138 | return content
139 |
140 | return copy_osx_pyobjc, paste_osx_pyobjc
141 |
142 |
143 | def init_gtk_clipboard():
144 | global gtk
145 | import gtk
146 |
147 | def copy_gtk(text):
148 | global cb
149 | text = _stringifyText(text) # Converts non-str values to str.
150 | cb = gtk.Clipboard()
151 | cb.set_text(text)
152 | cb.store()
153 |
154 | def paste_gtk():
155 | clipboardContents = gtk.Clipboard().wait_for_text()
156 | # for python 2, returns None if the clipboard is blank.
157 | if clipboardContents is None:
158 | return ''
159 | else:
160 | return clipboardContents
161 |
162 | return copy_gtk, paste_gtk
163 |
164 |
165 | def init_qt_clipboard():
166 | global QApplication
167 | # $DISPLAY should exist
168 |
169 | # Try to import from qtpy, but if that fails try PyQt5 then PyQt4
170 | try:
171 | from qtpy.QtWidgets import QApplication
172 | except:
173 | try:
174 | from PyQt5.QtWidgets import QApplication
175 | except:
176 | from PyQt4.QtGui import QApplication
177 |
178 | app = QApplication.instance()
179 | if app is None:
180 | app = QApplication([])
181 |
182 | def copy_qt(text):
183 | text = _stringifyText(text) # Converts non-str values to str.
184 | cb = app.clipboard()
185 | cb.setText(text)
186 |
187 | def paste_qt():
188 | cb = app.clipboard()
189 | return STR_OR_UNICODE(cb.text())
190 |
191 | return copy_qt, paste_qt
192 |
193 |
194 | def init_xclip_clipboard():
195 | DEFAULT_SELECTION='c'
196 | PRIMARY_SELECTION='p'
197 |
198 | def copy_xclip(text, primary=False):
199 | text = _stringifyText(text) # Converts non-str values to str.
200 | selection=DEFAULT_SELECTION
201 | if primary:
202 | selection=PRIMARY_SELECTION
203 | p = subprocess.Popen(['xclip', '-selection', selection],
204 | stdin=subprocess.PIPE, close_fds=True)
205 | p.communicate(input=text.encode(ENCODING))
206 |
207 | def paste_xclip(primary=False):
208 | selection=DEFAULT_SELECTION
209 | if primary:
210 | selection=PRIMARY_SELECTION
211 | p = subprocess.Popen(['xclip', '-selection', selection, '-o'],
212 | stdout=subprocess.PIPE,
213 | stderr=subprocess.PIPE,
214 | close_fds=True)
215 | stdout, stderr = p.communicate()
216 | # Intentionally ignore extraneous output on stderr when clipboard is empty
217 | return stdout.decode(ENCODING)
218 |
219 | return copy_xclip, paste_xclip
220 |
221 |
222 | def init_xsel_clipboard():
223 | DEFAULT_SELECTION='-b'
224 | PRIMARY_SELECTION='-p'
225 |
226 | def copy_xsel(text, primary=False):
227 | text = _stringifyText(text) # Converts non-str values to str.
228 | selection_flag = DEFAULT_SELECTION
229 | if primary:
230 | selection_flag = PRIMARY_SELECTION
231 | p = subprocess.Popen(['xsel', selection_flag, '-i'],
232 | stdin=subprocess.PIPE, close_fds=True)
233 | p.communicate(input=text.encode(ENCODING))
234 |
235 | def paste_xsel(primary=False):
236 | selection_flag = DEFAULT_SELECTION
237 | if primary:
238 | selection_flag = PRIMARY_SELECTION
239 | p = subprocess.Popen(['xsel', selection_flag, '-o'],
240 | stdout=subprocess.PIPE, close_fds=True)
241 | stdout, stderr = p.communicate()
242 | return stdout.decode(ENCODING)
243 |
244 | return copy_xsel, paste_xsel
245 |
246 |
247 | def init_klipper_clipboard():
248 | def copy_klipper(text):
249 | text = _stringifyText(text) # Converts non-str values to str.
250 | p = subprocess.Popen(
251 | ['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents',
252 | text.encode(ENCODING)],
253 | stdin=subprocess.PIPE, close_fds=True)
254 | p.communicate(input=None)
255 |
256 | def paste_klipper():
257 | p = subprocess.Popen(
258 | ['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'],
259 | stdout=subprocess.PIPE, close_fds=True)
260 | stdout, stderr = p.communicate()
261 |
262 | # Workaround for https://bugs.kde.org/show_bug.cgi?id=342874
263 | # TODO: https://github.com/asweigart/pyperclip/issues/43
264 | clipboardContents = stdout.decode(ENCODING)
265 | # even if blank, Klipper will append a newline at the end
266 | assert len(clipboardContents) > 0
267 | # make sure that newline is there
268 | assert clipboardContents.endswith('\n')
269 | if clipboardContents.endswith('\n'):
270 | clipboardContents = clipboardContents[:-1]
271 | return clipboardContents
272 |
273 | return copy_klipper, paste_klipper
274 |
275 |
276 | def init_dev_clipboard_clipboard():
277 | def copy_dev_clipboard(text):
278 | text = _stringifyText(text) # Converts non-str values to str.
279 | if text == '':
280 | warnings.warn('Pyperclip cannot copy a blank string to the clipboard on Cygwin. This is effectively a no-op.')
281 | if '\r' in text:
282 | warnings.warn('Pyperclip cannot handle \\r characters on Cygwin.')
283 |
284 | fo = open('/dev/clipboard', 'wt')
285 | fo.write(text)
286 | fo.close()
287 |
288 | def paste_dev_clipboard():
289 | fo = open('/dev/clipboard', 'rt')
290 | content = fo.read()
291 | fo.close()
292 | return content
293 |
294 | return copy_dev_clipboard, paste_dev_clipboard
295 |
296 |
297 | def init_no_clipboard():
298 | class ClipboardUnavailable(object):
299 |
300 | def __call__(self, *args, **kwargs):
301 | raise PyperclipException(EXCEPT_MSG)
302 |
303 | if PY2:
304 | def __nonzero__(self):
305 | return False
306 | else:
307 | def __bool__(self):
308 | return False
309 |
310 | return ClipboardUnavailable(), ClipboardUnavailable()
311 |
312 |
313 |
314 |
315 | # Windows-related clipboard functions:
316 | class CheckedCall(object):
317 | def __init__(self, f):
318 | super(CheckedCall, self).__setattr__("f", f)
319 |
320 | def __call__(self, *args):
321 | ret = self.f(*args)
322 | if not ret and get_errno():
323 | raise PyperclipWindowsException("Error calling " + self.f.__name__)
324 | return ret
325 |
326 | def __setattr__(self, key, value):
327 | setattr(self.f, key, value)
328 |
329 |
330 | def init_windows_clipboard():
331 | global HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, HINSTANCE, HMENU, BOOL, UINT, HANDLE
332 | from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND,
333 | HINSTANCE, HMENU, BOOL, UINT, HANDLE)
334 |
335 | windll = ctypes.windll
336 | msvcrt = ctypes.CDLL('msvcrt')
337 |
338 | safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA)
339 | safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT,
340 | INT, INT, HWND, HMENU, HINSTANCE, LPVOID]
341 | safeCreateWindowExA.restype = HWND
342 |
343 | safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow)
344 | safeDestroyWindow.argtypes = [HWND]
345 | safeDestroyWindow.restype = BOOL
346 |
347 | OpenClipboard = windll.user32.OpenClipboard
348 | OpenClipboard.argtypes = [HWND]
349 | OpenClipboard.restype = BOOL
350 |
351 | safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard)
352 | safeCloseClipboard.argtypes = []
353 | safeCloseClipboard.restype = BOOL
354 |
355 | safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard)
356 | safeEmptyClipboard.argtypes = []
357 | safeEmptyClipboard.restype = BOOL
358 |
359 | safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData)
360 | safeGetClipboardData.argtypes = [UINT]
361 | safeGetClipboardData.restype = HANDLE
362 |
363 | safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData)
364 | safeSetClipboardData.argtypes = [UINT, HANDLE]
365 | safeSetClipboardData.restype = HANDLE
366 |
367 | safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc)
368 | safeGlobalAlloc.argtypes = [UINT, c_size_t]
369 | safeGlobalAlloc.restype = HGLOBAL
370 |
371 | safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock)
372 | safeGlobalLock.argtypes = [HGLOBAL]
373 | safeGlobalLock.restype = LPVOID
374 |
375 | safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock)
376 | safeGlobalUnlock.argtypes = [HGLOBAL]
377 | safeGlobalUnlock.restype = BOOL
378 |
379 | wcslen = CheckedCall(msvcrt.wcslen)
380 | wcslen.argtypes = [c_wchar_p]
381 | wcslen.restype = UINT
382 |
383 | GMEM_MOVEABLE = 0x0002
384 | CF_UNICODETEXT = 13
385 |
386 | @contextlib.contextmanager
387 | def window():
388 | """
389 | Context that provides a valid Windows hwnd.
390 | """
391 | # we really just need the hwnd, so setting "STATIC"
392 | # as predefined lpClass is just fine.
393 | hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0,
394 | None, None, None, None)
395 | try:
396 | yield hwnd
397 | finally:
398 | safeDestroyWindow(hwnd)
399 |
400 | @contextlib.contextmanager
401 | def clipboard(hwnd):
402 | """
403 | Context manager that opens the clipboard and prevents
404 | other applications from modifying the clipboard content.
405 | """
406 | # We may not get the clipboard handle immediately because
407 | # some other application is accessing it (?)
408 | # We try for at least 500ms to get the clipboard.
409 | t = time.time() + 0.5
410 | success = False
411 | while time.time() < t:
412 | success = OpenClipboard(hwnd)
413 | if success:
414 | break
415 | time.sleep(0.01)
416 | if not success:
417 | raise PyperclipWindowsException("Error calling OpenClipboard")
418 |
419 | try:
420 | yield
421 | finally:
422 | safeCloseClipboard()
423 |
424 | def copy_windows(text):
425 | # This function is heavily based on
426 | # http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard
427 |
428 | text = _stringifyText(text) # Converts non-str values to str.
429 |
430 | with window() as hwnd:
431 | # http://msdn.com/ms649048
432 | # If an application calls OpenClipboard with hwnd set to NULL,
433 | # EmptyClipboard sets the clipboard owner to NULL;
434 | # this causes SetClipboardData to fail.
435 | # => We need a valid hwnd to copy something.
436 | with clipboard(hwnd):
437 | safeEmptyClipboard()
438 |
439 | if text:
440 | # http://msdn.com/ms649051
441 | # If the hMem parameter identifies a memory object,
442 | # the object must have been allocated using the
443 | # function with the GMEM_MOVEABLE flag.
444 | count = wcslen(text) + 1
445 | handle = safeGlobalAlloc(GMEM_MOVEABLE,
446 | count * sizeof(c_wchar))
447 | locked_handle = safeGlobalLock(handle)
448 |
449 | ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar))
450 |
451 | safeGlobalUnlock(handle)
452 | safeSetClipboardData(CF_UNICODETEXT, handle)
453 |
454 | def paste_windows():
455 | with clipboard(None):
456 | handle = safeGetClipboardData(CF_UNICODETEXT)
457 | if not handle:
458 | # GetClipboardData may return NULL with errno == NO_ERROR
459 | # if the clipboard is empty.
460 | # (Also, it may return a handle to an empty buffer,
461 | # but technically that's not empty)
462 | return ""
463 | return c_wchar_p(handle).value
464 |
465 | return copy_windows, paste_windows
466 |
467 |
468 | def init_wsl_clipboard():
469 | def copy_wsl(text):
470 | text = _stringifyText(text) # Converts non-str values to str.
471 | p = subprocess.Popen(['clip.exe'],
472 | stdin=subprocess.PIPE, close_fds=True)
473 | p.communicate(input=text.encode(ENCODING))
474 |
475 | def paste_wsl():
476 | p = subprocess.Popen(['powershell.exe', '-command', 'Get-Clipboard'],
477 | stdout=subprocess.PIPE,
478 | stderr=subprocess.PIPE,
479 | close_fds=True)
480 | stdout, stderr = p.communicate()
481 | # WSL appends "\r\n" to the contents.
482 | return stdout[:-2].decode(ENCODING)
483 |
484 | return copy_wsl, paste_wsl
485 |
486 |
487 | # Automatic detection of clipboard mechanisms and importing is done in deteremine_clipboard():
488 | def determine_clipboard():
489 | '''
490 | Determine the OS/platform and set the copy() and paste() functions
491 | accordingly.
492 | '''
493 |
494 | global Foundation, AppKit, gtk, qtpy, PyQt4, PyQt5
495 |
496 | # Setup for the CYGWIN platform:
497 | if 'cygwin' in platform.system().lower(): # Cygwin has a variety of values returned by platform.system(), such as 'CYGWIN_NT-6.1'
498 | # FIXME: pyperclip currently does not support Cygwin,
499 | # see https://github.com/asweigart/pyperclip/issues/55
500 | if os.path.exists('/dev/clipboard'):
501 | warnings.warn('Pyperclip\'s support for Cygwin is not perfect, see https://github.com/asweigart/pyperclip/issues/55')
502 | return init_dev_clipboard_clipboard()
503 |
504 | # Setup for the WINDOWS platform:
505 | elif os.name == 'nt' or platform.system() == 'Windows':
506 | return init_windows_clipboard()
507 |
508 | if platform.system() == 'Linux':
509 | with open('/proc/version', 'r') as f:
510 | if "Microsoft" in f.read():
511 | return init_wsl_clipboard()
512 |
513 | # Setup for the MAC OS X platform:
514 | if os.name == 'mac' or platform.system() == 'Darwin':
515 | try:
516 | import Foundation # check if pyobjc is installed
517 | import AppKit
518 | except ImportError:
519 | return init_osx_pbcopy_clipboard()
520 | else:
521 | return init_osx_pyobjc_clipboard()
522 |
523 | # Setup for the LINUX platform:
524 | if HAS_DISPLAY:
525 | try:
526 | import gtk # check if gtk is installed
527 | except ImportError:
528 | pass # We want to fail fast for all non-ImportError exceptions.
529 | else:
530 | return init_gtk_clipboard()
531 |
532 | if _executable_exists("xsel"):
533 | return init_xsel_clipboard()
534 | if _executable_exists("xclip"):
535 | return init_xclip_clipboard()
536 | if _executable_exists("klipper") and _executable_exists("qdbus"):
537 | return init_klipper_clipboard()
538 |
539 | try:
540 | # qtpy is a small abstraction layer that lets you write applications using a single api call to either PyQt or PySide.
541 | # https://pypi.python.org/pypi/QtPy
542 | import qtpy # check if qtpy is installed
543 | except ImportError:
544 | # If qtpy isn't installed, fall back on importing PyQt4.
545 | try:
546 | import PyQt5 # check if PyQt5 is installed
547 | except ImportError:
548 | try:
549 | import PyQt4 # check if PyQt4 is installed
550 | except ImportError:
551 | pass # We want to fail fast for all non-ImportError exceptions.
552 | else:
553 | return init_qt_clipboard()
554 | else:
555 | return init_qt_clipboard()
556 | else:
557 | return init_qt_clipboard()
558 |
559 |
560 | return init_no_clipboard()
561 |
562 |
563 | def set_clipboard(clipboard):
564 | '''
565 | Explicitly sets the clipboard mechanism. The "clipboard mechanism" is how
566 | the copy() and paste() functions interact with the operating system to
567 | implement the copy/paste feature. The clipboard parameter must be one of:
568 | - pbcopy
569 | - pbobjc (default on Mac OS X)
570 | - gtk
571 | - qt
572 | - xclip
573 | - xsel
574 | - klipper
575 | - windows (default on Windows)
576 | - no (this is what is set when no clipboard mechanism can be found)
577 | '''
578 | global copy, paste
579 |
580 | clipboard_types = {'pbcopy': init_osx_pbcopy_clipboard,
581 | 'pyobjc': init_osx_pyobjc_clipboard,
582 | 'gtk': init_gtk_clipboard,
583 | 'qt': init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5'
584 | 'xclip': init_xclip_clipboard,
585 | 'xsel': init_xsel_clipboard,
586 | 'klipper': init_klipper_clipboard,
587 | 'windows': init_windows_clipboard,
588 | 'no': init_no_clipboard}
589 |
590 | if clipboard not in clipboard_types:
591 | raise ValueError('Argument must be one of %s' % (', '.join([repr(_) for _ in clipboard_types.keys()])))
592 |
593 | # Sets pyperclip's copy() and paste() functions:
594 | copy, paste = clipboard_types[clipboard]()
595 |
596 |
597 | def lazy_load_stub_copy(text):
598 | '''
599 | A stub function for copy(), which will load the real copy() function when
600 | called so that the real copy() function is used for later calls.
601 |
602 | This allows users to import pyperclip without having determine_clipboard()
603 | automatically run, which will automatically select a clipboard mechanism.
604 | This could be a problem if it selects, say, the memory-heavy PyQt4 module
605 | but the user was just going to immediately call set_clipboard() to use a
606 | different clipboard mechanism.
607 |
608 | The lazy loading this stub function implements gives the user a chance to
609 | call set_clipboard() to pick another clipboard mechanism. Or, if the user
610 | simply calls copy() or paste() without calling set_clipboard() first,
611 | will fall back on whatever clipboard mechanism that determine_clipboard()
612 | automatically chooses.
613 | '''
614 | global copy, paste
615 | copy, paste = determine_clipboard()
616 | return copy(text)
617 |
618 |
619 | def lazy_load_stub_paste():
620 | '''
621 | A stub function for paste(), which will load the real paste() function when
622 | called so that the real paste() function is used for later calls.
623 |
624 | This allows users to import pyperclip without having determine_clipboard()
625 | automatically run, which will automatically select a clipboard mechanism.
626 | This could be a problem if it selects, say, the memory-heavy PyQt4 module
627 | but the user was just going to immediately call set_clipboard() to use a
628 | different clipboard mechanism.
629 |
630 | The lazy loading this stub function implements gives the user a chance to
631 | call set_clipboard() to pick another clipboard mechanism. Or, if the user
632 | simply calls copy() or paste() without calling set_clipboard() first,
633 | will fall back on whatever clipboard mechanism that determine_clipboard()
634 | automatically chooses.
635 | '''
636 | global copy, paste
637 | copy, paste = determine_clipboard()
638 | return paste()
639 |
640 |
641 | def is_available():
642 | return copy != lazy_load_stub_copy and paste != lazy_load_stub_paste
643 |
644 |
645 | # Initially, copy() and paste() are set to lazy loading wrappers which will
646 | # set `copy` and `paste` to real functions the first time they're used, unless
647 | # set_clipboard() or determine_clipboard() is called first.
648 | copy, paste = lazy_load_stub_copy, lazy_load_stub_paste
649 |
650 |
651 | __all__ = ['copy', 'paste', 'set_clipboard', 'determine_clipboard']
652 |
--------------------------------------------------------------------------------