Uploading images via c2s

master
Bob Mottram 2019-07-16 15:23:06 +01:00
parent 92fb124713
commit c99828c264
5 changed files with 115 additions and 24 deletions

View File

@ -34,9 +34,6 @@ from threads import threadWithTrace
import os import os
import sys import sys
# Avoid giant messages
maxMessageLength=5000
# maximum number of posts to list in outbox feed # maximum number of posts to list in outbox feed
maxPostsInFeed=20 maxPostsInFeed=20
@ -573,14 +570,6 @@ class PubServer(BaseHTTPRequestHandler):
self.server.POSTbusy=False self.server.POSTbusy=False
return return
# refuse to receive non-json content
if self.headers['Content-type'] != 'application/json':
print("POST is not json: "+self.headers['Content-type'])
self.send_response(400)
self.end_headers()
self.server.POSTbusy=False
return
# remove any trailing slashes from the path # remove any trailing slashes from the path
self.path=self.path.replace('/outbox/','/outbox').replace('/inbox/','/inbox').replace('/sharedInbox/','/sharedInbox') self.path=self.path.replace('/outbox/','/outbox').replace('/inbox/','/inbox').replace('/sharedInbox/','/sharedInbox')
@ -615,7 +604,61 @@ class PubServer(BaseHTTPRequestHandler):
length = int(self.headers['Content-length']) length = int(self.headers['Content-length'])
if self.server.debug: if self.server.debug:
print('DEBUG: content-length: '+str(length)) print('DEBUG: content-length: '+str(length))
if length>maxMessageLength: if not self.headers['Content-type'].startswith('image/'):
if length>self.server.maxMessageLength:
self.send_response(400)
self.end_headers()
self.server.POSTbusy=False
return
else:
if length>self.server.maxImageSize:
self.send_response(400)
self.end_headers()
self.server.POSTbusy=False
return
# receive images to the outbox
if self.headers['Content-type'].startswith('image/') and \
'/users/' in self.path:
if not self.outboxAuthenticated:
if self.server.debug:
print('DEBUG: unathenticated attempt to post image to outbox')
self.send_response(403)
self.end_headers()
self.server.POSTbusy=False
return
pathUsersSection=self.path.split('/users/')[1]
if '/' not in pathUsersSection:
self.send_response(404)
self.end_headers()
self.server.POSTbusy=False
return
self.postFromNickname=pathUsersSection.split('/')[0]
accountsDir=self.server.baseDir+'/accounts/'+self.postFromNickname+'@'+self.server.domain
if not os.path.isdir(accountsDir):
self.send_response(404)
self.end_headers()
self.server.POSTbusy=False
return
mediaBytes=self.rfile.read(length)
mediaFilenameBase=accountsDir+'/upload'
mediaFilename=mediaFilenameBase+'.png'
if self.headers['Content-type'].endswith('jpeg'):
mediaFilename=mediaFilenameBase+'.jpg'
if self.headers['Content-type'].endswith('gif'):
mediaFilename=mediaFilenameBase+'.gif'
with open(mediaFilename, 'wb') as avFile:
avFile.write(mediaBytes)
if self.server.debug:
print('DEBUG: image saved to '+mediaFilename)
self.send_response(201)
self.end_headers()
self.server.POSTbusy=False
return
# refuse to receive non-json content
if self.headers['Content-type'] != 'application/json':
print("POST is not json: "+self.headers['Content-type'])
self.send_response(400) self.send_response(400)
self.end_headers() self.end_headers()
self.server.POSTbusy=False self.server.POSTbusy=False
@ -625,7 +668,7 @@ class PubServer(BaseHTTPRequestHandler):
print('DEBUG: Reading message') print('DEBUG: Reading message')
messageBytes=self.rfile.read(length) messageBytes=self.rfile.read(length)
messageJson = json.loads(messageBytes) messageJson=json.loads(messageBytes)
# https://www.w3.org/TR/activitypub/#object-without-create # https://www.w3.org/TR/activitypub/#object-without-create
if self.outboxAuthenticated: if self.outboxAuthenticated:
@ -767,6 +810,8 @@ def runDaemon(clientToServer: bool,baseDir: str,domain: str, \
httpd.postLog=[] httpd.postLog=[]
httpd.maxQueueLength=16 httpd.maxQueueLength=16
httpd.ocapAlways=ocapAlways httpd.ocapAlways=ocapAlways
httpd.maxMessageLength=5000
httpd.maxImageSize=10*1024*1024
httpd.acceptedCaps=["inbox:write","objects:read"] httpd.acceptedCaps=["inbox:write","objects:read"]
if noreply: if noreply:
httpd.acceptedCaps.append('inbox:noreply') httpd.acceptedCaps.append('inbox:noreply')

View File

@ -214,8 +214,8 @@ if args.tests:
if args.testsnetwork: if args.testsnetwork:
print('Network Tests') print('Network Tests')
testPostMessageBetweenServers() #testPostMessageBetweenServers()
testFollowBetweenServers() #testFollowBetweenServers()
testClientToServer() testClientToServer()
sys.exit() sys.exit()

View File

@ -26,6 +26,7 @@ from random import randint
from session import createSession from session import createSession
from session import getJson from session import getJson
from session import postJson from session import postJson
from session import postImage
from webfinger import webfingerHandle from webfinger import webfingerHandle
from httpsig import createSignedHeader from httpsig import createSignedHeader
from utils import getStatusNumber from utils import getStatusNumber
@ -723,15 +724,26 @@ def sendPostViaServer(session,fromNickname: str,password: str, \
inReplyTo,inReplyToAtomUri,subject) inReplyTo,inReplyToAtomUri,subject)
authHeader=createBasicAuthHeader(fromNickname,password) authHeader=createBasicAuthHeader(fromNickname,password)
if attachImageFilename:
headers = {'host': fromDomain, \
'Authorization': authHeader}
postResult = \
postImage(session,attachImageFilename,[],inboxUrl,headers,"inbox:write")
#if not postResult:
# if debug:
# print('DEBUG: Failed to upload image')
# return 9
headers = {'host': fromDomain, \ headers = {'host': fromDomain, \
'Content-type': 'application/json', \ 'Content-type': 'application/json', \
'Authorization': authHeader} 'Authorization': authHeader}
postResult = \ postResult = \
postJson(session,postJsonObject,[],inboxUrl,headers,"inbox:write") postJson(session,postJsonObject,[],inboxUrl,headers,"inbox:write")
if not postResult: #if not postResult:
if debug: # if debug:
print('DEBUG: POST failed for c2s to '+inboxUrl) # print('DEBUG: POST failed for c2s to '+inboxUrl)
return 5 # return 5
if debug: if debug:
print('DEBUG: c2s POST success') print('DEBUG: c2s POST success')

View File

@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
import os
import requests import requests
from utils import urlPermitted from utils import urlPermitted
import json import json
@ -55,3 +56,36 @@ def postJson(session,postJsonObject: {},federationList: [],inboxUrl: str,headers
postResult = session.post(url = inboxUrl, data = json.dumps(postJsonObject), headers=headers) postResult = session.post(url = inboxUrl, data = json.dumps(postJsonObject), headers=headers)
return postResult.text return postResult.text
def postImage(session,attachImageFilename: str,federationList: [],inboxUrl: str,headers: {},capability: str) -> str:
"""Post an image to the inbox of another person or outbox via c2s
Supplying a capability, such as "inbox:write"
"""
# always allow capability requests
if not capability.startswith('cap'):
# check that we are posting to a permitted domain
if not urlPermitted(inboxUrl,federationList,capability):
print('postJson: '+inboxUrl+' not permitted')
return None
if not (attachImageFilename.endswith('.jpg') or \
attachImageFilename.endswith('.jpeg') or \
attachImageFilename.endswith('.png') or \
attachImageFilename.endswith('.gif')):
print('Image must be png, jpg, or gif')
return None
if not os.path.isfile(attachImageFilename):
print('Image not found: '+attachImageFilename)
return None
contentType='image/jpeg'
if attachImageFilename.endswith('.png'):
contentType='image/png'
if attachImageFilename.endswith('.gif'):
contentType='image/gif'
headers['Content-type']=contentType
with open(attachImageFilename, 'rb') as avFile:
mediaBinary = avFile.read()
postResult = session.post(url=inboxUrl, data=mediaBinary, headers=headers)
return postResult.text
return None

View File

@ -1015,8 +1015,8 @@ def testClientToServer():
sessionAlice = createSession(aliceDomain,alicePort,useTor) sessionAlice = createSession(aliceDomain,alicePort,useTor)
followersOnly=False followersOnly=False
attachImageFilename=None attachedImageFilename=baseDir+'/img/logo.png'
imageDescription=None attachedImageDescription='Logo'
useBlurhash=False useBlurhash=False
cachedWebfingers={} cachedWebfingers={}
personCache={} personCache={}
@ -1030,7 +1030,7 @@ def testClientToServer():
aliceDomain,alicePort, \ aliceDomain,alicePort, \
'bob',bobDomain,bobPort,None, \ 'bob',bobDomain,bobPort,None, \
httpPrefix,'Sent from my ActivityPub client',followersOnly, \ httpPrefix,'Sent from my ActivityPub client',followersOnly, \
attachImageFilename,imageDescription,useBlurhash, \ attachedImageFilename,attachedImageDescription,useBlurhash, \
cachedWebfingers,personCache, \ cachedWebfingers,personCache, \
True,None,None,None) True,None,None,None)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
@ -1064,7 +1064,7 @@ def testClientToServer():
assert thrBob.isAlive()==False assert thrBob.isAlive()==False
os.chdir(baseDir) os.chdir(baseDir)
shutil.rmtree(aliceDir) #shutil.rmtree(aliceDir)
shutil.rmtree(bobDir) shutil.rmtree(bobDir)
def runAllTests(): def runAllTests():