def _print_files_by_acroread_exe(filenames, verbose=False): """Use Adobe Acrobat Reader. Windows only. - docs: http://www.robvanderwoude.com/printfiles.php#PrintPDF """ if os.name != 'nt': _log.debug('Acrobat Reader only used under Windows') return False for filename in filenames: cmd_line = [ 'AcroRd32.exe', # "AcroRd32.exe" must be in the PATH '/s', # no splash '/o', # no open-file dialog '/h', # minimized '/p', # go straight to printing dialog os.path.normpath(filename) ] success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = verbose) if not success: # retry with "acroread.exe" cmd_line[0] = r'acroread.exe' # "acroread.exe" must be in the PATH success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = verbose) if not success: return False return True
def _print_files_by_acroread_exe(filenames, verbose=False): """Use Adobe Acrobat Reader. Windows only. - docs: http://www.robvanderwoude.com/printfiles.php#PrintPDF """ if os.name != 'nt': _log.debug('Acrobat Reader only used under Windows') return False for filename in filenames: cmd_line = [ 'AcroRd32.exe', # "AcroRd32.exe" must be in the PATH '/s', # no splash '/o', # no open-file dialog '/h', # minimized '/p', # go straight to printing dialog os.path.normpath(filename) ] success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=verbose) if not success: # retry with "acroread.exe" cmd_line[0] = r'acroread.exe' # "acroread.exe" must be in the PATH success, returncode, stdout = gmShellAPI.run_process( cmd_line=cmd_line, verbose=verbose) if not success: return False return True
def _on_mail_items_button_pressed(self, event): event.Skip() _log.debug('gm-mail_doc(.bat) API: "MAIL-PROGRAM PRAXIS-VCF ZIP-ARCHIVE"') found, external_cmd = gmShellAPI.detect_external_binary('gm-mail_doc') if not found: gmDispatcher.send(signal = 'statustext', msg = _('Cannot send e-mail: <gm-mail_doc(.bat)> not found')) return False zip_file = self.__export_as_zip ( _('Mailing documents as zip archive'), encrypt = True ) if zip_file is None: gmDispatcher.send(signal = 'statustext', msg = _('Cannot send e-mail: no archive created.')) return False prax = gmPraxis.gmCurrentPraxisBranch() args = [external_cmd, prax.vcf, zip_file] success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug')) if not success: gmGuiHelpers.gm_show_error ( aMessage = _('Error mailing documents.'), aTitle = _('Mailing documents') ) return False self.__save_soap_note(soap = _('Mailed:\n - %s') % '\n - '.join([ i['description'] for i in items ])) return True
def encrypt_pdf(filename=None, passphrase=None, verbose=False): assert (filename is not None), '<filename> must not be None' assert (passphrase is not None), '<passphrase> must not be None' if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) _log.debug('attempting PDF encryption') for cmd in ['qpdf', 'qpdf.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no qpdf binary found') return None filename_encrypted = '%s.encrypted.pdf' % os.path.splitext(filename)[0] args = [ binary, '--verbose', '--encrypt', passphrase, '', '128', '--print=full', '--modify=none', '--extract=n', '--use-aes=y', '--', filename, filename_encrypted ] success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose) if success: return filename_encrypted return None
def _print_files_by_gsprint_exe(filenames=None, verbose=False): """Use gsprint.exe from Ghostscript tools. Windows only. - docs: http://pages.cs.wisc.edu/~ghost/gsview/gsprint.htm - download: http://www.cs.wisc.edu/~ghost/ """ if os.name != 'nt': _log.debug('<gsprint.exe> only available under Windows') return False conf_filename = gmTools.get_unique_filename( prefix='gm2gsprint-', suffix='.cfg').encode(sys.getfilesystemencoding()) for filename in filenames: conf_file = io.open(conf_filename, mode='wt', encoding='utf8') conf_file.write('-color\n') conf_file.write('-query\n') # printer setup dialog conf_file.write('-all\n') # all pages conf_file.write('-copies 1\n') conf_file.write('%s\n' % os.path.normpath(filename)) conf_file.close() cmd_line = ['gsprint.exe', '-config', conf_filename] # "gsprint.exe" must be in the PATH success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=verbose) if not success: return False return True
def _print_files_by_shellscript(filenames=None, jobtype=None, verbose=False): paths = gmTools.gmPaths() local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', 'gm-print_doc') candidates = ['gm-print_doc', local_script, 'gm-print_doc.bat'] found, binary = gmShellAPI.find_first_binary(binaries=candidates) if not found: binary = r'gm-print_doc.bat' cmd_line = [binary, jobtype] cmd_line.extend(filenames) success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=verbose) if not success: _log.debug( 'gm-print_doc(.bat) arguments: "DOCUMENT_TYPE LIST-OF-FILES-TO-PRINT"' ) _log.debug( 'gm-print_doc(.bat): call printing app, perhaps based on DOCUMENT_TYPE, and pass in LIST-OF-FILES-TO-PRINT' ) _log.debug('gm-print_doc(.bat): return 0 on success') _log.debug( 'gm-print_doc(.bat): DOCUMENT_TYPE - can be used to decide which way to process a particular print job (Example: medication_list)' ) _log.debug( 'gm-print_doc(.bat): LIST-OF-FILES-TO-PRINT - can be of any mimetype so the script needs to be able to process them, typically PDF though' ) return False return True
def decrypt_pdf(filename: str = None, passphrase: str = None, verbose: bool = False) -> str: assert (filename is not None), '<filename> must not be None' assert (passphrase is not None), '<passphrase> must not be None' gmLog2.add_word2hide(passphrase) _log.debug('attempting PDF decryption') for cmd in ['qpdf', 'qpdf.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no qpdf binary found') return None filename_decrypted = '%s.decrypted.pdf' % os.path.splitext(filename)[0] args = [ binary, '--verbose', '--password-mode=unicode', '--decrypt', '--password=%s' % passphrase, '--', filename, filename_decrypted ] success, exit_code, stdout = gmShellAPI.run_process( cmd_line=args, encoding='utf8', verbose=verbose, acceptable_return_codes=[0, 3]) if not success: return None return filename_decrypted
def _print_files_by_gsprint_exe(filenames=None, verbose=False): """Use gsprint.exe from Ghostscript tools. Windows only. - docs: http://pages.cs.wisc.edu/~ghost/gsview/gsprint.htm - download: http://www.cs.wisc.edu/~ghost/ """ if os.name != 'nt': _log.debug('<gsprint.exe> only available under Windows') return False conf_filename = gmTools.get_unique_filename ( prefix = 'gm2gsprint-', suffix = '.cfg' ).encode(sys.getfilesystemencoding()) for filename in filenames: conf_file = io.open(conf_filename, mode = 'wt', encoding = 'utf8') conf_file.write('-color\n') conf_file.write('-query\n') # printer setup dialog conf_file.write('-all\n') # all pages conf_file.write('-copies 1\n') conf_file.write('%s\n' % os.path.normpath(filename)) conf_file.close() cmd_line = ['gsprint.exe', '-config', conf_filename] # "gsprint.exe" must be in the PATH success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = verbose) if not success: return False return True
def __convert_odt_to_pdf(filename=None, verbose=False): cmd_line = [ 'lowriter', '--convert-to', 'pdf', '--outdir', os.path.split(filename)[0], filename ] success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=True) if not success: return None return gmTools.fname_stem_with_path(filename) + '.pdf'
def _print_files_by_gtklp(filenames=None, verbose=False): # if os.name != 'posix': if sys.platform != 'linux2': _log.debug('<gtklp> only available under Linux') return False cmd_line = ['gtklp', '-i', '-# 1'] cmd_line.extend(filenames) success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = verbose) if not success: return False return True
def aes_encrypt_file(filename=None, passphrase=None, comment=None, verbose=False, remove_unencrypted=False): assert (filename is not None), '<filename> must not be None' assert (passphrase is not None), '<passphrase> must not be None' if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) #add 7z/winzip url to comment.txt _log.debug('attempting 7z AES encryption') for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no 7z binary found, trying gpg') return None if comment is not None: archive_path, archive_name = os.path.split(os.path.abspath(filename)) comment_filename = gmTools.get_unique_filename( prefix='%s.7z.comment-' % archive_name, tmp_dir=archive_path, suffix='.txt') with open(comment_filename, mode='wt', encoding='utf8', errors='replace') as comment_file: comment_file.write(comment) else: comment_filename = '' filename_encrypted = '%s.7z' % filename args = [ binary, 'a', '-bb3', '-mx0', "-p%s" % passphrase, filename_encrypted, filename, comment_filename ] encrypted, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, encoding='utf8', verbose=verbose) gmTools.remove_file(comment_filename) if not encrypted: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None
def _on_fax_items_button_pressed(self, event): event.Skip() _log.debug('gm-fax_doc(.bat) API: "FAX-PROGRAM FAXNUMBER-OR-<EMPTY> LIST-OF-FILES-TO-FAX" (any file type !)') found, external_cmd = gmShellAPI.detect_external_binary('gm-fax_doc') if not found: gmDispatcher.send(signal = 'statustext', msg = _('Cannot send fax: <gm-fax_doc(.bat)> not found')) return False items = self._LCTRL_items.get_selected_item_data(only_one = False) if len(items) == 0: items = self._LCTRL_items.get_item_data() if len(items) == 0: gmDispatcher.send(signal = 'statustext', msg = _('Cannot send fax: no items')) return None if len(items) > 1: # ask, might be a lot process_all = gmGuiHelpers.gm_show_question ( title = _('Faxing documents'), question = _('You have not selected any entries.\n\nSend fax with all %s entries ?') % len(items), cancel_button = False ) if not process_all: return None return False files2fax = [] for item in items: files2fax.append(item.save_to_file()) fax_number = wx.GetTextFromUser ( _('Please enter the fax number here !\n\n' 'It can be left empty if the external\n' 'fax software knows how to get the number.'), caption = _('Faxing documents'), parent = self, centre = True ) if fax_number == '': fax_number = 'EMPTY' args = [external_cmd, fax_number, ' '.join(files2fax)] success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug')) if not success: gmGuiHelpers.gm_show_error ( aMessage = _('Error faxing documents to\n\n %s') % fax_number, aTitle = _('Faxing documents') ) return False self.__save_soap_note(soap = _('Faxed to [%s]:\n - %s') % (fax_number, '\n - '.join([ i['description'] for i in items ]))) return True
def encrypt_pdf(filename: str = None, passphrase: str = None, verbose: bool = False, remove_unencrypted: bool = False) -> str: """Encrypt a PDF file per spec (AES, that is). Args: filename: PDF file to encrypt passphrase: minimum of 5 characters remove_unencrypted: remove unencrypted source file if encryption succeeds Returns: Name of encrypted PDF or None. """ assert (filename is not None), '<filename> must not be None' assert (passphrase is not None), '<passphrase> must not be None' if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) _log.debug('attempting PDF encryption') for cmd in ['qpdf', 'qpdf.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no qpdf binary found') return None filename_encrypted = '%s.encrypted.pdf' % os.path.splitext(filename)[0] args = [ binary, '--verbose', '--password-mode=unicode', '--encrypt', passphrase, '', '128', '--print=full', '--modify=none', '--extract=n', '--use-aes=y', '--', filename, filename_encrypted ] success, exit_code, stdout = gmShellAPI.run_process( cmd_line=args, encoding='utf8', verbose=verbose, acceptable_return_codes=[0, 3]) if not success: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None
def convert_file(filename=None, target_mime=None, target_filename=None, target_extension=None, verbose=False): """Convert file from one format into another. target_mime: a mime type """ assert (target_mime is not None), '<target_mime> must not be None' assert (filename is not None), '<filename> must not be None' assert (filename != target_filename), '<target_filename> must be different from <filename>' source_mime = guess_mimetype(filename = filename) if source_mime.lower() == target_mime.lower(): _log.debug('source file [%s] already target mime type [%s]', filename, target_mime) if target_filename is None: return filename shutil.copyfile(filename, target_filename) return target_filename converted_ext = guess_ext_by_mimetype(target_mime) if converted_ext is None: if target_filename is not None: tmp, converted_ext = os.path.splitext(target_filename) if converted_ext is None: converted_ext = target_extension # can still stay None converted_fname = gmTools.get_unique_filename(suffix = converted_ext) _log.debug('attempting conversion: [%s] -> [<%s>:%s]', filename, target_mime, gmTools.coalesce(target_filename, converted_fname)) script_name = 'gm-convert_file' paths = gmTools.gmPaths() local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', script_name) candidates = [ script_name, local_script ] #, script_name + u'.bat' found, binary = gmShellAPI.find_first_binary(binaries = candidates) if not found: # try anyway binary = script_name# + r'.bat' _log.debug('<%s> API: SOURCEFILE TARGET_MIMETYPE TARGET_EXTENSION TARGET_FILENAME' % binary) cmd_line = [ binary, filename, target_mime, converted_ext.lstrip('.'), converted_fname ] success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = True) if not success: _log.error('conversion failed') return None if target_filename is None: return converted_fname shutil.copyfile(converted_fname, target_filename) return target_filename
def _print_files_by_gtklp(filenames=None, verbose=False): # if os.name != 'posix': if sys.platform != 'linux': _log.debug('<gtklp> only available under Linux') return False cmd_line = ['gtklp', '-i', '-# 1'] cmd_line.extend(filenames) success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=verbose) if not success: return False return True
def _print_files_by_shellscript(filenames=None, jobtype=None, verbose=False): paths = gmTools.gmPaths() local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', 'gm-print_doc') #candidates = [u'gm-print_doc', u'gm-print_doc.bat', local_script, u'gm-print_doc.bat'] candidates = ['gm-print_doc', local_script, 'gm-print_doc.bat'] found, binary = gmShellAPI.find_first_binary(binaries = candidates) if not found: binary = r'gm-print_doc.bat' cmd_line = [binary, jobtype] cmd_line.extend(filenames) success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = verbose) if not success: return False return True
def _print_files_by_mac_preview(filenames=None, verbose=False): # if os.name != 'mac': # does not work if sys.platform != 'darwin': _log.debug('MacOSX <open> only available under MacOSX/Darwin') return False for filename in filenames: cmd_line = [ 'open', # "open" must be in the PATH '-a Preview', # action = Preview filename ] success, returncode, stdout = gmShellAPI.run_process(cmd_line = cmd_line, verbose = verbose) if not success: return False return True
def _print_files_by_shellscript(filenames=None, jobtype=None, verbose=False): paths = gmTools.gmPaths() local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', 'gm-print_doc') candidates = ['gm-print_doc', local_script, 'gm-print_doc.bat'] found, binary = gmShellAPI.find_first_binary(binaries=candidates) if not found: binary = r'gm-print_doc.bat' cmd_line = [binary, jobtype] cmd_line.extend(filenames) success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=verbose) if not success: return False return True
def _print_files_by_mac_preview(filenames=None, verbose=False): # if os.name != 'mac': # does not work if sys.platform != 'darwin': _log.debug('MacOSX <open> only available under MacOSX/Darwin') return False for filename in filenames: cmd_line = [ 'open', # "open" must be in the PATH '-a Preview', # action = Preview filename ] success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=verbose) if not success: return False return True
def gpg_decrypt_file(filename=None, verbose=False, target_ext=None): """The system is expected to be set up for safely getting the passphrase from the user, typically via gpg-agent. """ assert (filename is not None), '<filename> must not be None' _log.debug('attempting GPG decryption') for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no gpg binary found') return None basename = os.path.splitext(filename)[0] filename_decrypted = gmTools.get_unique_filename(prefix = '%s-decrypted-' % basename, suffix = target_ext) args = [ binary, '--utf8-strings', '--display-charset', 'utf-8', '--batch', '--no-greeting', '--enable-progress-filter', '--decrypt', '--output', filename_decrypted ##'--use-embedded-filename' # not all encrypted files carry a filename ] if verbose: args.extend ([ '--verbose', '--verbose', '--debug-level', '8', '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog' ##'--debug-all', # will log passphrase ##'--debug, 'ipc', # will log passphrase ##'--debug-level', 'guru', # will log passphrase ##'--debug-level', '9', # will log passphrase ]) args.append(filename) success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = verbose, encoding = 'utf-8') if success: return filename_decrypted return None
def mirror_url(url: str, base_dir: str = None, verbose: bool = False) -> str: """Mirror the web*page* at _url_, non-recursively. Note: Not for mirroring a *site* (recursively). Args: url: the URL to mirror base_dir: where to store the page and its prerequisites, sandbox dir under tmp_dir if None """ assert (url is not None), '<url> must not be None' _log.debug('mirroring: %s', url) if base_dir is None: prefix = url.split('://')[-1] prefix = prefix.strip(':').strip('/').replace('/', '#') prefix = gmTools.fname_sanitize(prefix) base_dir = gmTools.mk_sandbox_dir(prefix=prefix + '-') _log.debug('base dir: %s', base_dir) wget_cmd = [ 'wget', '--directory-prefix=%s' % base_dir, #'--adjust-extension', '--no-remove-listing', '--timestamping', '--page-requisites', '--continue', '--convert-links', '--user-agent=""', '--execute', 'robots=off', '--wait=1' ] if verbose: wget_cmd.append('--debug') wget_cmd.append(url) #wget --output-file=logfile #'<a href="%s">%s</a>' % (url, url), success, ret_code, STDOUT = gmShellAPI.run_process(cmd_line=wget_cmd, timeout=15, verbose=verbose) if success: return base_dir return None
def aes_encrypt_file(filename=None, passphrase=None, comment=None, verbose=False, remove_unencrypted=False): assert (filename is not None), '<filename> must not be None' assert (passphrase is not None), '<passphrase> must not be None' if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) #add 7z/winzip url to comment.txt _log.debug('attempting 7z AES encryption') for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no 7z binary found, trying gpg') return None if comment is not None: archive_path, archive_name = os.path.split(os.path.abspath(filename)) comment_filename = gmTools.get_unique_filename ( prefix = '%s.7z.comment-' % archive_name, tmp_dir = archive_path, suffix = '.txt' ) with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file: comment_file.write(comment) else: comment_filename = '' filename_encrypted = '%s.7z' % filename args = [binary, 'a', '-bb3', '-mx0', "-p%s" % passphrase, filename_encrypted, filename, comment_filename] encrypted, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose) gmTools.remove_file(comment_filename) if not encrypted: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None
def join_files_as_pdf(files: [] = None, pdf_name: str = None) -> str: """Convert files to PDF and joins them into one final PDF. Returns: Name of final PDF or None """ assert (files is not None), '<files> must not be None' if len(files) == 0: return None sandbox = gmTools.mk_sandbox_dir() pdf_pages = [] page_idx = 1 for fname in files: pdf = convert_file(filename=fname, target_mime='application/pdf', target_filename=gmTools.get_unique_filename( prefix='%s-' % page_idx, suffix='.pdf', tmp_dir=sandbox), target_extension='.pdf', verbose=True) if pdf is None: return None pdf_pages.append(pdf) page_idx += 1 if pdf_name is None: pdf_name = gmTools.get_unique_filename(suffix='.pdf') cmd_line = ['pdfunite'] cmd_line.extend(pdf_pages) cmd_line.append(pdf_name) success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=True) if not success: _log.debug('cannot join files into one PDF') return None return pdf_name
def gpg_decrypt_file(filename=None, passphrase=None, verbose=False, target_ext=None): assert (filename is not None), '<filename> must not be None' _log.debug('attempting GPG decryption') for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no gpg binary found') return None basename = os.path.splitext(filename)[0] filename_decrypted = gmTools.get_unique_filename(prefix = '%s-decrypted-' % basename, suffix = target_ext) args = [ binary, '--utf8-strings', '--display-charset', 'utf-8', '--batch', '--no-greeting', '--enable-progress-filter', '--decrypt', '--output', filename_decrypted ##'--use-embedded-filename' # not all encrypted files carry a filename ] if verbose: args.extend ([ '--verbose', '--verbose', '--debug-level', '8', '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog' ##'--debug-all', # will log passphrase ##'--debug, 'ipc', # will log passphrase ##'--debug-level', 'guru', # will log passphrase ##'--debug-level', '9', # will log passphrase ]) args.append(filename) success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = verbose, encoding = 'utf-8') if success: return filename_decrypted return None
def is_encrypted_pdf(filename: str = None, verbose: bool = False) -> bool: """Check encryption status of PDF. Requires qpdf to be installed. Returns: True/False/None: None -- unknown or not PDF """ assert (filename is not None), '<filename> must not be None' mimetype = gmMimeLib.guess_mimetype(filename=filename) if mimetype != 'application/pdf': _log.info('not a PDF') return None _log.debug('checking PDF encryption') for cmd in ['qpdf', 'qpdf.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no qpdf binary found') return None args = [binary, '--verbose', '--is-encrypted', '--', filename] success, exit_code, stdout = gmShellAPI.run_process( cmd_line=args, encoding='utf8', verbose=verbose, acceptable_return_codes=[0, 2]) if not success: return None if exit_code == 0: return True if exit_code == 2: return False return None
def __burn_dir_to_disk(self, base_dir): _log.debug('gm-burn_doc(.bat) API: "DIRECTORY-TO-BURN-FROM"') found, burn_cmd = gmShellAPI.detect_external_binary('gm-burn_doc') if not found: gmDispatcher.send(signal = 'statustext', msg = _('Cannot burn to disk: Helper not found.')) # should not happen return False args = [burn_cmd, base_dir] wx.BeginBusyCursor() try: success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug')) finally: wx.EndBusyCursor() if success: return True gmGuiHelpers.gm_show_error ( aMessage = _('Error burning documents to CD/DVD.'), aTitle = _('Burning documents') ) return False
def create_encrypted_zip_archive_from_dir(source_dir: str, comment: str = None, overwrite: bool = True, passphrase: str = None, verbose: bool = False) -> bool: """Create encrypted archive of a directory. The encrypted archive file will always be named "datawrapper.zip" for confidentiality reasons. If callers want another name they will have to shutil.move() the zip file themselves. This archive will be compressed and AES256 encrypted with the given passphrase. Therefore, the result will not decrypt with earlier versions of unzip software. On Windows, 7z oder WinZip are needed. The zip format does not support header encryption thereby allowing attackers to gain knowledge of patient details by observing the names of files and directories inside the encrypted archive. To reduce that attack surface, GNUmed will create _another_ zip archive inside "datawrapper.zip", which eventually wraps up the patient data as "data.zip". That archive is not compressed and not encrypted, and can thus be unpacked with any old unzipper. Note that GNUmed does NOT remember the passphrase for you. You will have to take care of that yourself, and possibly also safely hand over the passphrase to any receivers of the zip archive. Args: source_dir: the directory to archive and encrypt comment: included as a file containing the comment overwrite: remove preexisting archive before creation, avoiding *updating* of same, and thereby including unintended data passphrase: minimum length of 5 if given Returns: True / False / None (other error) """ assert (source_dir is not None), '<source_dir> must not be <None>' if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) source_dir = os.path.abspath(source_dir) if not os.path.isdir(source_dir): _log.error('<source_dir> does not exist or is not a directory: %s', source_dir) return False for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no 7z binary found') return None sandbox_dir = gmTools.mk_sandbox_dir() archive_path_inner = os.path.join(sandbox_dir, 'data') if not gmTools.mkdir(archive_path_inner): _log.error('cannot create scratch space for inner achive: %s', archive_path_inner) archive_fname_inner = 'data.zip' archive_name_inner = os.path.join(archive_path_inner, archive_fname_inner) archive_path_outer = gmTools.gmPaths().tmp_dir archive_fname_outer = 'datawrapper.zip' archive_name_outer = os.path.join(archive_path_outer, archive_fname_outer) # remove existing archives so they don't get *updated* rather than newly created if overwrite: if not gmTools.remove_file(archive_name_inner, force=True): _log.error('cannot remove existing archive [%s]', archive_name_inner) return False if not gmTools.remove_file(archive_name_outer, force=True): _log.error('cannot remove existing archive [%s]', archive_name_outer) return False # 7z does not support ZIP comments so create a text file holding the comment if comment is not None: tmp, fname = os.path.split(source_dir.rstrip(os.sep)) comment_filename = os.path.join(sandbox_dir, '000-%s-comment.txt' % fname) with open(comment_filename, mode='wt', encoding='utf8', errors='replace') as comment_file: comment_file.write(comment) # create inner (data) archive: uncompressed, unencrypted, similar to a tar archive args = [ binary, 'a', # create archive '-sas', # be smart about archive name extension '-bd', # no progress indicator '-mx0', # no compression (only store files) '-mcu=on', # UTF8 filenames '-l', # store content of links, not links '-scsUTF-8', # console charset '-tzip' # force ZIP format ] if verbose: args.append('-bb3') args.append('-bt') else: args.append('-bb1') args.append(archive_name_inner) args.append(source_dir) if comment is not None: args.append(comment_filename) success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, encoding='utf8', verbose=verbose) if not success: _log.error('cannot create inner archive') return None # create "decompress instructions" file instructions_filename = os.path.join( archive_path_inner, '000-on_Windows-open_with-WinZip_or_7z_tools') open(instructions_filename, mode='wt').close() # create outer (wrapper) archive: compressed, encrypted args = [ binary, 'a', # create archive '-sas', # be smart about archive name extension '-bd', # no progress indicator '-mx9', # best available zip compression ratio '-mcu=on', # UTF8 filenames '-l', # store content of links, not links '-scsUTF-8', # console charset '-tzip', # force ZIP format '-mem=AES256', # force useful encryption '-p%s' % passphrase # set passphrase ] if verbose: args.append('-bb3') args.append('-bt') else: args.append('-bb1') args.append(archive_name_outer) args.append(archive_path_inner) success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, encoding='utf8', verbose=verbose) if success: return archive_name_outer _log.error('cannot create outer archive') return None
def create_zip_archive_from_dir(source_dir, archive_name=None, comment=None, overwrite=True, verbose=False): source_dir = os.path.abspath(source_dir) if not os.path.isdir(source_dir): _log.error('<source_dir> does not exist or is not a directory: %s', source_dir) return False for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no 7z binary found') return None if archive_name is None: # do not assume we can write to "sourcedir/../" archive_path = gmTools.gmPaths().tmp_dir # but do take archive name from source_dir tmp, archive_fname = os.path.split(source_dir.rstrip(os.sep) + '.zip') archive_name = os.path.join(archive_path, archive_fname) # remove any existing archives so they don't get *updated* # rather than newly created if overwrite: if not gmTools.remove_file(archive_name, force = True): _log.error('cannot remove existing archive [%s]', archive_name) return False # 7z does not support ZIP comments so create # a text file holding the comment ... if comment is not None: comment_filename = os.path.abspath(archive_name) + '.comment.txt' if gmTools.remove_file(comment_filename, force = True): with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file: comment_file.write(comment) else: _log.error('cannot remove existing archive comment file [%s]', comment_filename) comment = None # compress args = [ binary, 'a', # create archive '-sas', # be smart about archive name extension '-bd', # no progress indicator '-mx9', # best available zip compression ratio '-mcu=on', # UTF8 filenames '-l', # store content of links, not links '-scsUTF-8', # console charset '-tzip' # force ZIP format ] if verbose: args.append('-bb3') args.append('-bt') else: args.append('-bb1') args.append(archive_name) args.append(source_dir) if comment is not None: args.append(comment_filename) success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose) if comment is not None: gmTools.remove_file(comment_filename) if success: return archive_name return None
def create_zip_archive_from_dir(source_dir: str, archive_name: str = None, comment: str = None, overwrite: bool = True, verbose: bool = False) -> str: """Create archive of a directory. Args: source_dir: the directory to archive and encrypt archive_name: name of resulting zip archive comment: included as a file containing the comment overwrite: remove preexisting archive before creation, avoiding *updating* of same, and thereby including unintended data Returns: archive name / False / None (other error) """ assert (source_dir is not None), '<source_dir> must not be <None>' source_dir = os.path.abspath(source_dir) if not os.path.isdir(source_dir): _log.error('<source_dir> does not exist or is not a directory: %s', source_dir) return False for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no 7z binary found') return None if archive_name is None: # do not assume we can write to "sourcedir/../" archive_path = gmTools.gmPaths().tmp_dir # but do take archive name from source_dir tmp, archive_fname = os.path.split(source_dir.rstrip(os.sep) + '.zip') archive_name = os.path.join(archive_path, archive_fname) # remove any existing archives so they don't get *updated* # rather than newly created if overwrite: if not gmTools.remove_file(archive_name, force=True): _log.error('cannot remove existing archive [%s]', archive_name) return False # 7z does not support ZIP comments so create # a text file holding the comment ... if comment is not None: comment_filename = os.path.abspath(archive_name) + '.comment.txt' if gmTools.remove_file(comment_filename, force=True): with open(comment_filename, mode='wt', encoding='utf8', errors='replace') as comment_file: comment_file.write(comment) else: _log.error('cannot remove existing archive comment file [%s]', comment_filename) comment = None # compress args = [ binary, 'a', # create archive '-sas', # be smart about archive name extension '-bd', # no progress indicator '-mx9', # best available zip compression ratio '-mcu=on', # UTF8 filenames '-l', # store content of links, not links '-scsUTF-8', # console charset '-tzip' # force ZIP format ] if verbose: args.append('-bb3') args.append('-bt') else: args.append('-bb1') args.append(archive_name) args.append(source_dir) if comment is not None: args.append(comment_filename) success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, encoding='utf8', verbose=verbose) if comment is not None: gmTools.remove_file(comment_filename) if success: return archive_name return None
def print_generic_document(parent=None, jobtype:str=None, episode=None): """Call LibreOffice Writer with a generated (fake) ODT file. Once Writer is closed, the ODT is imported if different from its initial content. Note: The file passed to LO _must_ reside in a directory the parents of which are all R-X by the user or else LO will throw up its arms in despair and fall back to the user's home dir upon saving. So, /tmp/user/$UID/some-file.odt will *not* work (because .../user/... is "drwx--x--x root.root") while /home/$USER/some/dir/some-file.odt does. """ sandbox = os.path.join(gmTools.gmPaths().user_tmp_dir, 'libreoffice') gmTools.mkdir(sandbox) fpath = gmTools.get_unique_filename(suffix = '.txt', tmp_dir = sandbox) doc_file = open(fpath, mode = 'wt') doc_file.write(__ODT_FILE_PREAMBLE) doc_file.write(_('Today: %s') % gmDateTime.pydt_now_here().strftime('%c')) doc_file.write('\n\n') prax = gmPraxis.gmCurrentPraxisBranch() doc_file.write('Praxis:\n') doc_file.write(prax.format()) doc_file.write('\n') doc_file.write('Praxis branch:\n') doc_file.write('\n'.join(prax.org_unit.format ( with_address = True, with_org = True, with_comms = True ))) doc_file.write('\n\n') pat = gmPerson.gmCurrentPatient(gmPerson.cPatient(12)) if pat.connected: doc_file.write('Patient:\n') doc_file.write(pat.get_description_gender()) doc_file.write('\n\n') for adr in pat.get_addresses(): doc_file.write(adr.format(single_line = False, verbose = True, show_type = True)) doc_file.write('\n\n') for chan in pat.get_comm_channels(): doc_file.werite(chan.format()) doc_file.write('\n\n') doc_file.write('Provider:\n') doc_file.write('\n'.join(gmStaff.gmCurrentProvider().get_staff().format())) doc_file.write('\n\n-----------------------------------------------------------------------------\n\n') doc_file.close() # convert txt -> odt success, ret_code, stdout = gmShellAPI.run_process ( cmd_line = [ 'lowriter', '--convert-to', 'odt', '--outdir', os.path.split(fpath)[0], fpath ], verbose = True ) if success: fpath = gmTools.fname_stem_with_path(fpath) + '.odt' else: _log.warning('cannot convert .txt to .odt') md5_before = gmTools.file2md5(fpath) gmShellAPI.run_process(cmd_line = ['lowriter', fpath], verbose = True) md5_after = gmTools.file2md5(fpath) if md5_before == md5_after: gmDispatcher.send(signal = 'statustext', msg = _('Document not modified. Discarding.'), beep = False) return if not pat.connected: shutil.move(fpath, gmTools.gmPaths().user_work_dir) gmDispatcher.send(signal = 'statustext', msg = _('No patient. Moved file into %s') % gmTools.gmPaths().user_work_dir, beep = False) return pat.export_area.add_file ( filename = fpath, hint = _('Generic letter, written at %s') % gmDateTime.pydt_now_here().strftime('%Y %b %d %H:%M') ) gmDispatcher.send(signal = 'display_widget', name = 'gmExportAreaPlugin')
def convert_file(filename=None, target_mime=None, target_filename=None, target_extension=None, verbose=False): """Convert file from one format into another. target_mime: a mime type """ assert (target_mime is not None), '<target_mime> must not be None' assert (filename is not None), '<filename> must not be None' assert (filename != target_filename ), '<target_filename> must be different from <filename>' source_mime = guess_mimetype(filename=filename) if source_mime.casefold() == target_mime.casefold(): _log.debug('source file [%s] already target mime type [%s]', filename, target_mime) if target_filename is None: return filename shutil.copyfile(filename, target_filename) return target_filename converted_ext = guess_ext_by_mimetype(target_mime) if converted_ext is None: if target_filename is not None: tmp, converted_ext = os.path.splitext(target_filename) if converted_ext is None: converted_ext = target_extension # can still stay None converted_ext = gmTools.coalesce(converted_ext, '').strip().lstrip('.') converted_fname = gmTools.get_unique_filename(suffix=converted_ext) _log.debug('attempting conversion: [%s] -> [<%s>:%s]', filename, target_mime, gmTools.coalesce(target_filename, converted_fname)) # try user-local conversion script script_name = 'gm-convert_file' binary = os.path.join(gmTools.gmPaths().home_dir, 'bin', script_name) _log.debug('trying user-local script: %s', binary) _log.debug( '<%s> API: SOURCEFILE TARGET_MIMETYPE TARGET_EXTENSION TARGET_FILENAME', script_name) found, binary = gmShellAPI.detect_external_binary(binary=binary) if found: cmd_line = [ binary, filename, target_mime, converted_ext, converted_fname ] success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=True) if success: if target_filename is None: return converted_fname shutil.copyfile(converted_fname, target_filename) return target_filename # try built-in conversions _log.debug('trying built-in conversion function') try: conversion_func = __CONVERSION_DELEGATES[source_mime][target_mime] except KeyError: conversion_func = None if conversion_func is not None: converted_fname = conversion_func(filename=filename, verbose=verbose) if converted_fname is not None: if target_filename is None: return converted_fname shutil.copyfile(converted_fname, target_filename) return target_filename # try system-wide conversion script paths = gmTools.gmPaths() local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', script_name) candidates = [script_name, local_script] #, script_name + u'.bat' _log.debug('trying system-wide scripts: %s', candidates) found, binary = gmShellAPI.find_first_binary(binaries=candidates) if not found: # try anyway _log.debug('trying anyway as last-ditch resort') binary = script_name # + r'.bat' cmd_line = [binary, filename, target_mime, converted_ext, converted_fname] success, returncode, stdout = gmShellAPI.run_process(cmd_line=cmd_line, verbose=True) if success: if target_filename is None: return converted_fname shutil.copyfile(converted_fname, target_filename) return target_filename # seems to have failed but check for target file anyway _log.error( 'conversion script returned error exit code, checking target file anyway' ) if not os.path.exists(converted_fname): return None _log.info('conversion target file found') stats = os.stat(converted_fname) if stats.st_size == 0: return None _log.info('conversion target file size > 0') achieved_mime = guess_mimetype(filename=converted_fname) if achieved_mime != target_mime: _log.error('target: [%s], achieved: [%s]', target_mime, achieved_mime) return None # we may actually have something despite a non-0 exit code _log.info( 'conversion target file mime type [%s], as expected, might be usable', achieved_mime) if target_filename is None: return converted_fname shutil.copyfile(converted_fname, target_filename) return target_filename
def convert_latex_to_pdf(filename: str = None, verbose: bool = False, is_sandboxed: bool = False) -> str: """Compile LaTeX code to PDF using pdflatex. Args: is_sandboxed: whether or not to create a sandbox for compiling Returns: Name of resulting PDF, or None on failure. """ global __LaTeX_version_checked global __pdflatex_executable if not __LaTeX_version_checked: __LaTeX_version_checked = True found, __pdflatex_executable = gmShellAPI.detect_external_binary( binary='pdflatex') if not found: _log.error('pdflatex not found') return None cmd_line = [__pdflatex_executable, '-version'] success, ret_code, stdout = gmShellAPI.run_process(cmd_line=cmd_line, encoding='utf8', verbose=True) if not success: _log.error('[%s] failed, LaTeX not usable', cmd_line) return None if is_sandboxed: sandbox_dir = os.path.split(filename)[0] else: sandbox_dir = gmTools.mk_sandbox_dir( prefix=gmTools.fname_stem(filename) + '_') shutil.copy(filename, sandbox_dir) filename = os.path.join(sandbox_dir, os.path.split(filename)[1]) _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) cmd_final = [ __pdflatex_executable, '-recorder', '-interaction=nonstopmode', "-output-directory=%s" % sandbox_dir ] cmd_draft = cmd_final + ['-draftmode'] # LaTeX can need up to three runs to get cross references et al right for cmd2run in [cmd_draft, cmd_draft, cmd_final]: success, ret_code, stdout = gmShellAPI.run_process( cmd_line=cmd2run + [filename], acceptable_return_codes=[0], encoding='utf8', verbose=True #_cfg.get(option = 'debug') ) if not success: _log.error( 'problem running pdflatex, cannot generate form output, trying diagnostics' ) found, binary = gmShellAPI.find_first_binary( binaries=['lacheck', 'miktex-lacheck.exe']) if not found: _log.debug('lacheck not found') else: cmd_line = [binary, filename] success, ret_code, stdout = gmShellAPI.run_process( cmd_line=cmd_line, encoding='utf8', verbose=True) found, binary = gmShellAPI.find_first_binary( binaries=['chktex', 'ChkTeX.exe']) if not found: _log.debug('chcktex not found') else: cmd_line = [binary, '--verbosity=2', '--headererr', filename] success, ret_code, stdout = gmShellAPI.run_process( cmd_line=cmd_line, encoding='utf8', verbose=True) return None return '%s.pdf' % os.path.splitext(filename)[0]
def encrypt_file_symmetric_gpg(filename=None, comment=None, verbose=False, passphrase=None, remove_unencrypted=False) -> str: """Encrypt file symmetrically with GPG. Args: filename: the file to encrypt passphrase: minimum of 5 characters if given comment: a comment on the file to be put into a sidecar file, will also be encrypted remove_unencrypted: remove unencrypted source file if encryption succeeded Returns: Name of encrypted file or None. """ #add short decr instr to comment assert (filename is not None), '<filename> must not be None' if passphrase is not None: if len(passphrase) < 5: _log.error( '<passphrase> must be at least 5 characters/signs/digits') return None _log.debug('attempting symmetric GPG encryption') for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no gpg binary found') return None filename_encrypted = filename + '.asc' args = [ binary, '--utf8-strings', '--display-charset', 'utf-8', '--batch', '--no-greeting', '--enable-progress-filter', '--symmetric', '--cipher-algo', 'AES256', '--armor', '--output', filename_encrypted ] if comment is not None: args.extend(['--comment', comment]) if verbose: args.extend([ '--verbose', '--verbose', '--debug-level', '8', '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog', ##'--debug-all', # will log passphrase ##'--debug, 'ipc', # will log passphrase ##'--debug-level', 'guru', # will log passphrase ##'--debug-level', '9', # will log passphrase ]) pwd_fname = None if passphrase is not None: pwd_file = tempfile.NamedTemporaryFile(mode='w+t', encoding='utf8', delete=False) pwd_fname = pwd_file.name args.extend( ['--pinentry-mode', 'loopback', '--passphrase-file', pwd_fname]) pwd_file.write(passphrase) pwd_file.close() args.append(filename) try: success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, verbose=verbose, encoding='utf-8') finally: if pwd_fname is not None: os.remove(pwd_fname) if not success: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None
def encrypt_file_symmetric_7z(filename: str = None, passphrase: str = None, comment: str = None, verbose: bool = False, remove_unencrypted: bool = False) -> str: """Encrypt a file symmetrically with 7zip. Args: filename: the file to encrypt passphrase: minimum of 5 characters comment: a comment on the file to be put into a sidecar file, will also be encrypted remove_unencrypted: remove unencrypted source file if encryption succeeded Returns: Name of encrypted file or None. """ assert (filename is not None), '<filename> must not be None' assert (passphrase is not None), '<passphrase> must not be None' if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) #add 7z/winzip url to comment.txt _log.debug('attempting 7z AES encryption') for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no 7z binary found, trying gpg') return None if comment is not None: archive_path, archive_name = os.path.split(os.path.abspath(filename)) comment_filename = gmTools.get_unique_filename( prefix='%s.7z.comment-' % archive_name, tmp_dir=archive_path, suffix='.txt') with open(comment_filename, mode='wt', encoding='utf8', errors='replace') as comment_file: comment_file.write(comment) else: comment_filename = '' filename_encrypted = '%s.7z' % filename args = [ binary, 'a', '-bb3', '-mx0', "-p%s" % passphrase, filename_encrypted, filename, comment_filename ] encrypted, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, encoding='utf8', verbose=verbose) gmTools.remove_file(comment_filename) if not encrypted: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None
def create_encrypted_zip_archive_from_dir(source_dir, comment=None, overwrite=True, passphrase=None, verbose=False): """Use 7z to create an encrypted ZIP archive of a directory. <source_dir> will be included into the archive <comment> included as a file containing the comment <overwrite> remove existing archive before creation, avoiding *updating* of those, and thereby including unintended data <passphrase> minimum length of 5 The resulting zip archive will always be named "datawrapper.zip" for confidentiality reasons. If callers want another name they will have to shutil.move() the zip file themselves. This archive will be compressed and AES256 encrypted with the given passphrase. Therefore, the result will not decrypt with earlier versions of unzip software. On Windows, 7z oder WinZip are needed. The zip format does not support header encryption thereby allowing attackers to gain knowledge of patient details by observing the names of files and directories inside the encrypted archive. To reduce that attack surface, GNUmed will create _another_ zip archive inside "datawrapper.zip", which eventually wraps up the patient data as "data.zip". That archive is not compressed and not encrypted, and can thus be unpacked with any old unzipper. Note that GNUmed does NOT remember the passphrase for you. You will have to take care of that yourself, and possibly also safely hand over the passphrase to any receivers of the zip archive. """ if len(passphrase) < 5: _log.error('<passphrase> must be at least 5 characters/signs/digits') return None gmLog2.add_word2hide(passphrase) source_dir = os.path.abspath(source_dir) if not os.path.isdir(source_dir): _log.error('<source_dir> does not exist or is not a directory: %s', source_dir) return False for cmd in ['7z', '7z.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no 7z binary found') return None sandbox_dir = gmTools.mk_sandbox_dir() archive_path_inner = os.path.join(sandbox_dir, 'data') if not gmTools.mkdir(archive_path_inner): _log.error('cannot create scratch space for inner achive: %s', archive_path_inner) archive_fname_inner = 'data.zip' archive_name_inner = os.path.join(archive_path_inner, archive_fname_inner) archive_path_outer = gmTools.gmPaths().tmp_dir archive_fname_outer = 'datawrapper.zip' archive_name_outer = os.path.join(archive_path_outer, archive_fname_outer) # remove existing archives so they don't get *updated* rather than newly created if overwrite: if not gmTools.remove_file(archive_name_inner, force = True): _log.error('cannot remove existing archive [%s]', archive_name_inner) return False if not gmTools.remove_file(archive_name_outer, force = True): _log.error('cannot remove existing archive [%s]', archive_name_outer) return False # 7z does not support ZIP comments so create a text file holding the comment if comment is not None: tmp, fname = os.path.split(source_dir.rstrip(os.sep)) comment_filename = os.path.join(sandbox_dir, '000-%s-comment.txt' % fname) with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file: comment_file.write(comment) # create inner (data) archive: uncompressed, unencrypted, similar to a tar archive args = [ binary, 'a', # create archive '-sas', # be smart about archive name extension '-bd', # no progress indicator '-mx0', # no compression (only store files) '-mcu=on', # UTF8 filenames '-l', # store content of links, not links '-scsUTF-8', # console charset '-tzip' # force ZIP format ] if verbose: args.append('-bb3') args.append('-bt') else: args.append('-bb1') args.append(archive_name_inner) args.append(source_dir) if comment is not None: args.append(comment_filename) success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose) if not success: _log.error('cannot create inner archive') return None # create "decompress instructions" file instructions_filename = os.path.join(archive_path_inner, '000-on_Windows-open_with-WinZip_or_7z_tools') open(instructions_filename, mode = 'wt').close() # create outer (wrapper) archive: compressed, encrypted args = [ binary, 'a', # create archive '-sas', # be smart about archive name extension '-bd', # no progress indicator '-mx9', # best available zip compression ratio '-mcu=on', # UTF8 filenames '-l', # store content of links, not links '-scsUTF-8', # console charset '-tzip', # force ZIP format '-mem=AES256', # force useful encryption '-p%s' % passphrase # set passphrase ] if verbose: args.append('-bb3') args.append('-bt') else: args.append('-bb1') args.append(archive_name_outer) args.append(archive_path_inner) success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose) if success: return archive_name_outer _log.error('cannot create outer archive') return None
def gpg_encrypt_file_symmetric(filename=None, comment=None, verbose=False, passphrase=None, remove_unencrypted=False): #add short decr instr to comment assert (filename is not None), '<filename> must not be None' _log.debug('attempting symmetric GPG encryption') for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']: found, binary = gmShellAPI.detect_external_binary(binary = cmd) if found: break if not found: _log.warning('no gpg binary found') return None filename_encrypted = filename + '.asc' args = [ binary, '--utf8-strings', '--display-charset', 'utf-8', '--batch', '--no-greeting', '--enable-progress-filter', '--symmetric', '--cipher-algo', 'AES256', '--armor', '--output', filename_encrypted ] if comment is not None: args.extend(['--comment', comment]) if verbose: args.extend ([ '--verbose', '--verbose', '--debug-level', '8', '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog', ##'--debug-all', # will log passphrase ##'--debug, 'ipc', # will log passphrase ##'--debug-level', 'guru', # will log passphrase ##'--debug-level', '9', # will log passphrase ]) pwd_fname = None if passphrase is not None: pwd_file = tempfile.NamedTemporaryFile(mode = 'w+t', encoding = 'utf8', delete = False) pwd_fname = pwd_file.name args.extend ([ '--pinentry-mode', 'loopback', '--passphrase-file', pwd_fname ]) pwd_file.write(passphrase) pwd_file.close() args.append(filename) try: success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = verbose, encoding = 'utf-8') finally: if pwd_fname is not None: os.remove(pwd_fname) if not success: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None
def gpg_encrypt_file_symmetric(filename=None, comment=None, verbose=False, passphrase=None, remove_unencrypted=False): #add short decr instr to comment assert (filename is not None), '<filename> must not be None' _log.debug('attempting symmetric GPG encryption') for cmd in ['gpg2', 'gpg', 'gpg2.exe', 'gpg.exe']: found, binary = gmShellAPI.detect_external_binary(binary=cmd) if found: break if not found: _log.warning('no gpg binary found') return None filename_encrypted = filename + '.asc' args = [ binary, '--utf8-strings', '--display-charset', 'utf-8', '--batch', '--no-greeting', '--enable-progress-filter', '--symmetric', '--cipher-algo', 'AES256', '--armor', '--output', filename_encrypted ] if comment is not None: args.extend(['--comment', comment]) if verbose: args.extend([ '--verbose', '--verbose', '--debug-level', '8', '--debug', 'packet,mpi,crypto,filter,iobuf,memory,cache,memstat,trust,hashing,clock,lookup,extprog', ##'--debug-all', # will log passphrase ##'--debug, 'ipc', # will log passphrase ##'--debug-level', 'guru', # will log passphrase ##'--debug-level', '9', # will log passphrase ]) pwd_fname = None if passphrase is not None: pwd_file = tempfile.NamedTemporaryFile(mode='w+t', encoding='utf8', delete=False) pwd_fname = pwd_file.name args.extend( ['--pinentry-mode', 'loopback', '--passphrase-file', pwd_fname]) pwd_file.write(passphrase) pwd_file.close() args.append(filename) try: success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args, verbose=verbose, encoding='utf-8') finally: if pwd_fname is not None: os.remove(pwd_fname) if not success: return None if not remove_unencrypted: return filename_encrypted if gmTools.remove_file(filename): return filename_encrypted gmTools.remove_file(filename_encrypted) return None