| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | __filename__ = "delete.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2019-08-29 13:35:29 +00:00
										 |  |  | __version__ = "1.0.0" | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							|  |  |  | __email__ = "bob@freedombone.net" | 
					
						
							|  |  |  | __status__ = "Production" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import json | 
					
						
							|  |  |  | import commentjson | 
					
						
							|  |  |  | from utils import getStatusNumber | 
					
						
							|  |  |  | from utils import createOutboxDir | 
					
						
							|  |  |  | from utils import urlPermitted | 
					
						
							|  |  |  | from utils import getNicknameFromActor | 
					
						
							|  |  |  | from utils import getDomainFromActor | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  | from utils import locatePost | 
					
						
							|  |  |  | from utils import deletePost | 
					
						
							| 
									
										
										
										
											2019-08-12 18:02:29 +00:00
										 |  |  | from utils import removeModerationPostFromIndex | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | from posts import sendSignedJson | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  | from session import postJson | 
					
						
							|  |  |  | from webfinger import webfingerHandle | 
					
						
							|  |  |  | from auth import createBasicAuthHeader | 
					
						
							|  |  |  | from posts import getPersonBox | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def createDelete(session,baseDir: str,federationList: [], \ | 
					
						
							|  |  |  |                  nickname: str, domain: str, port: int, \ | 
					
						
							|  |  |  |                  toUrl: str, ccUrl: str, httpPrefix: str, \ | 
					
						
							|  |  |  |                  objectUrl: str,clientToServer: bool, \ | 
					
						
							|  |  |  |                  sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                  personCache: {},cachedWebfingers: {}, \ | 
					
						
							|  |  |  |                  debug: bool) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-12 09:49:12 +00:00
										 |  |  |     """Creates a delete message
 | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  |     Typically toUrl will be https://www.w3.org/ns/activitystreams#Public | 
					
						
							|  |  |  |     and ccUrl might be a specific person whose post is to be deleted | 
					
						
							|  |  |  |     objectUrl is typically the url of the message, corresponding to url | 
					
						
							|  |  |  |     or atomUri in createPostBase | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not urlPermitted(objectUrl,federationList,"inbox:write"): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							|  |  |  |         fullDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fullDomain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							|  |  |  |     newDeleteId= \ | 
					
						
							|  |  |  |         httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber | 
					
						
							|  |  |  |     newDelete = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |         "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  |         'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, | 
					
						
							|  |  |  |         'atomUri': httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber, | 
					
						
							|  |  |  |         'cc': [], | 
					
						
							|  |  |  |         'id': newDeleteId+'/activity', | 
					
						
							|  |  |  |         'object': objectUrl, | 
					
						
							|  |  |  |         'published': published, | 
					
						
							|  |  |  |         'to': [toUrl], | 
					
						
							|  |  |  |         'type': 'Delete' | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ccUrl: | 
					
						
							|  |  |  |         if len(ccUrl)>0: | 
					
						
							|  |  |  |             newDelete['cc']=[ccUrl] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     deleteNickname=None | 
					
						
							|  |  |  |     deleteDomain=None | 
					
						
							|  |  |  |     deletePort=None | 
					
						
							|  |  |  |     if '/users/' in objectUrl: | 
					
						
							|  |  |  |         deleteNickname=getNicknameFromActor(objectUrl) | 
					
						
							|  |  |  |         deleteDomain,deletePort=getDomainFromActor(objectUrl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if deleteNickname and deleteDomain: | 
					
						
							|  |  |  |         sendSignedJson(newDelete,session,baseDir, \ | 
					
						
							|  |  |  |                        nickname,domain,port, \ | 
					
						
							|  |  |  |                        deleteNickname,deleteDomain,deletePort, \ | 
					
						
							|  |  |  |                        'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							|  |  |  |                        httpPrefix,True,clientToServer,federationList, \ | 
					
						
							|  |  |  |                        sendThreads,postLog,cachedWebfingers,personCache,debug) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |     return newDelete | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | def sendDeleteViaServer(baseDir: str,session, \ | 
					
						
							|  |  |  |                         fromNickname: str,password: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |                         fromDomain: str,fromPort: int, \ | 
					
						
							|  |  |  |                         httpPrefix: str,deleteObjectUrl: str, \ | 
					
						
							|  |  |  |                         cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                         debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |     """Creates a delete request message via c2s
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendDeleteViaServer') | 
					
						
							|  |  |  |         return 6 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fromDomainFull=fromDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if fromPort: | 
					
						
							|  |  |  |         if fromPort!=80 and fromPort!=443: | 
					
						
							|  |  |  |             if ':' not in fromDomain: | 
					
						
							|  |  |  |                 fromDomainFull=fromDomain+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     toUrl = 'https://www.w3.org/ns/activitystreams#Public' | 
					
						
							|  |  |  |     ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     newDeleteJson = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |         "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |         'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, | 
					
						
							|  |  |  |         'cc': [ccUrl], | 
					
						
							|  |  |  |         'object': deleteObjectUrl, | 
					
						
							|  |  |  |         'to': [toUrl], | 
					
						
							|  |  |  |         'type': 'Delete' | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # lookup the inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |     wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                                 fromDomain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: announce webfinger failed for '+handle) | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     postToBox='outbox' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # get the actor inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                      projectVersion,httpPrefix,fromDomain,postToBox) | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |                       | 
					
						
							|  |  |  |     if not inboxUrl: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No '+postToBox+' was found for '+handle) | 
					
						
							|  |  |  |         return 3 | 
					
						
							|  |  |  |     if not fromPersonId: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No actor was found for '+handle) | 
					
						
							|  |  |  |         return 4 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     authHeader=createBasicAuthHeader(fromNickname,password) | 
					
						
							|  |  |  |       | 
					
						
							|  |  |  |     headers = {'host': fromDomain, \ | 
					
						
							|  |  |  |                'Content-type': 'application/json', \ | 
					
						
							|  |  |  |                'Authorization': authHeader} | 
					
						
							|  |  |  |     postResult = \ | 
					
						
							|  |  |  |         postJson(session,newDeleteJson,[],inboxUrl,headers,"inbox:write") | 
					
						
							|  |  |  |     #if not postResult: | 
					
						
							|  |  |  |     #    if debug: | 
					
						
							|  |  |  |     #        print('DEBUG: POST announce failed for c2s to '+inboxUrl) | 
					
						
							|  |  |  |     #    return 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: c2s POST delete request success') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return newDeleteJson | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | def deletePublic(session,baseDir: str,federationList: [], \ | 
					
						
							|  |  |  |                  nickname: str, domain: str, port: int, httpPrefix: str, \ | 
					
						
							|  |  |  |                  objectUrl: str,clientToServer: bool, \ | 
					
						
							|  |  |  |                  sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                  personCache: {},cachedWebfingers: {}, \ | 
					
						
							|  |  |  |                  debug: bool) -> {}: | 
					
						
							|  |  |  |     """Makes a public delete activity
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     fromDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fromDomain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     toUrl = 'https://www.w3.org/ns/activitystreams#Public' | 
					
						
							|  |  |  |     ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers' | 
					
						
							|  |  |  |     return createDelete(session,baseDir,federationList, \ | 
					
						
							|  |  |  |                         nickname,domain,port, \ | 
					
						
							|  |  |  |                         toUrl,ccUrl,httpPrefix, \ | 
					
						
							|  |  |  |                         objectUrl,clientToServer, \ | 
					
						
							|  |  |  |                         sendThreads,postLog, \ | 
					
						
							|  |  |  |                         personCache,cachedWebfingers, \ | 
					
						
							|  |  |  |                         debug) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  | def deletePostPub(session,baseDir: str,federationList: [], \ | 
					
						
							|  |  |  |                   nickname: str, domain: str, port: int, httpPrefix: str, \ | 
					
						
							|  |  |  |                   deleteNickname: str, deleteDomain: str, \ | 
					
						
							|  |  |  |                   deletePort: int, deleteHttpsPrefix: str, \ | 
					
						
							|  |  |  |                   deleteStatusNumber: int,clientToServer: bool, \ | 
					
						
							|  |  |  |                   sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                   personCache: {},cachedWebfingers: {}, \ | 
					
						
							|  |  |  |                   debug: bool) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  |     """Deletes a given status post
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     deletedDomain=deleteDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if deletePort: | 
					
						
							|  |  |  |         if deletePort!=80 and deletePort!=443: | 
					
						
							|  |  |  |             if ':' not in deletedDomain: | 
					
						
							|  |  |  |                 deletedDomain=deletedDomain+':'+str(deletePort) | 
					
						
							| 
									
										
										
										
											2019-07-11 21:38:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     objectUrl = deleteHttpsPrefix + '://'+deletedDomain+'/users/'+ \ | 
					
						
							|  |  |  |         deleteNickname+'/statuses/'+str(deleteStatusNumber) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return deletePublic(session,baseDir,federationList, \ | 
					
						
							|  |  |  |                         nickname,domain,port,httpPrefix, \ | 
					
						
							|  |  |  |                         objectUrl,clientToServer, \ | 
					
						
							|  |  |  |                         sendThreads,postLog, \ | 
					
						
							|  |  |  |                         personCache,cachedWebfingers, \ | 
					
						
							|  |  |  |                         debug) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-17 18:05:07 +00:00
										 |  |  | def outboxDelete(baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                  nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-08-12 18:02:29 +00:00
										 |  |  |                  messageJson: {},debug: bool, | 
					
						
							|  |  |  |                  allowDeletion: bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-07-17 18:05:07 +00:00
										 |  |  |     """ When a delete request is received by the outbox from c2s
 | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     if not messageJson.get('type'): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: delete - no type') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson['type']=='Delete': | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: not a delete') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson.get('object'): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: no object in delete') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not isinstance(messageJson['object'], str): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: delete object is not string') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: c2s delete request arrived in outbox') | 
					
						
							| 
									
										
										
										
											2019-08-12 18:02:29 +00:00
										 |  |  |     deletePrefix=httpPrefix+'://'+domain | 
					
						
							|  |  |  |     if not allowDeletion and \ | 
					
						
							|  |  |  |        (not messageJson['object'].startswith(deletePrefix) or \ | 
					
						
							|  |  |  |         not messageJson['actor'].startswith(deletePrefix)): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: delete not permitted from other instances') | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |     messageId=messageJson['object'].replace('/activity','') | 
					
						
							|  |  |  |     if '/statuses/' not in messageId: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: c2s delete object is not a status') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if '/users/' not in messageId: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: c2s delete object has no nickname') | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     deleteNickname=getNicknameFromActor(messageId) | 
					
						
							| 
									
										
										
										
											2019-07-17 18:05:07 +00:00
										 |  |  |     if deleteNickname!=nickname: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print("DEBUG: you can't delete a post which wasn't created by you (nickname does not match)") | 
					
						
							|  |  |  |         return         | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |     deleteDomain,deletePort=getDomainFromActor(messageId) | 
					
						
							| 
									
										
										
										
											2019-07-17 18:05:07 +00:00
										 |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							|  |  |  |     if deleteDomain!=domain: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print("DEBUG: you can't delete a post which wasn't created by you (domain does not match)") | 
					
						
							|  |  |  |         return         | 
					
						
							| 
									
										
										
										
											2019-08-12 18:02:29 +00:00
										 |  |  |     removeModerationPostFromIndex(baseDir,messageId,debug) | 
					
						
							| 
									
										
										
										
											2019-07-17 17:16:48 +00:00
										 |  |  |     postFilename=locatePost(baseDir,deleteNickname,deleteDomain,messageId) | 
					
						
							|  |  |  |     if not postFilename: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: c2s delete post not found in inbox or outbox') | 
					
						
							|  |  |  |             print(messageId) | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  |     deletePost(baseDir,httpPrefix,deleteNickname,deleteDomain,postFilename,debug) | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: post deleted via c2s - '+postFilename) |