def is_probably_pgp(self): ''' Returns true if this is probably an OpenPGP message. >>> from goodcrypto_tests.mail.message_utils import get_encrypted_message_name >>> with open(get_encrypted_message_name('open-pgp-mime.txt')) as input_file: ... mime_message = EmailMessage(input_file) ... mime_message.is_probably_pgp() True ''' is_pgp = is_open_pgp_mime(self.get_message()) if not is_pgp: content = self.get_content() if is_string(content): is_pgp = self.contains_pgp_message_delimters(content) self.log_message('message uses in line pgp: {}'.format(is_pgp)) elif isinstance(content, list): for part in content: if isinstance(part, Message): part_content = part.get_payload() else: part_content = part if is_string(part_content): is_pgp = self.contains_pgp_message_delimters(part_content) if is_pgp: self.log_message('part of message uses in line pgp: {}'.format(is_pgp)) break else: self.log_message('part of content type is: {}'.format(repr(part_content))) else: self.log_message('content type is: {}'.format(type(content))) return is_pgp
def trim(string, xfix): ''' Trim all prefixes or suffixes of xfix from string. ''' if is_string(string): string = string.encode() if is_string(xfix): xfix = xfix.encode() length = len(xfix) while string.startswith(xfix): string = string[length:] while string.endswith(xfix): string = string[:-length] return string
def _decrypt(self, from_user, data, charset, crypto, passcode): ''' Decrypt the data from a message (internal use only). ''' decrypted_data = signed_by = None if crypto is None or data is None: decrypted_data = None self.log_message("no crypto defined") else: if is_string(data): encrypted_data = data else: encrypted_data = data.encode(errors='replace') # ASCII armored plaintext looks just like armored ciphertext, # so check that we actually have encrypted data if (OpenPGPAnalyzer().is_encrypted( encrypted_data, passphrase=passcode, crypto=crypto)): if self.DEBUGGING: self.log_message('encrypted data before decryption:\n{}'.format(encrypted_data)) decrypted_data, signed_by, result_code = crypto.decrypt(encrypted_data, passcode) if (decrypted_data == None or (is_string(decrypted_data) and len(decrypted_data) <= 0)): if self.DEBUGGING: self.log_message('decrypted data:\n{}'.format(decrypted_data)) decrypted_data = None self.log_message("unable to decrypt data") if self.DEBUGGING: self.log_message('data bytearray after decryption:\n{}'.format(decrypted_data)) else: if result_code == 0: tag = tags.get_decrypt_signature_tag( self.crypto_message, from_user, signed_by, crypto.get_name()) if tag is not None: self.crypto_message.add_prefix_to_tag_once(tag) self.log_message('decrypt sig tag: {}'.format(tag)) elif result_code == 2: self.crypto_message.add_error_tag_once( i18n("Can't verify signature because the message was encrypted using an unknown key. Ask the sender to send their key if they aren't using GoodCrypto.")) if is_string(decrypted_data): self.log_message('plaintext length: {}'.format(len(decrypted_data))) if self.DEBUGGING: self.log_message('plaintext:\n{}'.format(decrypted_data)) else: decrypted_data = None self.log_message("data appeared encrypted, but wasn't") if self.DEBUGGING: self.log_message('data:\n{}'.format(data)) return decrypted_data
def delete(name_or_software): ''' Delete the encryption software with a matching name. >>> updated_software = EncryptionSoftware(name='test', active=True, classname='goodcrypto.oce.test') >>> set(updated_software) True >>> delete('test') True ''' result_ok = True try: if is_string(name_or_software): software = get(name_or_software) else: software = name_or_software if software is None: log_message('nothing to delete') else: encryption_software = get(software.name) if encryption_software is None: log_message('{} does not exist so need to delete'.format( name_or_software)) else: encryption_software.delete() log_message("deleted {}".format(software.name)) except Exception: result_ok = False record_exception() log_message('EXCEPTION - see syr.exception.log for details') return result_ok
def set_message(self, new_message): ''' Set the new message. # Get a basic message first so we can avoid recursion >>> from goodcrypto_tests.mail.message_utils import get_basic_email_message >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER >>> basic_email_message = get_basic_email_message().get_message() >>> email_message = EmailMessage() >>> email_message.get_message().get(mime_constants.FROM_KEYWORD) is None True >>> email_message.set_message(basic_email_message) >>> email_message.get_message().get(mime_constants.FROM_KEYWORD) == EDWARD_LOCAL_USER True ''' old_message = self._message if is_string(new_message): try: if isinstance(self.parser, Parser): self._message = self.parser.parsestr(new_message) else: self._message = self.parser.parsebytes(new_message.encode()) except: self._message = old_message record_exception() else: self._message = new_message # restore the old message if the new one isn't valid. if not self.validate_message(): self._message = old_message self.log_message('restored previous message')
def chmod(mode, path, recursive=False): ''' Change permissions of path. mode is a string or octal integer for the chmod command. Examples:: 'o+rw,g+rw' 0660 ''' # arg order used to be chmod(path, mode, ...), so check types # delete this assert if no assertion errors 2015-01-01 # after we remove this assert, mode can be a string assert is_string(path) if isinstance(mode, int): # chmod wants an octal int, not decimal # so if it's an int we convert to an octal string mode = '0'+oct(mode) try: if recursive: sh.chmod('--recursive', mode, path) else: sh.chmod(mode, path) except sh.ErrorReturnCode as sh_exception: log.error('unable to chmod: path={}, mode={}'.format(path, mode)) log.error(sh_exception) raise
def delete(name_or_software): ''' Delete the encryption software with a matching name. >>> updated_software = EncryptionSoftware(name='test', active=True, classname='goodcrypto.oce.test') >>> set(updated_software) True >>> delete('test') True ''' result_ok = True try: if is_string(name_or_software): software = get(name_or_software) else: software = name_or_software if software is None: log_message('nothing to delete') else: encryption_software = get(software.name) if encryption_software is None: log_message('{} does not exist so need to delete'.format(name_or_software)) else: encryption_software.delete() log_message("deleted {}".format(software.name)) except Exception: result_ok = False record_exception() log_message('EXCEPTION - see syr.exception.log for details') return result_ok
def write(filename, lines, append=False): '''Write the lines to a text file. Log any io errors and then raise another ioerrr. ''' try: if append: method = 'at' else: method = 'wt' outputFile = open(filename, method) for line in lines: if isinstance(line, list): # line must be another list # let's assume there aren't any more nested lists for l in line: if IS_PY2: text = l else: if is_string(l): text = l.decode() else: text = l outputFile.write(text) else: if IS_PY2: text = line else: if is_string(line): text = line else: text = line.decode() outputFile.write(text) outputFile.close() except IOError: log('Unable to write %s' % filename) log(format_exc()) raise IOError return lines
def get_addendum_value(addendum, keyword): value = addendum[keyword] if is_string(value): value = value.strip() if value == 'True': value = True elif value == 'False': value = False return value
def strip_input(data): '''Strip the leading and trailing spaces.''' try: if data is not None: if is_string(data) or isinstance(data, CharField): data = data.strip() elif isinstance(data, EmailField): data = '{}'.format(data) data = data.strip() elif isinstance(data, bytes): data = data.decode().strip() except: log(traceback.format_exc()) return data
def delete(email_or_address): ''' Delete the contact with a matching email address. >>> # In honor of Amy Goodman, who hosts Democracy Now! # returns true because the address doesn't exist, even though it wasn't necessary to delete it. >>> delete('*****@*****.**') True # test the extreme cases >>> delete(None) False ''' result_ok = True try: if is_string(email_or_address): name, address = parse_address(email_or_address) contact = get(address) if contact is None: log_message('no {} <{}> contact to delete'.format( name, address)) else: contact.delete() log_message("deleted {}".format(contact.email)) elif email_or_address is None: result_ok = False elif isinstance(email_or_address, Contact): contact = email_or_address contact.delete() log_message("deleted {}".format(contact.email)) else: result_ok = False log_message( "unable to delete contact because wront type: {}".format( type(email_or_address))) except Exception: result_ok = False record_exception() log_message('EXCEPTION - see syr.exception.log for details') return result_ok
def delete(email_or_address): ''' Delete the contact with a matching email address. >>> # In honor of Amy Goodman, who hosts Democracy Now! # returns true because the address doesn't exist, even though it wasn't necessary to delete it. >>> delete('*****@*****.**') True # test the extreme cases >>> delete(None) False ''' result_ok = True try: if is_string(email_or_address): name, address = parse_address(email_or_address) contact = get(address) if contact is None: log_message('no {} <{}> contact to delete'.format(name, address)) else: contact.delete() log_message("deleted {}".format(contact.email)) elif email_or_address is None: result_ok = False elif isinstance(email_or_address, Contact): contact = email_or_address contact.delete() log_message("deleted {}".format(contact.email)) else: result_ok = False log_message("unable to delete contact because wront type: {}".format(type(email_or_address))) except Exception: result_ok = False record_exception() log_message('EXCEPTION - see syr.exception.log for details') return result_ok
def set_tag(self, new_tag): ''' Sets the tag to be added to the email_message text. >>> crypto_message = CryptoMessage() >>> crypto_message.set_tag(None) >>> tag = crypto_message.get_tag() >>> tag == '' True ''' if new_tag is None: if self.DEBUGGING: self.log_message("tried to set blank tag") elif new_tag == '': self.tags = [] if self.DEBUGGING: self.log_message("reset tags") else: if is_string(new_tag): new_tag = new_tag.strip('\n') self.tags = [new_tag] else: self.tags = new_tag if self.DEBUGGING: self.log_message("new tag:\n{}".format(new_tag))
def log(message, filename=None, mode=None): ''' Log message that syr.log can't. ''' if filename is None: filename = '/tmp/_log.{}.log'.format(whoami()) if mode is None: mode = '0666' # print(message) sh.touch(filename) try: sh.chmod(mode, filename) except sh.ErrorReturnCode_1: # hopefully the perms are already ok pass with open(filename, 'a') as logfile: try: logfile.write('{} {}\n'.format(timestamp(), message)) except UnicodeDecodeError: from syr.python import is_string logfile.write('unable to write message because it is a type: {}'.format(type(message))) if not is_string(message): logfile.write('{} {}\n'.format(timestamp(), message.decode(errors='replace')))
def _decrypt(self, from_user, data, charset, crypto, passcode): ''' Decrypt the data from a message (internal use only). ''' decrypted_data = signed_by = None if crypto is None or data is None: decrypted_data = None self.log_message("no crypto defined") else: if is_string(data): encrypted_data = data else: encrypted_data = data.encode(errors='replace') # ASCII armored plaintext looks just like armored ciphertext, # so check that we actually have encrypted data if (OpenPGPAnalyzer().is_encrypted(encrypted_data, passphrase=passcode, crypto=crypto)): if self.DEBUGGING: self.log_message( 'encrypted data before decryption:\n{}'.format( encrypted_data)) decrypted_data, signed_by, result_code = crypto.decrypt( encrypted_data, passcode) if (decrypted_data == None or (is_string(decrypted_data) and len(decrypted_data) <= 0)): if self.DEBUGGING: self.log_message( 'decrypted data:\n{}'.format(decrypted_data)) decrypted_data = None self.log_message("unable to decrypt data") if self.DEBUGGING: self.log_message( 'data bytearray after decryption:\n{}'.format( decrypted_data)) else: if result_code == 0: tag = tags.get_decrypt_signature_tag( self.crypto_message, from_user, signed_by, crypto.get_name()) if tag is not None: self.crypto_message.add_prefix_to_tag_once(tag) self.log_message('decrypt sig tag: {}'.format(tag)) elif result_code == 2: self.crypto_message.add_error_tag_once( i18n( "Can't verify signature because the message was encrypted using an unknown key. Ask the sender to send their key if they aren't using GoodCrypto." )) if is_string(decrypted_data): self.log_message('plaintext length: {}'.format( len(decrypted_data))) if self.DEBUGGING: self.log_message( 'plaintext:\n{}'.format(decrypted_data)) else: decrypted_data = None self.log_message("data appeared encrypted, but wasn't") if self.DEBUGGING: self.log_message('data:\n{}'.format(data)) return decrypted_data
def edit_file_in_place(filename, replacements, regexp=False, lines=False): ''' Replace text in file. 'replacements' is a dict of {old: new, ...}. Every occurence of each old string is replaced with the matching new string. If regexp=True, the old string is a regular expression. If lines=True, each line is matched separately. Perserves permissions. >>> # note double backslashes because this is a string within a docstring >>> text = ( ... 'browser.search.defaultenginename=Startpage HTTPS\\n' + ... 'browser.search.selectedEngine=Startpage HTTPS\\n' + ... 'browser.startup.homepage=https://tails.boum.org/news/\\n' + ... 'spellchecker.dictionary=en_US') >>> f = tempfile.NamedTemporaryFile(mode='w', delete=False) >>> f.write(text) >>> f.close() >>> HOMEPAGE = 'http://127.0.0.1/' >>> replacements = { ... 'browser.startup.homepage=.*': ... 'browser.startup.homepage={}'.format(HOMEPAGE), ... } >>> edit_file_in_place(f.name, replacements, regexp=True, lines=True) >>> with open(f.name) as textfile: ... newtext = textfile.read() >>> assert HOMEPAGE in newtext >>> os.remove(f.name) ''' # import delayed to avoid infinite recursion import syr.utils log.debug('edit file in place: {}, replacements {}'.format(filename, replacements)) # sometimes replace_strings() gets a type error for old, new in replacements.items(): assert is_string(old), 'replacement old "{}" should be string but is type {}'.format(old, type(old)) assert is_string(new), 'replacement new "{}" should be string but is type {}'.format(new, type(new)) # read text mode = os.stat(filename).st_mode with open(filename) as textfile: text = textfile.read() if lines: newtext = [] for line in text.split('\n'): # sometimes replace_strings() gets a type error assert is_string(line), 'line should be string but is {}'.format(type(line)) newline = syr.utils.replace_strings(line, replacements, regexp) newtext.append(newline) text = '\n'.join(newtext) else: text = syr.utils.replace_strings(text, replacements, regexp) # write text with open(filename, 'w') as textfile: textfile.write(text) os.chmod(filename, mode) assert mode == os.stat(filename).st_mode
def write(self, message): ''' Write message to log. write() writes to a log as you would to a file. This lets you redirect sys.stdout to the log and log print output generated by third party libraries, even if the library uses print statements without '>>' or print functions without 'stream='. ''' def notify_webmaster(message): ''' Send the message to the webmaster. We can't use jean.dmail.send because it imports this module. ''' if alert_from_address is not None and alert_to_address is not None: msg = ("From: %s\nTo: %s\nSubject: %s\n\n%s\n" % (alert_from_address, alert_to_address, subject, message)) server = smtplib.SMTP('localhost') server.sendmail(alert_from_address, alert_to_address, msg) # we don't use @synchronized here because of import errors global _raise_logging_errors try: from syr.python import is_string if is_string(message): self._write(message) elif isinstance(message, bytes) or isinstance(message, bytearray): self._write(message.decode(errors='replace')) else: self.write('unable to write message because it is a {}'.format(type(message))) if self.verbose: print(message) if _use_master_log and not self.is_master(): try: _master_logs[self.user]._write('- %s - %s' % (self.filename, message)) except UnicodeDecodeError: _master_logs[self.user]._write('- %s - !! Unable to log message -- UnicodeDecodeError !!' % (self.filename)) except UnicodeDecodeError: try: _debug(message.decode(errors='replace')) except UnicodeDecodeError: self.write('unable to write message because it is a type: {}'.format(type(message))) subject = '!! Unable to log message !!' self._write(subject) if _raise_logging_errors: _raise_logging_errors = False notify_webmaster(message) except: raise self._write(format_exc()) subject = '!! Unable to log message !!' self._write(subject) if _raise_logging_errors: _raise_logging_errors = False notify_webmaster(message)