-
-
Save brianbruggeman/f032f5b8e4b7fc1c63c8691071be5946 to your computer and use it in GitHub Desktop.
| Public Domain |
| click |
| #!/usr/bin/env python | |
| # -*- coding: utf-8 -*- | |
| """Converts viscosity export files into an open vpn package | |
| Usage: viscosity-to-openvpn.py <input> <output> | |
| """ | |
| import io | |
| import os | |
| import sys | |
| import tarfile | |
| import click | |
| if sys.version.startswith('3'): | |
| unicode = str | |
| # ---------------------------------------------------------------------- | |
| # Exceptions | |
| # ---------------------------------------------------------------------- | |
| class ConversionError(Exception): | |
| """Base conversion error""" | |
| pass | |
| class NoConnectionName(ConversionError): | |
| """No connection name was available""" | |
| pass | |
| class NoCertificateData(ConversionError): | |
| """No certificate data was found within certificate file""" | |
| pass | |
| class NoCertificateFile(ConversionError): | |
| """File was not available within archive""" | |
| pass | |
| # ---------------------------------------------------------------------- | |
| # Command-line Interface | |
| # ---------------------------------------------------------------------- | |
| @click.command() | |
| @click.argument('input-path', type=click.Path(exists=True)) | |
| @click.argument('output', required=False, type=click.Path(), default=None) | |
| def convert(input_path, output=None): | |
| '''Converts Viscosity package | |
| Args: | |
| input (str): path to folder or file input | |
| output (str): path to folder output [default: None] | |
| ''' | |
| if input_path.endswith('.visc'): | |
| output = input_path if output is None else output | |
| if output and not os.path.exists(output): | |
| output = input_path | |
| files = [os.path.join(input_path, filename) for filename in os.listdir(input_path)] | |
| for config_fp in files: | |
| new_config = [] | |
| if config_fp.endswith('.conf'): | |
| with io.open(config_fp, encoding='utf-8') as stream: | |
| connection_name = extract(stream, new_config, input_path=input_path) | |
| new_config.insert(0, '# OpenVPN Config for {}'.format(connection_name)) | |
| new_config = '\n'.join(new_config) + '\n' | |
| output_filepath = os.path.join(output, '{}.ovpn'.format(connection_name)) | |
| with io.open(output_filepath, 'w', encoding='utf-8') as out: | |
| out.write(unicode(new_config)) | |
| print('Wrote: {}'.format(output_filepath)) | |
| elif input_path.endswith('.visz'): | |
| if output is None: | |
| output = os.path.dirname(input_path) | |
| data = {} | |
| with tarfile.open(input_path) as zipped: | |
| for filepath, fileinfo in zip(zipped.getnames(), zipped.getmembers()): | |
| if not fileinfo.isfile(): | |
| continue | |
| filename = filepath.split(os.path.sep)[-1] | |
| data[filename] = zipped.extractfile(filepath).read() | |
| for key in data: | |
| if not key.endswith('.conf') or key.startswith('.'): | |
| continue | |
| new_config = [] | |
| lines = data[key].split('\n') | |
| connection_name = extract(lines, new_config, file_data=data) | |
| new_config.insert(0, '# OpenVPN Config for {}'.format(connection_name)) | |
| new_config = '\n'.join(new_config) + '\n' | |
| output_filepath = os.path.join(output, '{}.ovpn'.format(connection_name)) | |
| with io.open(output_filepath, 'w', encoding='utf-8') as out: | |
| out.write(unicode(new_config)) | |
| print('Wrote: {}'.format(output_filepath)) | |
| # ---------------------------------------------------------------------- | |
| # CLI Support | |
| # ---------------------------------------------------------------------- | |
| def extract(data, new_config, input_path=None, file_data={}): | |
| certificate_files = ['ca', 'cert', 'key', 'tls-auth'] | |
| connection_name = '' | |
| for line in data: | |
| line = line.rstrip() | |
| if not line.strip(): | |
| continue | |
| # This was an invalid configuration, for some reason | |
| elif line == 'compress lzo': | |
| continue | |
| elif line.startswith('#'): | |
| if line.startswith('#viscosity name'): | |
| connection_name = line.split('#viscosity name ', 1)[-1] | |
| connection_name = connection_name.strip() | |
| continue | |
| try: | |
| key, value = line.split(' ', 1) | |
| value = value.strip() | |
| except ValueError: | |
| key, value = line, '' | |
| if key in certificate_files: | |
| if key == 'tls-auth': | |
| try: | |
| value, direction = value.split(' ', 1) | |
| new_config.append('key-direction {}'.format(direction)) | |
| except ValueError: | |
| pass | |
| if input_path: | |
| cert_filepath = os.path.join(input_path, value) | |
| with io.open(cert_filepath, encoding='utf-8') as cf: | |
| certificate = cf.read() | |
| else: | |
| if value not in file_data: | |
| raise NoCertificateFile('Could not find certificate file in archive') | |
| certificate = file_data.get(value) | |
| if not certificate: | |
| raise NoCertificateData('Could not find certificate data') | |
| new_config.append('<%s>' % key) | |
| new_config.append(certificate) | |
| new_config.append('</%s>' % key) | |
| continue | |
| new_config.append(line) | |
| if not connection_name.strip(): | |
| raise NoConnectionName('Could not find connection name in file. Aborting') | |
| return connection_name | |
| if __name__ == '__main__': | |
| convert() |
Thank you!
Hello, sorry for the question but I would like to know how we use this script?
I am not familiar with python.
Thank.
Please look at this: https://www.amazon.com/Learn-Python-Hard-Way-Introduction/dp/0321884914
I have a visz and a visc file format for my viscosity vpn profile. I would just like to know what is the syntax in order to convert the visz and visc file so I can use it on my OpenVPN client?
Do I save this script with extension .py?
How Do I then point my visc/visz file to convert it to an OpenVPN file format.
FYI: In Ubuntu, to install click, you cannot use pip3. Use the following to install click:
sudo apt install python-pip
pip install click
Then you'll be able to run the script above :)
Im getting error:
Error: Missing argument 'INPUT_PATH'.
In with line should i write the path ?
Maybe try running:
python viscosity-to-openvpn.py --help
I'm getting this error:
Traceback (most recent call last): File "viscosity-to-openvpn.py", line 162, in <module> convert() File "C:\Python38\lib\site-packages\click\core.py", line 829, in __call__ return self.main(*args, **kwargs) File "C:\Python38\lib\site-packages\click\core.py", line 782, in main rv = self.invoke(ctx) File "C:\Python38\lib\site-packages\click\core.py", line 1066, in invoke return ctx.invoke(self.callback, **ctx.params) File "C:\Python38\lib\site-packages\click\core.py", line 610, in invoke return callback(*args, **kwargs) File "viscosity-to-openvpn.py", line 88, in convert lines = data[key].split('\n') TypeError: a bytes-like object is required, not 'str'
Makes sense. I think the original script may have been built in python2. You'll probably have to update for python 3.8.
There's a hint in that trackback (line 88: data[key].split('\n')) and TypeError: a bytes-like object is required....
Python 3 modified a bunch of APIs to use bytes rather than string. The reason for this is to make Python (by default) more compatible with globalization and unicode data. There are 7 forks for the original code. I suspect one of them has upgraded to python 3.8. But if you want to take a SWAG at fixing it, then:
- you can often preface a string with
bto convert a string to bytes. For example:'\n'-->b'\n' - there's also a
bytestype: https://docs.python.org/3.8/library/stdtypes.html#bytes
I suspect that once you fix the above error, you'll have to dig through a few more related errors. But once you wade through it, you should have something working.
Thank you so much for the script but having some problem.
TypeError: convert() got an unexpected keyword argument "/path to my .visc/" file.
What could be the possible solution for it. Your kind reply will be much appreciated. Thanks
Traceback (most recent call last):
File "viscosity-to-openvpn.py", line 162, in
convert()
File "/opt/anaconda3/lib/python3.8/site-packages/click/core.py", line 829, in call
return self.main(*args, **kwargs)
File "/opt/anaconda3/lib/python3.8/site-packages/click/core.py", line 782, in main
rv = self.invoke(ctx)
File "/opt/anaconda3/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/opt/anaconda3/lib/python3.8/site-packages/click/core.py", line 610, in invoke
return callback(*args, **kwargs)
TypeError: convert() got an unexpected keyword argument '/path to my .visc/'
Complete log for your perusal please.
Thank you so much for the script but having some problem.
TypeError: convert() got an unexpected keyword argument "/path to my .visc/" file.
What could be the possible solution for it. Your kind reply will be much appreciated. Thanks
You have to specify the file path to your viscosity file, eg:
c:_TEMP>python viscosity-to-openvpn.py c:_TEMP\Your_viscosity_profile.visc
in this example, i have a folder '_TEMP' on C:
inside this folder is the python script located, aswell as the *.visc profile
Thank you for this, it was super helpful!
i update the code, https://gist.github.com/vinicius795/e975688fa8ffcba549d8240ecf0a7f9f now it works in python 3.10
if __name__ == '__main__':
preconvert()
def preconvert(input_path, output=None):
if input_path.endswith('.visc') or input_path.endswith('.visz'):
convert(input_path,output)
return
for myfile in os.listdir(input_path):
if myfile.endswith('.visc') or myfile.endswith('.visz'):
convert(os.path.join(input_path, myfile))
this change give ability to convert all files in a folder : python viscosity-to-openvpn.py clients/
at the same time you still cat convert just single file: viscosity-to-openvpn.py clients/client1.visz
something still needs to be fixed:
python3 viscosity-to-openvpn.py ovpn.visz
Traceback (most recent call last):
File "viscosity-to-openvpn.py", line 165, in <module>
convert()
File "/usr/lib/python3/dist-packages/click/core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "/usr/lib/python3/dist-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/usr/lib/python3/dist-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/lib/python3/dist-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "viscosity-to-openvpn.py", line 81, in convert
data[filename] = zipped.extractfile(filepath).read().decode('utf8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xab in position 45: invalid start byte
This other python 2 script worked for me: https://gist.github.com/ishahid/693c2c97b3236a3c2416fc09ab170244
Great job 🥇