10 from os
.path
import join
11 from subprocess
import PIPE
, CalledProcessError
, Popen
15 """ create openssl cert bundle from system certificates """
17 def __init__(self
, openssl
):
18 self
.openssl
= openssl
20 def is_valid_cert(self
, cert
):
21 """ check if cert is valid according to openssl"""
22 cmd
= [self
.openssl
, "x509", "-inform", "pem", "-checkend", "0", "-noout"]
23 # print("D: is_valid_cert %r" % cmd)
24 proc
= Popen(cmd
, stdin
=PIPE
, stdout
=PIPE
, stderr
=PIPE
)
25 stdout
, stderr
= proc
.communicate(cert
)
26 # print("out: %s; err:%s; ret:%i" % (stdout, stderr, proc.returncode))
27 return proc
.returncode
== 0
30 """ extract System's certificates then filter them by validity
31 and return a list of text of valid certs
33 cmd
= ["security", "find-certificate", "-a", "-p",
34 "/System/Library/Keychains/SystemRootCertificates.keychain"]
35 cert_re
= re
.compile(b
"^-----BEGIN CERTIFICATE-----$"
37 + b
"^-----END CERTIFICATE-----$", re
.M | re
.S
)
39 certs_str
= subprocess
.check_output(cmd
)
40 all_certs
= cert_re
.findall(certs_str
)
41 print("I: extracted %i certificates" % len(all_certs
))
42 valid_certs
= [cert
for cert
in all_certs
43 if self
.is_valid_cert(cert
)]
44 print("I: of which %i are valid certificates" % len(valid_certs
))
47 print("E: extracting certificates using %r" % cmd
)
49 except CalledProcessError
as err
:
50 print(("E: extracting certificates using %r, exit=%i" %
51 (cmd
, err
.returncode
)))
54 def write_certs(certs
, dest
):
55 """ write concatenated certs to dest """
56 with
open(dest
, "wb") as output
:
57 output
.write(b
"\n".join(certs
))
59 def regen(self
, dest
):
61 print("I: make_cert_pem %s %s" % (self
.openssl
, dest
))
62 certs
= self
.get_certs()
64 print("E: no certificate extracted")
67 self
.write_certs(certs
, dest
)
68 print("I: updated %s with %i certificates" % (dest
, len(certs
)))
72 # print("launcher.py sys.argv=", sys.argv)
73 bundlepath
= sys
.argv
.pop(0)
74 app
= os
.path
.basename(sys
.argv
[0])
76 bundle_contents
= join(bundlepath
, 'Contents')
77 bundle_res
= join(bundle_contents
, 'Resources')
79 bundle_lib
= join(bundle_res
, 'lib')
80 bundle_bin
= join(bundle_res
, 'bin')
81 bundle_data
= join(bundle_res
, 'share')
82 bundle_etc
= join(bundle_res
, 'etc')
84 os
.environ
['CHARSETALIASDIR'] = bundle_lib
85 os
.environ
['DYLD_LIBRARY_PATH'] = bundle_lib
86 os
.environ
['GTK_DATA_PREFIX'] = bundle_res
87 os
.environ
['GTK_EXE_PREFIX'] = bundle_res
88 os
.environ
['GTK_PATH'] = bundle_res
89 os
.environ
['LD_LIBRARY_PATH'] = bundle_lib
90 os
.environ
['XDG_CONFIG_DIRS'] = bundle_etc
91 os
.environ
['XDG_DATA_DIRS'] = bundle_data
93 os
.environ
['PANGO_LIBDIR'] = bundle_lib
94 os
.environ
['PANGO_RC_FILE'] = join(bundle_etc
, 'pango', 'pangorc')
95 os
.environ
['PANGO_SYSCONFDIR'] = bundle_etc
96 os
.environ
['GDK_PIXBUF_MODULE_FILE'] = join(bundle_lib
, 'gdk-pixbuf-2.0',
97 '2.10.0', 'loaders.cache')
98 if int(platform
.release().split('.')[0]) > 10:
99 os
.environ
['GTK_IM_MODULE_FILE'] = join(bundle_etc
, 'gtk-3.0',
102 os
.environ
['GI_TYPELIB_PATH'] = join(bundle_lib
, 'girepository-1.0')
105 os
.environ
['PYTHONHOME'] = bundle_res
106 # Set $PYTHON to point inside the bundle
108 sys
.path
.append(bundle_res
)
109 print('System Path:\n', '\n'.join(sys
.path
))
111 # see https://gpodder.github.io/docs/user-manual.html#gpodder-home-folder-and-download-location
112 # To override gPodder home and/or download directory:
113 # 1. uncomment (remove the pound sign and space) at the beginning of the relevant line
114 # 2. replace ~/gPodderData or ~/gPodderDownloads with the path you want for your gPodder home
115 # (you can move the original folder in the Finder first,
116 # then drag and drop to the launcher.py in TextEdit to ensure the correct path is set)
117 # uncomment the following line to override gPodder home
118 # os.environ['GPODDER_HOME'] = expanduser('~/gPodderData')
119 # uncomment the following line to override gPodder download directory
120 # os.environ['GPODDER_DOWNLOAD_DIR'] = expanduser('~/gPodderDownloads')
122 for k
, v
in os
.environ
.items():
123 print("%s=%s" % (k
, v
))
127 # don't inadvertently create the new gPodder home,
128 # it would be preferred to the old one
129 default_path
= join(os
.environ
['HOME'], 'Library', 'Application Support', 'gPodder')
131 os
.path
.expanduser(os
.environ
.get('GPODDER_HOME')) if 'GPODDER_HOME' in os
.environ
else None,
133 join(os
.environ
['HOME'], 'gPodder'),
136 if cand
and os
.path
.exists(cand
):
141 gphome
= gpodder_home()
142 os
.makedirs(join(gphome
, 'openssl'), exist_ok
=True)
143 # generate cert.extracted.pem
144 cert_gen
= join(gphome
, 'openssl', 'cert.extracted.pem')
145 cert_pem
= join(gphome
, 'openssl', 'cert.pem')
147 if not os
.path
.isfile(cert_gen
):
150 last_modified
= os
.stat(cert_gen
).st_mtime
151 regen
= last_modified
< time
.time() - 3600 * 7
154 print('(Re)generating', cert_pem
)
155 openssl
= join(bundle_bin
, 'openssl')
156 MakeCertPem(openssl
).regen(cert_gen
)
158 print('No regenerating', cert_gen
, 'it\'s fresh enough')
160 # and link to it by default. Users may want to point cert.pem to MacPorts
161 # /opt/local/etc/openssl/cert.pem, for instance.
162 if not os
.path
.exists(cert_pem
):
163 os
.symlink(os
.path
.basename(cert_gen
), cert_pem
)
164 # Set path to CA files
165 os
.environ
['SSL_CERT_FILE'] = cert_pem
167 if app
== 'run-python':
168 python_exe
= join(bundle_contents
, 'MacOS', 'python3')
169 # executable is repeated as argv[0].
170 # Old sys.argv[0] points to Contents/MacOS so must be removed
171 args
= [python_exe
] + sys
.argv
[1:]
172 # print("running", args)
173 os
.execv(python_exe
, args
)
174 elif app
== 'run-pip':
175 python_exe
= join(bundle_contents
, 'MacOS', 'python3')
176 pip
= join(bundle_contents
, 'Resources', 'bin', 'pip3')
177 # executable is repeated as argv[0].
178 # Old sys.argv[0] points to Contents/MacOS so must be removed
179 args
= [python_exe
, pip
] + sys
.argv
[1:]
180 # print("running", args)
181 os
.execv(python_exe
, args
)
184 runpy
.run_path(join(bundle_bin
, app
), run_name
='__main__')