HEX
Server: Apache/2.4.63 (Unix)
System: Linux Synopilou92 4.4.302+ #72806 SMP Mon Jul 21 23:16:00 CST 2025 x86_64
User: pilou92 (1026)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: /volume1/@appstore/HyperBackup/addon/azure_blob/python/azure_agent.py
#!/usr/bin/env python3
#-*-coding: utf-8 -*-

import os, sys, json, ssl
import urllib.parse
from struct import pack, unpack
from time import strptime, mktime
from syslog import syslog, LOG_ERR
from ctypes import CDLL, create_string_buffer, c_char_p, c_uint

# add include path
from pathlib import Path
script_path = Path(os.path.realpath(sys.argv[0]))
sys.path.insert(1, str(script_path.parent.joinpath('azure-sdk-for-python')))
sys.path.insert(2, str(script_path.parent.joinpath('module')))
sys.path.insert(3, str(script_path.parents[2].joinpath('common').joinpath('python_modules')))

debug = False
def log_debug(*args):
	if debug:
		print >> sys.stderr, ' '.join(args)

def _is_scoket_exception(e):
	import socket
	# NOTE: socket.error
	# In python 2, a child class of IOError, ref: https://docs.python.org/2/library/socket.html?#socket.error
	# In python 3, alias of OSError, ref: https://docs.python.org/3/library/socket.html#socket.error
	if type(e) == socket.gaierror or issubclass(type(e), socket.error) or type(e) == socket.herror or type(e) == socket.timeout:
		return True
	return False

def _convert_socket_error_code(message, auth_done):
	error_code = -1
	if -1 != message.find("[Errno -2] Name or service not known"):
		if auth_done:
			error_code = -4
		else:
			error_code = -2
	elif -1 != message.find("[Errno -3] Temporary failure in name resolution"):
		error_code = -4
	elif -1 != message.find("[Errno -5] No address associated with hostname"):
		error_code = -4
	elif -1 != message.find("[Errno 110] Connection timed out"):
		error_code = 408
	elif -1 != message.find("[Errno 101] Network is unreachable"):
		error_code = -4
	elif -1 != message.find("[Errno 111] Connection refused"):
		error_code = -4
	elif -1 != message.find("[Errno 113] No route to host"):
		error_code = -4
	elif -1 != message.find("[Errno 104] Connection reset by peer"):
		error_code = -4
	elif -1 != message.find("[Errno 32] Broken pipe"):
		error_code = 408
	return error_code

def _is_ssl_timeout(message):
	#ssl.SSLErr_timeouteption log
	if -1 != message.find("timed out"):
		# The read operation timed out
		# The write operation timed out
		# The handshake operation timed out
		return True
	return False

def _convert_socket_exception(e, auth_done):
	import socket

	error_code = -1
	error_msg = 'Unknown'
	error_cls = 'Unknown'

	try:
		error_cls = type(e).__name__
		if hasattr(e, '__module__') and e.__module__:
			error_cls = e.__module__ + '.' + error_cls

		if type(e) == socket.gaierror:
			if hasattr(e, 'strerror') and e.strerror:
				error_msg = e.strerror
			if e.errno == -2:
				# socket.gaierror: [Errno -2] Name or service not known
				# 1. dns not set
				# 2. set to a wrong dns
				if auth_done:
					error_code = -4
				else:
					error_code = -2
			elif e.errno == -3:
				# socket.gaierror: [Errno -3] Temporary failure in name resolution
				# man gai_strerror, and find EAI_AGAIN:
				# The name server returned a temporary failure indication.  Try again later.
				error_code = -4
			elif e.errno == -5:
				# socket.gaierror: [Errno -5] No address associated with hostname
				# man gai_strerror, and find EAI_NODATA:
				# The specified network host exists, but does not have any network addresses defined.
				error_code = -4
			elif e.errno < 100 and e.errno > 0:
				error_code = e.errno
		elif type(e) == socket.error or type(e) == socket.herror:
			if hasattr(e, 'strerror') and e.strerror:
				error_msg = e.strerror
			elif hasattr(e, 'args'):
				error_msg = str(e.args)
				if -1 != error_msg.find('Tunnel connection failed'):
					error_code = -4
			if e.errno == socket.errno.ETIMEDOUT:
				# socket.error: [Errno 110] Connection timed out
				error_code = 408
			elif e.errno == socket.errno.ENETUNREACH:
				# socket.error: [Errno 101] Network is unreachable
				error_code = -4
			elif e.errno == socket.errno.ECONNREFUSED:
				# socket.error: [Errno 111] Connection refused
				error_code = -4
			elif e.errno == socket.errno.EHOSTUNREACH:
				# socket.error: [Errno 113] No route to host
				error_code = -4
			elif e.errno == socket.errno.ECONNRESET:
				# socket.error: [Errno 104] Connection reset by peer
				error_code = -4
			elif e.errno == socket.errno.EPIPE:
				# socket.error: [Errno 32] Broken pipe
				error_code = 408
			elif e.errno < 100 and e.errno > 0:
				error_code = e.errno
			#syslog(LOG_ERR, "get network error %d:%s" % (e.errno, error_msg))
		elif type(e) == socket.timeout:
			error_msg = 'timed out'
			error_code = 408
		#ref: https://www.python.org/dev/peps/pep-3151/
		elif type(e) == BrokenPipeError:
			#EPIPE, ESHUTDOWN
			error_msg = 'Broken pipe'
			error_code = 408
		elif type(e) == ConnectionRefusedError:
			#ECONNREFUSED
			if hasattr(e, 'strerror') and e.strerror:
				error_msg = e.strerror
			error_code = -4
		elif type(e) == ConnectionResetError:
			#ECONNRESET
			if hasattr(e, 'strerror') and e.strerror:
				error_msg = e.strerror
			error_code = -4
		elif type(e) == ConnectionError:
			if hasattr(e, 'strerror') and e.strerror:
			        error_msg = e.strerror
			error_code = _convert_socket_error_code(str(e), auth_done)
		else:
			syslog(LOG_ERR, "BUG: exception [%s], type [%s]" % (str(e), type(e)))
		#syslog(LOG_ERR, "error %s:%d:%s" % (error_cls, error_code, str(error_msg)))

		if -1 == error_code:
			if hasattr(e, "args"):
				syslog(LOG_ERR, "error args: [%s]" % (str(e.args)))
			if hasattr(e, "errno"):
				syslog(LOG_ERR, "error errno: [%s]" % (str(e.errno)))
			if hasattr(e, "strerror"):
				syslog(LOG_ERR, "error strerror: [%s]" % (str(e.strerror)))
	except Exception as ee:
		syslog(LOG_ERR, "parse socket exception failed: [%s]" % (str(ee)))
		pass

	return {
		'success': False,
		'error_class': error_cls,
		'error_message': error_msg,
		'error_code': error_code
	}

class AzureErrorBody():
	'''
	Windows Azure Error Body class from xml.
	https://msdn.microsoft.com/en-us/library/azure/dd179382.aspx
	'''
	# parsing something like:
	#<?xml version="1.0" encoding="utf-8"?>
	#<Error>
	#	<Code>...</Code>
	#	<Message>...</Message>
	#	<AuthenticationErrorDetail>...</AuthenticationErrorDetail>
	#	<QueryParameterName>...</QueryParameterName>
	#	<QueryParameterValue>...</QueryParameterValue>
	#	<Reason>...</Reason>
	#</Error>

	def __init__(self):
		self.code = u''
		self.message = u''
		self.authenticationerrordetail = u''

def _convert_exception(e, auth_done):
	import azure
	import requests
	import http.client
	import azure.storage.common._error
	#import pdb; pdb.set_trace()

	error_code = -1
	error_msg = 'Unknown'
	error_cls = 'Unknown'

	try:
		error_cls = type(e).__name__
		if hasattr(e, '__module__') and e.__module__:
			error_cls = e.__module__ + '.' + error_cls

		try:
			error_msg = str(e).split('\n')[0]
			log_debug("\033[31mexception: \033[0m", error_msg)
		except:
			log_debug("\033[31mexception: \033[0m", "could not parse error msg")

		if type(e) == azure.common.AzureConflictHttpError:
			error_msg = str(e.args)
			error_code = 409
		elif type(e) == azure.common.AzureMissingResourceHttpError:
			error_msg = str(e.args)
			error_code = 404
		elif type(e) == azure.common.AzureHttpError:
			error_msg = str(e.args)
			error_code = e.status_code;
			if "AuthenticationFailed" == e.error_code:
				error_code = -2
			elif "InvalidResourceName" == e.error_code:
				error_code = -5
			elif error_msg == azure.storage.common._error._ERROR_STORAGE_MISSING_INFO:
				error_code = -2
		elif type(e) == azure.storage.common._error.AzureSigningError:
			error_msg = str(e.args)
			error_code = -2
		elif type(e) == TypeError:
			# this exception will raise when base64 decode for auth failed
			if error_msg == "Incorrect padding":
				error_code = -2
		elif type(e) == UnicodeDecodeError:
			error_code = -5
		elif type(e) == azure.common.AzureException:
			error_msg = str(e.args)

			error_code = -4
			if type(e.args[0]) == requests.packages.urllib3.exceptions.ProtocolError:
				if "BadStatusLine" in str(e.args[0]):
					error_code = -4
				elif _is_scoket_exception(e.args[0].args[1]):
					return _convert_socket_exception(e.args[0].args[1], auth_done)
				else:
					syslog(LOG_ERR, "ProtocolError: exception [%s], type [%s]" % (str(e.args[0].args[1]), type(e.args[0].args[1])))
			elif type(e.args[0]) == requests.packages.urllib3.exceptions.MaxRetryError:
				if hasattr(e.args[0], 'reason') and e.args[0].reason:
					if type(e.args[0].reason) == requests.packages.urllib3.exceptions.NewConnectionError:
						error_code = _convert_socket_error_code(str(e.args[0].reason), auth_done)
					else:
						syslog(LOG_ERR, "MaxRetryError: exception [%s], type [%s]" % (str(e.args[0].reason), type(e.args[0].reason)))
			elif type(e.args[0]) == ssl.SSLError or type(e.args[0]) == requests.packages.urllib3.exceptions.SSLError:
				error_msg = str(e.args[0])
				if _is_ssl_timeout(str(error_msg)):
					error_code = 408
				else:
					syslog(LOG_ERR, "SSLError: exception [%s], type [%s]" % (str(error_msg), type(error_msg)))
			elif type(e.args[0]) == requests.exceptions.SSLError:
				error_code = -4
				# EOF occurred in violation of protocol
				# SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC
				# SSL: SSLV3_ALERT_BAD_RECORD_MAC
				# SSL: SSLV3_ALERT_ILLEGAL_PARAMETER
				# SSL: TLSV1_ALERT_DECRYPT_ERROR
				# SSL: TLSV1_ALERT_DECODE_ERROR
				# SSL: CERTIFICATE_VERIFY_FAILED
				if _is_ssl_timeout(str(e.args[0])):
					error_code = 408
			elif type(e.args[0]) == requests.packages.urllib3.exceptions.NewConnectionError:
				error_code = _convert_socket_error_code(str(e.args[0]), auth_done)
			elif type(e.args[0]) == requests.packages.urllib3.exceptions.ConnectTimeoutError:
				error_code = 408
			elif type(e.args[0]) == requests.packages.urllib3.exceptions.ReadTimeoutError:
				error_code = 408
			elif type(e.args[0]) == requests.exceptions.ConnectTimeout:
				error_code = 408
			elif type(e.args[0]) == requests.exceptions.ReadTimeout:
				error_code = 408
			elif _is_scoket_exception(e.args[0]):
				return _convert_socket_exception(e.args[0], auth_done)
		elif type(e) == http.client.ResponseNotReady:
			error_code = -4
		elif type(e) == http.client.BadStatusLine:
			error_code = -4
		elif type(e) == http.client.IncompleteRead:
			error_code = -4
		elif type(e) == ssl.SSLError:
			error_code = -4
			if hasattr(e, "strerror") and e.strerror:
				# SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC
				# SSL: SSLV3_ALERT_BAD_RECORD_MAC
				# SSL: CERTIFICATE_VERIFY_FAILED
				error_msg = e.strerror
			if _is_ssl_timeout(str(error_msg)):
				error_code = 408
		elif type(e) == ssl.SSLEOFError:
			# ssl.SSLEOFError: [Errno 8] EOF occurred in violation of protocol
			if hasattr(e, 'strerror') and e.strerror:
				error_msg = e.strerror
			error_code = -4
		elif type(e) == TimeoutError:
			#ETIMEDOUT
			error_msg = 'timed out'
			error_code = 408
		elif type(e) == IOError:
			if hasattr(e, 'strerror') and e.strerror:
				error_msg = e.strerror
			error_code = _convert_socket_error_code(str(e), auth_done)
		elif _is_scoket_exception(e):
			return _convert_socket_exception(e, auth_done)
		else:
			syslog(LOG_ERR, "exception [%s]" % str(e))
			syslog(LOG_ERR, "type [%s]" % type(e))
		#syslog(LOG_ERR, "error %s:%d:%s" % (error_cls, error_code, str(error_msg)))

		if -1 == error_code:
			if hasattr(e, "args"):
				syslog(LOG_ERR, "error args: [%s]" % (str(e.args)))
			if hasattr(e, "errno"):
				syslog(LOG_ERR, "error errno: [%s]" % (str(e.errno)))
			if hasattr(e, "strerror"):
				syslog(LOG_ERR, "error strerror: [%s]" % (str(e.strerror)))
	except Exception as ee:
		syslog(LOG_ERR, "parse exception failed. %s" % str(ee))
		pass

	return {
		'success': False,
		'error_class': error_cls,
		'error_message': error_msg,
		'error_code': error_code
	}
def _convert_time_str(val):
	origin_tz = None
	if os.environ.get('TZ'):
		origin_tz = os.environ.get('TZ')
	os.environ['TZ'] = 'GMT'

	timestamp = int(mktime(val.timetuple()))

	if origin_tz:
		os.environ['TZ'] = origin_tz
	return timestamp
def _convert_properties(properties):
	out_json = {
		# Fri, 25 Jul 2014 09:36:56 GMT
		'LastModified': _convert_time_str(properties.last_modified),
		'MD5': properties.content_settings.content_md5,
		'ETag': properties.etag,
		'ContentType': properties.content_settings.content_type,
		'ContentLength': properties.content_length,
		'BlobType': properties.blob_type
	}
	return out_json
def _convert_properties_by_dict(properties, metadata):
	out_json = {
		'LastModified': _convert_time_str(properties.last_modified),
		'MD5': properties.content_settings.content_md5,
		# remove etag dobule quote
		'ETag': properties.etag[1:-1],
		'ContentType': properties.content_settings.content_type,
		'ContentLength': properties.content_length,
		'BlobType': properties.blob_type
	}
	if 'hdi_isfolder' in metadata:
		out_json['hdi_isfolder'] = 'true' == metadata['hdi_isfolder']
	return out_json
def _convert_properties_by_put(properties):
	out_json = {
		'LastModified': _convert_time_str(properties.last_modified),
		'MD5': '',
		#'MD5': properties['content-md5'],
		# remove etag dobule quote
                'ETag': properties.etag[1:-1],
	}
	return out_json
def _convert_properties_by_commit(properties):
	out_json = {
		'LastModified': _convert_time_str(properties['last-modified']),
		'MD5': '',
		# remove etag dobule quote
		'ETag': properties['etag'][1:-1],
	}
	return out_json
def _convert_block_id(id_prefix, counter):
	return id_prefix + '_{0:08d}'.format(counter)

class SimpleIO(object):
	def read_int(self):
		data = bytes()
		n = 4
		while n > 0:
			read_data = sys.stdin.buffer.read(n)
			if 0 == len(read_data):
				raise StopIteration
			data += read_data
			n -= len(read_data)
		if data:
			return unpack('i', data)[0]
		else:
			# check eof?
			raise StopIteration
	def read_string(self):
		n = self.read_int()
		if 0 == n:
			return ''
		data = bytes()
		while n > 0:
			read_data = sys.stdin.buffer.read(n)
			if 0 == len(read_data):
				raise StopIteration
			data += read_data
			n -= len(read_data)
		if data:
			return data.decode('utf-8')
		else:
			raise SystemError
	def read_json(self):
		json_str = self.read_string()
		if json_str:
			return json.loads(json_str)
		else:
			return None
	def write_int(self, val):
		data = pack('i', val)
		sys.stdout.buffer.write(data)
		sys.stdout.flush()
	def write_string(self, val):
		self.write_int(len(val))
		sys.stdout.write(val)
		sys.stdout.flush()
		# log
	def write_json(self, val):
		s = json.dumps(val)
		self.write_string(s)
	def write_exception(self, e, auth_done = False):
		self.write_json(_convert_exception(e, auth_done))

class AzureBlobStoreageApi(object):
	def __init__(self):
		from azure.storage.blob.baseblobservice import BaseBlobService
		from azure.storage.blob.blockblobservice import BlockBlobService

		blob_service_opt = {
			"account_name": os.environ.get('AZURE_ACCESS_KEY'),
			"account_key": os.environ.get('AZURE_SECRET_KEY'),
			"protocol": os.environ.get('AZURE_SCHEME', 'https')
			}

		if os.environ.get('AZURE_HOST_BASE') == 'china':
			blob_service_opt["endpoint_suffix"] = 'core.chinacloudapi.cn'
		elif os.environ.get('AZURE_HOST_BASE'):
			blob_service_opt["endpoint_suffix"] = os.environ.get('AZURE_HOST_BASE')

		# Export synoproxy settings to environment if needed
		if "endpoint_suffix" in blob_service_opt:
			azure_blob_url = '%s.blob.%s' % (blob_service_opt["account_name"], blob_service_opt["endpoint_suffix"])
		else:
			azure_blob_url = '%s.blob.core.windows.net' % (blob_service_opt['account_name'])
		synoproxy_exporter = SynoProxyExporter(azure_blob_url)
		synoproxy_exporter.export_proxy_settings()

		# os.environ['AZURE_DEBUG'] = 'yes'
		self._azure = BaseBlobService(**blob_service_opt)
		self._azure_blob = BlockBlobService(**blob_service_opt)

		del os.environ['AZURE_ACCESS_KEY']
		del os.environ['AZURE_SECRET_KEY']
		log_debug('agent created')

	def createContainer(self, in_json):
		kwargs = {
			'fail_on_exist': True
		}

		if in_json.get('PublicAccess'):
			kwargs['x_ms_blob_public_access'] = in_json['PublicAccess']

		res = self._azure.create_container(in_json['container'], **kwargs)

		return {'success': res}
	def getContainerProperties(self, in_json):
		res = self._azure.get_container_properties(in_json['container'])

		return {
			'success': True,
			'LastModified': _convert_time_str(res.properties.last_modified),
			'ETag': res.properties.etag
		}
	def listContainers(self, in_json):
		kwargs = {}
		# FIXME add prefix and marker to ta

		for key in ('prefix', 'marker'):
			if in_json.get(key):
				kwargs[key.lower()] = in_json[key]

		res = self._azure.list_containers(**kwargs)

		out_json = {
			'success': True,
			'container': []
		}
		for rec in res:
			out_json['container'].append({
				'Name': rec.name
			})

		return out_json
	def deleteContainer(self, in_json):
		kwargs = {
			'fail_not_exist': True
		}

		res = self._azure.delete_container(in_json['container'], **kwargs)

		return {'success': res}

	def createBlockBlob(self, in_json):
		res = self._azure_blob.create_blob_from_path(
				in_json['container'],
				in_json['blob'],
				in_json['fileInput'])

		return {'success': True,
				'Properties': _convert_properties_by_put(res)
		}
	def listBlobs(self, in_json):
		from azure.storage.blob.models import Blob, BlobPrefix

		kwargs = {}
		for key in ('delimiter', 'prefix', 'marker'):
			if in_json.get(key):
				kwargs[key.lower()] = in_json[key]

		res = self._azure.list_blobs(in_json['container'], **kwargs)

		targetType = in_json.get('TargetType', 'all')
		count = 0
		out_json = {
			'success': True,
			'folder': [],
			'file': []
		}

		for rec in res:
			if targetType == 'folder' or targetType == 'all':
				if type(rec) == BlobPrefix:
					out_json['folder'].append({'Name': rec.name})
					count += 1
			if targetType == 'file' or targetType == 'all':
				if type(rec) == Blob:
					out_json['file'].append({
						'Name': rec.name,
						'Properties': _convert_properties(rec.properties)
					})
					count += 1

		if res.next_marker:
			out_json['NextMarker'] = res.next_marker

		out_json['count'] = count
		return out_json
	def getBlobProperties(self, in_json):

		res = self._azure.get_blob_properties(
				in_json['container'],
				in_json['blob'])

		return {
			'success': True,
			'Properties': _convert_properties_by_dict(res.properties, res.metadata),
		}
	def deleteBlob(self, in_json):
		from azure.storage.blob.models import DeleteSnapshot
		res = self._azure.delete_blob(
				in_json['container'],
				in_json['blob'],
				delete_snapshots=DeleteSnapshot.Include)

		return {'success': True}
	def getBlob(self, in_json):
                res = self._azure.get_blob_to_path(
                        in_json['container'],
                        in_json['blob'],
                        in_json['fileOutput'])

                return {'success': True}

def start_server():
	io = SimpleIO()

	try:
		api = AzureBlobStoreageApi()
	except Exception as e:
		io.write_exception(e)
		return False

	io.write_string('start')
	auth_done = False

	while True:
		try:
			in_json = io.read_json()
			fn_name = in_json['fn']
			fn = getattr(api, fn_name)
			del in_json['fn']

			if fn is None:
				raise SystemError('no such fn: ' + fn_name)

			#log_debug('\033[34mexecute: ' + fn_name + ' ' + json.dumps(in_json) + '\033[0m');

			res = fn(in_json)
			if not auth_done and res['success']:
				auth_done = True
			io.write_json(res)
		except StopIteration:
			break
		except Exception as e:
			io.write_exception(e, auth_done)
			continue


class SynoProxyExporter:
	def __init__(self, host, port=None):
		self.host = host
		self.port = port
		self.synoproxy_host = ''
		self.synoproxy_port = ''
		self.synoproxy_auth = ''
		self.default_port = 443  # https port

	def _get_hostport(self, host, port):
		if port is None:
			i = host.rfind(':')
			j = host.rfind(']')  # ipv6 addresses have [...]
			if i > j:
				try:
					port = int(host[i + 1:])
				except ValueError:
					if host[i + 1:] == "":  # http://foo.com:/ == http://foo.com/
						port = self.default_port
					else:
						raise ValueError("nonnumeric port: '%s'" % host[i + 1:])
				host = host[:i]
			else:
				port = self.default_port
			if host and host[0] == '[' and host[-1] == ']':
				host = host[1:-1]

		return (host, port)

	def _get_syno_proxy_info(self):
		(synoproxy_host, synoproxy_port, synoproxy_auth) = (None, ) * 3
		BUFSIZE = 4096

		synoproxy = CDLL('libsynoproxy.so')
		proxy_addr_buf = create_string_buffer(BUFSIZE)
		proxy_auth_buf = create_string_buffer(BUFSIZE)

		if 1 == synoproxy.SYNOProxyGetAddrByUrl(
			c_char_p((self.host + ':' + str(self.port)).encode('utf-8')), proxy_addr_buf, c_uint(BUFSIZE)):
			(synoproxy_host, synoproxy_port) = self._get_hostport(proxy_addr_buf.value.decode('utf-8'), None)

		if 1 == synoproxy.SYNOProxyGetAuth(proxy_auth_buf, c_uint(BUFSIZE)):
			# in format <user>:<password>
			synoproxy_auth = proxy_auth_buf.value.decode('utf-8')

		return (synoproxy_host, synoproxy_port, synoproxy_auth)

	def export_proxy_settings(self):
		(self.synoproxy_host, self.synoproxy_port, self.synoproxy_auth) = self._get_syno_proxy_info()

		if self.synoproxy_host is None or self.synoproxy_port is None:
			# No synoproxy setting found, do nothing
			return

		synoproxy_destination = "%s:%s" % (self.synoproxy_host, self.synoproxy_port)

		if None is self.synoproxy_auth:
			os.environ["HTTP_PROXY"] = "http://%s" % (synoproxy_destination)
			os.environ["HTTPS_PROXY"] = "http://%s" % (synoproxy_destination)
			return

		# User and password is present in setting, but we need to handle special character
		# self.synoproxy_auth is in format <user>:<password>, so we do url encoding without changing ':'
		synoproxy_auth_encoded = urllib.parse.quote(self.synoproxy_auth, safe=':')

		os.environ["HTTP_PROXY"] = "http://%s@%s" % (synoproxy_auth_encoded, synoproxy_destination)
		os.environ["HTTPS_PROXY"] = "http://%s@%s" % (synoproxy_auth_encoded, synoproxy_destination)


if __name__ == '__main__':
	try:
		res = start_server()
		sys.exit(0 if res else 1)
	except Exception as e:
		syslog(LOG_ERR, "Error: exception [%s], type [%s]" % (str(e), type(e)));