2020-04-04 11:00:19 +00:00
|
|
|
__filename__ = "session.py"
|
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
__license__ = "AGPL3+"
|
2021-01-26 10:07:42 +00:00
|
|
|
__version__ = "1.2.0"
|
2020-04-04 11:00:19 +00:00
|
|
|
__maintainer__ = "Bob Mottram"
|
2021-09-10 16:14:50 +00:00
|
|
|
__email__ = "bob@libreserver.org"
|
2020-04-04 11:00:19 +00:00
|
|
|
__status__ = "Production"
|
2021-09-17 15:12:50 +00:00
|
|
|
__module_group__ = "Session"
|
2019-06-28 18:55:29 +00:00
|
|
|
|
2019-07-16 14:23:06 +00:00
|
|
|
import os
|
2019-06-28 18:55:29 +00:00
|
|
|
import requests
|
2019-07-02 10:39:55 +00:00
|
|
|
from utils import urlPermitted
|
2021-07-04 17:55:29 +00:00
|
|
|
from utils import isImageFile
|
2021-08-31 09:10:49 +00:00
|
|
|
from httpsig import createSignedHeader
|
2019-06-28 18:55:29 +00:00
|
|
|
import json
|
2020-06-23 13:28:41 +00:00
|
|
|
from socket import error as SocketError
|
|
|
|
import errno
|
2021-06-18 11:53:25 +00:00
|
|
|
from http.client import HTTPConnection
|
2019-06-28 18:55:29 +00:00
|
|
|
|
2020-04-04 11:00:19 +00:00
|
|
|
baseDirectory = None
|
|
|
|
|
2019-06-30 18:23:18 +00:00
|
|
|
|
2020-06-09 11:03:59 +00:00
|
|
|
def createSession(proxyType: str):
|
2020-06-08 17:10:53 +00:00
|
|
|
session = None
|
|
|
|
try:
|
2020-06-23 22:17:48 +00:00
|
|
|
session = requests.session()
|
2020-06-23 21:39:19 +00:00
|
|
|
except requests.exceptions.RequestException as e:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: requests error during createSession ' + str(e))
|
2020-06-23 13:41:12 +00:00
|
|
|
return None
|
2020-06-23 13:28:41 +00:00
|
|
|
except SocketError as e:
|
|
|
|
if e.errno == errno.ECONNRESET:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: connection was reset during createSession ' + str(e))
|
2020-06-23 13:28:41 +00:00
|
|
|
else:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: socket error during createSession ' + str(e))
|
2020-06-08 17:10:53 +00:00
|
|
|
return None
|
2020-06-23 21:39:19 +00:00
|
|
|
except ValueError as e:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: error during createSession ' + str(e))
|
2020-06-23 21:39:19 +00:00
|
|
|
return None
|
2020-06-09 11:03:59 +00:00
|
|
|
if not session:
|
|
|
|
return None
|
|
|
|
if proxyType == 'tor':
|
2020-04-04 11:00:19 +00:00
|
|
|
session.proxies = {}
|
|
|
|
session.proxies['http'] = 'socks5h://localhost:9050'
|
|
|
|
session.proxies['https'] = 'socks5h://localhost:9050'
|
2020-06-09 11:03:59 +00:00
|
|
|
elif proxyType == 'i2p':
|
|
|
|
session.proxies = {}
|
2020-06-19 22:09:20 +00:00
|
|
|
session.proxies['http'] = 'socks5h://localhost:4447'
|
|
|
|
session.proxies['https'] = 'socks5h://localhost:4447'
|
2020-06-09 11:51:51 +00:00
|
|
|
elif proxyType == 'gnunet':
|
|
|
|
session.proxies = {}
|
|
|
|
session.proxies['http'] = 'socks5h://localhost:7777'
|
|
|
|
session.proxies['https'] = 'socks5h://localhost:7777'
|
2020-09-25 14:38:50 +00:00
|
|
|
# print('New session created with proxy ' + str(proxyType))
|
2019-06-28 18:55:29 +00:00
|
|
|
return session
|
|
|
|
|
2020-04-04 11:00:19 +00:00
|
|
|
|
2021-07-13 15:49:29 +00:00
|
|
|
def urlExists(session, url: str, timeoutSec: int = 3,
|
|
|
|
httpPrefix: str = 'https', domain: str = 'testdomain') -> bool:
|
2021-02-14 13:43:05 +00:00
|
|
|
if not isinstance(url, str):
|
|
|
|
print('url: ' + str(url))
|
|
|
|
print('ERROR: urlExists failed, url should be a string')
|
|
|
|
return False
|
|
|
|
sessionParams = {}
|
|
|
|
sessionHeaders = {}
|
|
|
|
sessionHeaders['User-Agent'] = 'Epicyon/' + __version__
|
|
|
|
if domain:
|
|
|
|
sessionHeaders['User-Agent'] += \
|
|
|
|
'; +' + httpPrefix + '://' + domain + '/'
|
|
|
|
if not session:
|
|
|
|
print('WARN: urlExists failed, no session specified')
|
|
|
|
return True
|
|
|
|
try:
|
|
|
|
result = session.get(url, headers=sessionHeaders,
|
|
|
|
params=sessionParams,
|
|
|
|
timeout=timeoutSec)
|
2021-02-14 15:22:03 +00:00
|
|
|
if result:
|
2021-02-14 13:43:05 +00:00
|
|
|
if result.status_code == 200 or \
|
|
|
|
result.status_code == 304:
|
|
|
|
return True
|
2021-02-14 14:05:51 +00:00
|
|
|
else:
|
|
|
|
print('urlExists for ' + url + ' returned ' +
|
|
|
|
str(result.status_code))
|
2021-02-14 13:43:05 +00:00
|
|
|
except BaseException:
|
2021-10-29 18:48:15 +00:00
|
|
|
print('EX: urlExists GET failed ' + str(url))
|
2021-02-14 13:43:05 +00:00
|
|
|
pass
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2021-08-31 09:10:49 +00:00
|
|
|
def _getJsonRequest(session, url: str, domainFull: str, sessionHeaders: {},
|
|
|
|
sessionParams: {}, timeoutSec: int,
|
2021-12-23 20:59:36 +00:00
|
|
|
signingPrivateKeyPem: str, quiet: bool, debug: bool,
|
|
|
|
returnJson: bool) -> {}:
|
2021-08-31 09:10:49 +00:00
|
|
|
"""http GET for json
|
2021-08-30 22:21:14 +00:00
|
|
|
"""
|
2019-07-04 17:31:41 +00:00
|
|
|
try:
|
2021-03-02 18:02:29 +00:00
|
|
|
result = session.get(url, headers=sessionHeaders,
|
|
|
|
params=sessionParams, timeout=timeoutSec)
|
2021-06-18 11:53:25 +00:00
|
|
|
if result.status_code != 200:
|
|
|
|
if result.status_code == 401:
|
2021-09-08 12:38:22 +00:00
|
|
|
print("WARN: getJson " + url + ' rejected by secure mode')
|
2021-06-18 11:53:25 +00:00
|
|
|
elif result.status_code == 403:
|
|
|
|
print('WARN: getJson Forbidden url: ' + url)
|
|
|
|
elif result.status_code == 404:
|
|
|
|
print('WARN: getJson Not Found url: ' + url)
|
2021-09-07 17:05:26 +00:00
|
|
|
elif result.status_code == 410:
|
|
|
|
print('WARN: getJson no longer available url: ' + url)
|
2021-06-18 11:54:49 +00:00
|
|
|
else:
|
2021-06-18 11:53:25 +00:00
|
|
|
print('WARN: getJson url: ' + url +
|
|
|
|
' failed with error code ' +
|
2021-08-02 20:43:53 +00:00
|
|
|
str(result.status_code) +
|
|
|
|
' headers: ' + str(sessionHeaders))
|
2021-12-23 20:59:36 +00:00
|
|
|
if returnJson:
|
|
|
|
return result.json()
|
|
|
|
return result.content
|
2020-06-23 21:39:19 +00:00
|
|
|
except requests.exceptions.RequestException as e:
|
2021-03-02 20:41:26 +00:00
|
|
|
sessionHeaders2 = sessionHeaders.copy()
|
|
|
|
if sessionHeaders2.get('Authorization'):
|
2021-03-02 20:44:00 +00:00
|
|
|
sessionHeaders2['Authorization'] = 'REDACTED'
|
2021-03-14 20:55:37 +00:00
|
|
|
if debug and not quiet:
|
2021-06-18 09:22:16 +00:00
|
|
|
print('ERROR: getJson failed, url: ' + str(url) + ', ' +
|
|
|
|
'headers: ' + str(sessionHeaders2) + ', ' +
|
|
|
|
'params: ' + str(sessionParams) + ', ' + str(e))
|
2020-06-23 13:41:12 +00:00
|
|
|
except ValueError as e:
|
2021-03-02 20:41:26 +00:00
|
|
|
sessionHeaders2 = sessionHeaders.copy()
|
|
|
|
if sessionHeaders2.get('Authorization'):
|
2021-03-02 20:44:00 +00:00
|
|
|
sessionHeaders2['Authorization'] = 'REDACTED'
|
2021-03-14 20:55:37 +00:00
|
|
|
if debug and not quiet:
|
2021-06-18 09:22:16 +00:00
|
|
|
print('ERROR: getJson failed, url: ' + str(url) + ', ' +
|
|
|
|
'headers: ' + str(sessionHeaders2) + ', ' +
|
|
|
|
'params: ' + str(sessionParams) + ', ' + str(e))
|
2020-06-23 13:41:12 +00:00
|
|
|
except SocketError as e:
|
2021-03-10 15:47:12 +00:00
|
|
|
if not quiet:
|
|
|
|
if e.errno == errno.ECONNRESET:
|
|
|
|
print('WARN: getJson failed, ' +
|
2021-05-20 12:04:05 +00:00
|
|
|
'connection was reset during getJson ' + str(e))
|
2019-07-04 17:31:41 +00:00
|
|
|
return None
|
2019-06-28 19:36:39 +00:00
|
|
|
|
2020-04-04 11:00:19 +00:00
|
|
|
|
2021-08-31 09:10:49 +00:00
|
|
|
def _getJsonSigned(session, url: str, domainFull: str, sessionHeaders: {},
|
|
|
|
sessionParams: {}, timeoutSec: int,
|
2021-08-31 14:17:11 +00:00
|
|
|
signingPrivateKeyPem: str, quiet: bool, debug: bool) -> {}:
|
2021-08-31 20:20:58 +00:00
|
|
|
"""Authorized fetch - a signed version of GET
|
2021-08-31 09:10:49 +00:00
|
|
|
"""
|
|
|
|
if not domainFull:
|
|
|
|
if debug:
|
|
|
|
print('No sending domain for signed GET')
|
|
|
|
return None
|
|
|
|
if '://' not in url:
|
2021-09-20 16:51:53 +00:00
|
|
|
print('Invalid url: ' + url)
|
2021-08-31 09:10:49 +00:00
|
|
|
return None
|
|
|
|
httpPrefix = url.split('://')[0]
|
|
|
|
toDomainFull = url.split('://')[1]
|
|
|
|
if '/' in toDomainFull:
|
|
|
|
toDomainFull = toDomainFull.split('/')[0]
|
|
|
|
|
|
|
|
if ':' in domainFull:
|
|
|
|
domain = domainFull.split(':')[0]
|
|
|
|
port = domainFull.split(':')[1]
|
|
|
|
else:
|
|
|
|
domain = domainFull
|
|
|
|
if httpPrefix == 'https':
|
|
|
|
port = 443
|
|
|
|
else:
|
|
|
|
port = 80
|
|
|
|
|
|
|
|
if ':' in toDomainFull:
|
|
|
|
toDomain = toDomainFull.split(':')[0]
|
|
|
|
toPort = toDomainFull.split(':')[1]
|
|
|
|
else:
|
|
|
|
toDomain = toDomainFull
|
|
|
|
if httpPrefix == 'https':
|
|
|
|
toPort = 443
|
|
|
|
else:
|
|
|
|
toPort = 80
|
|
|
|
|
2021-09-08 11:25:11 +00:00
|
|
|
if debug:
|
|
|
|
print('Signed GET domain: ' + domain + ' ' + str(port))
|
|
|
|
print('Signed GET toDomain: ' + toDomain + ' ' + str(toPort))
|
|
|
|
print('Signed GET url: ' + url)
|
|
|
|
print('Signed GET httpPrefix: ' + httpPrefix)
|
2021-08-31 20:20:58 +00:00
|
|
|
messageStr = ''
|
2021-08-31 20:53:53 +00:00
|
|
|
withDigest = False
|
2021-09-01 18:46:28 +00:00
|
|
|
if toDomainFull + '/' in url:
|
|
|
|
path = '/' + url.split(toDomainFull + '/')[1]
|
|
|
|
else:
|
|
|
|
path = '/actor'
|
2021-09-14 22:03:26 +00:00
|
|
|
contentType = 'application/activity+json'
|
|
|
|
if sessionHeaders.get('Accept'):
|
|
|
|
contentType = sessionHeaders['Accept']
|
2021-08-31 09:10:49 +00:00
|
|
|
signatureHeaderJson = \
|
2021-09-14 21:22:49 +00:00
|
|
|
createSignedHeader(None, signingPrivateKeyPem, 'actor', domain, port,
|
2021-09-01 18:46:28 +00:00
|
|
|
toDomain, toPort, path, httpPrefix, withDigest,
|
2021-09-14 22:03:26 +00:00
|
|
|
messageStr, contentType)
|
2021-09-08 11:25:11 +00:00
|
|
|
if debug:
|
|
|
|
print('Signed GET signatureHeaderJson ' + str(signatureHeaderJson))
|
2021-09-14 21:48:34 +00:00
|
|
|
# update the session headers from the signature headers
|
2021-09-14 21:22:49 +00:00
|
|
|
sessionHeaders['Host'] = signatureHeaderJson['host']
|
|
|
|
sessionHeaders['Date'] = signatureHeaderJson['date']
|
|
|
|
sessionHeaders['Accept'] = signatureHeaderJson['accept']
|
|
|
|
sessionHeaders['Signature'] = signatureHeaderJson['signature']
|
2021-09-14 22:13:27 +00:00
|
|
|
sessionHeaders['Content-Length'] = '0'
|
2021-11-09 20:11:56 +00:00
|
|
|
if debug:
|
|
|
|
print('Signed GET sessionHeaders ' + str(sessionHeaders))
|
2021-08-31 09:10:49 +00:00
|
|
|
|
2021-12-23 20:59:36 +00:00
|
|
|
returnJson = True
|
|
|
|
if 'json' not in contentType:
|
|
|
|
returnJson = False
|
2021-08-31 09:10:49 +00:00
|
|
|
return _getJsonRequest(session, url, domainFull, sessionHeaders,
|
2021-12-23 20:59:36 +00:00
|
|
|
sessionParams, timeoutSec, None, quiet,
|
|
|
|
debug, returnJson)
|
2021-08-31 09:10:49 +00:00
|
|
|
|
|
|
|
|
2021-08-31 14:17:11 +00:00
|
|
|
def getJson(signingPrivateKeyPem: str,
|
|
|
|
session, url: str, headers: {}, params: {}, debug: bool,
|
2021-08-31 09:10:49 +00:00
|
|
|
version: str = '1.2.0', httpPrefix: str = 'https',
|
|
|
|
domain: str = 'testdomain',
|
|
|
|
timeoutSec: int = 20, quiet: bool = False) -> {}:
|
|
|
|
if not isinstance(url, str):
|
|
|
|
if debug and not quiet:
|
|
|
|
print('url: ' + str(url))
|
|
|
|
print('ERROR: getJson failed, url should be a string')
|
|
|
|
return None
|
|
|
|
sessionParams = {}
|
|
|
|
sessionHeaders = {}
|
|
|
|
if headers:
|
|
|
|
sessionHeaders = headers
|
|
|
|
if params:
|
|
|
|
sessionParams = params
|
|
|
|
sessionHeaders['User-Agent'] = 'Epicyon/' + version
|
|
|
|
if domain:
|
|
|
|
sessionHeaders['User-Agent'] += \
|
|
|
|
'; +' + httpPrefix + '://' + domain + '/'
|
|
|
|
if not session:
|
|
|
|
if not quiet:
|
|
|
|
print('WARN: getJson failed, no session specified for getJson')
|
|
|
|
return None
|
|
|
|
|
|
|
|
if debug:
|
|
|
|
HTTPConnection.debuglevel = 1
|
|
|
|
|
2021-09-08 11:25:11 +00:00
|
|
|
if signingPrivateKeyPem:
|
|
|
|
return _getJsonSigned(session, url, domain,
|
|
|
|
sessionHeaders, sessionParams,
|
|
|
|
timeoutSec, signingPrivateKeyPem,
|
|
|
|
quiet, debug)
|
|
|
|
else:
|
|
|
|
return _getJsonRequest(session, url, domain, sessionHeaders,
|
|
|
|
sessionParams, timeoutSec,
|
2021-12-23 20:59:36 +00:00
|
|
|
None, quiet, debug, True)
|
|
|
|
|
|
|
|
|
|
|
|
def downloadHtml(signingPrivateKeyPem: str,
|
|
|
|
session, url: str, headers: {}, params: {}, debug: bool,
|
|
|
|
version: str = '1.2.0', httpPrefix: str = 'https',
|
|
|
|
domain: str = 'testdomain',
|
|
|
|
timeoutSec: int = 20, quiet: bool = False) -> {}:
|
|
|
|
if not isinstance(url, str):
|
|
|
|
if debug and not quiet:
|
|
|
|
print('url: ' + str(url))
|
|
|
|
print('ERROR: downloadHtml failed, url should be a string')
|
|
|
|
return None
|
|
|
|
sessionParams = {}
|
|
|
|
sessionHeaders = {}
|
|
|
|
if headers:
|
|
|
|
sessionHeaders = headers
|
|
|
|
if params:
|
|
|
|
sessionParams = params
|
|
|
|
sessionHeaders['Accept'] = 'text/html'
|
|
|
|
sessionHeaders['User-Agent'] = 'Epicyon/' + version
|
|
|
|
if domain:
|
|
|
|
sessionHeaders['User-Agent'] += \
|
|
|
|
'; +' + httpPrefix + '://' + domain + '/'
|
|
|
|
if not session:
|
|
|
|
if not quiet:
|
|
|
|
print('WARN: downloadHtml failed, ' +
|
|
|
|
'no session specified for downloadHtml')
|
|
|
|
return None
|
|
|
|
|
|
|
|
if debug:
|
|
|
|
HTTPConnection.debuglevel = 1
|
|
|
|
|
|
|
|
if signingPrivateKeyPem:
|
|
|
|
return _getJsonSigned(session, url, domain,
|
|
|
|
sessionHeaders, sessionParams,
|
|
|
|
timeoutSec, signingPrivateKeyPem,
|
|
|
|
quiet, debug)
|
|
|
|
else:
|
|
|
|
return _getJsonRequest(session, url, domain, sessionHeaders,
|
|
|
|
sessionParams, timeoutSec,
|
|
|
|
None, quiet, debug, False)
|
2021-08-31 09:10:49 +00:00
|
|
|
|
|
|
|
|
2021-06-20 13:39:53 +00:00
|
|
|
def postJson(httpPrefix: str, domainFull: str,
|
|
|
|
session, postJsonObject: {}, federationList: [],
|
2021-06-20 11:28:35 +00:00
|
|
|
inboxUrl: str, headers: {}, timeoutSec: int = 60,
|
|
|
|
quiet: bool = False) -> str:
|
2019-06-28 19:36:39 +00:00
|
|
|
"""Post a json message to the inbox of another person
|
|
|
|
"""
|
2020-09-27 19:27:24 +00:00
|
|
|
# check that we are posting to a permitted domain
|
|
|
|
if not urlPermitted(inboxUrl, federationList):
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
|
|
|
print('postJson: ' + inboxUrl + ' not permitted')
|
2020-09-27 19:27:24 +00:00
|
|
|
return None
|
2019-06-28 20:22:36 +00:00
|
|
|
|
2021-06-20 13:39:53 +00:00
|
|
|
sessionHeaders = headers
|
|
|
|
sessionHeaders['User-Agent'] = 'Epicyon/' + __version__
|
|
|
|
sessionHeaders['User-Agent'] += \
|
|
|
|
'; +' + httpPrefix + '://' + domainFull + '/'
|
|
|
|
|
2020-06-08 18:05:36 +00:00
|
|
|
try:
|
|
|
|
postResult = \
|
|
|
|
session.post(url=inboxUrl,
|
|
|
|
data=json.dumps(postJsonObject),
|
2021-03-10 18:41:13 +00:00
|
|
|
headers=headers, timeout=timeoutSec)
|
2021-03-21 13:17:59 +00:00
|
|
|
except requests.Timeout as e:
|
|
|
|
if not quiet:
|
|
|
|
print('ERROR: postJson timeout ' + inboxUrl + ' ' +
|
|
|
|
json.dumps(postJsonObject) + ' ' + str(headers))
|
|
|
|
print(e)
|
|
|
|
return ''
|
2020-06-23 21:39:19 +00:00
|
|
|
except requests.exceptions.RequestException as e:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
|
|
|
print('ERROR: postJson requests failed ' + inboxUrl + ' ' +
|
2021-05-20 12:04:05 +00:00
|
|
|
json.dumps(postJsonObject) + ' ' + str(headers) +
|
|
|
|
' ' + str(e))
|
2020-06-23 13:41:12 +00:00
|
|
|
return None
|
2020-06-23 13:28:41 +00:00
|
|
|
except SocketError as e:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet and e.errno == errno.ECONNRESET:
|
2020-06-23 13:28:41 +00:00
|
|
|
print('WARN: connection was reset during postJson')
|
2020-06-08 18:05:36 +00:00
|
|
|
return None
|
2020-06-23 21:39:19 +00:00
|
|
|
except ValueError as e:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
|
|
|
print('ERROR: postJson failed ' + inboxUrl + ' ' +
|
2021-05-20 12:04:05 +00:00
|
|
|
json.dumps(postJsonObject) + ' ' + str(headers) +
|
|
|
|
' ' + str(e))
|
2020-06-23 21:39:19 +00:00
|
|
|
return None
|
2019-10-26 12:01:22 +00:00
|
|
|
if postResult:
|
|
|
|
return postResult.text
|
|
|
|
return None
|
2019-07-16 14:23:06 +00:00
|
|
|
|
2020-04-04 11:00:19 +00:00
|
|
|
|
|
|
|
def postJsonString(session, postJsonStr: str,
|
|
|
|
federationList: [],
|
|
|
|
inboxUrl: str,
|
|
|
|
headers: {},
|
2021-03-10 19:11:14 +00:00
|
|
|
debug: bool,
|
2021-06-20 11:28:35 +00:00
|
|
|
timeoutSec: int = 30,
|
2021-10-18 10:20:57 +00:00
|
|
|
quiet: bool = False) -> (bool, bool, int):
|
2019-08-17 10:15:01 +00:00
|
|
|
"""Post a json message string to the inbox of another person
|
2019-10-23 18:44:03 +00:00
|
|
|
The second boolean returned is true if the send is unauthorized
|
2019-08-17 10:15:01 +00:00
|
|
|
NOTE: Here we post a string rather than the original json so that
|
|
|
|
conversions between string and json format don't invalidate
|
|
|
|
the message body digest of http signatures
|
|
|
|
"""
|
2020-06-08 18:05:36 +00:00
|
|
|
try:
|
|
|
|
postResult = \
|
2021-03-10 19:11:14 +00:00
|
|
|
session.post(url=inboxUrl, data=postJsonStr,
|
|
|
|
headers=headers, timeout=timeoutSec)
|
2020-06-23 21:39:19 +00:00
|
|
|
except requests.exceptions.RequestException as e:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: error during postJsonString requests ' + str(e))
|
2021-10-18 10:20:57 +00:00
|
|
|
return None, None, 0
|
2020-06-23 13:28:41 +00:00
|
|
|
except SocketError as e:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet and e.errno == errno.ECONNRESET:
|
2020-06-23 13:28:41 +00:00
|
|
|
print('WARN: connection was reset during postJsonString')
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
|
|
|
print('ERROR: postJsonString failed ' + inboxUrl + ' ' +
|
|
|
|
postJsonStr + ' ' + str(headers))
|
2021-10-18 10:20:57 +00:00
|
|
|
return None, None, 0
|
2020-06-23 21:39:19 +00:00
|
|
|
except ValueError as e:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: error during postJsonString ' + str(e))
|
2021-10-18 10:20:57 +00:00
|
|
|
return None, None, 0
|
2020-04-04 11:00:19 +00:00
|
|
|
if postResult.status_code < 200 or postResult.status_code > 202:
|
|
|
|
if postResult.status_code >= 400 and \
|
|
|
|
postResult.status_code <= 405 and \
|
|
|
|
postResult.status_code != 404:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
|
|
|
print('WARN: Post to ' + inboxUrl +
|
|
|
|
' is unauthorized. Code ' +
|
|
|
|
str(postResult.status_code))
|
2021-10-18 10:20:57 +00:00
|
|
|
return False, True, postResult.status_code
|
2019-08-26 12:46:08 +00:00
|
|
|
else:
|
2021-03-10 19:24:52 +00:00
|
|
|
if not quiet:
|
|
|
|
print('WARN: Failed to post to ' + inboxUrl +
|
2021-10-18 09:58:41 +00:00
|
|
|
' with headers ' + str(headers) +
|
|
|
|
' status code ' + str(postResult.status_code))
|
2021-10-18 10:20:57 +00:00
|
|
|
return False, False, postResult.status_code
|
|
|
|
return True, False, 0
|
2020-04-04 11:00:19 +00:00
|
|
|
|
2019-08-17 10:15:01 +00:00
|
|
|
|
2020-04-04 11:00:19 +00:00
|
|
|
def postImage(session, attachImageFilename: str, federationList: [],
|
2020-09-27 19:27:24 +00:00
|
|
|
inboxUrl: str, headers: {}) -> str:
|
2019-07-16 14:23:06 +00:00
|
|
|
"""Post an image to the inbox of another person or outbox via c2s
|
|
|
|
"""
|
2020-09-27 19:27:24 +00:00
|
|
|
# check that we are posting to a permitted domain
|
|
|
|
if not urlPermitted(inboxUrl, federationList):
|
|
|
|
print('postJson: ' + inboxUrl + ' not permitted')
|
|
|
|
return None
|
2019-07-16 14:23:06 +00:00
|
|
|
|
2021-07-04 17:55:29 +00:00
|
|
|
if not isImageFile(attachImageFilename):
|
2021-07-04 18:01:31 +00:00
|
|
|
print('Image must be png, jpg, webp, avif, gif or svg')
|
2019-07-16 14:23:06 +00:00
|
|
|
return None
|
|
|
|
if not os.path.isfile(attachImageFilename):
|
2020-04-04 11:00:19 +00:00
|
|
|
print('Image not found: ' + attachImageFilename)
|
2019-07-16 14:23:06 +00:00
|
|
|
return None
|
2020-04-04 11:00:19 +00:00
|
|
|
contentType = 'image/jpeg'
|
2019-07-16 14:23:06 +00:00
|
|
|
if attachImageFilename.endswith('.png'):
|
2020-04-04 11:00:19 +00:00
|
|
|
contentType = 'image/png'
|
2021-07-04 17:55:29 +00:00
|
|
|
elif attachImageFilename.endswith('.gif'):
|
2020-04-04 11:00:19 +00:00
|
|
|
contentType = 'image/gif'
|
2021-07-04 17:55:29 +00:00
|
|
|
elif attachImageFilename.endswith('.webp'):
|
|
|
|
contentType = 'image/webp'
|
|
|
|
elif attachImageFilename.endswith('.avif'):
|
|
|
|
contentType = 'image/avif'
|
|
|
|
elif attachImageFilename.endswith('.svg'):
|
2021-01-11 22:27:57 +00:00
|
|
|
contentType = 'image/svg+xml'
|
2020-04-04 11:00:19 +00:00
|
|
|
headers['Content-type'] = contentType
|
2019-07-16 14:23:06 +00:00
|
|
|
|
|
|
|
with open(attachImageFilename, 'rb') as avFile:
|
2020-04-04 11:00:19 +00:00
|
|
|
mediaBinary = avFile.read()
|
2020-06-08 18:05:36 +00:00
|
|
|
try:
|
|
|
|
postResult = session.post(url=inboxUrl, data=mediaBinary,
|
|
|
|
headers=headers)
|
2020-06-23 21:39:19 +00:00
|
|
|
except requests.exceptions.RequestException as e:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: error during postImage requests ' + str(e))
|
2020-06-23 13:41:12 +00:00
|
|
|
return None
|
2020-06-23 13:28:41 +00:00
|
|
|
except SocketError as e:
|
|
|
|
if e.errno == errno.ECONNRESET:
|
|
|
|
print('WARN: connection was reset during postImage')
|
2020-06-08 18:05:36 +00:00
|
|
|
print('ERROR: postImage failed ' + inboxUrl + ' ' +
|
2021-05-20 12:04:05 +00:00
|
|
|
str(headers) + ' ' + str(e))
|
2020-06-08 18:05:36 +00:00
|
|
|
return None
|
2020-06-23 21:39:19 +00:00
|
|
|
except ValueError as e:
|
2021-05-20 12:04:05 +00:00
|
|
|
print('WARN: error during postImage ' + str(e))
|
2020-06-23 21:39:19 +00:00
|
|
|
return None
|
2019-10-26 12:01:22 +00:00
|
|
|
if postResult:
|
|
|
|
return postResult.text
|
2019-07-16 14:23:06 +00:00
|
|
|
return None
|
2021-11-01 17:12:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def downloadImage(session, baseDir: str, url: str,
|
|
|
|
imageFilename: str, debug: bool,
|
|
|
|
force: bool = False) -> bool:
|
2021-12-17 10:04:18 +00:00
|
|
|
"""Downloads an image with an expected mime type
|
2021-11-01 17:12:17 +00:00
|
|
|
"""
|
|
|
|
if not url:
|
|
|
|
return None
|
|
|
|
|
|
|
|
# try different image types
|
|
|
|
imageFormats = {
|
|
|
|
'png': 'png',
|
|
|
|
'jpg': 'jpeg',
|
|
|
|
'jpeg': 'jpeg',
|
|
|
|
'gif': 'gif',
|
|
|
|
'svg': 'svg+xml',
|
|
|
|
'webp': 'webp',
|
2021-12-17 10:04:18 +00:00
|
|
|
'avif': 'avif',
|
|
|
|
'ico': 'x-icon'
|
2021-11-01 17:12:17 +00:00
|
|
|
}
|
|
|
|
sessionHeaders = None
|
|
|
|
for imFormat, mimeType in imageFormats.items():
|
|
|
|
if url.endswith('.' + imFormat) or \
|
|
|
|
'.' + imFormat + '?' in url:
|
|
|
|
sessionHeaders = {
|
|
|
|
'Accept': 'image/' + mimeType
|
|
|
|
}
|
2021-11-01 20:27:29 +00:00
|
|
|
break
|
2021-11-01 17:12:17 +00:00
|
|
|
|
|
|
|
if not sessionHeaders:
|
2021-11-01 20:27:29 +00:00
|
|
|
if debug:
|
|
|
|
print('downloadImage: no session headers')
|
2021-11-01 17:12:17 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
if not os.path.isfile(imageFilename) or force:
|
|
|
|
try:
|
|
|
|
if debug:
|
|
|
|
print('Downloading image url: ' + url)
|
|
|
|
result = session.get(url,
|
|
|
|
headers=sessionHeaders,
|
|
|
|
params=None)
|
|
|
|
if result.status_code < 200 or \
|
|
|
|
result.status_code > 202:
|
|
|
|
if debug:
|
|
|
|
print('Image download failed with status ' +
|
|
|
|
str(result.status_code))
|
|
|
|
# remove partial download
|
|
|
|
if os.path.isfile(imageFilename):
|
|
|
|
try:
|
|
|
|
os.remove(imageFilename)
|
2021-11-25 18:42:38 +00:00
|
|
|
except OSError:
|
2021-11-01 17:12:17 +00:00
|
|
|
print('EX: downloadImage unable to delete ' +
|
|
|
|
imageFilename)
|
|
|
|
else:
|
|
|
|
with open(imageFilename, 'wb') as f:
|
|
|
|
f.write(result.content)
|
|
|
|
if debug:
|
|
|
|
print('Image downloaded from ' + url)
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
|
|
print('EX: Failed to download image: ' +
|
|
|
|
str(url) + ' ' + str(e))
|
|
|
|
return False
|
2021-12-16 20:57:30 +00:00
|
|
|
|
|
|
|
|
2021-12-17 10:04:18 +00:00
|
|
|
def downloadImageAnyMimeType(session, url: str, timeoutSec: int, debug: bool):
|
|
|
|
"""http GET for an image with any mime type
|
2021-12-16 20:57:30 +00:00
|
|
|
"""
|
2021-12-17 10:15:05 +00:00
|
|
|
mimeType = None
|
2021-12-17 09:48:45 +00:00
|
|
|
contentType = None
|
2021-12-17 09:55:19 +00:00
|
|
|
result = None
|
2021-12-17 12:55:30 +00:00
|
|
|
sessionHeaders = {
|
2021-12-17 12:58:58 +00:00
|
|
|
'Accept': 'image/x-icon, image/png, image/webp, image/jpeg, image/gif'
|
2021-12-17 12:55:30 +00:00
|
|
|
}
|
2021-12-16 20:57:30 +00:00
|
|
|
try:
|
2021-12-17 12:55:30 +00:00
|
|
|
result = session.get(url, headers=sessionHeaders, timeout=timeoutSec)
|
2021-12-16 20:57:30 +00:00
|
|
|
except requests.exceptions.RequestException as e:
|
2021-12-17 10:12:11 +00:00
|
|
|
print('ERROR: downloadImageAnyMimeType failed: ' +
|
|
|
|
str(url) + ', ' + str(e))
|
|
|
|
return None, None
|
2021-12-16 20:57:30 +00:00
|
|
|
except ValueError as e:
|
2021-12-17 10:12:11 +00:00
|
|
|
print('ERROR: downloadImageAnyMimeType failed: ' +
|
|
|
|
str(url) + ', ' + str(e))
|
|
|
|
return None, None
|
2021-12-16 20:57:30 +00:00
|
|
|
except SocketError as e:
|
|
|
|
if e.errno == errno.ECONNRESET:
|
2021-12-17 10:12:11 +00:00
|
|
|
print('WARN: downloadImageAnyMimeType failed, ' +
|
2021-12-16 20:57:30 +00:00
|
|
|
'connection was reset ' + str(e))
|
2021-12-17 10:12:11 +00:00
|
|
|
return None, None
|
2021-12-17 09:55:19 +00:00
|
|
|
|
|
|
|
if not result:
|
|
|
|
return None, None
|
|
|
|
|
|
|
|
if result.status_code != 200:
|
2021-12-17 10:12:11 +00:00
|
|
|
print('WARN: downloadImageAnyMimeType: ' + url +
|
2021-12-17 09:55:19 +00:00
|
|
|
' failed with error code ' + str(result.status_code))
|
2021-12-17 10:12:11 +00:00
|
|
|
return None, None
|
2021-12-17 09:55:19 +00:00
|
|
|
|
|
|
|
if result.headers.get('content-type'):
|
|
|
|
contentType = result.headers['content-type']
|
|
|
|
elif result.headers.get('Content-type'):
|
|
|
|
contentType = result.headers['Content-type']
|
|
|
|
elif result.headers.get('Content-Type'):
|
|
|
|
contentType = result.headers['Content-Type']
|
|
|
|
|
|
|
|
if not contentType:
|
|
|
|
return None, None
|
|
|
|
|
2021-12-17 10:07:49 +00:00
|
|
|
imageFormats = {
|
|
|
|
'ico': 'x-icon',
|
|
|
|
'png': 'png',
|
|
|
|
'jpg': 'jpeg',
|
|
|
|
'jpeg': 'jpeg',
|
|
|
|
'gif': 'gif',
|
|
|
|
'svg': 'svg+xml',
|
|
|
|
'webp': 'webp',
|
|
|
|
'avif': 'avif'
|
|
|
|
}
|
|
|
|
for imFormat, mType in imageFormats.items():
|
|
|
|
if 'image/' + mType in contentType:
|
|
|
|
mimeType = 'image/' + mType
|
2021-12-17 09:55:19 +00:00
|
|
|
return result.content, mimeType
|